神经网络训练过程loss注意事项

						<p>作者:<a href="http://blog.csdn.net/han_xiaoyang?viewmode=contents" rel="nofollow" target="_blank">寒小阳</a><br>

时间:2016年1月。

出处:http://blog.csdn.net/han_xiaoyang/article/details/50521064

声明:版权所有,转载请联系作者并注明出处

1.训练

在前一节当中我们讨论了神经网络静态的部分:包括神经网络结构、神经元类型、数据部分、损失函数部分等。这个部分我们集中讲讲动态的部分,主要是训练的事情,集中在实际工程实践训练过程中要注意的一些点,如何找到最合适的参数。

1.1 关于梯度检验

之前的博文我们提到过,我们需要比对数值梯度和解析法求得的梯度,实际工程中这个过程非常容易出错,下面提一些小技巧和注意点:

使用中心化公式,这一点我们之前也说过,使用如下的数值梯度计算公式:
d f ( x ) d x = f ( x + h ) f ( x h ) 2 h ( ) d f ( x ) d x = f ( x + h ) f ( x h ) 2 h ( ) d f ( x ) d x = f ( x + h ) f ( x h ) 2 h (好的形式) df(x)dx=f(x+h)−f(x−h)2h(好的形式)df(x)dx=f(x+h)−f(x−h)2h(好的形式) \frac{df(x)}{dx} = \frac{f(x + h) - f(x - h)}{2h} \hspace{0.1in} \text{(好的形式)} f(x)和梯度下降里看到的一样,是一个梯度向量。直观理解是Hessian矩阵描绘出了损失函数的曲度,因此能让我们更高效地迭代和靠近最低点:乘以Hessian矩阵进行参数迭代会让在曲度较缓的地方,会用更激进的步长更新参数,而在曲度很陡的地方,步伐会放缓一些。因此相对一阶的更新算法,在这点上它还是有很足的优势的。

比较尴尬的是,实际深度学习过程中,直接使用二次迭代的方法并不是很实用。原因是直接计算Hessian矩阵是一个非常耗时耗资源的过程。举个例子说,一个一百万参数的神经网络的Hessian矩阵维度为[1000000*1000000],算下来得占掉3725G的内存。当然,我们有L-BFGS这种近似Hessian矩阵的算法,可以解决内存问题。但是L-BFGS一般在全部数据集上计算,而不像我们用的mini-batch SGD一样在小batch上迭代。现在有很多人在努力研究这个问题,试图让L-BFGS也能以mini-batch的方式稳定迭代更新。但就目前而言,大规模数据上的深度学习很少用到L-BFGS或者类似的二次迭代方法,倒是随机梯度下降这种简单的算法被广泛地使用着。

感兴趣的同学可以参考以下文献:

1.4.4 逐参更新学习率

到目前为止大家看到的学习率更新方式,都是全局使用同样的学习率。调整学习率是一件很费时同时也容易出错的事情,因此大家一直希望有一种学习率自更新的方式,甚至可以细化到逐参数更新。现在确实有一些这种方法,其中大多数还需要额外的超参数设定,优势是在大多数超参数设定下,效果都比使用写死的学习率要好。下面稍微提一下常见的自适应方法(原谅博主底子略弱,没办法深入数学细节讲解):

Adagrad是Duchi等在论文Adaptive Subgradient Methods for Online Learning and Stochastic Optimization中提出的自适应学习率算法。简单代码实现如下:

# 假定梯度为dx,参数向量为x
cache += dx**2
x += - learning_rate * dx / np.sqrt(cache + 1e-8)

  
  
  • 1
  • 2
  • 3

其中变量cache有着和梯度一样的维度,然后我们用这个变量持续累加梯度平方。之后这个值被用作参数更新步骤中的归一化。这种方法的好处是,对于高梯度的权重,它们的有效学习率被降低了;而小梯度的权重迭代过程中学习率提升了。而分母开根号这一步非常重要,不开根号的效果远差于开根号的情况。平滑参数1e-8避免了除以0的情况。

RMSprop是一种非常有效,然而好像还没有被公开发布的自适应学习率更新方法。有意思的是,现在使用这个方法的人,都引用的大神Geoff Hinton的coursera课程第6节的讲义第29页。RMSProp方法对Adagrad算法做了一个简单的优化,以减缓它的迭代强度,它开方的部分cache做了一个平滑处理,大致的示意代码如下:

cache = decay_rate * cache + (1 - decay_rate) * dx**2
x += - learning_rate * dx / np.sqrt(cache + 1e-8)

  
  
  • 1
  • 2

这里的decay_rate是一个手动敲定的超参数,我们通常会在[0.9, 0.99, 0.999]中取值。需要特别注意的是,x+=这个累加的部分和Adagrad是完全一样的,但是cache本身是迭代变化的。

另外的方法还有:

下图是上述提到的多种参数更新方法下,损失函数最优化的示意图:

![参数更新1](http://cs231n.github.io/assets/nn3/opt2.gif) ![参数更新2](http://cs231n.github.io/assets/nn3/opt1.gif)

1.5 超参数的设定与优化

神经网络的训练过程中,不可避免地要和很多超参数打交道,这是我们需要手动设定的,大致包括:

  • 初始学习率
  • 学习率衰减程度
  • 正则化系数/强度(包括l2正则化强度,dropout比例)

对于大的深层次神经网络而言,我们需要很多的时间去训练。因此在此之前我们花一些时间去做超参数搜索,以确定最佳设定是非常有必要的。最直接的方式就是在框架实现的过程中,设计一个会持续变换超参数实施优化,并记录每个超参数下每一轮完整训练迭代下的验证集状态和效果。实际工程中,神经网络里确定这些超参数,我们一般很少使用n折交叉验证,一般使用一份固定的交叉验证集就可以了。

一般对超参数的尝试和搜索都是在log域进行的。例如,一个典型的学习率搜索序列就是learning_rate = 10 ** uniform(-6, 1)。我们先生成均匀分布的序列,再以10为底做指数运算,其实我们在正则化系数中也做了一样的策略。比如常见的搜索序列为[0.5, 0.9, 0.95, 0.99]。另外还得注意一点,如果交叉验证取得的最佳超参数结果在分布边缘,要特别注意,也许取的均匀分布范围本身就是不合理的,也许扩充一下这个搜索范围会有更好的参数。

1.6 模型融合与优化

实际工程中,一个能有效提高最后神经网络效果的方式是,训练出多个独立的模型,在预测阶段选结果中的众数。模型融合能在一定程度上缓解过拟合的现象,对最后的结果有一定帮助,我们有一些方式可以得到同一个问题的不同独立模型:

  • 使用不同的初始化参数。先用交叉验证确定最佳的超参数,然后选取不同的初始值进行训练,结果模型能有一定程度的差别。
  • 选取交叉验证排序靠前的模型。在用交叉验证确定超参数的时候,选取top的部分超参数,分别进行训练和建模。
  • 选取训练过程中不同时间点的模型。神经网络训练确实是一件非常耗时的事情,因此有些人在模型训练到一定准确度之后,取不同的时间点的模型去做融合。不过比较明显的是,这样模型之间的差异性其实比较小,好处是一次训练也可以有模型融合的收益。

还有一种常用的有效改善模型效果的方式是,对于训练后期,保留几份中间模型权重和最后的模型权重,对它们求一个平均,再在交叉验证集上测试结果。通常都会比直接训练的模型结果高出一两个百分点。直观的理解是,对于碗状的结构,有很多时候我们的权重都是在最低点附近跳来跳去,而没法真正到达最低点,而两个最低点附近的位置求平均,会有更高的概率落在离最低点更近的位置。

2. 总结

  • 用一部分的数据测试你梯度计算是否正确,注意提到的注意点。
  • 检查你的初始权重是否合理,在关掉正则化项的系统里,是否可以取得100%的准确度。
  • 在训练过程中,对损失函数结果做记录,以及训练集和交叉验证集上的准确度。
  • 最常见的权重更新方式是SGD+Momentum,推荐试试RMSProp自适应学习率更新算法。
  • 随着时间推进要用不同的方式去衰减学习率。
  • 用交叉验证等去搜索和找到最合适的超参数。
  • 记得也做做模型融合的工作,对结果有帮助。

参考资料与原文

cs231n 神经网络训练与注意点

        </div>
					<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-258a4616f7.css" rel="stylesheet">
                  </div>

猜你喜欢

转载自blog.csdn.net/eefresher/article/details/89576468