文章

模型优化方法及思路

模型优化方法及思路

1. 一个合适的梯度下降优化算法

参考论文:1609.04747.pdf (arxiv.org)

梯度下降法是最小化目标函数 J(θ) 的一种方法,其中, θ ∈ ℝd 为模型参数,梯度下降法利用目标函数关于参数的梯度 ∇θJ(θ) 的反方向更新参数。学习率 η 决定达到最小值或者局部最小值过程中所采用的步长的大小。即我们沿着目标函数的斜面下降的方向,直到到达谷底。

梯度下降法是最著名的优化算法之一,也是迄今优化神经网络时最常用的方法。同时,在每一个最新的深度学习库中都包含了各种优化的梯度下降法的实现。然而,这些算法通常是作为黑盒优化器使用,因此,很难对其优点和缺点的进行实际的解释。目前在我见过的所有文章中只有《An overview of gradient descent optimization algoritms》这篇给出了最清楚的解释,下面主要是参考这篇文章的内容完成的。

1.1 梯度下降法的变形形式

梯度下降法有3种变形形式,它们之间的区别为我们在计算目标函数的梯度时使用到多少数据。根据数据量的不同,我们在参数更新的精度和更新过程中所需要的时间两个方面做出权衡。

1.1.1 批梯度下降法

Vanilla梯度下降法,又称为批梯度下降法(batch gradient descent),在整个训练数据集上计算损失函数关于参数 θ 的梯度:

\[θ = θ − η ⋅ ∇θJ(θ)\]

因为在执行每次更新时,我们需要在整个数据集上计算所有的梯度,所以批梯度下降法的速度会很慢,同时,批梯度下降法无法处理超出内存容量限制的数据集。批梯度下降法同样也不能在线更新模型,即在运行的过程中,不能增加新的样本。

批梯度下降法的代码如下所示:

1
2
3
for i in range(nb_epochs):
    params_grad = evaluate_gradient(loss_function, data, params)
    params = params - learning_rate * params_grad

对于给定的迭代次数,首先,我们利用全部数据集计算损失函数关于参数向量params的梯度向量params_grad。然后,我们利用梯度的方向和学习率更新参数,学习率决定我们将以多大的步长更新参数。对于凸误差函数,批梯度下降法能够保证收敛到全局最小值,对于非凸函数,则收敛到一个局部最小值。

1.1.2 随机梯度下降

随机梯度下降法(stochastic gradient descent, SGD)根据每一条训练样本 x(i) 和标签 y(i) 更新参数:

\[θ = θ − η ⋅ ∇θJ(θ; x(i); y(i))\]

对于大数据集,因为批梯度下降法在每一个参数更新之前,会对相似的样本计算梯度,所以在计算过程中会有冗余。而SGD在每一次更新中只执行一次,从而消除了冗余。因而,通常SGD的运行速度更快,同时,可以用于在线学习。SGD以高方差频繁地更新,导致目标函数出现如下图所示的剧烈波动。

fd3d34e73811035e071b70878c24a5a6

与批梯度下降法的收敛会使得损失函数陷入局部最小相比,由于SGD的波动性,一方面,波动性使得SGD可以跳到新的和潜在更好的局部最优。另一方面,这使得最终收敛到特定最小值的过程变得复杂,因为SGD会一直持续波动。然而,已经证明当我们缓慢减小学习率,SGD与批梯度下降法具有相同的收敛行为,对于非凸优化和凸优化,可以分别收敛到局部最小值和全局最小值。与批梯度下降的代码相比,SGD的代码片段仅仅是在对训练样本的遍历和利用每一条样本计算梯度的过程中增加一层循环。在每一次循环中,我们打乱训练样本。

1
2
3
4
5
for i in range(nb_epochs):
    np.random.shuffle(data)
    for example in data:
        params_grad = evaluate_gradient(loss_function, example, params)
        params = params - learning_rate * params_grad

1.1.3 小批量梯度下降法

小批量梯度下降法最终结合了上述两种方法的优点,在每次更新时使用 n 个小批量训练样本:

\[θ = θ − η ⋅ ∇θJ(θ; x(i : i + n); y(i : i + n))\]

a)减少参数更新的方差,这样可以得到更加稳定的收敛结果;b)可以利用最新的深度学习库中高度优化的矩阵优化方法,高效地求解每个小批量数据的梯度。通常,小批量数据的大小在 50 50 50 到 256 256 256之间,也可以根据不同的应用有所变化。当训练神经网络模型时,小批量梯度下降法是典型的选择算法,当使用小批量梯度下降法时,也将其称为SGD。注意:在下文的改进的SGD中,为了简单,我们省略了参数 x(i : i + n); y(i : i + n) 。

1
2
3
4
5
for i in range(nb_epochs):
    np.random.shuffle(data)
    for batch in get_batches(data, batch_size=50):
        params_grad = evaluate_gradient(loss_function, batch, params)
        params = params - learning_rate * params_grad

1.2 梯度下降优化算法

1.2.1 动量法

SGD很难通过陡谷,即在一个维度上的表面弯曲程度远大于其他维度的区域,这种情况通常出现在局部最优点附近。在这种情况下,SGD摇摆地通过陡谷的斜坡,同时,沿着底部到局部最优点的路径上只是缓慢地前进,这个过程如图所示。

8f9013fd4a2474c53d51e28f64be9ec5

如图所示,动量法是一种帮助SGD在相关方向上加速并抑制摇摆的一种方法。动量法将历史步长的更新向量的一个分量 *γ *增加到当前的更新向量中(部分实现中交换了公式中的符号)

\[\begin{aligned} v_{t} &=\gamma v_{t-1}+\eta \nabla_{\theta} J(\theta) \\\\ \theta &=\theta-v_{t} \end{aligned}\]

动量项 γ 通常设置为0.9或者类似的值。

从本质上说,动量法,就像我们从山上推下一个球,球在滚下来的过程中累积动量,变得越来越快(直到达到终极速度,如果有空气阻力的存在,则 γ < 1 。同样的事情也发生在参数的更新过程中:对于在梯度点处具有相同的方向的维度,其动量项增大,对于在梯度点处改变方向的维度,其动量项减小。因此,我们可以得到更快的收敛速度,同时可以减少摇摆。

be2ab57697f0bbbd3477568bf24c6245

1.2.2 Nesterov

然而,球从山上滚下的时候,盲目地沿着斜率方向,往往并不能令人满意。我们希望有一个智能的球,这个球能够知道它将要去哪,以至于在重新遇到斜率上升时能够知道减速。

Nesterov加速梯度下降法(Nesterov accelerated gradient,NAG)是一种能够给动量项这样的预知能力的方法。我们知道,我们利用动量项 $\gamma v_{t-1}$

来更新参数 *θ *。通过计算 $\theta - \gamma v_{t-1}$ 能够告诉我们参数未来位置的一个近似值(梯度并不是完全更新),这也就是告诉我们参数大致将变为多少。通过计算关于参数未来的近似位置的梯度,而不是关于当前的参数 θ 的梯度,我们可以高效的求解:

\[\begin{aligned}v_{t} &=\gamma v_{t-1}+\eta \nabla_{\theta} J\left(\theta-\gamma v_{t-1}\right) \\\\ \theta &=\theta-v_{t} \end{aligned}\]

同时,我们设置动量项 *γ *大约为 0.9 。动量法首先计算当前的梯度值(下图中的小的蓝色向量),然后在更新的累积梯度(大的蓝色向量)方向上前进一大步,Nesterov加速梯度下降法NAG首先在先前累积梯度(棕色的向量)方向上前进一大步,计算梯度值,然后做一个修正(绿色的向量)。这个具有预见性的更新防止我们前进得太快,同时增强了算法的响应能力,这一点在很多的任务中对于RNN的性能提升有着重要的意义。

79f51efb98033b59a2717258f79d3c41

既然我们能够使得我们的更新适应误差函数的斜率以相应地加速SGD,我们同样也想要使得我们的更新能够适应每一个单独参数,以根据每个参数的重要性决定大的或者小的更新。

1.2.3 Adagrad

Adagrad是这样的一种基于梯度的优化算法:让学习率适应参数,对于出现次数较少的特征,我们对其采用更大的学习率,对于出现次数较多的特征,我们对其采用较小的学习率。因此,Adagrad非常适合处理稀疏数据。

Dean等人发现Adagrad能够极大提高了SGD的鲁棒性并将其应用于Google的大规模神经网络的训练,其中包含了YouTube视频中的猫的识别。此外,Pennington等人利用Adagrad训练Glove词向量,因为低频词比高频词需要更大的步长。

前面,我们每次更新所有的参数 θ 时,每一个参数 θi 都使用的是相同的学习率 η 。由于Adagrad在 t 时刻对每一个参数 thetai 使用了不同的学习率,我们首先介绍Adagrad对每一个参数的更新,然后我们对其向量化。为了简洁,令 gt, i 为在 t 时刻目标函数关于参数 θi 的梯度:

\[g_{t, i} = ∇_θJ(θ_i)\]

在 t 时刻,对每个参数 θi 的更新过程变为:

\[θ_{t + 1, i} = θ_{t, i} − η ⋅ g_{t, i}\]

对于上述的更新规则,在 t 时刻,基于对 θi 计算过的历史梯度,Adagrad修正了对每一个参数 θi 的学习率:

\[\theta_{t+1, i}=\theta_{t, i}-\frac{\eta}{\sqrt{G_{t, i i}+\epsilon}} \cdot g_{t, i}\]

其中, Gt ∈ ℝd × d 是一个对角矩阵,对角线上的元素 i, i 是直到 t 时刻为止,所有关于 θi 的梯度的平方和(Duchi等人将该矩阵作为包含所有先前梯度的外积的完整矩阵的替代,因为即使是对于中等数量的参数 d ,矩阵的均方根的计算都是不切实际的。), ϵ 是平滑项,用于防止除数为 0(通常大约设置为 1e − 8 )。比较有意思的是,如果没有平方根的操作,算法的效果会变得很差。

由于 Gt 的对角线上包含了关于所有参数 θ 的历史梯度的平方和,现在,我们可以通过 Gtgt 之间的元素向量乘法 ⊙ 向量化上述的操作:

\[\theta_{t+1}=\theta_{t}-\frac{\eta}{\sqrt{G_{t}+\epsilon}} \odot g_{t}\]

Adagrad算法的一个主要优点是无需手动调整学习率。在大多数的应用场景中,通常采用常数 0.01 。

Adagrad的一个主要缺点是它在分母中累加梯度的平方:由于没增加一个正项,在整个训练过程中,累加的和会持续增长。这会导致学习率变小以至于最终变得无限小,在学习率无限小时,Adagrad算法将无法取得额外的信息。接下来的算法旨在解决这个不足。

1.2.4 Adadelta

Adadelta是Adagrad的一种扩展算法,以处理Adagrad学习速率单调递减的问题。不是计算所有的梯度平方,Adadelta将计算计算历史梯度的窗口大小限制为一个固定值 w

在Adadelta中,无需存储先前的 w 个平方梯度,而是将梯度的平方递归地表示成所有历史梯度平方的均值。在 t 时刻的均值 E[g2]t 只取决于先前的均值和当前的梯度(分量 γ 类似于动量项):

\[[g^2]_t = γE[g^2]_{t − 1} + (1 − γ)g_t^2\]

我们将 γ *设置成与动量项相似的值,即 0.9 左右。为了简单起见,我们利用参数更新向量 $θ_t$ *重新表示SGD的更新过程:

\[\begin{array}{c} \Delta \theta_{t}=-\eta \cdot g_{t, i} \\\\ \theta_{t+1}=\theta_{t}+\Delta \theta_{t} \end{array}\]

我们先前得到的Adagrad参数更新向量变为:

\[\Delta \theta_{t}=-\frac{\eta}{\sqrt{G_{t}+\epsilon}} \odot g_{t}\]

现在,我们简单将对角矩阵 Gt 替换成历史梯度的均值 $E[g^2]_t$ :

\[\Delta \theta_{t}=-\frac{\eta}{\sqrt{E\left[g^{2}\right]_{t}+\epsilon}} g_{t}\]

作者指出上述更新公式中的每个部分(与SGD,动量法或者Adagrad)并不一致,即更新规则中必须与参数具有相同的假设单位。为了实现这个要求,作者首次定义了另一个指数衰减均值,这次不是梯度平方,而是参数的平方的更新:

\[\left[\Delta \theta^{2}\right]_{t}=\gamma E\left[\Delta \theta^{2}\right]_{t-1}+(1-\gamma) \Delta \theta_{t}^{2}\]

因此,参数更新的均方根误差为:

\[RMS[\Delta \theta]_{t}=\sqrt{E\left[\Delta \theta^{2}\right]_{t}+\epsilon}\]

由于 $RMS[Δθ]t$ 是未知的,我们利用参数的均方根误差来近似更新。利用 $RMS[Δθ]{t − 1}$ 替换先前的更新规则中的学习率 η,最终得到Adadelta的更新规则:

\[\Delta \theta_{t}=-\frac{RMS[\Delta \theta]_{t-1}}{RMS[g]_{t}} g_{t} \\\\ \theta_{t+1}=\theta_{t}+\Delta \theta_{t}\]

使用Adadelta算法,我们甚至都无需设置默认的学习率,因为更新规则中已经移除了学习率。

1.2.5 RMSprop

RMSprop是一个未被发表的自适应学习率的算法,该算法由Geoff Hinton在其Coursera课堂的课程6e中提出。

RMSprop和Adadelta在相同的时间里被独立的提出,都起源于对Adagrad的极速递减的学习率问题的求解。实际上,RMSprop是先前我们得到的Adadelta的第一个更新向量的特例:

\[\begin{array}{c} E\left[g^{2}\right]_{t}=0.9 E\left[g^{2}\right]_{t-1}+0.1 g_{t}^{2} \\\\ \theta_{t+1}=\theta_{t}-\frac{\eta}{\sqrt{E\left[g^{2}\right]_{t}+\epsilon}} g_{t} \end{array}\]

同样,RMSprop将学习率分解成一个平方梯度的指数衰减的平均。Hinton建议将 γ 设置为 0.9,对于学习率 η ,一个好的固定值为 0.001 。

1.2.6 Adam

自适应矩估计(Adaptive Moment Estimation,Adam)是另一种自适应学习率的算法,Adam对每一个参数都计算自适应的学习率。除了像Adadelta和RMSprop一样存储一个指数衰减的历史平方梯度的平均 $v_t$,Adam同时还保存一个历史梯度的指数衰减均值 $m_t$,类似于动量:

\[\begin{array}{c} m_{t}=\beta_{1} m_{t-1}+\left(1-\beta_{1}\right) g_{t} \\\\ v_{t}=\beta_{2} v_{t-1}+\left(1-\beta_{2}\right) g_{t}^{2} \end{array}\]

$m_t$ 和 $v_t$ 分别是对梯度的一阶矩(均值)和二阶矩(非确定的方差)的估计,正如该算法的名称。当 $m_t$ 和 $v_t$ 初始化为 0 向量时,Adam的作者发现它们都偏向于0,尤其是在初始化的步骤和当衰减率很小的时候(例如 $β_1$ 和 $β_2$ 趋向于1)。

通过计算偏差校正的一阶矩和二阶矩估计来抵消偏差:

\[\hat{m}_{t}=\frac{m_{t}}{1-\beta_{1}^{t}} \\\\ \hat{v}_{t}=\frac{v_{t}}{1-\beta_{2}^{t}}\]

正如我们在Adadelta和RMSprop中看到的那样,他们利用上述的公式更新参数,由此生成了Adam的更新规则:

\[\theta_{t+1}=\theta_{t}-\frac{\eta}{\sqrt{\hat{v}_{t}}+\epsilon} \hat{m}_{t}\]

作者建议 $β_1$ 取默认值为 0.9 , $β_2$ 为 0.999 ϵ 为 $10^{−8}$。他们从经验上表明Adam在实际中表现很好,同时,与其他的自适应学习算法相比,其更有优势。

1.2.7 算法可视化

下面两张图给出了上述优化算法的优化行为的直观理解。

在下图左中,我们看到不同算法在损失曲面的等高线上走的不同路线。所有的算法都是从同一个点出发并选择不同路径到达最优点。注意:Adagrad,Adadelta和RMSprop能够立即转移到正确的移动方向上并以类似的速度收敛,而动量法和NAG会导致偏离,想像一下球从山上滚下的画面。然而,NAG能够在偏离之后快速修正其路线,因为NAG通过对最优点的预见增强其响应能力。

下图右中展示了不同算法在鞍点出的行为,鞍点即为一个点在一个维度上的斜率为正,而在其他维度上的斜率为负,正如我们前面提及的,鞍点对SGD的训练造成很大困难。这里注意,SGD,动量法和NAG在鞍点处很难打破对称性,尽管后面两个算法最终设法逃离了鞍点。而Adagrad,RMSprop和Adadelta能够快速想着梯度为负的方向移动,其中Adadelta走在最前面。

5d5166a3d3712e7c03af74b1ccacbeac

正如我们所看到的,自适应学习速率的方法,即 Adagrad、 Adadelta、 RMSprop 和Adam,最适合这些场景下最合适,并在这些场景下得到最好的收敛性。

1.2.8 选择使用哪种优化算法?

那么,我们应该选择使用哪种优化算法呢?如果输入数据是稀疏的,选择任一自适应学习率算法可能会得到最好的结果。选用这类算法的另一个好处是无需调整学习率,选用默认值就可能达到最好的结果。

总的来说,RMSprop是Adagrad的扩展形式,用于处理在Adagrad中急速递减的学习率。RMSprop与Adadelta相同,所不同的是Adadelta在更新规则中使用参数的均方根进行更新。最后,Adam是将偏差校正和动量加入到RMSprop中。在这样的情况下,RMSprop、Adadelta和Adam是很相似的算法并且在相似的环境中性能都不错。Kingma等人指出在优化后期由于梯度变得越来越稀疏,偏差校正能够帮助Adam微弱地胜过RMSprop。综合看来,Adam可能是最佳的选择。

有趣的是,最近许多论文中采用不带动量的SGD和一种简单的学习率的退火策略。已表明,通常SGD能够找到最小值点,但是比其他优化的SGD花费更多的时间,与其他算法相比,SGD更加依赖鲁棒的初始化和退火策略,同时,SGD可能会陷入鞍点,而不是局部极小值点。因此,如果你关心的是快速收敛和训练一个深层的或者复杂的神经网络,你应该选择一个自适应学习率的方法。

1.3 优化SGD的其他策略

最后,我们介绍可以与前面提及到的任一算法配合使用的其他的一些策略,以进一步提高SGD的性能。

1.3.1 数据集的洗牌和课程学习

总的来说,我们希望避免向我们的模型中以一定意义的顺序提供训练数据,因为这样会使得优化算法产生偏差。因此,在每一轮迭代后对训练数据洗牌是一个不错的主意。

另一方面,在很多情况下,我们是逐步解决问题的,而将训练集按照某个有意义的顺序排列会提高模型的性能和SGD的收敛性,如何将训练集建立一个有意义的排列被称为课程学习

Zaremba and Sutskever只能使用课程学习训练LSTM来评估简单程序,并表明组合或混合策略比单一的策略更好,通过增加难度来排列示例。

1.3.2 批量归一化

为了便于学习,我们通常用0均值和单位方差初始化我们的参数的初始值来归一化。 随着不断训练,参数得到不同的程度的更新,我们失去了这种归一化,随着网络变得越来越深,这种现象会降低训练速度,且放大参数变化。

批量归一化[8]在每次小批量数据反向传播之后重新对参数进行0均值单位方差标准化。通过将模型架构的一部分归一化,我们能够使用更高的学习率,更少关注初始化参数。批量归一化还充当正则化的作用,减少(有时甚至消除)Dropout的必要性。

1.3.3 Early stopping

在训练的过程中时常在验证集上监测误差,在验证集上如果损失函数不再显著地降低,那么应该提前结束训练。

1.3.4 梯度噪音

Neelakantan等人在每个梯度更新中增加满足高斯分布 N(0, σt2)的噪音:

\[g_{t, i}=g_{t, i}+N\left(0, \sigma_{t}^{2}\right)\]

高斯分布的方差需要根据如下的策略退火:

\[\sigma_{t}^{2}=\frac{\eta}{(1+t)^{\gamma}}\]

他们指出增加了噪音,使得网络对不好的初始化更加鲁棒,同时对深层的和复杂的网络的训练特别有益。他们猜测增加的噪音使得模型更优机会逃离当前的局部最优点,以发现新的局部最优点,这在更深层的模型中更加常见。

1.4 总结

在这篇论文中,初步研究了梯度下降的三个变形形式,其中,小批量梯度下降是最受欢迎的。 然后研究了最常用于优化SGD的算法:动量法,Nesterov加速梯度,Adagrad,Adadelta,RMSprop,Adam以及不同的优化异步SGD的算法。 最后,讨论了其他一些改善SGD的策略,如洗牌和课程学习,批量归一化和early stopping。

2. 训练误差和泛化误差

机器学习模型在训练数据集和测试数据集上的表现。如果你改变过实验中的模型结构或者超参数,你也许发现了:当模型在训练数据集上更准确时,它在测试数据集上却不⼀定更准确。这是为什么呢?

因为存在着训练误差和泛化误差:

  • 训练误差:模型在训练数据集上表现出的误差。
  • 泛化误差:模型在任意⼀个测试数据样本上表现出的误差的期望,并常常通过测试数据集上的误差来近似。

训练误差的期望小于或等于泛化误差。也就是说,⼀般情况下,由训练数据集学到的模型参数会使模型在训练数据集上的表现优于或等于在测试数据集上的表现。由于无法从训练误差估计泛化误差,⼀味地降低训练误差并不意味着泛化误差⼀定会降低。

机器学习模型应关注降低泛化误差,我们发论文最终的目的也是降低模型的泛化误差

3. 有哪些改善模型的思路

3.1 数据角度

增强数据集。无论是有监督还是无监督学习,数据永远是最重要的驱动力。更多的类型数据对良好的模型能带来更好的稳定性和对未知数据的可预见性。对模型来说,“看到过的总比没看到的更具有判别的信心”。

3.1.1 数据增强是什么?

数据增强是一种数据扩充技术,指的是利用有限的数据创造尽可能多的利用价值。虽然现在各种任务的公开数据集有很多,但是其实数据量远远不够,而工业界或者学术界去采集、制作这些数据的成本其实是很高的,像人工标注数据的任务量就很大,因此,只能通过一些方法去更好的利用现有的成本,这些方法里最直接最方便的就是数据增强。

3.1.2 离线增强与在线增强

数据增强分为在线数据增强离线数据增强离线数据增强就是一次性把数据读入内存并扩充好,然后用这个扩充后的数据集进行训练,适用于较小的数据集。在线数据增强就是深度学习框架中所提供的数据增强方式,即在每个epoch训练前,对数据集进行随机缩放、平移、旋转等,在线数据增强适合任意数量的数据集。

在线数据增强后每个epoch的训练原始图片数量是没有改变的;但是由于增强方法中的随机缩放、平移、旋转等方法中的 随机 ,导致训练中每个epoch的训练样本都不一样,这样就一定程度上增加了训练的样本的数量。

3.1.3 常见数据增强方式

在线的基础数据增强方式种类非常多,例如随机裁剪、缩放、平移、旋转、翻转、颜色变换、加噪声等,还有一些是将这些基础的数据增强方式又进行了组合改进,一个比较有名的改进数据增强方式就是Mosaic数据增强。Mosaic数据增强采用 n n n张图片,对其进行了随机裁剪、缩放、旋转等操作,最终将 n n n张图像合成了 1 1 1张图像,在扩充了数据集的同时也增加了小样本的数量。

除此之外还有Mixup、Cutout、CutMix等方式。

离线数据增强通常要考虑具体的应用场景,比如特殊天气条件下带来的光照、雨雪等条件的影响等。这里再给大家分享一个离线的数据增强工具,是一个B站Up主开源的,支持很多种的数据增强方式(标签和图片一起增强,不是只增强图片):

https://github.com/Fafa-DL/Image-Augmentation

3.1.4 数据增强的限制

数据增强虽然被称为深度学习模型的免费性能增强器,但是数据增强并不能解决所有数据问题。在条件允许的情况下我们是依然需要尽可能大的初始数据集。

在某些场景中,训练数据可能太过有限,无法通过数据扩充来提供帮助。在这些情况下,必须收集更多的数据,直到达到最小阈值,才能使用数据增强。有时,我们可以使用迁移学习的方式,即在一个通用的大数据集上训练一个模型,然后通过微调其针对目标应用程序的有限数据的更高层次来重新利用它。此外,在数据扩充过程还需要考虑类别不平衡问题,以减少类别不平衡带来的影响。

如果使用得当,数据增强毫无疑问是涨点成本最低的技巧。

3.2 模型角度

模型的容限能力决定着模型可优化的空间。在数据量充足的前提下,对同类型的模型,增大模型规模来提升容限无疑是最直接和有效的手段。

3.2.1 什么是基线(baseline)?

我们深度学习领域的基线和工程上的“基线”有些许不同,深度学习里面的基线通常指的是我们改进一个算法参照的模型,我们以此模型作为基础来改进,和这个模型对比我们的改进是否有效。

比如我想改进YOLOv5算法,YOLOv5提供了很多的型号(S,M,L,X),我们选择在YOLOv5s上改进,并且将改进后的结果和YOLOv5s对比,那么YOLOv5s就是我们的基线。

通常在一些比赛中我们经常会看到“提供Baseline”的说法,这里的Baseline就是让我们用来改进的,最终目的也是要比Baseline的效果好。

3.2.2 如何选择一个合适的基线?

3.2.2.1 从硬件性能角度考虑

  • 即考虑自己的硬件条件,本身是否具备训练一个大模型的硬件环境,比如显卡性能或者显存很小,那么就无法训练参数量过大的模型。

3.2.2.2 从训练成本角度考虑

  • 即考虑自身的资金成本或者时间成本,如果实验室没有GPU,那么就要使用云平台,通常实验都会有几十次,所以资金成本很大,这时可以考虑使用参数量较小的基线,这样训练时间快,对硬件的要求也没有那么高。

3.2.2.3 从评价指标角度考虑

  • 我们通常在发论文时都会加上一个应用场景,加场景的作用就是满足实际的使用,我们知道理想情况下,模型的精度和参数量是成正比的,假如满足实际使用要求的mAP值是0.9,YOLOv5s的mAP只有0.6,那么无论你怎样优化,模型也很难达到要求,所以这时候就要考虑使用一个更大的基线,比如5L\X等。

3.2.2.4 从代码开源角度考虑

  • 现在开源工作做得很好,有时候我们自己忙了一个月编写的代码甚至不如GitHub上一个开源的项目,所以在选择优化算法时要考虑自身的代码水平,即这个算法的有没有开源的源代码,这个代码是否清清晰易读,是否方便改进。

3.3 调参优化角度

如果你知道模型的性能为什么不再提高了,那已经向提升性能跨出了一大步。 超参数调整本身是一个比较大的问题。一般可以包含模型初始化的配置,优化算法的选取、学习率的策略以及如何配置正则和损失函数等等。

3.3.1 学习率策略角度

之前的文章中介绍过一些学习率的调整策略,学习率可以说是最重要的一个超参数,所以一个好的学习率调整策略通常可以让模型的精度显著上升。

(后面的章节会单独介绍各种超参数对模型的影响)

3.3.2 数据增强参数

这个也是非常重要的部分,深度学习领域数据永远是最核心的内容,在进行数据增强时要考虑实际的应用场景,有些数据增强参数对图像影响很大,我们应该控制在一个合理的范围内,如果增强后的图像丢失了原本的特征信息,那么这个增强就属于反向的“优化”。例如:仿射变换,透射变换。

3.4 训练角度

在大规模的数据集或者模型上,一个好的优化算法总能加速收敛或者提升精度。并且各种训练策略也会让模型产生意想不到的效果。

3.4.1 加大训练轮数

在未探索到模型的上限之前,永远不知道训练多久算训练完成。所以在改善模型上充分训练永远是最必要的过程。充分训练的含义不仅仅只是增大训练轮数。有效的学习率衰减和正则同样是充分训练中非常必要的手段。

3.4.2 标签平滑

机器学习的样本中通常会存在少量错误标签,这些错误标签会影响到预测的效果。标签平滑采用如下思路解决这个问题:在训练时即假设标签可能存在错误,避免“过分”相信训练样本的标签。在几乎所有的情况下,使用标签平滑训练可以产生更好的校准网络,从而更好地去泛化网路,最终对不可见的生产数据产生更准确的预测。

3.4.3 多尺度训练

多尺度训练(Multi Scale Training, MST)通常是指设置几种不同的图片输入尺度,训练时从多个尺度中随机选取一种尺度,将输入图片缩放到该尺度并送入网络中,是一种简单又有效的提升多尺度物体检测的方法。虽然一次迭代时都是单一尺度的,但每次都各不相同,增加了网络的鲁棒性,又不至于增加过多的计算量。而在测试时,为了得到更为精准的检测结果,也可以将测试图片的尺度放大,例如放大4倍,这样可以避免过多的小物体。多尺度训练是一种十分有效的trick方法,放大了小物体的尺度,同时增加了多尺度物体的多样性,在多个检测算法中都可以直接嵌入,在不要求速度的场合或者各大物体检测竞赛中尤为常见。

多尺度类似于数字图像处理中的图像金字塔,即将输入图片缩放到多个尺度下,每一个尺度单独地计算特征图,并进行后续的检测。这种方式虽然一定程度上可以提升检测精度,但由于多个尺度完全并行,训练耗时巨大。

3.4.4 是否使用权重

是否使用权重,这一直是一个非常火爆的问题,李沐老师其实早就给了回答,“当你的训练轮数足够多,那使不使用权重的效果都是一样的”,但是我在交流群中经常看到同学们反应,使用了权重效果比不用好,所以当你想更改主干时,有一个对应的预训练权重往往会取得更好的效果。

本文由作者按照 CC BY 4.0 进行授权