深度学习的基础知识与问题汇总

2023-11-02

20200813 -

引言

这里记录一下深度学习使用过程中的一些细节的地方。

  1. 多分类时
  2. 预测过程
  3. 损失函数为Nan或者Inf
  4. 如何在keras中计算精确率和召回率
  5. 如何获取中间某一层的输出
  6. 如何获取网络结构(从别人的存储的h5模型文件中)
  7. 关于softmax层的再理解
  8. 关闭tensorflow的警告
  9. 保存神经网络模型(针对keras库)
  10. 多输入时候的验证数据集
  11. VAE的相关问题

最近发现了一个比较不错的网站[2],记录了很多关于kera的基础内容,有时间可以好好看看。

问题

1. 多分类时

对于二分类时,最后一层只需要一个神经元,也就是Dense(1)即可;而多分类问题时,需要Dense(classes)。
同时,还需要注意,使用的损失函数,二分类是binary_crossentropy,而分类时需要采用categorical_crossentropy。另外需要注意的问题是,多分类时,需要将输入的标签数据进行one-hot编码。

from keras.utils import to_categorical
input_data = to_categorical(input_data)

to_categorical的使用过程中,需要注意,他的输入已经是编码好的,而是必须是连续的,我试了一下,0-9删除了5之后就会提示不够。同时一般来说,还要进行的工作就是当类别是字符串类别的时候,就需要首先将这部分进行编码。

from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
encoded_Y = encoder.fit_transform(Y)

在预测时,输出的是各个类别的概率,需要利用方法将概率转化回来。

import numpy as np
output_class = np.argmax(output_data, axis = 1)

2. 预测过程

预测时一般采用predict或者predict_classes方法[1]。两种方法返回的分别时概率和类别。这里需要注意的是,在构造模型的时候,经常使用两种形式:Sequence形式和Model,sequence形式是可以使用predict_classes,也就是支持前面提到的两种说法。但是Model只支持predict。

3. 损失函数为Nan或者Inf

在训练过程中打印实时的输出,包括损失函数和准确率,但是最开始的时候实验,就发现准确率一点也不变化,而损失函数始终输出是Nan或者Inf;有时候,如果训练比较慢的话(单步训练过程),能够看到损失函数的数值是一个非常大的整数。
一开始的时候,我也很纳闷,到底是为什么呢?为什么准确率能够一点也不变化,主要通过以下几个内容进行了尝试:
1)调整了最终的损失函数,(本身是针对的二分类问题),我把它改成了多分类问题,使用softmax损失函数,发现并没有任何意义,依然是这么个变化趋势。
2)调整了优化器,一开始是直接复制的代码,使用了SGD,adam,RMSprop这些。最后发现使用rmsprop的时候,准确率最高,但是依然是没有变化。

到此未知我就不知道是什么回事了;当然,事后我能明白这到底是怎么回事,因为代码中含有归一化的过程,但是实际情况是,最终还是没有归一化。这样一来就能解释了,为什么准确率不变化,因为损失函数太大了,而你的优化器的那点变化率,根本不足以让他能够支撑后续的变化。
在谷歌了这部分内容之后,在keras的github上找到了这部分issue[3],而其中有一个人的回答就正面回答了我的问题。
在这里插入图片描述
意思就是说,由于没有归一化,导致损失函数的数值过大,才会出现这种情况。在问答[4]中,提出了几种其他的解决方案:
1)增加正则项
2)减小网络的大小
3)增加batch size,例如从32到128。
目前这些方案没有尝试,因为在我的数据集上, 直接进行归一化就好使了。

4. 如何在keras中计算精确率和召回率

默认情况下,一般都是在训练的时候指定。metrics = ["accuracy"],但是平时还是需要使用精确率或者召回率,搜索了之后,在问答[5]中指出,keras在2.3.0之后,就在自己的库中实现了这部分的支持。

model.compile(optimizer="sgd",
              loss="binary_crossentropy",
              metrics=[keras.metrics.Precision(), keras.metrics.Recall()])

但是这种方式就缺失了准确率,通过查询API手册,直接使用keras.metrics.Accuracy结果不太对,可以通过keras.metrics.BinaryAccuracy(),来实现。

5. 如何获取中间某一层的输出

有些场景下,我需要知道某个中间层的输出,例如残差网络那种形式,当然那种还不是很一样。这里的需求就是,假设一个比较简单的全连接神经网络,我需要提取中间看某个层的输出。在谷歌上搜索了一下,其中回答[6]的解释不错,其中有一个还是来自官方的解释。

from keras.models import Model
model = ...  # include here your original model
layer_name = 'my_layer'
intermediate_layer_model = Model(inputs=model.input, \
 			outputs=model.get_layer(layer_name).output)
intermediate_output = intermediate_layer_model.\
						predict(data)

注意,在使用这种方法的时候,定义原始模型的时候,最好是加载一下名字;或者你自己打印每层名字之后选择。尽量还是自己定义,这样能够代码复用的稳定性。
在问答[6]中还有另外一种方法,就是使用K.function的方法,但是我看到的方法跟原文这个方法还不一样,直接搜索代码,找到了在github[8]上的具体代码。

from keras import backend as K
def get_activations(model, layer, X_batch):
    get_activations = K.function([model.layers[0].input, K.learning_phase()], model.layers[layer].output)
    activations = get_activations([X_batch,0])
    return activations

据其所说,问答[6]中的方法不行好像是版本的问题。

从他们的说法中来看,而且加上我之前的时候对tensorflow的理解,本质上tensorflow是可以直接获取到各个层的输出的,但是keras毕竟封装得更精简,所以这部分信息就不容易得到。

6. 如何获取网络结构(从别人的存储的h5模型文件中)

以往在训练自己的模型的时候,在构建好模型之后,会使用model.summary()来查看一些网络信息,有时候还会采用图形化的方法将模型绘制出来。但是如果我是使用别人的模型的时候应该怎么办,也就是给了一个h5文件,我想知道他的这个网络模型具体是什么样的。
当然首先还是可以利用summary()的函数来得到网络结构,代码如下:

from keras.models import load_model
model = load_model('MNIST.h5')
model.summary()

但是这种方式仅仅显示了网络结构,我想知道每个层的激活函数是什么样子的应该怎么办?在问答[7]中介绍了两种方法。

from tensorflow.keras.models import load_model
model = load_model('model.h5')

for l in model.layers:
  try:
    print(l.activation)
  except: # some layers don't have any activation
    pass

在原文中还有另外一种方法,就是导入了另外一个库,这里不展开介绍。

7. softmax的再理解

可能时间比较久了,虽然我还记得softmax仅仅是一个概率归一化的方式,但是还是把这个东西给记错了,先来看一段代码,平时进行多分类的时候,一般都是采用这种方式。

model = Sequential()
model.add(Dense(512,activation='relu',input_shape=(784,)))
model.add(Dense(256,activation='relu'))
model.add(Dense(10,activation='softmax'))
model.summary()

输出如下:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_6 (Dense)              (None, 512)               401920    
_________________________________________________________________
dense_7 (Dense)              (None, 256)               131328    
_________________________________________________________________
dense_8 (Dense)              (None, 10)                2570      
=================================================================

最后的维度一般来说等于多分类的类别个数。可能就是因为这样用的比较多了,就觉得softmax自己能成为一层了。
而实际上,softmax仅仅是一个激活函数而已,并不是一层。

model = Sequential()
model.add(Dense(512,activation='relu',input_shape=(784,)))
model.add(Dense(256,activation='relu'))
model.add(Dense(10,activation='relu'))
model.add(layers.Activation(activations.softmax))
model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 512)               401920    
_________________________________________________________________
dense_1 (Dense)              (None, 256)               131328    
_________________________________________________________________
dense_2 (Dense)              (None, 10)                2570      
_________________________________________________________________
activation (Activation)      (None, 10)                0         
=================================================================

从这部分可以看出,softmax仅仅试讲前面的输出进行相关的归一化,最终输出统一的概率。

8. 关闭tensorflow的警告

在利用keras的方法,在前面5中,获取中间层的输出方法时,会出现tensorflow的警告,主要是需要查看这部分的打印信息,但是这个警告太多了,导致自己的信息看不到,所以需要关闭tensorflow的警告。

import logging
tf.get_logger().setLevel(logging.ERROR)

tf2.0的版本可以使用上述办法[9]。

9. 保存神经网络模型(keras库)

对于比较简单的需求,我就是要保存并在未来加载数据,那么使用两个api就好(默认参数),当然,如果有具体的需求,比如说仅仅保存参数什么的,可以参考官方文档[10]

#保存模型
model.save("some_file_name.h5")
del model


from keras.models import load_model
model = load_model("some_file_name.h5")

当然保存为其他格式也是可以的,可以具体查看官方文档来根据需求调整。

10. 多输入时的验证数据集

在训练过程中,如果要添加验证数据集,可以通过参数validation_split来指定多少来划分,这样的话,即使是多输入,通过这个方式就不用自己来控制数据集的划分。这里这里多说一句,指定多输入的时候可以多种方式,具体见官方文档:[11],我经常使用的就是两种,直接按照数组的方式传递进去,一定要按照模型定义时输入的顺序,另一种是传递字典进去,不同的输入的键值分别对应模型定义时输入的名字。

如果想自己定义验证数据集,在单输入的时候,调用sklearn的train_test_split分割数据集,然后传递进去就行。但是传递的时候有限制,必须是元组。这个就是我之前的时候调用这个东西发现并不好使的原因,我直接使用了数组的形式,假设是单输入,传递的数据应该是这个形式:

(val_X, val_y)

而多输入的时候:

([val_X1, val_X2], [val_y1, val_y2])

11. VAE的权值调整

how to weight KLD loss vs reconstruction loss in variational auto-encoder

12. 准确率与损失函数的关系

Good accuracy despite high loss value

Why Using Mean Squared Error(MSE) Cost Function for Binary Classification is a Bad Idea?

参考文章

[1]Keras中predict()方法和predict_classes()方法的区别
[2]keras系列︱Sequential与Model模型、keras基本结构功能
[3]NAN loss for regression while training
[4]NaN loss when training regression network
[5]How to calculate precision and recall in Keras
[6]Keras, How to get the output of each layer?
[7]How to Access Activation Functions from Saved Model .h5 without importing tensorflow?
[8]How can I get hidden layer representation of the given data? #41
[9]Disable Tensorflow debugging information
[10]Model saving & serialization APIs
[11]Model training APIs

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深度学习的基础知识与问题汇总 的相关文章

随机推荐