cs231n neural networks 3笔记
- Gradient checks
- 1. Use the centered formula
- 2. Use relative error for the comparison
- 3. Use double precision
- 4. Stick around active range of floating point
- 5. Kinks in the objective
- 6. Use only few datapoints
- 7. Be careful with the step size h
- 8. Gradcheck during a “characteristic” mode of operation
- 9. Don’t let the regularization overwhelm the data
- 10. Remember to turn off dropout/augmentations
- 11. Check only few dimensions
- Sanity checks(完整性检查)
- Babysitting the learning process(监视训练过程)
- Parameter updates(参数更新)
- Hyperparameter Optimization(超参数优化)
- Model Ensembles(模型集成)
部分内容参照了这篇博客:深度学习知识点笔记汇总
Gradient checks
梯度检验就是将解析法(也就是用导数公式求解梯度)计算的梯度与用数值法(也就是通过导数定义求解梯度)计算的梯度进行对比,以检验解析法公式的正确性。因为数值法是通过导数定义进行求解,当步长h设置的足够小时,就可以求得较为精确的梯度值,准确性较高,但是存在求解速度慢的缺点。相反,解析法直接按照给定的公式计算梯度就可以了,但是当问题比较复杂时,公式往往难以求出,而且容易出错。于是,就有了梯度检验这个过程了。
1. Use the centered formula
在用数值法计算梯度的时候,有两种计算方法。使用第二个式子的计算结果会比第一个式子好,因为第二个往往比第一个更加精确。使用泰勒展开式看,可以验证第一个式子的误差是
O
(
h
)
O(h)
O(h),而第二个式子只有
O
(
h
2
)
O(h^2)
O(h2)
2. Use relative error for the comparison
在对比两种方法求解的梯度误差时,应计算相对误差,因为一般的求差值/差值平方的方法是有些问题的。如,考虑他们的差值为1e-4的情况。如果两个梯度大约是1.0,这似乎是一个非常合适的差异,所以我们会认为这两个梯度是匹配的。但是如果两个梯度都是1e-5或更低,那么我们会认为1e-4是一个巨大的差异,很可能是相对错误。因此,考虑相对误差总是更合适。
在实践中:
- 相对误差>1e-2 通常意味着计算的梯度可能是错误的
- 1e-2>相对误差>1e-4 的结果不是很理想
- 相对误差<1e-4 在有kinks(第5点会讲到kinks是指什么)时这个结果通常是可以的。但如果没有kinks(例如使用非线性tanh和softmax),则1e-4过高。
- 1e-7及以下 则表明结果理想
网络越深,相对误差就越大。因此,如果你对一个10层网络的输入数据进行梯度检查,1e-2的相对误差可能是可以的,因为误差是在路上积累的。相反,对于一个单一的可微分函数,1e-2的误差可能表明梯度不正确。
3. Use double precision
通常情况下,使用单精度浮点来计算梯度检查,即使梯度实现正确,你也可能得到很高的相对误差(高达 1e-2)。使用双精度可以确保计算结果更准确。
4. Stick around active range of floating point
在计算或显示结果时,保持数值在浮点数的精度范围内,才能获得正确结果。如果结果非常小(eg. 1e-12),则考虑将其乘以一定的常数(eg. 1e12)。结果在1.0数量级上最佳。
推荐读物:“What Every Computer Scientist Should Know About Floating-Point Arithmetic”
5. Kinks in the objective
kinks是指目标函数的不可微部分。由ReLU(max(0,x))SVM损失函数、Maxout神经元等函数引入。 在梯度检验过程中要注意的一个不准确的来源就是kinks。考虑在x=1e-6处对ReLU函数进行梯度检查。由于x<0,这一点上的分析梯度正好为零。然而,数值梯度会突然计算出一个非零梯度,因为f(x+h)可能会越过kink(例如,如果h>1e-6)并引入一个非零贡献。
请注意,有可能知道在评估损失时是否越过了一个kink。这可以通过跟踪所有 "赢家 "的身份来完成,其函数形式为max(x,y);也就是说,在向前传递过程中,x或y是否更高。如果在评估f(x+h)和f(x-h)时,至少有一个赢家的身份发生了变化,那么就有可能越过了kink,数字梯度将不精确。
6. Use only few datapoints
解决kinks问题的一个方法是在梯度检验时只使用少量的数据点进行计算,因为含有kink的损失函数(例如由于使用ReLU或margin loss等)在较少的数据点上会有较少的结点,所以当你进行有限差分近似(即使用1中的两个公式求导数的方法)时,就不太可能越过kink。用很少的数据点进行计算也可以让你的梯度检验更快更有效。
7. Be careful with the step size h
检验时h不一定是越小越好。因为当h非常小时,就有可能会遇到数值精度的问题。在刚开始进行检验的时候,取h为1e-4到1e-6之间进行测试将更容易得到正确的检验结果。
这篇Wikipedia文章包含一个图表,在X轴上标出h的值,在Y轴上标出数值梯度误差。
8. Gradcheck during a “characteristic” mode of operation
梯度检验是在参数空间中的一个特定(通常是随机的)单点执行的。即使梯度检验在那一点上成功,也不能立即确定梯度是否被正确地全局执行。此外,随机初始化可能不是参数空间中最 "characteristic "的点,事实上可能会引入病态的情况,即梯度似乎被正确地执行了,但实际上并没有。
9. Don’t let the regularization overwhelm the data
通常情况下,损失函数是数据损失和正则化损失的总和(e.g L2正则化)。需要注意的一个危险是正则化损失可能会压盖(overwhelm)数据损失,在这种情况下,梯度将主要来自正则化项(通常具有更简单的梯度表达式)。这可能会掩盖数据损失梯度的错误实现。因此,建议先关闭正则化并单独检查数据损失,然后再独立检查正则化项。执行后者的一种方法是hake代码以消除数据损失的贡献。另一种方法是增加正则化的强度,以确保其效果在梯度检查中不可忽略,这样就能检测到不正确的实现。
10. Remember to turn off dropout/augmentations
在执行梯度检验时,请记住关闭网络中的任何不确定性影响,如丢失(dropout)、随机数据增强(random data augmentations)等。否则,在估计数值梯度时,这些明显会引入巨大的错误。关闭这些部分的缺点是不会对它们进行梯度检查(例如,可能是dropout没有正确地反向传播)。因此,更好的解决方案可能是在计算f(x+h)和f(x−h)之前,以及在计算解析梯度时,强制特定的随机种子(force a particular random seed)。
11. Check only few dimensions
在实践中,梯度的大小可以达到百万个参数。在这些情况下,只检验梯度的部分维度并假设其他尺寸是正确的才是可行的。要注意的一个问题是确保为每个单独的参数检查几个维度。在一些应用中,为了方便起见,人们把参数组合成一个大的参数向量。例如,在这些情况下,偏差(bias)只能从整个向量中占用少量参数,因此重要的是不要随机采样,而是要考虑到这一点,并检查所有参数是否获得正确的梯度。
Sanity checks(完整性检查)
在进行高运算量的网络优化之前,可以先考虑进行以下几个检验:
1. Look for correct loss at chance performance
当使用少量参数进行初始化时,请确保得到了预期的损失。最好先单独检查数据损失(将正则化强度设置为零)。例如,对于具有Softmax分类器的CIFAR-10,我们期望初始损失为2.302,因为我们期望每个类的扩散概率为0.1(因为有10个类),并且Softmax损失是正确类的负对数概率,所以:-ln(0.1)=2.302。如果检验时没有获得预期损失,那么初始化可能会出现问题。
2. Increasing the regularization strength should increase the loss
经过上一步的初次检验后,增加正则化强度时,损失(loss)也应该随之增加
3. Overfit a tiny subset of data
最后也是最重要的一点,在对完整数据集进行训练之前,试着先对数据的一小部分(例如20个样本)进行训练,并确保可以实现损失(loss)为0。对于这个实验,最好将正则化设置为零,否则会影响你获得0损失。如果没有通过这个检验,则该网络模型就不值得继续使用完整的数据集进行训练。
请注意,可能会发生这样的情况:你可以过拟合非常小的数据集,但仍然有一个不正确的实现。例如,如果你的数据点的特征由于某些错误而随机,那么就有可能过度拟合你的小训练集,但是当你把它折合到你的完整数据集时,你将永远不会注意到任何泛化现象。
Babysitting the learning process(监视训练过程)
在神经网络的训练过程中,有许多有用的量需要监控。下面介绍的几种曲线图是分析训练过程的有用工具,可以用来获得关于不同超参数设置的直观信息,以及如何更改这些参数以提高训练效率。
下面的图的x轴都是以epoch为单位,用来衡量每个样本在预期训练中被使用的次数(例如,一个epoch意味着每个例子都被使用了一次)。由于迭代次数取决于batch大小的设置,因此最好跟踪epoch而不是迭代次数。
1. Loss function
在训练过程中,第一个有用的跟踪量是损失(loss),因为它是在前向传播过程中对各个batch进行评估的。
其中左图是不同学习率下损失的变化,右图是正常训练时损失随着epoch的变化情况。在右图中,损失中的“抖动”量与batch大小有关。当batch大小为1时,抖动将相对较高。当batch大小为完整数据集时,抖动将最小,因为每次梯度更新都应单调地改进损失函数(除非学习速率设置得太高)。
有些人喜欢在log域中绘制损失函数,因为学习进度通常呈指数形式。
如果将多个交叉验证模型绘制在同一损失图上,它们之间的差异将变得更加明显。
从这个网站上可以看到一些有趣的损失函数图。
2. Train/Val accuracy
训练分类器时要跟踪的第二个重要指标是验证/训练(validation/training)精度。此图可以让你深入了解模型中过度拟合的程度:
训练和验证精度之间的差距表明了过度拟合的程度。两种可能的情况如上图所示。蓝色的validation accuracy曲线显示,与training accuracy相比,validation accuracy非常小,表明过拟合很严重(注意,validation accuracy甚至可能在某个点之后开始下降)。在实践中看到这种过拟合时,可以通过增加正则化(更强的L2权重惩罚、更多的dropout等)或收集更多的数据来解决。另一种可能的情况是validation accuracy很好地跟踪了training accuracy(绿线所示)。这种情况表明你的模型不够大(欠拟合):可以通过增加参数的数量使模型变大。
这张图片的内容可看我之前的吴恩达机器学习笔记
3. Ratio of weights: updates
可以独立地为每组参数计算和跟踪此比率。一个粗略的推断是这个比率应该在1e-3左右。如果低于此值,则学习率可能太低。如果高于此值,那么学习率可能太高。下面是一个具体的例子:
# assume parameter vector W and its gradient vector dW
param_scale = np.linalg.norm(W.ravel())
update = -learning_rate*dW # simple SGD update
update_scale = np.linalg.norm(update.ravel())
W += update # the actual update
print update_scale / param_scale # want ~1e-3
与跟踪最小值或最大值不同,有些人更喜欢计算和跟踪梯度的范数及其更新。这些指标通常是相关的,通常会给出大致相同的结果。
4. Activation / Gradient distributions per layer
不正确的初始化可能会减慢甚至使学习过程完全停止。幸运的是,这个问题相对容易诊断。一种方法是绘制网络所有层的激活/梯度直方图。看到任何奇怪的分布不是一个好的迹象。例如,对于tanh神经元,我们希望看到神经元激活值分布在整个[-1,1]范围,而不是看到所有的神经元输出零,或所有的神经元集中于-1或1。
5. First-layer Visualizations
最后,如果处理的是图像像素时,可以直观地将第一层特征绘制出来:
上图是神经网络第一层的权值可视化示例。左:噪声特征表明网络存在问题:网络未融合,学习率设置不当,权重正则化惩罚过低。右图:漂亮、流畅、干净和多样的特征很好地表明训练进展顺利。
Parameter updates(参数更新)
有关这部分的内容在我的“纽约大学深度学习PyTorch课程笔记Week5&6”中也有
一旦用反向传播计算出解析梯度,梯度就被用来进行参数更新。有几种执行更新的方法,我们接下来讨论。
1. SGD and bells and whistles
1.1 Vanilla update
最简单的更新形式是沿负梯度方向更新参数(因为梯度指示增加的方向,但我们通常希望最小化损失函数)。设参数x和梯度dx,最简单的更新形式如下:
# Vanilla update
x += - learning_rate * dx
其中learning_rate是一个超参数(一个固定常数)。
1.2 Momentum update
动量更新是另一种在深度网络上几乎总是具有更好收敛速度的方法。这个更新从物理角度来看待优化问题。
特别是,损失可以解释为丘陵地形的高度(因此也可以解释为势能,因为U=mgh,因此U∝h)。用随机数初始化参数,相当于在某个位置设置一个初始速度为零的粒子。然后,优化过程可以被视为等同于模拟参数向量(即粒子)在landscape上滚动的过程。由于粒子上的力与势能的梯度有关(即F=-∇U),粒子感受到的力正是损失函数的(负)梯度。此外,F=ma,所以(负)梯度在这个观点中与粒子的加速度成正比
与SGD优化中梯度直接对位置进行积分不同,它认为梯度只直接影响速度,而速度反过来又影响位置。
# Momentum update
v = mu * v - learning_rate * dx # integrate velocity
x += v # integrate position
这里,v通常初始化为0,mu在最优化中被称为动量(其典型值约为0.9),但其物理意义与摩擦系数更为一致。这个变量有效地抑制了速度,降低了系统的动能,否则优化结果永远不会停在最低点。交叉验证时,此参数通常设置为[0.5、0.9、0.95、0.99]。与学习率的退火计划(annealing schedules,稍后讨论)类似,优化有时可以从动量计划(momentum schedules)中受益,动量在学习的后期阶段增加。一个典型的设置是从大约0.5的动量开始,在多个时间段内退火到0.99左右。
1.3 Nesterov Momentum
Nesterov Momentum是Momentum更新的一个稍有不同的版本,该版本最近越来越流行。它对凸函数具有较强的理论收敛性保证,在实际应用中也始终比标准Momentum略好。两种方法的对比如下图所示:
其运行代码示例:
x_ahead = x + mu * v
# evaluate dx_ahead (the gradient at x_ahead instead of at x)
v = mu * v - learning_rate * dx_ahead
x += v
然而,在实践中,人们更喜欢表达的更像vanilla SGD或1.2中的动量更新的形式。这可以通过变量代换x_ahead = x + mu * v来实现,然后用x_ahead替换x来表示更新。也就是说,我们实际存储的参数向量始终是ahead版本。上面代码块中的x_ahead的方程式(但将其重新命名为x)变成:
v_prev = v # back this up
v = mu * v - learning_rate * dx # velocity update stays the same
x += -mu * v_prev + (1 + mu) * v # position update changes form
更多信息参考:
- Advances in optimizing Recurrent Networks by Yoshua Bengio, Section 3.5,
- TRAINING RECURRENT NEURAL NETWORKS by Ilya Sutskever, Section 7.2。
2 Annealing the learning rate
在训练深层网络时,随着时间的推移对学习速度进行退火通常是有帮助的。要记住的好直觉是,在高学习率下,系统包含太多动能(kinetic energy),参数向量无序地反弹,无法稳定到损失函数的更深但更窄的部分。想知道什么时候降低学习速率是很棘手的:慢慢地降低学习速度,你会浪费计算时间,在很长一段时间内,你的学习速度几乎没有提高。但是,如果它的降低过于剧烈,系统会冷却得太快,无法达到它所能达到的最佳位置。实现学习率衰减有三种常见类型:
- Step decay。每过几个epochs,学习率就会降低一倍。典型的值可能是每5个epochs减少一半的学习率,或每20个epochs减少0.1。这些数字在很大程度上取决于问题的类型和模型。在实践中,你可能会看到一种启发式方法,即在使用固定学习率进行训练时观察validation error,并在validation error停止改善时将学习率降低一个常数(例如0.5)。
- Exponential decay。具有数学形式α=α0e−kt,其中α0,k是超参数,t是迭代次数(但也可以使用epochs为单位)。
- 1/t decay。具有数学形式α=α0/(1+kt),其中a0,k是超参数,t是迭代次数。
在实践中,Step decay更为可取,因为它所涉及的超参数(the fraction of decay和以epochs为单位的step timings)比超参数k更易于解释。
3 Second order methods
另一类流行的优化方法是基于牛顿法的二阶优化算法,它迭代了以下更新:
这里,Hf(x)是Hessian矩阵,它是函数的二阶偏导数的平方矩阵。 Hessian矩阵直观地描述了损失函数的局部曲率,从而使我们能够执行更有效的更新。特别是,与Hessian的逆相乘导致优化在平缓曲率方向上采取更大的step,而在陡峭曲率方向上采取更小的step。值得注意的是,更新公式中没有任何学习率超参数,这被认为时该方法相比于一阶方法的一大优势。
然而,上述更新对于大多数深度学习应用程序是不切实际的,因为计算(和求逆)显式形式的Hessian在空间和时间上都是一个非常昂贵的过程。因此,各种各样的quasi-Newton方法被提出,用以寻求近似求逆Hessian。其中,最流行的是L-BFGS,它使用随时间变化的梯度中的信息隐式地进行近似(即不计算完整矩阵)。
在实际应用中,L-BFGS或类似的二阶方法应用于大规模深度学习和卷积神经网络的情况并不常见。相反,基于Nesterov动量的SGD变体更常见,因为它们更简单,更容易扩展。
更多信息参考:
- Large Scale Distributed Deep Networks from the Google Brain team,
- SFO algorithm。
4 Per-parameter adaptive learning rate methods
到目前为止,我们讨论的所有以前的方法都全局地、平等地操纵所有参数的学习速率。在本节中,我们将重点介绍一些在实践中可能遇到的常见自适应方法:
4.1 Adagrad
Adagrad是Duchi等人提出的一种自适应学习率方法。
# Assume the gradient dx and parameter vector x
cache += dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
其中,变量cache的大小等于梯度的大小,并跟踪每个参数的梯度平方和。然后,这将用于规范化参数更新步骤(按元素)。请注意,接收高梯度的权重将降低其有效学习率,而接收少量或不频繁更新的权重将提高其有效学习率。有趣的是,平方根运算非常重要,没有平方根运算,算法的性能就差得多。平滑项eps(通常设置在1e-4到1e-8的范围内)避免被零除。Adagrad的一个缺点是,在深度学习的情况下,单调的学习率通常过于激进,会出现过早停止学习的情况。
4.2 RMSprop
RMSprop是一种非常有效的自适应学习率方法。但是RMSprop并没有发表,而是在 slide 29 of Lecture 6 of Geoff Hinton’s Coursera 中。RMSProp以一种非常简单的方式改进了Adagrad方法,试图降低其具有激进的、单调递减的学习速率。特别是,它使用梯度平方的移动平均值:
cache = decay_rate * cache + (1 - decay_rate) * dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
这里,decay_rate是一个超参数,典型值是[0.9,0.99,0.999]。注意,x+=的更新与Adagrad相同,但是* *cache**变量是一个“leaky”。因此,RMSProp仍然基于梯度的大小来调节每个权重的学习率,这具有有益的均衡效果,但与Adagrad不同的是,更新不会单调地变小。
4.3 Adam
Adam是最近提出的一个更新方法,看起来有点像RMSProp。(简化的)更新如下所示:
m = beta1*m + (1-beta1)*dx
v = beta2*v + (1-beta2)*(dx**2)
x += - learning_rate * m / (np.sqrt(v) + eps)
这里,更新看起来与RMSProp update完全相同,只是使用了梯度 m的“平滑”版本,而不是原始(可能是有噪声的)梯度向量dx。原文的推荐值为eps=1e-8,beta1=0.9,beta2=0.999。在实践中,目前推荐使用Adam作为默认算法,并且通常比RMSProp稍微好一点。不过,作为替代方案,SGD+Nesterov Momentum通常也值得一试。完全Adam更新还包括bias校正机制,该机制补偿在向量m、v完全“预热”(“warm up”)之前,在最初的几个时间步中向量m、v都被初始化并且bias被置零的问题。使用bias校正机制,更新如下所示:
# t is your iteration counter going from 1 to infinity
m = beta1*m + (1-beta1)*dx
mt = m / (1-beta1**t)
v = beta2*v + (1-beta2)*(dx**2)
vt = v / (1-beta2**t)
x += - learning_rate * mt / (np.sqrt(vt) + eps)
这里,更新是迭代和其他参数的函数。
其他参考资料:
- Unit Tests for Stochastic Optimization提出了一系列测试作为随机优化的标准化基准
Hyperparameter Optimization(超参数优化)
训练神经网络会涉及到许多超参数设置。神经网络中最常见的超参数包括:
- the initial learning rate
- learning rate decay schedule (such as the decay constant)
- regularization strength (L2 penalty, dropout strength)
但正除此之外,还有许多相对不太敏感的超参数,例如在单参数自适应学习方法中,动量的设置及其schedule等方面。在本节中,我们将介绍一些执行超参数搜索的tips和tricks:
1. Implementation
大型神经网络通常需要很长时间来训练,因此执行超参数搜索可能需要很多天/周的时间。记住这一点很重要,因为它会影响代码库的设计。一种特殊的设计是让一个worker不断地对随机超参数进行采样并执行优化。在训练期间,worker将跟踪每个epoch之后的validation performance,并将模型检查点(以及其他训练统计信息,如随时间的损失 the loss over time)写入文件,最好是在共享文件系统上。将validation performance直接包含在文件名中是很有用的,这样可以方便地检查和排序进度。然后还有第二个程序,我们称之为master,它在计算集群中启动或终止worker,还可以检查worker编写的检查点,绘制他们的训练统计数据等。
2. Prefer one validation fold to cross-validation
在大多数情况下,一个大小合适的单一验证集大大简化了代码库,而不需要多次交叉验证。你会听到人们说他们“交叉验证”了一个参数,但很多时候都假设他们仍然只使用了一个验证集。
3. Hyperparameter ranges
在对数尺度上搜索超参数。例如,学习率的典型抽样如下learning_rate = 10 ** uniform(-6, 1)。也就是说,我们从 一个均匀分布中生成一个随机数,然后把它乘以10的幂。正则化强度也应采用相同的策略。直观地说,这是因为学习率和正则化强度对training dynamics有乘法效应(multiplicative effects)。例如,当学习率为0.001时,学习率加0.01的固定变化对dynamics有很大影响,而当学习率为10时,几乎没有影响。这是因为在更新中学习率和计算的梯度相乘了。因此,将一个范围内的学习率乘以或除以某个值,比将这个范围内的学习率乘以或减去某个值要合适得多。一些参数(如dropout)通常在原始比例(original scale)中搜索(如dropout = uniform(0,1))。
4. Prefer random search to grid search
正如Bergstra and Bengio在Random Search for Hyper-Parameter Optimization上所说的:“randomly chosen trials比trials on a grid更有效”。事实证明,这通常也更容易实现。
图片来自论文Random Search for Hyper-Parameter Optimization,要更深入了解可以下载来仔细阅读。
通常情况下,某些超参数比其他超参数更重要(例如图中顶部的超参数与左侧的超参数相比)。相比于网格搜索,执行随机搜索可以更精确地发现重要值。
5. Careful with best values on border
有时可能发生的情况是,你正在一个坏的范围(bad range)内搜索一个超参数(如学习率)。例如,假设我们使用learning_rate = 10 ** uniform(-6, 1)。一旦我们获取到结果,重要的是要再次检查最终的学习率不在此间隔的边缘,否则你可能会错过超出此间隔的更优化的超参数设置。
6. Stage your search from coarse to fine
在实践中,一个好的方法是首先在粗略范围(例如10**[-6,1])中搜索,然后根据最佳结果出现的位置缩小范围。此外,在只训练1个epoch或更少epoch的情况下进行初始粗搜索也是有帮助的,因为许多超参数设置会导致模型根本不进行学习,或者因为无穷大的代价(infinite cost)而直接崩溃,代价是无限的。然后,第二阶段可以对5个epochs执行更窄的搜索,最后一阶段可以在最终范围内对更多epochs执行详细搜索。
7. Bayesian Hyperparameter Optimization
贝叶斯超参数优化是一个致力于提出更有效地寻找超参数空间的算法研究领域。其核心思想是在查询不同超参数下的性能时,适当地平衡exploration - exploitation之间的权衡。在这些模型的基础上还开发了多个库,其中一些比较著名的是Spearmint、SMAC和Hyperopt。然而,在使用ConvNets的实际环境中,carefully-chosen intervals击败random search仍然是相对困难的。
Model Ensembles(模型集成)
在实践中,将神经网络的性能提高几个百分点的一个可靠方法是训练多个独立的模型,并在测试时平均它们的预测。随着集成中模型数量的增加,性能通常会单调地提高(尽管回报率会逐渐减少)。此外,随着集合中模型的变化越大,改进就越显著。组建一个集成模型有几种方法:
1. Same model, different initializations
利用交叉验证法确定最优超参数,然后训练多个具有最优超参数集但随机初始化不同的模型。这种方法的问题在于,变化仅仅是由于初始化的不同引起的。
2. Top models discovered during cross-validation
使用交叉验证来确定最佳的超参数,然后选择前几个(例如10个)模型来形成集合。这提高了集合的多样性,但也有包含次优模型的危险。在实践中,这可能更容易执行,因为它不需要在交叉验证后对模型进行额外的再训练。
3. Different checkpoints of a single model
如果训练的代价非常昂贵,那么有些人在一段时间内(例如在每个epoch之后)对单个网络的不同检查点进行检查并使用这些检查点来形成一个集合的效果是有限的。显然,这种方法缺乏多样性,但在实践中仍然可以很好地工作。这种方法的优点是代价非常低。
4. Running average of parameters during training
与最后一点相关,一种几乎总是获得额外性能的廉价方法是,在内存中保持网络权重的第二个副本,该副本在训练期间保持先前权重的指数衰减总和。这样,就可以平均网络在过去几次迭代中的状态。在最后几个步骤中,这种“平滑”的权重版本几乎总能获得更好的validation error。粗略的直觉是,目标是碗状的,网络在mode周围跳跃,所以平均值有更高的机会接近mode。
模型集成的一个缺点是,它们需要较长的时间来评估test样本。