分享

自学构建深度学习项目:需要思考的问题及如何实现2

本帖最后由 yuwenge 于 2018-6-5 20:25 编辑
问题导读

1.如何可视化深度学习模型和性能指标?
2.深度学习的问题解决包含哪些步骤?
3.如何分析误差?
4.如何提升深度学习模型性能及网络调参?











上一篇:
自学构建深度学习项目:需要思考的问题及如何实现1
http://www.aboutyun.com/forum.php?mod=viewthread&tid=24611


04 可视化深度神经网络模型和指标


在为深度神经网络排除故障方面,人们总是太快、太早地下结论了。在了解如何排除故障前,我们要先考虑要寻找什么,再花费数小时时间追踪故障。这部分我们将讨论如何可视化深度学习模型和性能指标。


1. TensorBoard

在每一步追踪每个动作、检查结果非常重要。在预置包如 TensorBoard 的帮助下,可视化模型和性能指标变得简单,且奖励几乎是同时的。

2. 数据可视化(输入、输出)

验证模型的输入和输出。在向模型馈送数据之前,先保存一些训练和验证样本用于视觉验证。取消数据预处理。将像素值重新调整回 [0, 255]。检查多个批次,以确定我们没有重复相同批次的数据。左下图像是一些训练样本,右下方验证样本。

p.png

有时,验证输入数据的直方图很棒。完美情况下,它应该是以 0 为中心的,区间在 -1 和 1 之间。如果特征在不同的尺度中,那么梯度要么下降要么爆炸(根据学习率而定)。

p.png

定期保存对应模型的输出,用于验证和误差分析。例如,验证输出中的颜色稍浅。

p.png

3. 指标(损失 & 准确率)

除了定期记录损失和准确率之外,我们还可以记录和绘制它们,以分析其长期趋势。下图是 TensorBoard 上展示的准确率和交叉熵损失。

p.png

绘制损失图能够帮助我们调整学习率。损失的任意长期上升表明学习率太高了。如果学习率较低,则学习的速度变慢。

p.png

这里是另一个学习率太高的真实样本。我们能看到损失函数突然上升(可能由梯度突然上升引起)。

p.png

我们使用准确率图调整正则化因子。如果验证和训练准确率之间存在很大差距,则该模型出现过拟合。为了缓解过拟合,我们需要提高正则化因子。

p.png

4. 小结

权重 & 偏置:我们紧密监控权重和偏置。下图是层 1 在不同训练迭代中的权重和偏置。出现大型(正/负)权重是不正常的。正态分布的权重表明训练过程很顺利(但是也不一定)。

p.png


激活:为了梯度下降以实现最佳性能,激活函数之前的节点输出应该呈正态分布。如果不是,那么我们可能向卷积层应用批归一化,或者向 RNN 层应用层归一化。我们还监控激活函数之后无效节点(0 激活)的数量。

p.png

梯度:我们监控每一层的梯度,以确定一个最严肃的深度学习问题:梯度消失或爆炸。如果梯度从最右层向最左层快速下降,那么就出现了梯度消失问题。

p.png


这或许不是很常见:我们可视化了 CNN 滤波器。它识别出模型提取的特征的类型。如下图所示,前两个卷积层在检测边界和颜色。

p.png

对于 CNN,我们可以看到特征图在学习什么。下图捕捉了特定图中具备最高激活函数的 9 张图(右侧)。它还使用解卷积网络从特征图中重建空间图像(左图)。

p.png

▲Visualizing and Understanding Convolutional Networks, Matthew D Zeiler et al.

这种图像重建很少进行。但是在生成模型中,我们经常改变一个潜在因子、保持其他不变。它验证该模型是否在智能地学习。

p.png

▲Dynamic Routing Between Capsules, Sara Sabour, Nicholas Frosst, Geoffrey E Hinton


05 调试深度学习网络


1. 深度学习的问题解决步骤

在前期开发中,我们会同时遇到多个问题。就像前面提到的,深度学习训练由数百万次迭代组成。找到 bug 非常难,且容易崩溃。从简单开始,渐渐做一些改变。正则化这样的模型优化可以在代码 degug 后做。以功能优先的方式检查模型:

  • 把正则化因子设置为 0;
  • 不要其他正则化(包括 dropouts);
  • 使用默认设置的 Adam 优化器;
  • 使用 ReLU;
  • 不要数据增强;
  • 更少的深度网络层;
  • 扩大输入数据,但不要非必要预处理;
  • 不要在长时间训练迭代或者大 batch size 上浪费时间。


用小量的训练数据使模型过拟合是 debug 深度学习的最好方式。如果在数千次迭代内,损失值不下降,进一步 debgug 代码。准确率超越瞎猜的概念,你就获得了第一个里程碑。然后对模型做后续的修改:增加网络层和自定义;开始用完整训练数据做训练;通过监控训练和验证数据集之间的准确率差别,来增加正则化控制过拟合。

如果卡住了,去掉所有东西,从更小的问题开始上手。

2. 初始化超参数

许多超参数与模型优化更为相关。关掉超参数或者使用缺省值。使用 Adam 优化器,它速度快、高效且缺省学习率也很好。前期的问题主要来自于 bug,而不是模型设计和精调问题。在做微调之前,先过一遍下面的检查列表。这些问题更常见,也容易检查。如果损失值还没下降,就调整学习率。如果损失值降的太慢,学习率增加 10。如果损失值上升或者梯度爆炸,学习率降低 10。重复这个过程,直到损失值逐渐下降。典型的学习率在 1 到 1e-7 之间。

p.png


3. 检查列表

数据:

  • 可视化并检查输入数据(在数据预处理之后,馈送到模型之前);
  • 检查输入标签的准确率(在数据扰动之后);
  • 不要一遍又一遍的馈送同一 batch 的数据;
  • 适当的缩放输入数据(一般可缩放到区间 (-1, 1) 之间,且具有零均值);
  • 检查输出的范围(如,在区间 (-1, 1) 之间);
  • 总是使用训练集的平均值/方差来重新调节验证/测试集;
  • 模型所有的输入数据有同样的维度;
  • 获取数据集的整体质量(是否有太多异常值或者坏样本)。


模型:

  • 模型参数准确的初始化,权重不要全部设定为 0;
  • 对激活或者梯度消失/爆炸的网络层做 debug(从最右边到最左边);
  • 对权重大部分是 0 或者权重太大的网络层做 debug;
  • 检查并测试损失函数;
  • 对预训练模型,输入数据范围要匹配模型中使用的范围;
  • 推理和测试中的 Dropout 应该总是关掉。


4. 权重初始化

把权重全部初始化到 0 是最常见的错误,深度网络也学不到任何东西。权重要按照高斯分布做初始化:

p.png

5. 缩放与归一化

人们对缩放与归一化都有很好地理解,但这仍旧是最被轻视的问题之一。如果输入特征和节点输出都被归一化,就能更容易地训练模型。如果做的不准确,损失值就不会随着学习率降低。我们应该监控输入特征和每层节点输出的的直方图。要适当的缩放输入。而对节点的输出,完美的形状是零均值,且值不太大(正或负)。如果不是且遇到该层有梯度问题,则在卷积层做批归一化,在 RNN 单元上做层归一化。

p.png

6. 损失函数

检查和测试损失函数的准确性。模型的损失值一定要比随机猜测的值低。例如,在 10 类别分类问题中,随机猜测的的交叉熵损失是-ln(1/10)。

7. 分析误差

检查表现不好(误差)的地方并加以改进,且对误差进行可视化。在我们的项目中,模型表现对结构高度纠缠的图像表现不好。例如,增加更多带有更小滤波器的卷积层来解开小特征。如果有必要就增强数据或者收集更多类似的样本来更好的训练模型。在一些情景下,你可能想要移除这些样本,限制在更聚焦的模型。

p.png

8. 正则化精调

关掉正则化(使得模型过拟合)直到做出合理的预测。

一旦模型代码可以工作了,接下来调整的参数是正则化因子。我们需要增加训练数据的体量,然后增加正则化来缩小训练和验证准确率之间的差别。不要做的太过分,因为我们想要稍微让模型过拟合。密切监测数据和正则化成本。长时间尺度下,正则化损失不应该控制数据损失。如果用大型正则化还不能缩小两个准确率间的差距,那先 degug 正则化代码或者方法。

类似于学习率,我们以对数比例改变测试值,例如开始时改变 1/10。注意,每个正则化因子都可能是完全不同的数量级,我们可以反复调整这些参数。

p.png

9. 多个损失函数

在第一次实现中,避免使用多个数据损失函数。每个损失函数的权重可能有不同的数量级,也需要一些精力去调整。如果我们只有一个损失函数,就可以只在意学习率了。

10. 固定变量

当我们使用预训练模型,我们可以固定特定层的模型参数,从而加速计算。一定要再次检查是否有变量固定的错误。

11. 单元测试

正如极少会被谈到的,我们应该对核心模块进行单元测试,以便于代码改变时实现依旧稳健。如果其参数用随机发生器(randomizer)做初始化,检查一个网络层的输出不太简单。另外,我们可以模仿输入数据、检查输出。对每个模块(层),我们可以检查:

  • 训练和推理输出的形状;
  • 可训练变量的数量(不是参数的数量)。


12. 维度误匹配

要一直跟踪 Tensor(矩阵)的形状,并将其归档到代码中。对形状是 [N, channel, W, H ] 的 Tensor,如果 W(宽)和 H(高)有同样的维度,二者交换代码不会出错。因此,我们应该用非对称形状做代码单元测试。例如,我们用 [4, 3]Tensor,而非 [4, 4] 做测试。


06 提升深度学习模型性能及网络调参

1. 提升模型容量
要想提升模型容量,我们可以向深度网络(DN)逐渐添加层和节点。更深的层会输出更复杂的模型。我们还可以降低滤波器大小。较小的滤波器(3×3 或 5×5)性能通常优于较大的滤波器。

p.png

调参过程更重实践而非理论。我们逐渐添加层和节点,可以与模型过拟合,因为我们可以用正则化方式再将其调低。重复该迭代过程直到准确率不再提升,不再值得训练、计算性能的降低。

但是,GPU 的内存是有限的。截止 2018 年初,高端显卡 NVIDIA GeForce GTX 1080 TI 的内存为 11GB。两个仿射层之间隐藏节点的最大数量受内存大小的限制。

p.png

对于非常深层的网络,梯度消失问题很严重。我们可以添加跳跃连接(类似 ResNet 中的残差连接)来缓解该问题。

2. 模型 & 数据集设计变化

以下是提升性能的检查列表:

  • 在验证数据集中分析误差(糟糕的预测结果);
  • 监控激活函数。在激活函数不以零为中心或非正态分布时,考虑批归一化或层归一化;
  • 监控无效节点的比例;
  • 使用梯度截断(尤其是 NLP 任务中)来控制梯度爆炸问题;
  • Shuffle 数据集(手动或通过程序);
  • 平衡数据集(每个类别具备相似数量的样本)。


我们应该在激活函数之前密切监控激活直方图。如果它们的规模差别很大,那么梯度下降将会无效。使用归一化。如果深度网络有大量无效节点,那么我们应该进一步追踪该问题。它可能是由 bug、权重初始化或梯度消失导致的。如果都不是,则试验一些高级 ReLU 函数,如 leaky ReLU。

3. 数据集收集 & 清洗

如果你想构建自己的数据集,那么最好的建议就是仔细研究如何收集样本。找最优质的资源,过滤掉与你问题无关的所有数据,分析误差。在我们的项目中,具备高度纠缠结构的图像性能非常糟糕。我们可以添加卷积层和小型滤波器来改变模型。但是模型已经很难训练了。我们可以添加更多纠缠样本做进一步训练,但是已经有了很多了……另一种方式:我们可以精细化项目范围,缩小样本范围。

4. 数据增强

收集有标签的数据是一件昂贵的工作。对于图片来说,我们可以使用数据增强方法如旋转、随机剪裁、移位等方式来对已有数据进行修改,生成更多的数据。颜色失真则包括色调、饱和度和曝光偏移。

p.png

5. 半监督学习

我们还可以使用无标注数据补充训练数据。使用模型分类数据。把具备高置信预测的样本添加到具备对应标签预测的训练数据集中。

6. 调整

学习率调整

我们先简单回顾一下如何调整学习率。在早期开发阶段,我们关闭任意非关键超参数或设置为 0,包括正则化。在具备 Adam 优化器的情况下,默认学习率通常性能就很好了。如果我们对自己的代码很有信心,但是损失并没有下降,则需要调整学习率。典型的学习率在 1 和 1e-7 之间。每次把学习率降低 10%,并在简短迭代中进行测试,密切监控损失。如果它持续上升,那么学习率太高了。如果它没有下降,则学习率太低。提高学习率,直到损失提前变得平缓。

p.png

下面是一个真实样本,展示了学习率太高的情况,这导致成本突然上涨:

p.png

在不经常用的实践中,人们监控 W ratio 的更新情况:

p.png

  • 如果 ratio > 1e-3,则考虑调低学习率;
  • 如果 ratio < 1e-3,则考虑提高学习率。


超参数调整

在模型设计稳定后,我们也可以进一步调整模型。最经常调整的超参数是:

  • mini-batch 尺寸;
  • 学习率;
  • 正则化因子;
  • 特定层的超参数(如 dropout)。


Mini-batch 尺寸

通常的批尺寸是 8、16、32 或 64。如果批尺寸太小,则梯度下降不会很顺畅,模型学习的速度慢,损失可能会振荡。如果批尺寸太大,则完成一次训练迭代(一轮更新)的时间太长,得到的返回结果较小。在我们的项目中,我们降低批尺寸,因为每次训练迭代时间太长。我们密切监控整个学习速度和损失。如果损失振荡剧烈,则我们会知道批尺寸降低的幅度太大了。批尺寸影响正则化因子等超参数。一旦我们确定好批尺寸,我们通常就锁定了值。

学习率 & 正则化因子

我们可以使用上述方法进一步调整学习率和正则化因子。我们监控损失,来控制学习率和验证与训练准确率之间的差距,从而调整正则化因子。我们没有把学习率降低 10%,而是降低 3%(精细调整中或许更小)。

p.png

调参不是线性过程。超参数是有关联的,我们将反复调整超参数。学习率和正则化因子高度相关,有时需要一起调。不要太早进行精细调整,有可能浪费时间。设计改变的话这些努力就白费了。

Dropout

Dropout 率通常在 20% 到 50% 之间。我们先从 20% 开始。如果模型出现过拟合,则提高值。

其他调整

  • 稀疏度
  • 激活函数


模型参数的稀疏度能使计算优化变得简单,并减少能耗(这对于移动设备来说至关重要)。如果需要,我们可以用 L1 正则化替代 L2 正则化。ReLU 是最流行的激活函数。对于一些深度学习竞赛,人们使用更高级的 ReLU 变体以提高准确率。在一些场景中它还可以减少无效节点。

高级调参

一些高级精细调参方法:

  • 学习率衰减调度
  • 动量(Momentum)
  • 早停


我们没有使用固定的学习率,而是定期降低学习率。超参数包括学习率下降的频率和幅度。例如,你可以在每十万次迭代时减少 0.95 的学习率。要调整这些参数,我们需要监控成本,以确定参数下降地更快但又不至于过早平缓。

高级优化器使用动量使梯度下降过程流畅进行。Adam 优化器中存在两种动量设置,分别控制一阶(默认 0.9)和二阶(默认 0.999)动量。对于具备梯度陡降的问题领域如 NLP,我们可以稍稍提高动量值。

当验证误差持续上升时,过拟合可通过停止训练来缓解。

p.png

但是,这只是概念的可视化。实时误差可能暂时上升,然后再次下降。我们可以定期检查模型,记录对应的验证误差。稍后我们来选择模型。

7. 网格搜索

一些超参数是高度相关的。我们应该使用对数尺度上的可能性网格一起调整它们。例如:对于两个超参数λ和γ,我们从相应的初始值开始,并在每个步骤中将其降低 10 倍:

  • (e-1, e-2, … and e-8);
  • (e-3, e-4, … and e-6)。


相应的网格会是 [(e-1, e-3), (e-1, e-4), … , (e-8, e-5) 和 (e-8, e-6)]。

我们没有使用明确的交叉点,而是稍微随机移动了这些点。这种随机性可能会帮助我们发现一些隐藏的性质。如果最佳点位于网格的边界(蓝色点),我们则会在边界区域进行重新测试。

p.png

网格搜索的计算量很大。对于较小的项目,它们会被零星使用。我们开始用较少的迭代来调整粗粒度参数。在后期的细调阶段,我们会使用更长的迭代,并将数值调至 3(或更低)。

8. 模型集合

在机器学习中,我们可以从决策树中投票进行预测。这种方法非常有效,因为判断失误通常是有局部性质的:两个模型发生同一个错误的几率很小。在深度学习中,我们可以从随机猜测开始训练(提交一个没有明确设置的随机种子),优化模型也不是唯一的。我们可以使用验证数据集测试多次选出表现最佳的模型,也可以让多个模型进行内部投票,最终输出预测结果。这种方式需要进行多个会话,肯定非常耗费系统资源。我们也可以训练一次,检查多个模型,随后在这个过程中选出表现最佳的模型。通过集合模型,我们可以基于这些进行准确的预测:

  • 每个模型预测的「投票」;
  • 基于预测置信度进行加权投票。


模型集合在提高一些问题的预测准确率上非常有效,经常会被深度学习数据竞赛的队伍所采用。

9. 模型提升

在微调模型以外,我们也可以尝试使用模型的不同变体来提升性能。例如,我们可以考虑使用色彩生成器部分或全部替代标准 LSTM。这种概念并不陌生:我们可以分步绘制图片。

p.png

直观地说,在图像生成任务中引入时间序列方法是有优势的,这种方法已经在 DRAW: A Recurrent Neural Network For Image Generation 中被证明过了。

微调与模型提升

性能重大提升的背后往往是模型设计的改变。不过有些时候对模型进行微调也可以提升机器学习的性能。最终的判断可能会取决于你对相应任务的基准测试结果。

10. Kaggle

在开发过程中,你或许会有一些简单的问题,如:我需要使用 Leak ReLU 吗?。有时候问题很简单,但你永远无法在任何地方找到答案。在一些论文中,你会看到 Leak ReLU 的优越性,但另一些项目的经验显示并没有性能提升。太多的项目,太多的变量都缺乏衡量多种可能性的验证结果。Kaggle 是一个数据科学竞赛的开放平台,其中深度学习是很重要的一部分。深入观察一些优秀选手的方法,你或许就可以找到最为普遍的性能指标了。而且,一些数据竞赛团队还会把自己的代码(被称为 kernel)上传开源。只要留心探索,Kaggle 会是一个很棒的信息源。

11. 实验框架

深度学习开发需要依赖大量经验,调节超参数是一件非常乏味的工作。创建一个实验框架可以加速这一过程。例如:一些人会开发代码将模型定义外化为字符串以便调节。然而这些努力通常不能为小团队带来收益。以我的经验,这样做的话代码的简洁性和可追溯性损失会远比受益要大,这意味着难以对代码进行简单的修改。易于阅读的代码必然有着简洁和灵活的特性。与此相反,很多 AI 云产品已经开始提供自动调节超参数的特性。虽然目前这种技术仍处于初始阶段,但无需人类自己编写框架的流程应该是大势所趋,请时刻注意这一趋势。

12. 结论

现在,你已拥有了调整完毕的模型,可以正式部署了。希望这个系列教程对你有所帮助。深度学习可以帮助我们解决很多问题——其适用范围超出你的想象。想使用深度学习代替前端设计?你可以尝试一下 pix2code!


作者:Jonathan Hui
来源:机器之心(ID:almosthuman2014)编译
https://blog.csdn.net/ufv59to8/article/details/80276534

本帖被以下淘专辑推荐:

没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条