- 本篇为外文翻译,英语原文地址:https://stanford.edu/~shervine/teaching/cs-230/cheatsheet-deep-learning-tips-and-tricks
- 不是逐句直译,根据个人理解意译。理解可能有误,如果发现错误希望读者能通过邮件告知,谢谢。
数据处理
- 数据增强(Data augmentation) -- 深度学习模型通常需要大量的数据。数据增强就是通过各种手段(如下图所示),基于我们已有的数据来获取更多训练数据。
原始图片 | 翻转 | 旋转 | 随机剪裁 |
---|---|---|---|
image.png
|
image.png
|
image.png
|
image.png
|
- 没有任何修改的图片 | -左右或者上下翻转,但是要保持图片的语义 | - 旋转一丢丢角度 - 模拟不正确的水平校准 |
- 随机聚焦于图片的某一部分 |
颜色变换 | 添加噪点 | 信息丢失 | 调对比度 |
---|---|---|---|
image.png
|
image.png
|
image.png
|
image.png
|
- 稍微改变一点 RGB 数值 - 模拟不同曝光产生的噪音 |
- 添加一些噪点 - 提高模型对输入图片质量地容忍度 |
- 无视(遮住)输入图片地一部分 - 模拟输入图片地部分信息丢失或模糊 |
- 模拟不同光源 - 模拟一天内不同时间段拍出来的照片 |
注意:通常我们不会先把数据扩充好保存下来再训练,一般是在训练过程中实时生成扩充数据
- 批归一(Batch normalization) -- 对一个小批量(batch)做归一化(减均值除方差),然后用两个参数
来控制归一化的程度。下列式子中的
就是对应每个小批量(batch)的均值和方差:
批归一通常跟在全连接/卷积层之后,激活层之前。目的是可以使用更高的学习率,因为它可以让训练过程变得更加稳定,然后也能降低不同的权重初始化方法对模型的影响。
训练一个网络模型
一些定义
Epoch -- 指一次迭代,就是过一整遍训练数据。
小批量梯度下降 -- 一般来说,每次过一整遍训练数据,取平均梯度,再更新梯度的方法不可行。一个原因是(训练数据很多的情况下)计算量太大,另一个原因是容易过拟合。所以我们一般用小批量,批量大小是个超参数。
损失函数 -- 用来衡量模型当前的准确性的计算函数。
交叉熵 -- 大部分分类模型所采用的损失函数。对于二分类问题,交叉熵如下:
找到最好的权重
- 反向传播 -- 通过对损失函数的求导,可以一层一层的算出神经网络中每一层的参数的梯度,然后用这些梯度来更新这些权重,这个过程就叫做反向传播。
梯度下降的公式如下:
- 更新权重 -- 权重的更新步骤:
- Step 1:取一个批量的数据,前向传播,得到损失函数的结果。
- Step 2:反向传播算出各个权重对应的梯度。
- Step 3:工具对应的梯度,使用梯度下降更新每个权重。
参数(权重)调整
权重初始化
- Xavier 初始化 -- Xavier 初始化的目的是让每一层激活函数(tanh)输出值的方差都差不多。稳定的方差可以有效防止梯度爆炸和梯度消失。
推理过程:
推理过程
可以看到 Xavier 初始化推理的前提是我们用了 激活函数。对于使用
激活函数的网络层,现在通常的做法是用何凯明提出的这个初始化方法(现在好像都叫 He initialization):
具体推导过程类似,这里不展开了。
- 迁移学习 -- 训练一个神经网络通常需要大量的数据和时间。所以人们就想着先用大量的通用数据和很长的时间去预训练一个通用模型,然后想办法把它用在一些特定的下游任务上,一般做法是用特定任务对应的专用数据来微调预训练好的模型。根据某一特定任务的数据的多少,我们有以下三种不同的做法:
数据量大小 | 图示 | 解释 |
---|---|---|
数据很少 | 只更新输出层
|
冻结所有网络层,只微调最后的输出层 |
不多不少 | 更新小部分隐藏层
|
冻结大部分网络层,微调最后几层加输出层 |
数据贼多 | 更新所有参数
|
所有参数都进行微调 |
如何更好地收敛
学习率 -- 决定权重更新的速度。可以是固定的,也可以是自适应调整的。比较流行的做法是让它可以自适应调整。
自适应学习率 -- 让学习率能在训练期间调整不仅能缩短训练时间还能得到更好的最终模型。虽然目前大家基本都用 Adam,但是另外几个其实也挺有用的。下面的表格简述了典型的三种方法:
方法 | 解释 | 更新权重 |
更新偏置 |
---|---|---|---|
Momentum | - 抑制梯度的不稳定跳动 - 是对 SGD 的改进 - 有2个超参数要调 |
||
RMSprop | - 全称 Root Mean Square propagation - 目的和 Moemtum 类似,都是为了稳定小批量可能会导致的梯度不稳定 |
||
Adam | - 前两种方法的结合 - 最常用的方法 - 4个超参数可调 |
注意:还有一些别的衍生方法,可以自行搜索优化器相关内容
正则化
- 丢弃法 -- 丢弃法是用来防止过拟合的常用手段。做法是设置一个概率
, 训练的时候每个神经元有
的概率会被丢弃。这么做能强制要求模型不去过度依赖某一两个神经元(表现在训练数据上就是不过度依赖于某一两种特定的特征)。
注意:大多数训练框架(如 pytorch)设置的不是丢弃神经元的概率而是保留的概率,也就是说实际我们设置的是
权重正则化 -- 正则化(regularization)也是用来防止过拟合常用的方法。常用的有
和
正则化,做法是分别在损失函数后面加
和
提前停止(early stopping) -- 当验证集上的错误率不降反升,但是训练集上的 loss 还没收敛的时候,可以提前停止训练。
两个实践技巧
在小批量上过拟合 -- 需要 debug 一个模型的时候,我们会先看看这个模型能不能在一个很小的数据集上过拟合。就是让模型一直循环在同一个小批量上做训练,然后看它是否会过拟合。如果不能过拟合,说明这个模型设计有问题,这时候就需要换个结构试试看,而不是在原结构上继续改进。
梯度检查 -- 梯度检查是用在实现一个神经网络的反向传播的时候。对比解析解和数值解能很快且很直观的验证目前的实现是否正确。
类型 | 数值解 | 解析解 |
---|---|---|
公式 | ||
注解 | - 计算昂贵;每个维度的 loss 需要算两遍 - 用来验证解析解的正确性 - h 太大会算不准,太小会很难算,所以要有取舍 |
- 精确解 - 直接计算 - 最终实现的方法 |