原文:http://theorangeduck.com/page/neural-network-not-working
原文标题:My Neural Network isn’t working! What should I do?
译文作者:kbsc13
联系方式:
Github:https://github.com/ccc013
知乎专栏:机器学习与计算机视觉,AI 论文笔记
微信公众号:AI 算法笔记
如果你的神经网络不收敛,应该怎么办呢?一般来说,神经网络不收敛的原因有以下 11 种原因:
接下来将一一解释以上11 种原因并给出对应的解决办法;
在神经网络训练中,如何对你的数据进行归一化是非常重要的。这是一个不能省略的步骤,几乎不可能在不进行归一化的前提下可以训练得到一个很好的网络模型。不过正因为这个步骤非常重要,而且在深度学习社区也很有名,所以很少人会提到它,但是对于初学者则是可能会犯下的一个错误。
我们需要对数据进行归一化操作的原因,主要是我们一般假设输入和输出数据都是服从均值为 0,标准差为 1 的正态分布。这种假设在深度学习理论中非常常见,从权重初始化,到激活函数,再到对训练网络的优化算法。
常用的归一化方法主要是零均值归一化,它会将原始数据映射到均值为 0,标准差为 1 的分布上。假设原始特征的均值是 μ \mu μ、方差是 σ \sigma σ,则公式如下:
z = x − μ σ z = \frac{x-\mu}{\sigma} z=σx−μ
另一种常用的归一化方法是线性函数归一化(Min-Max Scaling)。它对原始数据进行线性变换,使得结果映射到[0,1]
的范围,实现对原始数据的等比缩放,公式如下:
X n o r m = X − X m i n X m a x − X m i n X_{norm}=\frac{X-X_{min}}{X_{max}-X_{min}} Xnorm=Xmax−XminX−Xmin
其中 X 是原始数据, X m a x , X m i n X_{max}, X_{min} Xmax,Xmin分别表示数据最大值和最小值。
未经训练的神经网络通常输出的值大致在-1到1之间。如果希望它输出一些其他范围的值(例如RGB图像,存储为字节的范围是0到255),那将会有一些问题。当开始训练时,网络将非常不稳定,因为当预期值为255时,它将产生-1或1,这个错误被用于训练神经网络的大多数优化算法认为是巨大的。这将产生巨大的梯度,你的训练误差可能会爆发。如果你的训练没有爆炸,那么训练的前几个阶段仍然是浪费,因为网络将学习的第一件事是缩放和转移输出值到大致期望的范围。如果你规范化你的数据(在这种情况下你可以简单地除以128减去1),那么这些都不是问题。
一般来说,神经网络中特征的规模也会决定它们的重要性。如果你在输出中有一个大尺度的特征,那么与其他特征相比,它会产生更大的错误。同样,输入中的大尺度特征会主导网络,导致下游更大的变化。由于这个原因,使用许多神经网络库的自动归一化并不总是足够的,这些库盲目地减去平均值,然后除以每个特征的标准差。你可能有一个输入特征一般范围在0.0和0.001之间,这个特性的范围如此之小,因为它是一个重要的特性(在这种情况下,也许你不想对它再缩放),或因为它有一些小型单位相比其他特性?
同样地,要小心那些有如此小范围的特性,它们的标准偏差接近或精确地接近于零——如果规范化它们,这些特性将产生nan的不稳定性。仔细考虑这些问题是很重要的——考虑你的每个特性真正代表了什么,并将标准化视为制作“单元”的过程。所有输入特征都相等。这是我认为在深度学习中真正需要人类参与的少数几个方面之一。
当你开始训练你的网络几个 epoch 后,发现误差在减小了。这表示成功训练网络了吗?很不幸这并不是,这说明你的代码中很可能还有一些
问题,可能是在数据预处理、训练代码或者推理部分有问题。仅仅因为误差在减小并不意味着你的网络正在学习有用的信息。
与传统编程不同,机器学习系统几乎在所有情况下都会悄无声息地失败。在传统的编程中,我们习惯于电脑在出现问题时抛出一个错误,并
以此作为返回和检查错误的信号。
不幸的是在机器学习中并不是这样的机制,所以我们应该非常小心检查的通过人眼来观察每个阶段的处理过程,这样当一个错误已经产生的
时候,我们可以及时发现并且可以更彻底的检查代码。
在管道的每个阶段检查数据是否正确是非常重要的。通常这意味着找到一些方法使结果形象化。如果你有图像数据,那么很容易,动画数据也可以可视化,没有太多的麻烦。
如果你有一些更奇特的东西,必须找到一种方法来检查它,以确保它在预处理、训练和推理管道的每个阶段看起来都是正确的,并将其与地面真实数据进行比较。
有许多方法可以检查你的网络是否正常工作。其中一部分是找出报告的训练错误的真正含义。可视化应用于训练集数据的输出结果,可以观察到网络的输出结果和真实标签的对比
在训练的时候,可能会看到误差从 1.0 到 0.01,但如果 0.01仍然是一个不可接受的结果,那么输出结果仍可能无法使用。如果它在训练集上是有用的,那请在验证集上检查它,看看它仍然适用于以前从未见过的数据吗?
我的建议是,从一开始就习惯于可视化一切,不要只在网络不收敛的时候才开始,并且确保在开始尝试不同的神经网络结构之前,已经准备了通完最终用户的完整管道,并一路进行完整的检查。这是准确评估许多潜在不同方法的唯一方法。
大部分的数据都是棘手的——通常我们知道的相似的东西,其数据可以有非常不同的数字表示。举个例子,以角色动画为例,如果我们使用角色关节相对于运动捕捉工作室中心的 3D 位置表示我们的数据,然后在一个位置或朝向一个方向执行运动,则可能具有与在不同位置执行相同运动或朝向不同方向执行相同运动截然不同的数字表示。相反,我们需要做的是以不同的方式表示数据 - 例如,在某些本地参考帧中(例如相对于字符的质量中心),以便我们知道两个动作相似,获得类似的数字表示。
神经网络只对它们作为输入的数据做出几个基本假设,但这些基本假设之一是数据所处的空间有些连续性,对于大多数空间来说,两个数据点之间的点至少在某种程度上是这两个数据点的"混合",而附近的两个数据点在某种程度上代表着"类似"的东西。数据空间中出现大的不连续性,或者代表同一事物的大组分离数据,将使学习任务更加困难。
想想使用的特征所表示的意思,是否可以对它们进行一些简单的转换,以确保代表我们知道的相似内容的数据点始终获得相似的数字表示?是否有一个本地坐标系统来表示数据使得事情更自然,比如可能是一个更好的颜色空间,或者其他不同的格式?
另一种考虑数据预处理的方法是尝试减少可能需要的数据变化导致的组合爆炸。例如,如果一个受过角色动画数据训练的神经网络必须学习每个位置和方向中针对角色的相同动作集,那么网络的大部分容量就会被浪费,许多学习过程就会重复。
正则化是现在训练神经网络一个非常重要的方法,通常是以 dropout、噪音或者其他某种随机过程的形式来加入到网络中。
即便数据维度比参数更多,或者是在某种情况下不需要在意过拟合或者不可能出现过拟合,加入 dropout 或者某些形式的噪音仍然是很有帮助的。
正则化方法不仅仅是用于控制过拟合,通过在训练过程中引入一些随机过程,在某种程度上是"平滑"了成本格局。这可以加快训练收敛的速度,帮助处理数据中的噪声或异常值,并防止网络的极端权值配置。
最常用的正则化方法就是在卷积层或者全连接层之前采用 dropout 。一般会采用一个较高的概率,比如 0.75 或者 0.9,然后基于网络可能过拟合的概率来调整这个概率值,比如觉得不太可能出现过拟合,那么就把保留神经元的概率设置得非常高,比如 0.99。
数据增强或其他类型的噪音也可以像dropout一样实现正则化,有时候使用了足够的数据增强就可以不用 dropout。通常 dropout 被认为是将许多随机子网络的预测相结合的技术,但也可以将它视为一种数据增强的形式,在训练期间产生许多相似的输入数据变化。正如我们所知,避免过度拟合的最好方法是拥有足够多的数据,使得神经网络永远不会看到同样的数据两次!
最后,像训练神经网络其他方面一样,你需要小心你使用的正规化。请记住,在预测期间将其关闭,并注意,一旦它被关闭,您通常会得到略有不同的结果。在你需要极其精确的数字预测的情况下,某些形式的正则化有时会使这一切变得困难。
使用一个太大的 batch size 会因为降低了梯度下降的随机性,导致降低了网络的准确率。
使用较小的batch大小会产生波动更大,更随机的权值更新。这有两个好处:
在训练的时候,找到一个可以容忍的最小的 batch 大小。可以让 GPU 并行使用最优的 batch 大小并不一定可以得到最好的准确率,因为更大的 batch 可能需要训练更多时间才能达到相同的准确率。所以大胆的从一个很小的 batch 大小开始训练,比如 16,8,甚至是 1。
数据中的某些其他元素有时可以有效地像 batch 大小一样工作。例如,以两倍的分辨率处理图像,其效果与使用 4 倍的 batch 大小相似。
简单的说明这个原因,考虑在 CNN 中,每个滤波器的权值更新将平均显示在输入图像中应用的所有像素以及 batch 中的每一个图像上。将图像分辨率提高 2 倍,平均效果将提高 4 倍以上,其效果与将batch大小增加 4 倍的方式非常相似。
总体而言,重要的是考虑每次迭代中最终梯度更新的平均值,并确保您平衡其不利影响与尽可能多地使用 GPU 潜在并行性的必要性。
学习率对训练网络的容易程度有很大的影响,如果你是新手,几乎可以肯定你的设置是错误的,这是因为在常见的深度学习框架中使用的各种默认选项。
许多深度学习框架在默认情况下启用梯度裁剪。这个操作是通过在训练中的每一步中改变一个最大数量的权值来防止出现梯度爆炸的情况。
这可能很有用——特别是当你的数据包含许多异常值,这会产生很大的误差,从而产生很大的梯度和权重更新,但默认设置也会使手工找到最佳学习率变得非常困难。我发现大多数刚接触深度学习的人都将学习速率设置得过高,并通过梯度裁剪来解释这一点,使整体训练速度变慢,并且改变学习率的效果不可预测。
不采用梯度裁剪。找出在训练过程中不会导致误差爆炸的最大学习率。将学习率设置为比这个低一个数量级,这可能是非常接近最佳学习率。
如果你已经正确地清理了你的数据,删除了大部分的异常值,并正确地设置了学习速率,那么你真的不应该需要梯度剪裁。如果没有它,你会发现你的训练误差偶尔变得非常大,那么请使用梯度裁剪,但是请记住,看到你的训练错误爆发几乎总是表明你的一些数据有其他错误,梯度裁剪只是一个临时措施。
在最后一层使用激活函数有时候会导致网络不能生成要求数值的完整范围,比如最常见的错误就是在最后一层采用 ReLU ,它会导致网络只能输出正数。
想想你的数据值实际代表什么,以及标准化后其范围是什么。最有可能的情况是,你的输出值是无限的正数或负数,在这种情况下,不应该在最后一层使用激活功能。如果输出值可能只在某个范围内有意义,例如它由范围 0-1 中的概率组成,则很可能在最后一层(如 sigmoid 激活功能)上应使用特定的激活功能。
如果是在做回归任务,大部分情况下是不需要在最后一层使用任何激活函数,除非是你知道希望输出的数值的一些信息。
在最后一层上使用激活函数有许多微妙之处。在神经网络产生输出后,系统最终将把输出裁剪到 [-1,1] 范围内。那么添加这个裁剪过程作为最终层的激活似乎是有意义的,因为这将确保你的网络误差函数不会惩罚大于1或小于-1的值。
但是,没有误差意味着这些大于或小于 1 的数值也不会有梯度,在某些情况下,这会使得网络无法训练。或者,可能很想在最后一层使用 Tanh,因为知道此激活函数输出的值在 -1 到 1 范围内,但这也会导致问题,因为该函数接近 1 或 -1 的梯度变得非常小,这可能导致权值增长巨大,试图产生 -1 或 1。
一般来说,你最好的选择是谨慎行事,在最后一层不使用任何激活功能,而不是尝试一些可能适得其反的聪明做法。
使用 ReLU激活函数的神经网络经常会遇到一些因为坏的梯度导致的“死亡神经元”的情况。它会导致网络性能下降,甚至某些情况下导致网络无法继续训练。
对于 ReLU 激活函数来说,其梯度对于正数和负数分别是 1 和 0。这是因为输入的微小更改不会影响小于零的输入输出。这对于正数的大梯度来说似乎不是一个问题,但与许多网络层堆叠在一起,负权重能够将大的正值与强梯度变成负值与零梯度,通常情况下,如果无论输入是什么,网络中有一部分或者所有的权值对于损失函数的梯度都是,这种情况就是网络是死了,权值是没办法更新,也就是无法继续训练下去了。
如果你发现你的训练误差没有随着迭代次数的增加而变化,那么很可能就是出现了因为是 ReLU 激活函数导致的神经元死亡的情况。可以尝试使用如 leaky ReLU 或者 ELUs 等激活函数,看看是否还出现这种情况。
任何带有零梯度的操作,如裁剪、舍入或取最大/最小值,如果在计算损失函数对权值的导数时使用它们,也会产生糟糕的梯度。如果这些元素出现在你的符号图中,你必须非常小心,因为它们经常会导致不可预见的困难,例如,如果它们被用于作为损失函数的一部分的自定义错误度量中。
如果你不能正确初始化你的神经网络的权值,那么你的神经网络就不太可能进行训练。神经网络中的许多其他组件假定某种形式的正确或标准化的权值初始化,并将权值设置为零,一般使用自己的自定义随机初始化是行不通的。
可能你听说过可以使用“小的随机的数值”来初始化网络的权值,但并没有这么简单。常用的“he”,“xaiver”和“lecun”等权值初始化方式都是使用了很复杂和详细的数学公式并证明了它们为什么是最优的方法。更重要的是,其他的神经网络组件经常是围绕着这些初始化方式建立并根据经验来测试它们,因此如果使用自定义的初始化方式会增加了复现其他研究者成果的难度。
目前比较常用而且在任何情况下效果都不错的初始化方式包括了“he”,“xaiver”和“lecun”。所以可以任意选择其中一种,但是可以先进行实验来找到最适合你的任务的权值初始化方式。
其他网络层可能也需要小心的初始化。网络偏差被初始化为零,而其他更复杂的层,如参数激活函数,可能会有它们自己的初始化,这些初始化对于得到正确的结果同样重要。
网络是越深越好吗?实际上并总是这样的,越深越好一般是在做基准实验或者是希望在某些任务上尝试增加 1%甚至更多的准确率,但是如果 3,4,5 层的网络都学不到任何东西,那么使用 100+的网络层也会同样失败, 甚至更加糟糕。
虽然看起来是这样,但神经网络并不是在某人决定堆叠数百层的时候就突然开始获得突破性的结果的。过去十年里对神经网络所做的所有改进都是微小的、根本性的改变,这些改变既适用于深度网络,也适用于小型网络。如果你的网络不工作,更有可能是其他问题,而不是深度问题。
从一个3到8层的神经网络开始。只有当训练的网络有不错的性能,并开始研究如何提高准确性时,才开始尝试更深层次的网络。
从小处开始也意味着训练你的网络会更快,推理会更快,迭代不同的设计和设置会更快。最初,所有这些东西对网络的准确性的影响要比简单地堆叠更多的网络层大得多。
在某些情况下,使用过多或过少的隐藏神经元会让网络难以训练。神经元数量过少,它可能无法表达所需的任务,而神经元数量过多,它可能变得缓慢而笨拙,难以去除残余噪声进行训练。
在决定要使用的隐藏神经元的数量时,关键是要大致考虑你认为表达你希望通过网络传递的信息所需的实际值的最少数量。然后你应该把这个数字放大一点。这将允许 dropout,以便网络使用更冗余的表示,并在你的估计中有一点余地。如果你在做分类,你可能会使用类数量的5到10倍作为一个好的初始猜测,而如果你在做回归,你可能会使用输入或输出变量数量的 2 到 3 倍。当然,所有这些都高度依赖于环境,并且不存在简单的自动解决方案,良好的直觉仍然是决定隐藏单位数量的最重要因素。
从256到1024个隐藏神经元数量开始。然后,看看其他研究人员在相似应用上使用的数字,并以此为灵感。如果其他研究人员使用的数字与上面给出的数字有很大不同,那么可能有一些具体的原因,这可能对你来说很重要。
在现实中,与其他因素相比,隐藏神经元的数量往往对神经网络的性能有相当小的影响,在很多情况下,高估所需的隐藏神经元的数量只会使训练变慢,而没有什么负面影响。一旦网络开始工作,如果你仍然担心,就尝试一大堆不同的数字,并测量其准确性,直到找到最有效的一个。
在公众号“AI 算法笔记”后台回复以下文字,可以获取对应资料: