特斯拉AI:神经网络的训练秘籍

推荐会员: lex 所属分类: 行业精选 发布时间: 2019-10-10 16:04

作者Andrej Karpathy是特斯拉的人工智能研究负责人。他博士毕业于斯坦福AI实验室,师从斯坦福“以人为本“人工智能研究院院长李飞飞。主要研究方向是卷积神经网络结构,自然语言处理,以及它们在计算机视觉上的应用。之前在谷歌大脑实习的工作内容是基于YouTube视频进行大规模特征提取。在DeepMind实习主要是做深度增强学习。

Andrej Karpathy曾连发推文细数六种神经网络研究中的常见错误。引发了一波研究者们对于自己入过坑的吐槽。

1)没有尝试先装一个单独的batch。

2)忘记切换网络的训练/评估模式。

3)在.backward()之前忘了.zero_grad()(在pytorch中)。

4)将softmaxed输出传递给期望原始logits的损失。

5)使用BatchNorm时,没有对Linear / Conv2d图层使用bias = False,或者忘记将它包含在输出图层中。这个不会让你无声地失败,但它们是虚假的参数

6)认为view()和permute()是一回事(&错误地使用视图)

Andrej Karpathy今天发文表示,“我不想深入研究这些更常见的错误或将它们列全,我想深入挖掘并讨论如何避免完全出现这些错误(或者非常快速地修复它们)。这样做的诀窍是遵循某个过程,据我所知,这个过程并不常见。“

以下跟图灵君一起细细学习他的那些秘诀吧!

1、神经网络训练并不简单

据称很容易开始训练神经网络。许多库和框架都以展示可以解决您数据问题的30行奇迹代码而自豪,这会给人一种错误的印象,即这些东西即插即用。常见的做法如下:

这些库和示例激活了我们大脑中熟悉标准软件的部分——在这里,通常可以获得干净的api和abstraction。Request库演示如下:

这很酷!一个勇敢的开发人员已经承担了理解查询字符串、URL、GET / POST请求,HTTP连接等等的重担,并且在很大程度上隐藏了几行代码背后的复杂性。这是我们所熟悉和期待的。

不幸的是,神经网络并不是那样的。如果您稍微偏离了培训ImageNet分类器,那么它们就不是“现成的”技术。我试图通过使用反向传播并将其称为“leaky abstraction”来说明这一点,但情况更加糟糕。

Backprop + SGD并没有神奇地让你的网络运作。批归一化(Batch norm)不会神奇地使其更快收敛。RNN不会神奇地让你“插入”文本。不要仅仅因为你可以用RL表示问题,就觉得应该这样做。如果您坚持使用该技术而不了解其工作原理,则可能会失败。这让我想到……

2、神经网络训练的无声失败

当您破坏或错误配置代码时,您通常会遇到某种异常。本来需要一个字符串,你却插入了一个整数。这里import 失败。那里key不存在。两个列表中的元素数量不相等。此外,为了方便 debug,你通常会为特定功能创建单元测试。

这还只是训练神经网络的开始。从语法上来说,一切都是正确的,但整件事的安排并不正确,而且很难说清楚。“可能的错误表面”很大,符合逻辑(与语法相反),但很难进行单元测试。

例如,在数据增强过程中,当您从左到右翻转图像时,可能忘记了翻转标签。您的网络仍然可以(令人震惊地)工作得非常好,因为您的网络可以在内部学习检测翻转的图像,然后左右翻转其预测。

或许你的自回归模型会因为一个off-by-one的bug而意外地将它想要预测的东西作为输入。或者你试着修剪你的梯度,但结果却减少了损失,导致异常值的例子在训练中被忽略。或者您从预训练检查点初始化了您的权重,但没有使用原始均值。或者你只是搞砸了正则化强度、学习率、衰减率、模型大小等设置。

因此,错误配置的神经网络只有在你运气好的时候才会抛出异常;大部分时间它会训练,但默默地work更糟。

过犹不及,训练神经网络的“快速和大强度”的方法不起作用,只能带来一系列麻烦,这在过去是对的。但现在,这些麻烦可以成为让神经网络运作良好的一个部分,主要通过可视化来达到。

深度学习要想成功,最需要的品质是耐心和对细节的关注。

解决之道

鉴于上述两个事实,我为自己开发了一个特定的过程,我将神经网络应用于新问题,我将遵循这个过程尝试描述它。你会发现它非常重视上述两个原则。

特别是,按照从简单到复杂的方式构建,并且在每一步我们对将要发生的事情做出具体假设,然后通过实验验证它们或进行调查直到我们发现某些问题。

我们极力避免的是同时引入了许多“未经验证的”复杂性,这必然会引入需要永远查找的bug/错误配置(如果有的话)。如果编写您的神经网络代码就像训练代码一样,您需要使用非常小的学习速率并猜测,然后在每次迭代后评估完整的测试集。

1、与数据融为一体

训练神经网络的第一步是根本不接触任何神经网络代码,而是从彻底检查数据开始。这一步至关重要。

我喜欢花费大量时间(以小时为单位)浏览数千个示例,理解它们的分布并寻找模式。幸运的是,你的大脑非常擅长这一点。有一次我发现数据中包含重复的例子。另一次我发现了损坏的图像/标签。我寻找数据不平衡和偏差。我通常也会关注自己的数据分类过程,这些过程暗示了我们最终会探索的各种架构。

举个例子,是非常本地的特征是否足够?还是我们需要全局语境?有多少变量,它们以什么形式出现?哪些变量是虚假的,可以预处理?空间位置是否重要,或者我们是否想要将其平均化?细节有多重要,我们可以在多大程度上对图像进行缩减采样?标签噪声有多大?

此外,由于神经网络实际上是数据集的压缩/编译版本,因此您将能够查看网络(错误)预测并了解它们的来源。如果你的网络给你的预测看起来与你在数据中看到的内容不一致,那么就会有所收获。

一旦你有了一种感知,你可以编写一些简单的代码来搜索/过滤/排序,无论你能想到什么(例如标签的类型,注释的大小,注释的数量等),并可视化它们的分布和沿任何轴的异常值。尤其是异常值几乎总能揭示数据质量或预处理中的一些错误。

2. 设置端到端的训练/评估架构+获得简单的基线结果

既然我们了解了自己的数据,就可以找到我们超级精彩的多尺度ASPP FPN ResNet并开始训练棒棒的模型吗?肯定没有。这是一条痛苦的道路。我们的下一步是建立一个完整的训练+评估框架,并通过一系列实验获得对其正确性的信任。

在这个阶段,最好选择一些你不可能以某种方式搞砸的简单模型 – 例如 线性分类器,或非常小的ConvNet。我们希望对其进行训练,可视化其损失和任何其他指标(例如准确度),模型预测,并在此过程中使用明确的假设执行一系列消融实验。

这个阶段的建议和技巧:

  • 固定随机种子(seed)。始终使用固定的随机种子来保证当您运行代码两次时,您将获得相同的结果。这消除了变化因子,并将帮助您保持理智。
  • 简化。确保禁用不必要的技巧。例如,在此阶段肯定会关闭任何数据扩充。数据扩充是一种我们可能在以后合并的正规化策略,但是现在它只是引入一些愚蠢bug的另一个机会。
  • 在您的评估中添加有效数字。在绘制测试损失时,对整个(大)测试集进行评估。不要只是在批量上绘制测试损失,然后依靠在Tensorboard中平滑它们。我们追求正确,很愿意放弃时间保持理智。
  • 在初始化中验证损失。验证你的损失函数在初始化中有比较合理的损失值。例如,如果你正确地初始化最终层,那么你应该通过-log(1/n_classes) 度量初始化的 Softmax 值。L2 回归和 Huber 损失函数等都有相同的默认值。
  • 正确初始化。正确初始化最终图层权重。例如。如果你正在回归一些平均值为50的值,那么将最终偏差初始化为50.如果你有一个比例为1:10的积极的不平衡数据集:负数,请在你的logits上设置偏差,这样你的网络就可以预测 初始化时为0.1。正确设置这些将加速收敛并消除“hockey stick”损失曲线,在最初的几次迭代中,您的网络基本上只是学习偏置。
  • 人类基线结果。监控除人为可解释和可检查的损失之外的指标(例如准确性)。尽可能评估您自己(人类)的准确性并与之进行比较。或者,对测试数据进行两次标注,并且对于每个示例,将一个视为预测值,将第二个视为标注值。
  • input-indepent基线。训练一个独立于输入的基线结果(例如,最简单的方法就是将所有输入设置为零)。这应该比实际插入数据而不将其归零的情况更糟糕。不是吗?也就是说,您的模型是否学会从输入中提取任何信息?
  • 在批数据上过拟合:在单个批数据上使得过拟合(两个或多个少样本)。为此,我们增加了模型的容量(例如添加层或过滤器)并验证我们可以达到可实现的最低损失(例如0)。我还想在同一个图中同时显示标签和预测,并确保一旦达到最小损失,它们就会完美对齐。如果他们不这样做,那么某个地方就会出现一个错误,我们无法继续下一个阶段。
  • 验证减少训练损失。在这个阶段,您可能会对数据集感到不适应,因为您正在处理一个玩具模型。尝试稍微增加容量。您的训练损失是否应该下降?
  • 在输入网络前可视化。在您的y_hat = model(x)(或tf中的sess.run)之前,明确正确地显示您的数据的位置。也就是说 – 您希望可视化确切地了解网络中的内容,将原始张量的数据和标签解码为可视化。这是唯一的“事实来源”。我无法计算这为我节省了多少时间,并暴露了多少数据预处理和增强方面的问题。
  • 可视化预测动态。我喜欢在训练过程中对固定测试批次上的模型预测进行可视化。这些预测如何发生的“动态”将为您提供令人难以置信的良好直觉,使其了解训练的进展情况。很多时候,如果网络以某种方式摆动太多,显示出不稳定性,就有可能感觉网络“难以”适合您的数据。学习率非常低或非常高,抖动量也很明显。
  • 使用backprop(反向传播)来绘制依赖关系。您的深度学习代码通常包含复杂的,矢量化的和广播的操作。我遇到的一个相对常见的错误是人们弄错了(例如他们使用视图而不是某处的转置/置换)并且无意中在批量维度中混合信息。 令人沮丧的是,您的网络通常仍然可以正常训练,因为它会学会忽略其他示例中的数据。调试此问题(以及其他相关问题)的一种方法是将某个示例i的损失设置为1.0,将反向传递一直运行到输入,并确保仅在i-上获得非零梯度例子。更一般地说,渐变提供关于什么依赖于网络中的什么内容的信息,这对于调试非常有用。
  • 一般化特殊案例。这是一个通用的编码技巧,但是我经常看到人们在做超出他们能力范围的事情时产生bug,从零开始编写一个相对通用的功能。我喜欢写一个非常具体的函数到我现在正在做的,让它工作,然后推广它,确保得到相同的结果。这通常适用于矢量化代码,我几乎总是先写出完整的循环版本,然后每次只将其转换为矢量化代码。

3. Overfit(过拟合)

在这个阶段,我们应该对数据集有了一个很好的理解,我们有完整的训练+评估管道工作。对于任何给定的模型,我们可以(可重复地)计算我们信任的度量。我们还有一个独立于输入基线的性能,一些简单基线的性能(我们最好战胜这些基线),并且我们对人类的性能有一个粗略的感觉(我们希望达到这个目标)。现在已经为迭代一个好的模型做好了准备。

我喜欢采用的找到一个好的模型的方法有两个阶段:首先获得一个足够大的模型,它可以过度拟合(即关注训练损失),然后适当地对其进行正则化(放弃一些培训损失,以改进验证损失)。我喜欢这两个阶段的原因是,如果我们不能达到任何模型的低错误率,则可能再次表明一些问题、bug或错误配置。

这一阶段的一些建议和技巧:

  • 挑选模型。为了获得良好的训练损失,您需要为数据选择合适的架构。当谈到选择这个我的#1建议是:不要做一个英雄。我见过很多人都渴望疯狂和创造性地将神经网络工具箱的乐高积木堆叠在各种对他们有意义的异域架构中。在项目的早期阶段强烈抵制这种诱惑。我总是建议人们简单地找到最相关的论文并复制粘贴他们最简单的架构,以获得良好的性能。例如,如果你要对图片进行分类,不要做英雄,在第一次运行时复制粘贴一张ResNet-50即可。稍后你可以做一些更自定义的事情来解决这个问题。
  • adam是安全的。在设定基线的早期阶段,我喜欢使用学习率为3e-4的Adam。根据我的经验,亚当对超参数容忍度更高,包括不良的学习率。对于ConvNets来说,一个经过良好调整的SGD几乎总会略微优于Adam,但最佳学习率区域要窄得多,而且特定于问题。(注意:如果您使用的是RNN和相关的序列模型,那么使用Adam更为常见。在项目的初始阶段,再次强调,不要做一个英雄并遵循最相关的论文所做的事情。)
  • 一次只复杂化一个。如果您有多个特性插入分类器,我会建议您逐个插入它们,每次都确保您获得预期的性能提升。不要一开始全加上。还有其他增加复杂性的方法 – 例如 您可以尝试先插入较小的图像,然后再将它们放大,等等。
  • 不相信学习率衰减默认值。如果你重新使用来自其他领域的代码,那么学习速度衰减时一定要非常小心。您不仅希望针对不同的问题使用不同的衰减计划,而且 – 更糟糕的是 – 在典型的实施中,计划将基于当前的Epoch数,其可以根据数据集的大小而广泛变化。例如。ImageNet将在30epoch内衰减10个。如果你没有训练ImageNet那么你几乎肯定不会想要这个。如果你不小心,你的代码可能会过早地将你的学习率提高到零,不允许你的模型收敛。在我自己的工作中,我总是完全禁用学习率衰减(我使用常数LR)并在最后调整这一点。

4、正则化

理想情况下,我们现在拥有一个至少适合训练集的大型模型。现在是时候对其进行正则化,并通过放弃一些训练精度来获得一些验证精度。一些提示和技巧:

  • 得到更多的数据。首先,在任何实际设置中对模型进行规范化的最佳方法和首选方法是添加更多真实的训练数据。当您可以收集更多的数据时,花费大量的工程周期试图从一个小的数据集中挤出资源,这是一个非常常见的错误。据我所知,添加更多数据几乎是唯一无限期单调改善配置良好的神经网络性能的唯一保证方式。另一个是集成(如果你能负担得起的话),但是在5个模型之后达到极限。
  • 数据增强。仅次于真实数据的是半假数据——尝试更积极的数据增强。
  • 创造性增强。如果半假数据不能做到这一点,假数据也可能会有所作为。人们正在寻找扩展数据集的创新方法; 例如,域随机化,模拟的使用,巧妙的混合,例如将(可能模拟的)数据插入场景,甚至是GAN。
  • 预训练。即使你有足够的数据,如果可以的话,使用一个预先训练好的网络也不会有什么坏处。
  • 坚持有监督的学习。对于无人监督的预训练,不要过度兴奋。不像2008年的博客文章告诉你的那样,据我所知,没有任何一个版本在现代计算机视觉中报告了强劲的结果(尽管 NLP 领域现在有了 BERT 和其他类似模型,但这更多归功于文本更成熟的本质以及以及更高的信噪比)。
  • 较小的输入维度。删除可能包含虚假信号的特征。如果您的数据集很小,任何添加的虚假输入都是过度拟合的另一个机会。同样,如果低级细节无关紧要,请尝试输入较小的图像。
  • 减小模型大小。在许多情况下,您可以在网络上使用域知识约束来减小其大小。例如,过去使用ImageNet骨干网顶部的完全连接层是时髦的,但这些已经被简单的平均池替换,从而消除了过程中的大量参数。
  • 减少批大小。 由于 BN 基于批量大小来做归一化,较小的batch size有点对应于较强的正则化。这是因为批次经验均值/标准是完整均值/标准的更近似版本,因此标度和偏移量会使您的batch“摆动”更多。
  • drop。添加dropout。对ConvNets使用dropout2d(空间dropout)。谨慎地使用这一点,因为dropout似乎不能很好地处理batch归一化。
  • 权重衰减。增加权重衰减惩罚。
  • 及早停止。停止基于已测量验证损失的训练,以便在模型即将过度匹配时捕获它。
  • 尝试更大的模型。我只是在提前停止后才提到这一点,但我在过去发现过几次,当然更大的模型最终会过度适应得更多,但它们的“提前停止”性能往往比较小的模型好得多。

最后,为了让您更加确信您的网络是一个合理的分类器,我喜欢可视化网络的第一层权重,并确保您获得有意义的优质边缘。如果您的第一层滤波器看起来像噪音,那需要去掉些东西。类似地,网内的激活函数有时可以显示奇怪的伪像并暗示问题。

5、调优

您现在应该“在循环中”使用您的数据集,为实现低验证损失的体系结构探索广阔的模型空间。这一步的一些技巧和技巧:

  • 随机网格搜索。为了同时调整多个超参数,使用网格搜索确保覆盖所有设置听起来很诱人,但请记住,最好使用随机搜索。直观地说,这是因为神经网络对某些参数比其他参数更敏感。在极限情况下,如果一个参数a很重要,但是改变b没有影响,那么您宁愿更彻底地采样a,而不是多次在几个定点采样。
  • 超参数优化。有很多花哨的贝叶斯超参数优化工具箱,我的一些朋友也报告了它们的成功,但我个人的经验是,探索好的、宽的模型空间和超参数的最佳方法是找个实习生。哈哈哈,开个玩笑。

6、最后再榨出点东西

一旦找到最佳类型的体系结构和超参数,您仍然可以使用一些技巧从系统中提炼方法:

  • 集成。模型集成是一种非常有保证的方法,可以在任何事情上获得2%的准确率。如果您在测试时无法负担计算,请考虑使用黑暗知识将您的整体提升到网络中
  • 充分训练。我经常看到人们试图在验证损失趋于平稳时停止模型训练。在我的经验中,网络持续训练的时间长得出乎意料。有一次,我在寒假期间不小心离开了模型训练,当我一月份回来的时候,那是SOTA(“达到了最好的状态”)。

结论

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

原文链接:https://karpathy.github.io/2019/04/25/recipe/ 来源: http://www.sohu.com/a/311816389_817016

关键词:

版权声明:本站原创和会员推荐转载文章,仅供学习交流使用,不会用于任何商业用途,转载本站文章请注明来源、原文链接和作者,否则产生的任何版权纠纷与本站无关,如果有文章侵犯到原作者的权益,请您与我们联系删除或者进行授权,联系邮箱:service@datagold.com.cn。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据