神经网络训练中的优化器

198 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

在上一篇博客中实现了手写字体的识别,网络结构非常简单,仅有全连接层构成,后面我也附上了使用自己手写字体验证模型的代码,如果实际验证过了可能就会发现,模型在训练时准确率已经非常高了,但是在实际的使用过程中却还是经常把我们写的数字认错,这个问题,就是泛化性的问题。有些模型在数据集中的表现非常好,但是在实际使用过程中却表现非常差,这就说明模型缺乏泛化性。这样的问题该如何解决呢!不要慌,很多种解决方案已经被人提出,我们需要做的就是从中理解它的原理,以至于在后面的实际应用中我们可以选择合适的方法去应用到模型中,来增加模型的泛化性。

先看下面的代码

model.add(keras.layers.Dense(n_hidden, input_shape=(reshape, ), name='dense_layer', activation='relu'))
model.add(keras.layers.Dropout(0.2))
model.add(keras.layers.Dense(n_hidden, input_shape=(reshape, ), name='dense_layer1', activation='relu'))
model.add(keras.layers.Dropout(0.2))
model.add(keras.layers.Dense(nb_classes, input_shape=(reshape, ), name='dense_layer2', activation='softmax'))

有一部分非常熟悉,就在上一篇博客中出现过,但是还有两行代码没有出现过,这两行代码就是dropout,添加的这两行代码就叫做dropout层,其目的就是使得在训练过程中的一部分神经元随机失活,实际上这是一种正则化的形式,正则化在本文的后面会有讲解。随机失活一部分神经元的好处就是增加网络的泛化性能,不相信的话,将上一篇博客的代码进行稍微修改,加上上面两行,另外再将epoch调大一些做一下测试,用自己手写的图片验证时,你会发现真的比之前多正确识别了那么几个。

优化器

讲到优化器,就直接从手写字体识别代码中所应用的随机梯度下降法开始讲起,如下图所示

捕获1.PNG 图中,f(x)表示损失函数,橙色的点在右上角逐渐走到谷底的过程中,f(x)在不断的变小,此处的f(x)表示的即为斜率,由于橙色点在行走过程中不确定行走的方向,因此它总是一步一步的向着梯度增量最大的方向进行,橙色点每经过的一步,就是学习率,可以看到,如果学习率的选择较小,那么整个函数就可能收敛非常缓慢,但若是选择的学习率过大,则很有可能越过最低点,导致模型无法收敛,这些是调参的一门学问。

除了随机梯度下降(SGD)之外,还有许多的优化器可以使用,例如RMSProp等,可以在上一篇博客给出的代码基础上一一测试,观察训练的效果如何,此处不再一一赘述。

调参

调参是一门学问,模型小的时候没啥感觉,很快就训练完成了,没啥影响,随便改随便测,但是当模型和数据集变得庞大之后,训练一次的时间可能非常久,此时修改的代价相对就大了。

一般来说,调参主要修改的有下面几个参数

  1. epoch
  2. bach_size
  3. 学习率
  4. 修改损失函数
  5. 增加或减少内部神经元数量

当然,我给出的这几种方法也不一定全面,仅做一个参考。

正则化

3.jpg

也可以写为下面的公式,更加直观。

min:{loss(TrainingData|model)} + λ*complexity(model)

主要是为了解决模型的过拟合问题。 正则化的类型主要分为三种:

(1)L1正则化(LASSO),模型复杂性表示为权重绝对值的和

(2)L2正则化(Ridge),模型复杂性表示为权重的平方和再开平方

(3)弹性正则化(elastic regularization),模型的复杂性通过上面两种来计算。

上面叙述的三点,也可以用下面的公式来说明

4.png