二维码

训练神经网络的食谱

1536 人阅读 | 时间:2019年09月29日 09:06

几周前,我在“最常见的神经网络错误”上发布了一条推文,列出了与训练神经网络有关的一些常见陷阱。该推文的参与度比我预期的要多得多(包括一个网络研讨会 :))。显然,很多人亲身经历了“卷积层的工作原理”和“我们的卷积网达到最新状态”之间的巨大差距。

因此,我认为清除我尘土飞扬的博客以将我的推文扩展到该主题应有的较长格式可能很有趣。但是,我不想深入列举或更常见的错误,而是想更深入地探讨如何避免完全犯错(或非常快地纠正它们)。这样做的诀窍是遵循特定的过程,据我所知,该过程很少被记录。让我们从激发它的两个重要观察开始。

1)神经网络训练是一个泄漏的抽象

据称,训练神经网络很容易上手。许多库和框架都以显示30行奇迹片段来解决您的数据问题为荣,给人以(假)印象,即这些东西是即插即用的。常见的情况如下:

>>> your_data = # plug your awesome dataset here >>> model = SuperCrossValidator(SuperDuper.fit, your_data, ResNet50, SGDOptimizer) # conquer world here

这些库和示例激活了我们大脑中熟悉标准软件的部分-在该地方通常可以获得干净的API和抽象。请求库演示:

>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass')) >>> r.status_code 200

这很酷!一个勇敢的开发人员已经负担了从您那里了解查询字符串,URL,GET / POST请求,HTTP连接等的负担,并且在几行代码之后很大程度上隐藏了复杂性。这是我们熟悉并期望的。不幸的是,神经网络并非如此。如果您在训练ImageNet分类器时稍有偏离,它们并不是“现成的”技术。我已尝试在我的帖子“是的,您应该了解反向传播”中指出这一点。通过选择反向传播并将其称为“泄漏抽象”,但不幸的是情况更加严峻。Backprop + SGD不能神奇地使您的网络正常工作。批处理规范并不能神奇地使其收敛更快。RNN不会神奇地让您“插入”文本。仅仅因为您可以将问题表达为RL并不意味着您应该这样做。如果您坚持使用该技术而不了解其工作原理,则很可能会失败。带我到…

2)神经网络训练默默失败

当您破坏或错误配置代码时,您通常会遇到某种异常。您插入了一个整数,该整数应为字符串。该函数仅需要3个参数。导入失败。该密钥不存在。两个列表中的元素数量不相等。此外,通常可以为某些功能创建单元测试。

这只是训练神经网络的开始。语法上所有内容都可能是正确的,但整个内容的排列不正确,而且很难说出来。“可能的错误表面”很大,合乎逻辑(与语法相反),并且对于单元测试非常棘手。例如,也许您在数据增强过程中左右翻转图像时忘记了翻转标签。您的网络仍然可以(令人震惊地)运行良好,因为您的网络可以在内部学习检测翻转的图像,然后左右翻转预测。也许由于一个错误,您的自回归模型意外地将它试图预测的东西作为输入。或者,您尝试修剪渐变,但修剪了损失,导致在训练过程中忽略了异常示例。或者,您从预先训练的检查点初始化了权重,但未使用原始均值。或者,您只是搞砸了正则化强度,学习率,其衰减率,模型大小等设置。因此,只有当您幸运时,配置错误的神经网络才会引发异常。在大多数情况下,它会训练,但默默地工作会更糟。

结果,(而且很难过分强调)训练神经网络的“快速而愤怒”的方法行不通,只会导致痛苦。现在,苦难是使神经网络正常工作的一个自然而然的部分,但是可以通过彻底,防御性,偏执狂以及对几乎所有可能事物的可视化痴迷来减轻痛苦。以我的经验,与深度学习成功最密切相关的品质是耐心和对细节的关注。

食谱

鉴于以上两个事实,我为自己制定了一个特定的过程,将神经网络应用于新问题时将遵循该过程,我将尝试描述该过程。您将看到,它非常认真地考虑了以上两个原则。特别是,它从简单到复杂,在我们对即将发生的事情进行具体假设的每个步骤中,然后通过实验进行验证或进行调查,直到发现一些问题为止。我们很难避免的是立即引入很多“未经验证”的复杂性,这势必会引入错误/错误配置,而这些错误/错误配置将永远被发现(如果有的话)。如果编写神经网络代码就像训练一个神经网络代码,那么您希望使用很小的学习率并进行猜测,然后在每次迭代后评估完整的测试集。

1.与数据合而为一

训练神经网络的第一步是完全不接触任何神经网络代码,而是从彻底检查数据开始。此步骤至关重要。我喜欢花费大量的时间(以小时为单位)扫描成千上万个示例,了解它们的分布并寻找模式。幸运的是,您的大脑对此非常擅长。有一次我发现数据包含重复的示例。我又一次发现损坏的图像/标签。我在寻找数据不平衡和偏差。通常,我还将关注我自己的数据分类过程,这暗示了我们最终将探索的架构类型。举个例子-非常局部的功能是否足够?还是需要全局上下文?有多少变化,采取什么形式?哪些变化是虚假的,可以进行预处理?空间位置是否重要,还是我们希望将其平均化?细节有多重要?我们可以对图像进行下采样多远?标签有多吵?

此外,由于神经网络实际上是数据集的压缩/编译版本,因此您可以查看网络(误)预测并了解它们可能来自何处。而且,如果您的网络给您的预测似乎与您在数据中看到的不一致,那么情况就有些不对劲了。

一旦有了定性的感觉,最好编写一些简单的代码以根据您的想法(例如标签的类型,注释的大小,注释的数量等)进行搜索/过滤/排序,并可视化它们的分布以及沿任何轴的离群值。异常值几乎总是发现数据质量或预处理中的一些错误。

2.设置端到端的培训/评估框架+获取愚蠢的基线

现在,我们了解了我们的数据,是否可以获取我们的超级花式多尺度ASPP FPN ResNet并开始训练出色的模型?当然没有。那就是通往苦难的道路。我们的下一步是建立完整的培训+评估框架,并通过一系列实验获得对其正确性的信任。在此阶段,最好选择一些您可能无法解决的简单模型,例如线性分类器或非常小的ConvNet。我们将要对其进行训练,将损失,任何其他指标(例如准确性)可视化,对模型进行预测,并在此过程中使用明确的假设进行一系列的烧蚀实验。

此阶段的提示和技巧:

  • 修复随机种子始终使用固定的随机种子来确保两次运行代码时您将获得相同的结果。这消除了变化的因素,将有助于您保持理智。

  • 简化确保禁用任何不必要的幻想。例如,在此阶段,请务必关闭任何数据扩充功能。数据扩充是我们稍后可能会采用的一种正则化策略,但是目前这只是引入一些愚蠢错误的机会。

  • 向您的评估中添加有效数字绘制测试损失图时,对整个(大型)测试集进行评估。不要只绘制测试损失的批次,然后依靠在Tensorboard中对其进行平滑处理。我们追求正确,非常愿意放弃保持理智的时间。

  • 验证损失@ init验证您的损失是否从正确的损失值开始。例如,如果正确初始化了最后一层,则应-log(1/n_classes)在初始化时对softmax进行测量可以为L2回归,Huber损耗等导出相同的默认值。

  • 初始化好正确初始化最终图层权重。例如,如果您回归一些平均值为50的值,则将最终偏差设置为50。如果您的数据集不平衡,其正值与负值之比为1:10,请在对数上设置偏差,以使网络预测概率初始化时为0.1。正确设置这些参数将加快收敛速度并消除“曲棍球”损耗曲线,而在最初的几次迭代中,您的网络基本上只是在学习偏差。

  • 人类基线监控除损失之外的指标,这些指标是人类可以解释和检查的(例如准确性)。尽可能评估自己(人类)的准确性并与之进行比较。或者,对测试数据进行两次注释,对于每个示例,将一个注释视为预测,将第二个注释视为基础事实。

  • 输入独立基线训练与输入无关的基线(例如,最简单的方法就是将所有输入都设置为零)。这比实际插入数据而不将其清零时的性能要差。可以?也就是说,您的模型是否学会从输入中提取任何信息?

  • 超过一批只对几个示例进行过度拟合(例如,仅两个)。为此,我们增加了模型的容量(例如,添加图层或滤镜),并验证我们可以达到最低的可实现损耗(例如,零)。我还希望在同一图中可视化标签和预测,并确保一旦达到最小损失,它们最终就可以完美对齐。如果没有,那么某个地方存在错误,我们将无法继续进行下一阶段。

  • 验证减少的训练损失在这个阶段,由于您正在使用玩具模型,因此您可能对数据集的拟合不足。尝试稍微增加其容量。您的训练损失是否应有的减少了?

  • 在网上之前想象可视化数据的正确正确位置就在您的位置y_hat = model(x)(或sess.runtf中)之前。也就是说,您希望准确地可视化网络中所包含的内容,将原始数据张量和标签解码为可视化。这是唯一的“真理来源”。我无法计数这为我节省了多少时间,并揭示了数据预处理和扩充中的问题。

  • 可视化预测动态我喜欢在培训过程中可视化固定测试批次上的模型预测。这些预测如何运动的“动力”将使您对培训的进行方式有非常好的直觉。很多时候,如果网络以某种方式过度摆动,可能会感觉网络“在努力”以适应您的数据,这表明不稳定。抖动量也很容易注意到非常低或非常高的学习率。

  • 使用backprop绘制依赖关系图您的深度学习代码通常包含复杂的,向量化的和广播的操作。我几次遇到的一个相对常见的错误是,人们会弄错这一点(例如,他们viewtranspose/permute某个地方而不是在某个地方使用),并且无意间在批次维度中混合了信息。一个令人沮丧的事实是,您的网络通常仍会训练正常,因为它将学会忽略其他示例中的数据。调试此问题(以及其他相关问题)的一种方法是将损失设置为微不足道,例如示例i所有输出的总和,一直向后运行到输入,并确保得到非零值。仅第i个梯度输入。例如,可以使用相同的策略来确保在时间t处的自回归模型仅取决于1..t-1。更一般而言,渐变会为您提供有关哪些内容取决于网络中内容的信息,这对于调试很有用。

  • 概括特殊情况这更多的是通用的编码技巧,但我经常看到人们在咬的东西超过他们的咀嚼能力时会产生错误,从头开始编写相对通用的功能。我喜欢为我现在正在做的事情编写一个非常具体的功能,使其正常工作,然后在以后进行泛化,以确保获得相同的结果。通常这适用于向量化代码,在这里我几乎总是总是先写出完整的循环版本,然后才一次将其转换为向量化代码。

3.过度拟合

在这一阶段,我们应该对数据集有一个很好的理解,并且我们有完整的培训和评估流程。对于任何给定的模型,我们都可以(可重复)计算我们信任的指标。我们还拥有与输入无关的基准的性能,一些哑基准的性能(我们最好打败这些基准),并对人类的性能有大致的了解(我们希望达到这一目标)。现在可以迭代一个好的模型了。

我想采用的找到一个好的模型的方法有两个阶段:首先要建立一个足够大的模型,使其可以过度拟合(即专注于训练损失),然后适当地对其进行正则化(放弃一些训练损失以提高验证损失)。我喜欢这两个阶段的原因是,如果使用任何模型我们都无法达到较低的错误率,则可能再次表明存在某些问题,错误或配置错误。

此阶段的一些提示和技巧:

  • 选择模型为了减少培训损失,您需要为数据选择合适的体系结构。在选择此选项时,我的第一建议是:不要成为英雄我见过很多渴望疯狂和富有创造力的人,他们将神经网络工具箱的乐高积木堆叠在对他们来说有意义的各种奇异体系结构中。在项目的早期阶段强烈抵制这种诱惑。我始终建议人们仅找到最相关的论文,然后将其最简单的体系结构粘贴粘贴,以实现良好的性能。例如,如果您要对图像进行分类,请不要成为英雄,只需复制粘贴ResNet-50即可进行首次运行。您可以稍后再做一些自定义操作并击败它。

  • 亚当是安全的在设定基准的早期阶段,我喜欢以3e-4的学习率使用Adam 以我的经验,亚当更宽容超参数,包括不良的学习速度。对于ConvNets,调整良好的SGD几乎总是比Adam稍胜一筹,但是最佳学习率区域要狭窄得多且针对特定问题。(注意:如果您正在使用RNN和相关的序列模型,则使用Adam更为常见。再次,在项目的初始阶段,不要再成为英雄,而要遵循最相关的论文。)

  • 一次只使一个复杂化如果您有多个信号要插入您的分类器,我建议您将它们一个接一个地插入,并每次确保获得预期的性能提升。开始时,请勿将厨房水槽扔向您的模型。还有其他增加复杂性的方法-例如,您可以尝试先插入较小的图像,然后再使其较大,等等。

  • 不要相信学习率衰减的默认如果您要重新使用其他领域的代码,请务必小心学习率。您不仅要针对不同的问题使用不同的衰减计划,而且-更糟糕的是-在典型的实现中,计划将基于当前纪元号,该纪元号可以根据数据集的大小而变化很大。例如,ImageNet在第30个时代衰减10。如果您不训练ImageNet,则几乎可以肯定不希望这样做。如果您不小心,您的代码可能会秘密地过早地将您的学习率降至零,从而使模型无法收敛。在我自己的作品中,我总是完全禁用学习率衰减(我使用恒定的LR),并在最后完全进行调整。

4.正则化

理想情况下,我们现在处于一个拥有至少可以拟合训练集的大型模型的地方。现在是时候对其进行正规化并通过放弃一些训练准确性来获得一些验证准确性。一些提示和技巧:

  • 获得更多数据首先,到目前为止,在任何实际情况下对模型进行正则化的最佳方法是添加更多真实的训练数据。花费大量的工程周期试图从较小的数据集中榨汁是一个很常见的错误,而您本来可以收集更多的数据。据我了解,添加更多数据几乎是无限地单调提高配置良好的神经网络性能的唯一保证方法。另一个是合奏(如果您负担得起的话),但是在大约5个模型之后达到最高。

  • 数据扩充真实数据的第二大优点是半假数据-试用更具侵略性的数据扩充。

  • 创意增强如果半假数据没有做到这一点,伪造数据也可能会有所作为。人们正在寻找扩展数据集的创新方法。例如,域随机化模拟的使用,巧妙的混合,例如将(潜在模拟的)数据插入场景,甚至GAN。

  • 预训练即使可以,即使有足够的数据,使用预训练的网络也永远不会有什么坏处。

  • 坚持监督学习不要对无监督的预培训感到兴奋。据我所知,2008年的博客文章告诉您的是,没有哪个版本在现代计算机视觉方面报告了出色的成果(尽管近来NLP在BERT和朋友看来似乎做得很好,这很可能是由于文字的故意性质,以及较高的信噪比)。

  • 输入维数较小删除可能包含虚假信号的功能。如果您的数据集很小,则任何添加的虚假输入只是过度拟合的另一个机会。同样,如果底层细节不重要,请尝试输入较小的图像。

  • 较小的模型尺寸在许多情况下,您可以在网络上使用域知识约束来减小其大小。例如,在ImageNet的主干网络顶部使用完全连接层一直很流行,但是自那以后,这些层已被简单的平均池所取代,从而消除了过程中的大量参数。

  • 减小批量大小由于批次规范内的标准化,较小的批次大小在某种程度上对应于较强的正则化。这是因为批处理的经验均值/标准差是完全均值/标准差的更近似版本,因此比例和偏移量使您的批处理更多地“摆动”。

  • 下降添加辍学。对ConvNet使用dropout2d(空间辍学)。谨慎/谨慎地使用此选项,因为对于批量归一化而言,辍学似乎效果不佳。

  • 体重下降增加重量衰减惩罚。

  • 提早停车停止根据您测得的验证损失进行训练,以在模型过拟合时捕获模型。

  • 尝试更大的模型我最后提到这一点,只是在提早停止后才提到,但是过去我发现大型模型当然最终会过拟合得多,但是它们的“提前停止”性能通常会比小型模型好得多。

最后,为了进一步确信您的网络是合理的分类器,我想形象化网络的第一层权重,并确保获得有意义的优势。如果您的第一层滤波器看起来像噪音,那么可能有些问题了。同样,网络内部的激活有时可能会显示奇特的伪像并提示问题。

5.调整

现在,您应该将数据集“圈中”,为实现低验证损失的体系结构探索广阔的模型空间。此步骤的一些提示和技巧:

  • 随机网格搜索要同时调整多个超参数,使用网格搜索来确保覆盖所有设置可能很诱人,但请记住,最好改用随机搜索直观地讲,这是因为神经网络通常对某些参数比对其他参数更敏感。在极限情况下,如果一个参数一个问题,但改变b没有效果,那么你宁愿品尝一个不是在几个固定点多次更throughly。

  • 超参数优化周围有大量精美的贝叶斯超参数优化工具箱,我的一些朋友也报告说使用它们很成功,但是我的个人经验是,探索模型和超参数的广阔空间的最新技术是使用实习生:)。开玩笑。

6.榨汁

找到最佳类型的体系结构和超参数后,您仍然可以使用更多技巧来将系统中的剩余部分挤出来:

  • 合奏模型合奏几乎可以保证在任何情况下获得2%的精度。如果您在测试时无法负担计算费用,可以考虑使用黑暗的知识将整体提炼成网络

  • 离开训练吧我经常看到人们在验证损失似乎趋于平稳的情况下倾向于停止模型训练。根据我的经验,网络可以长期保持训练。一次,我在冬季休假时不小心离开了模型训练,而当我在一月份回来时,那是SOTA(“最先进的技术”)。

结论

一旦在这里实现,您将拥有成功的所有要素:您对技术,数据集和问题有了深刻的了解,您已经建立了整个培训/评估基础架构,并对其准确性抱有高度信心,并且您已经探索了越来越复杂的模型,并以预测每个步骤的方式获得了性能改进。您现在准备阅读大量论文,尝试大量实验,并获得SOTA结果。祝好运!


©著作权归作者所有:来自ZhiKuGroup博客作者没文化的原创作品,如需转载,请注明出处,否则将追究法律责任 来源:ZhiKuGroup博客,欢迎分享。

评论专区
  • 昵 称必填
  • 邮 箱选填
  • 网 址选填
◎已有 0 人评论
搜索
作者介绍
30天热门
×
×
本站会员尊享VIP特权,现在就加入我们吧!登录注册×
»
会员登录
新用户注册
×
会员注册
已有账号登录
×