20200813 -
引言
这里记录一下深度学习使用过程中的一些细节的地方。
- 多分类时
- 预测过程
- 损失函数为Nan或者Inf
- 如何在keras中计算精确率和召回率
- 如何获取中间某一层的输出
- 如何获取网络结构(从别人的存储的h5模型文件中)
- 关于softmax层的再理解
- 关闭tensorflow的警告
- 保存神经网络模型(针对keras库)
- 多输入时候的验证数据集
- 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],而其中有一个人的回答就正面回答了我的问题。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201216090455386.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE2OTg4MDA=,size_16,color_FFFFFF,t_70)
意思就是说,由于没有归一化,导致损失函数的数值过大,才会出现这种情况。在问答[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