《机器学习实战——基于Scikit-Learn和TensorFlow》
这是一本非常好的机器学习和深度学习入门书,既有基本理论讲解,也有实战代码示例。
我将认真阅读此书,并为每一章内容做一个知识笔记。
我会摘录一些原书中的关键语句和代码,若有错误请为我指出。
第十一章 训练深度神经网络
1 梯度消失/爆炸问题
梯度下降在更低层网络连接权值更新方面基本没有改变,而且训练不会收敛到好的结果。 梯度消失问题
另一种现象是梯度越来越大,导致很多层的权值疯狂增大,使得算法发散。 梯度爆炸问题
受制于不稳定的梯度,不同层可能以完全不同的速度学习。
1.1 Xavier初始化和He初始化
提出需要保持每一层的输入和输出的方差一致,并且需要在反向流动过某一层时,前后的方差也要一致。
该方法的折中方案公式为Xavier初始化。
ReLU激活函数的初始化方法有时称为He初始化。
1.2 非饱和激活函数
结果表明深度神经网络中表现好的是ReLU激活函数。
因为它不稀释正值,且计算速度很快。
但是会出现dyingReLU问题:训练中一些神经元实际上已经死了,只输出0。
learkyReLU可以解决这个问题,LeakyReLU(z)=max(alpha*z,z)
2015年提出是ELU表现最好。
1.3 批量归一化
在每一层激活函数之前在模型里加一个操作,简单零中心化和归一化输入,之后再通过每层的两个新参数缩放和移动结果。
1.4 梯度裁剪
反向传播的过程中,简单地剪裁梯度。
一般仍然倾向于批量归一化。
2 重用预训练图层
从头开始训练庞大的DNN比较麻烦,可以去找一个能处理相似问题的已有的神经网络,然后重用低层网络。 迁移学习
一般来说,迁移学习只适用于新旧任务的输入具有相似的低层的特征的情况。
2.1 重用TensorFlow模型
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
scope="hidden[123]") # regular expression
restore_saver = tf.train.Saver(reuse_vars) # to restore layers 1-3
init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
init.run()
restore_saver.restore(sess, "./my_model_final.ckpt")
for epoch in range(n_epochs): # not shown in the book
for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size): # not shown
sess.run(training_op, feed_dict={X: X_batch, y: y_batch}) # not shown
accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid}) # not shown
print(epoch, "Validation accuracy:", accuracy_val) # not shown
save_path = saver.save(sess, "./my_new_model_final.ckpt")
2.2 重用其他框架的模型
其他框架,如theano训练出来的话,需要手动加载所有权重并将它赋给适当的参数。
2.3 冻结低层
低层权重不变,高层权重比较容易训练。
给优化器列出要训练的变量列表,除去低层的变量。
with tf.name_scope("train"): # not shown in the book
optimizer = tf.train.GradientDescentOptimizer(learning_rate) # not shown
train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
scope="hidden[34]|outputs")
training_op = optimizer.minimize(loss, var_list=train_vars)
2.4 缓存冻结层
冻结层不会变化,所以有可能将每一个训练实例的最高冻结层的输出缓存起来。
2.5 调整、丢弃或替换高层
原始模型的输出层经常会被替换,因为对于新的任务基本没有什么用。
同样,原始模型的高隐藏层没有低层用处多。
首先,尝试冻结所有的复制层,然后训练你的模型观察效果。
接着尝试解冻一到两个顶部的隐藏层,用反向传播进行进行调整来观察是否有改善。
训练数据越多,越能解冻更多的层。
如果一直不能获得好的效果,而且训练数据很少,可尝试丢弃最高的一层或多层,然后重新冻结剩下的隐藏层。
一直迭代直到找到正确的重用层数。
如果有很多的训练数据,可以尝试替换顶部的隐藏层而不是丢弃它们,甚至可以添加一些隐藏层。
2.6 模型动物园
=> 自己已有的模型目录
=> 模型动物园
TensorFlow => https://github.com/tensorflow/models
caffe => https://goo.gl/XI02X3
2.7 无监督的预训练
逐层训练,利用无监督特性检测算法比如首先玻尔兹曼机或者自动编码器。
每一层都是基于提前训练好的图层(除去被冻结的训练层)的输出进行训练。
一旦所有层都用这个方式训练过之后,就可以用监督学习的方式(即反向传播)来位微调网络。
2.8 辅助任务中的预训练
在辅助任务中训练第一个神经网络,可以轻松获得或者生成标记过的训练数据,然后重用该网络的低层来实现你的实际任务。
3 快速优化器
提高训练速度的方法:
- 连接权重上应用一个良好的初始化策略
- 良好的激活函数
- 批量归一化
- 重用部分预处理网络
3.1 Momentum优化
梯度为加速度。
Momentum会以越来越快的速度滑向谷底。
标准的动量值 为0.9。
3.2 Nesterov梯度加速
用未来的加速度更新现在的速度。
3.3 AdaGrad
衰减了学习率。
适应性学习。
但是在训练神经网络时却经常很早就停滞了,在达到全局最优前算法就停止了。
尽管TensorFlow有此优化器,也不要使用它。
3.4 RMSProp
仅累计最近迭代中的梯度解决了这个问题。
3.5 Adam优化
3.6 学习速率调度
以高学习速率开始,然后一旦它停止快速过程就降低速率。
监督学习速率。 学习计划
- 预定分段常数学习速率
- 性能调度
- 指数调度
- 功能调度
4 通过正则化避免过度拟合
4.1 提前停止
当验证集的性能开始下降时停止训练。
定期对验证集进行模型评估,表现好的保存起来。
4.2 l1和l2正则化
加正则化加入到损失函数中。
4.3 dropout
最受欢迎的正则化技术。
超参数p为丢弃率,通常设置为50%。
dropout收敛变慢,但是如果微调合适,通常会收敛到一个更好的模型。
4.4 最大范数正则化
对每一个神经元,包含一个传入连接权值满足
降低r会增加正则化数目,同时帮助减少过度拟合。
缓解梯度消失/爆炸问题。
4.5 数据扩充
图像=>旋转、平移、大小。
5 实用指南
略。