【私人笔记】深度学习框架keras踩坑记

2019-05-29 15:06:28 浏览数 (1)

Keras 是一个用 Python 编写的高级神经网络 API,它能够以 TensorFlow, CNTK, 或者 Theano 作为后端运行。Keras 的开发重点是支持快速的实验。能够以最小的时间把你的想法转换为实验结果,是做好研究的关键。本人是keras的忠实粉丝,可能是因为它实在是太简单易用了,不用多少代码就可以将自己的想法完全实现,但是在使用的过程中还是遇到了不少坑,本文做了一个归纳,供大家参考。

Keras 兼容的 Python 版本: Python 2.7-3.6。

详细教程请参阅Keras官方中文文档:http://keras-cn.readthedocs.io/en/latest/

1、Keras输出的loss,val这些值如何保存到文本中去:

Keras中的fit函数会返回一个History对象,它的History.history属性会把之前的那些值全保存在里面,如果有验证集的话,也包含了验证集的这些指标变化情况,具体写法:

代码语言:javascript复制
hist=model.fit(train_set_x,train_set_y,batch_size=256,shuffle=True,nb_epoch=nb_epoch,validation_split=0.1)
with open('log_sgd_big_32.txt','w') as f:
    f.write(str(hist.history))

我觉得保存之前的loss,val这些值还是比较重要的,在之后的调参过程中有时候还是需要之前loss的结果作为参考的,特别是你自己添加了一些自己的loss的情况下,但是这样的写法会使整个文本的取名比较乱,所以其实可以考虑使用Aetros的插件,Aetros网址,这是一个基于Keras的一个管理工具,可以可视化你的网络结构,中间卷积结果的可视化,以及保存你以往跑的所有结果,还是很方便的,就是有些不稳定,有时候会崩。。。

history对象包含两个重要属性:

epoch:训练的轮数

history:它是一个字典,包含val_loss,val_acc,loss,acc四个key。

2、关于训练集,验证集和测试集:

其实一开始我也没搞清楚这个问题,拿着测试集当验证集用,其实验证集是从训练集中抽取出来用于调参的,而测试集是和训练集无交集的,用于测试所选参数用于该模型的效果的,这个还是不要弄错了。。。在Keras中,验证集的划分只要在fit函数里设置validation_split的值就好了,这个对应了取训练集中百分之几的数据出来当做验证集。但由于shuffle是在validation _split之后执行的,所以如果一开始训练集没有shuffle的话,有可能使验证集全是负样本。测试集的使用只要在evaluate函数里设置就好了。

print model.evaluate(test_set_x,test_set_y ,batch_size=256)

这里注意evaluate和fit函数的默认batch_size都是32,自己记得修改。

总结:

验证集是在fit的时候通过validation_split参数自己从训练集中划分出来的;

测试集需要专门的使用evaluate去进行评价。

3、关于优化方法使用的问题之学习率调整

开始总会纠结哪个优化方法好用,但是最好的办法就是试,无数次尝试后不难发现,Sgd的这种学习率非自适应的优化方法,调整学习率和初始化的方法会使它的结果有很大不同,但是由于收敛确实不快,总感觉不是很方便,我觉得之前一直使用Sgd的原因一方面是因为优化方法不多,其次是用Sgd都能有这么好的结果,说明你网络该有多好啊。其他的Adam,Adade,RMSprop结果都差不多,Nadam因为是adam的动量添加的版本,在收敛效果上会更出色。所以如果对结果不满意的话,就把这些方法换着来一遍吧。

(1)方法一:通过LearningRateScheduler实现学习率调整

有很多初学者人会好奇怎么使sgd的学习率动态的变化,其实Keras里有个反馈函数叫LearningRateScheduler,具体使用如下:

代码语言:javascript复制
def step_decay(epoch):
    initial_lrate = 0.01
    drop = 0.5
    epochs_drop = 10.0
    lrate = initial_lrate * math.pow(drop,math.floor((1 epoch)/epochs_drop))
    return lrate
lrate = LearningRateScheduler(step_decay)
sgd = SGD(lr=0.0, momentum=0.9, decay=0.0, nesterov=False)
model.fit(train_set_x, train_set_y, validation_split=0.1, nb_epoch=200, batch_size=256, callbacks=[lrate])

上面代码是使学习率指数下降,具体如下图:

(2)方式二:最直接的调整学习率方式

当然也可以直接在sgd声明函数中修改参数来直接修改学习率,学习率变化如下图:

代码语言:javascript复制
sgd = SGD(lr=learning_rate, decay=learning_rate/nb_epoch, momentum=0.9, nesterov=True)
代码语言:javascript复制

具体可以参考这篇文章Using Learning Rate Schedules for Deep Learning Models in Python with Keras

除此之外,还有一种学利率调整方式,即

(3)方法三:通过ReduceLROnPlateau调整学习率

代码语言:javascript复制
keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=10, verbose=0, mode='auto', epsilon=0.0001, cooldown=0, min_lr=0)

当评价指标不在提升时,减少学习率。当学习停滞时,减少2倍或10倍的学习率常常能获得较好的效果。该回调函数检测指标的情况,如果在patience个epoch中看不到模型性能提升,则减少学习率

参数

monitor:被监测的量

factor:每次减少学习率的因子,学习率将以lr = lr*factor的形式被减少

patience:当patience个epoch过去而模型性能不提升时,学习率减少的动作会被触发

mode:‘auto’,‘min’,‘max’之一,在min模式下,如果检测值触发学习率减少。在max模式下,当检测值不再上升则触发学习率减少。

epsilon:阈值,用来确定是否进入检测值的“平原区”

cooldown:学习率减少后,会经过cooldown个epoch才重新进行正常操作

min_lr:学习率的下限

代码示例如下:

代码语言:javascript复制
from keras.callbacks import ReduceLROnPlateau

reduce_lr = ReduceLROnPlateau(monitor='val_loss', patience=10, mode='auto')

model.fit(train_x, train_y, batch_size=32, epochs=5, validation_split=0.1, callbacks=[reduce_lr])
4、如何用 Keras 处理超过内存的数据集?

你可以使用 model.train_on_batch(x,y) 和 model.test_on_batch(x,y) 进行批量训练与测试。请参阅 模型文档。

或者,你可以编写一个生成批处理训练数据的生成器,然后使用

model.fit_generator(data_generator,steps_per_epoch,epochs) 方法。

5、Batchnormalization层的放置问题:

BN层是真的吊,简直神器,除了会使网络搭建的时间和每个epoch的时间延长一点之外,但是关于这个问题我看到了无数的说法,对于卷积和池化层的放法,又说放中间的,也有说池化层后面的,对于dropout层,有说放在它后面的,也有说放在它前面的,对于这个问题我的说法还是试!虽然麻烦。。。但是DL本来不就是一个偏工程性的学科吗。。。还有一点是需要注意的,就是BN层的参数问题,我一开始也没有注意到,仔细看BN层的参数:

代码语言:javascript复制
keras.layers.normalization.BatchNormalization(epsilon=1e-06, mode=0, axis=-1, momentum=0.9, weights=None, beta_init='zero', gamma_init='one')
代码语言:javascript复制
  • mode:整数,指定规范化的模式,取0或1 0:按特征规范化,输入的各个特征图将独立被规范化。规范化的轴由参数axis指定。注意,如果输入是形如(samples,channels,rows,cols)的4D图像张量,则应设置规范化的轴为1,即沿着通道轴规范化。输入格式是‘tf’同理。 1:按样本规范化,该模式默认输入为2D

我们大都使用的都是mode=0也就是按特征规范化,对于放置在卷积和池化之间或之后的4D张量,需要设置axis=1,而Dense层之后的BN层则直接使用默认值就好了。

6、在验证集的误差不再下降时,如何中断训练?

你可以使用 EarlyStopping 回调:

代码语言:javascript复制
from keras.callbacks import EarlyStopping
early_stopping = EarlyStopping(monitor='val_loss', patience=2)
model.fit(x, y, validation_split=0.2, callbacks=[early_stopping])

总结:关于callbacks参数的妙用

(1)查询每隔epoch之后的loss和acc

(2)通过LearningRateScheduler实现衰减学习率或自定义衰减学习率

(3)通过EarlyStopping实现中断训练

(4)我们还可以自己定义回调函数,所为回调函数其实就是在训练完每一个epoch之后我们希望实现的操作。

7.如何「冻结」网络层?

「冻结」一个层意味着将其排除在训练之外,即其权重将永远不会更新。这在微调模型或使用固定的词向量进行文本输入中很有用。有两种方式实现:

方式一:在构造层的时候传递一个bool类型trainable参数,如下:

代码语言:javascript复制
frozen_layer = Dense(32, trainable=False)

您可以将 trainable 参数(布尔值)传递给一个层的构造器,以将该层设置为不可训练的:

方式二:通过层对象的trainable属性去设置,如下:

代码语言:javascript复制
x = Input(shape=(32,))
layer = Dense(32)  #构造一个层
layer.trainable = False  #设置层的trainable属性
y = layer(x)

注意:可以在实例化之后将网络层的 trainable 属性设置为 True 或 False。为了使之生效,在修改 trainable 属性之后,需要在模型上调用 compile()。及重新编译模型。

8.如何从 Sequential 模型中移除一个层?

你可以通过调用模型的 .pop() 来删除 Sequential 模型中最后添加的层:

代码语言:javascript复制
model = Sequential()
model.add(Dense(32, activation='relu', input_dim=784))
model.add(Dense(32, activation='relu'))

print(len(model.layers))  # "2"

model.pop()
print(len(model.layers))  # "1"

总结

这次暂时先写这么多,这里面有些问题是自己遇见过,有些问题是收录自官方文档,自己觉得很有可能会采坑的地方,后面如果在再使用Keras的过程中遇见什么困难,会再进行补充。

0 人点赞