从渐层模型到深层模型,抛开模型框架、结构选择问题,仅在模型训练上就会出现如下问题:

  • 梯度消失、梯度爆炸
  • 数据量不够
  • 训练速度极其之慢
  • 参数过多,导致过拟合严重

损失函数

Logistic Sigmoid激活函数作为每层激活函数的问题:

  • 导数易饱和,当输入过大或者过小时,导数都向0饱和,导致层数增加后,开始层的参数变化非常微小
  • 平均值为0.5,而不是0

中间层通常使用的损失函数为ReLU,虽然突出先计算快速,但存在一个问题:容易出现网络中神经单元死亡的现象,因为输入小于0时,导数为0,反向传播时传递的误差导数也是0。

此后在损失函数上有诸多发展:LeakyReLU, RReLU(Randomized leaky ReLU), PReLU(Parametric leaky ReLU), ELU, SELU(Scaled ELU)

相较于ReLU,ELU的主要缺点就是计算慢,但它收敛率会更快一些,另外采用ELU,网络在进行数据测试时,计算也会稍微慢一些

ELU是在2015年提出,SELU是在2017年提出的一个改进,SELU可以使网络自正则化,训练时会输出均值为1,标准差为1的数据,效果相较于其它网络会好很多,但是需要满足一定条件:

  • 输入特征需要标准化

  • 隐藏层权重必须采用LeCun Initialization

  • 网络架构必须是序列型,在非序列性网络(如RNN)中使用的话不能确保自正则化,也不会一定优于其它激活函数的训练效果

  • 原论文只证明了所有隐藏层都是密集层(Dense)时,能够自正则化,其它类型的网络不确定

隐藏层激活函数选择建议:SELU>ELU>leaky ReLU>ReLU>tanh>logistic,如果网络架构导致SELU无法自正则化,ELU会优于SELU,如果比较在意运行延迟,可以选择leaky ReLU,如果不想调整另外的超参数,可以使用Keras默认的(比如leaky ReLU的从浅层到深层神经网络 - 图1为0.3)。

而如果时间和计算资源稍微充裕,可以交叉验证其它的几个激活函数效果:RReLU(如果网络过拟合)、PReLUde(如果训练样本集非常大),而速度第一位的话,ReLU是最佳选择(许多库对ReLU有特别的硬件优化)

数据集

数据集划分

在进行深度学习训练时,数据集通常需要作出划分,如果数据集不包含测试集,通常的划分为训练集、验证集,比例为:(80%, 20%), (75%, 25%)。而数据集中带测试集的话,通常划分为训练集、验证集合测试集,比例为:(60%, 20%, 20%)

上述是对于一般情况下的数据集,由于验证集、测试集只是为了能够检验不同模型的实际效果,在大数据时代,对于数据集较为庞大,如100万,测试集和验证集可能只有几万,1%,2%也有可能。

在训练时,很重要的一点是:训练集、验证集和测试集需要具有相同的概率分布,通常做法是事先将整个数据集进行随机重排,然后再进行分割。

数据集结果反馈

训练结束并经过测试后,首先观察在验证集上的表现,如果在验证集上的误差较大,说明网络欠拟合(高误差),需要调整模型、参数、训练时间等,如果在验证集上误差较小但是在测试集上误差较大,说明网络过拟合(高方差),需要更多数据、正则化等措施。

隐藏层网络参数初始化

  • Xavier
  • LeCun
  • He

另一个问题:网络参数初始化,Xavier initialization(或Glorot Initialization),如下(其中从浅层到深层神经网络 - 图2指该网络连接层的神经元数目,从浅层到深层神经网络 - 图3指连接的输入层, 从浅层到深层神经网络 - 图4指连接的输出层, 从浅层到深层神经网络 - 图5指两者的平均):

从浅层到深层神经网络 - 图6%5C%5C%20%0Ar%20%3D%20%5Csqrt%7B%5Cfrac%7B3%7D%7Bfan%7Bavg%7D%7D%7D%2C%20%5C%20%5C%20%E5%9D%87%E5%8C%80%E5%88%86%E5%B8%83U(-r%2C%20r)%0A#card=math&code=%5Csigma%5E2%20%3D%20%5Cfrac%7B1%7D%7Bfan%7Bavg%7D%7D%2C%20%5C%20%5C%20%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83N%280%2C%20%5Csigma%5E2%29%5C%5C%20%0Ar%20%3D%20%5Csqrt%7B%5Cfrac%7B3%7D%7Bfan_%7Bavg%7D%7D%7D%2C%20%5C%20%5C%20%E5%9D%87%E5%8C%80%E5%88%86%E5%B8%83U%28-r%2C%20r%29%0A)

如果将上面的从浅层到深层神经网络 - 图7换成从浅层到深层神经网络 - 图8,那么就是LeCun Initialization,另外还有He Initialization,每种初始方式又都分为正态分布和均匀分布。通常来说Xavier Initialization用于tanh、logistic、softmax,He Initialization用于ReLU和变量,LeCun Initialization用于SELU。

实际上随机正态初始化、Xavier、He的区别在于:Xavier在随机正态初始化之上乘上了从浅层到深层神经网络 - 图9,而He是在乘上了从浅层到深层神经网络 - 图10

注:不同初始化方法导致不同的训练结果,随机参数初始化是为了打破网络间的对称性,如果每层网络参数都设为相同,训练时会导致每层参数更新时基本都是相同的;初始化参数不应该设得过大,期望值最好设为0。

在Keras中,参数初始化默认为Xavier Initialization的均匀分布,可通过kernel_initializer进行更换,使用VarianceScaling更换从浅层到深层神经网络 - 图11

  1. keras.layers.Dense(10, activation='relu', kernel_initialize='he_normal')
  2. he_avg_init = keras.initializers.VarianceScaling(scale=2, mode='fan_avg')

梯度

梯度计算

  • 微分公式
  • 数值近似

梯度检查

通过数值近似,也可以来检查反向梯度计算是否有误,通常检查标准是:

从浅层到深层神经网络 - 图12%20%3D%20L(%5Ctheta_1%2C%5Ctheta_2%2C%20%5Ctheta_3%2C%5Ccdots%2C%5Ctheta_n)%0A#card=math&code=J%28%5Ctheta%29%20%3D%20L%28%5Ctheta_1%2C%5Ctheta_2%2C%20%5Ctheta_3%2C%5Ccdots%2C%5Ctheta_n%29%0A)

其中从浅层到深层神经网络 - 图13为需要进行更新的参数,从浅层到深层神经网络 - 图14为其合成的一个向量,通过数值近似可以分别得到所有从浅层到深层神经网络 - 图15的结果,即从浅层到深层神经网络 - 图16,然后计算下式:

从浅层到深层神经网络 - 图17

如果其值接近从浅层到深层神经网络 - 图18,可认为反向梯度计算结果正确

注意:

  • 只在debug中使用,不要在训练中使用
  • 如果检查有问题,仔细看看计算步骤,找出bug
  • 不要忘记正则化,如果使用了
  • 不要同Dropout一同使用
  • 在随机初始化的时候运行提督检查,然后在训练一段时间之后再检查

数据正规化

正规化

  • 通过scikit-learn的StandardScaler
  • 从浅层到深层神经网络 - 图19

BatchNormalization层

同时使用HE Initialization和ELU可以训练开始时时显著减少梯度爆炸/消失的风险,但是不能训练中这些问题不会产生。对于这个问题,Batch Normalization(BN,于2015年提出)能解决这个问题。

并且在初始层进行BN处理后,训练数据可以不用正规化(StandandScaler

具体操作(Keras):

  • 在层前或层后加入一个BatchNormalization(),通常用在层前的更多

实际操作:

对于每层激活函数的输入(对应上面的层前加入BN层)或输出(对应上面的层后加入BN层),如果是从浅层到深层神经网络 - 图20,有:

从浅层到深层神经网络 - 图21%5E2%20%5C%5C%5C%5C%0AZ%7Bnorm%7D%20%3D%20%5Cfrac%7BZ-%5Cmu%7D%7B%5Csqrt%7B%5Csigma%5E2%20%2B%20%5Cepsilon%7D%7D%20%5C%5C%5C%5C%0A%5Ctilde%20Z%20%3D%20%5Cgamma%20Z%7Bnorm%7D%20%2B%20%5Cbeta%0A#card=math&code=%5Cmu%20%3D%20%5Cfrac%7B1%7D%7Bm%7D%20%5Csum%20Z%20%5C%5C%5C%5C%0A%5Csigma%5E2%20%3D%20%5Cfrac%7B1%7D%7Bm%7D%20%5Csum%20%28Z%20-%20%5Cmu%29%5E2%20%5C%5C%5C%5C%0AZ%7Bnorm%7D%20%3D%20%5Cfrac%7BZ-%5Cmu%7D%7B%5Csqrt%7B%5Csigma%5E2%20%2B%20%5Cepsilon%7D%7D%20%5C%5C%5C%5C%0A%5Ctilde%20Z%20%3D%20%5Cgamma%20Z%7Bnorm%7D%20%2B%20%5Cbeta%0A)

因为并不是每个分布都是在0,1的正态分布,通过从浅层到深层神经网络 - 图22可以进行分布的调整

梯度值控制

为防止梯度爆炸,有一个思路是将梯度值控制在一定范围之内。对于Keras,有两种超参数设置:

optimizer = keras.optimizers.SGD(clipvalue=1.0)

optimizer = keras.optimizers.SGD(clipnorm=1.0)

上述代码都是讲梯度值控制在绝对值为1.0之内,区别是clipvalue(按值)只调整超过1的梯度值,将超过1的都设为1;而clipnorm(按模)按照同比例将所有梯度进行放缩,使得最大的梯度值在范围之内。

优化器

除了常规的梯度下降优化,常用的还有:momentum optimization(Vanilla momentum optimization), Nesterov Accelerated Gradient(Nesterov momentum optimization), AdaGrad, RMSProp, Adam and Nadam optimization.

  • momentum optimization[1964]
    保存了上一次迭代的部分梯度更新值,计算公式比较直白,如下式,右半部分为正常的梯度更新值(学习率乘以梯度值,加上负号,由于沿着负梯度方向),左半部分为改进,加入了对上次梯度更新值,加上一定的保存率从浅层到深层神经网络 - 图23,也可以认为是动量受到的阻力。
    由于是对SGD的调整,在Keras中只需在SGD上加入一个超参数即可,超参数的值对应于上述从浅层到深层神经网络 - 图24值。
    optimizer = keras.optimizers.SGD(lr=0.001, momentum=0.9)
    

从浅层到深层神经网络 - 图25%0A#card=math&code=mt%20%3D%20%5Cbeta%20m%7Bt-1%7D%20-%20%5Ceta%20%5Cnabla_%5Ctheta%20J%28%5Ctheta%29%0A)

  • Nesterov Accelerated Gradient[1983]
    对上面一种优化器的改进,一个小变种,从公式就可以很明显的看出差异和改进。
    解释:由于动量向量朝向正确的更新方向,可以将其引入到损失函数之中,可以使得计算值更朝向最优点靠近。
    optimizer = keras.optimizers.SGD(lr=0.001, momentum=0.9, nesterov=True)
    

从浅层到深层神经网络 - 图26%0A#card=math&code=mt%20%3D%20%5Cbeta%20m%7Bt-1%0A%7D%20-%20%5Ceta%20%5Cnabla%5Ctheta%20J%28%5Ctheta%2B%5Cbeta%20m%7Bt-1%7D%29%0A)

  • AdaGrad[2011]
    全称Adaptive Gradient,如其名,两步操作,使得梯度能够更快向极值点靠近,在一般情况下会比梯度下降要好,但是容易过早收敛、难以脱离局部极值点,一般不在深度神经网络中使用。 从浅层到深层神经网络 - 图27%20%5Cotimes%20%5Cnabla%5Ctheta%20J(%5Ctheta)%5C%5C%0A%5Ctheta%20%5Cgets%20%5Ctheta%20-%20%5Ceta%20%5Cnabla%5Ctheta%20J(%5Ctheta)%5Coslash%20%5Csqrt%7Bs%2B%5Cepsilon%7D%0A#card=math&code=s%20%5Cgets%20s%20%2B%20%5Cnabla%20%5Ctheta%20J%28%5Ctheta%29%20%5Cotimes%20%5Cnabla%5Ctheta%20J%28%5Ctheta%29%5C%5C%0A%5Ctheta%20%5Cgets%20%5Ctheta%20-%20%5Ceta%20%5Cnabla_%5Ctheta%20J%28%5Ctheta%29%5Coslash%20%5Csqrt%7Bs%2B%5Cepsilon%7D%0A)
  • RMSProp[2012]
    通过只累计最近迭代的梯度值(引入一个衰减率从浅层到深层神经网络 - 图28,基本可取固定值0.9),解决AdaGrad会快速收敛到局部极值的缺点(多极值下,基本不会收敛到全局最优),实测效果基本总是好于AdaGrad,即使在简单的问题上,实际中经常被使用。
    optimizer = keras.optimizers.RMSprop(lr=0.001, rho=0.9)
    

从浅层到深层神经网络 - 图29%5Cnabla%5Ctheta%20J(%5Ctheta)%5Cotimes%20J(%5Ctheta)%5C%5C%0A%5Ctheta%20%5Cgets%20%5Ctheta%20-%20%5Ceta%20%5Cnabla%5Ctheta%20J(%5Ctheta)%20%5Coslash%20%5Csqrt%7Bs%2B%20%5Cepsilon%20%7D%0A#card=math&code=s%20%5Cgets%20%5Cbeta%20s%20%2B%20%281%20-%20%5Cbeta%29%5Cnabla%5Ctheta%20J%28%5Ctheta%29%5Cotimes%20J%28%5Ctheta%29%5C%5C%0A%5Ctheta%20%5Cgets%20%5Ctheta%20-%20%5Ceta%20%5Cnabla%5Ctheta%20J%28%5Ctheta%29%20%5Coslash%20%5Csqrt%7Bs%2B%20%5Cepsilon%20%7D%0A)

  • Adam[2015]
    全称Adaptive Moment Estimation,结合Momentum optimization和RMSProp的思想,这两种分别是调节学习率和修正梯度,故可以采取综合的手段将两者结合。
    衰减率从浅层到深层神经网络 - 图30从浅层到深层神经网络 - 图31,通常取值为0.9和0.999,而从浅层到深层神经网络 - 图32默认即可,默认为1e-7。
    下面5个式子中1、2、5接近Momentum optimization和RMSProp,而3、4为细节处理,加快从浅层到深层神经网络 - 图33从浅层到深层神经网络 - 图34在刚开始训练时的值,因为初始时为0,刚开始会偏小。
    optimizer = keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999)
    

从浅层到深层神经网络 - 图35%5Cnabla%5Ctheta%20J(%5Ctheta)%5C%5C%0As%20%5Cgets%20%5Cbeta_2%20s%20%2B%20(1-%5Cbeta_2)%5Cnabla%5Ctheta%20J(%5Ctheta)%20%5Cotimes%20J(%5Ctheta)%5C%5C%0A%5Cwidehat%7Bm%7D%20%5Cgets%20%5Cfrac%7Bm%7D%7B1-%5Cbeta1%5Et%7D%20%5C%5C%0A%5Cwidehat%7Bs%7D%20%5Cgets%20%5Cfrac%7Bs%7D%7B1-%5Cbeta_2%5Et%7D%20%5C%5C%0A%5Ctheta%20%5Cgets%20%5Ctheta%20%2B%20%5Ceta%20%5Cwidehat%7Bm%7D%5Coslash%20%5Csqrt%7B%5Cwidehat%7Bs%7D%20%2B%20%5Cepsilon%7D%0A#card=math&code=m%20%5Cgets%20%5Cbeta_1%20m%20-%20%281-%5Cbeta_1%29%5Cnabla%5Ctheta%20J%28%5Ctheta%29%5C%5C%0As%20%5Cgets%20%5Cbeta2%20s%20%2B%20%281-%5Cbeta_2%29%5Cnabla%5Ctheta%20J%28%5Ctheta%29%20%5Cotimes%20J%28%5Ctheta%29%5C%5C%0A%5Cwidehat%7Bm%7D%20%5Cgets%20%5Cfrac%7Bm%7D%7B1-%5Cbeta_1%5Et%7D%20%5C%5C%0A%5Cwidehat%7Bs%7D%20%5Cgets%20%5Cfrac%7Bs%7D%7B1-%5Cbeta_2%5Et%7D%20%5C%5C%0A%5Ctheta%20%5Cgets%20%5Ctheta%20%2B%20%5Ceta%20%5Cwidehat%7Bm%7D%5Coslash%20%5Csqrt%7B%5Cwidehat%7Bs%7D%20%2B%20%5Cepsilon%7D%0A)

  • Nadam[2016]
    加Nesterov Momentum加速梯度的策略加入Adam中,收敛速度会略快一些。

正则化手段

  • L1正则化

  • L2正则化

  • L1、L2混合正则化

  • Dropout[2012]
    每轮训练时,按一定比例对网络中的神经元进行随机丢弃,即每次针对网络的一个子图进行训练,防止每次针对固定网络训练而导致的过拟合。
    有三点注意的:1. 只是在训练时进行随机丢弃(或者屏蔽),训练结束后使用时采用所有的网络神经元 2. 训练中的随机概率值固定,如50%,每次训练都随机屏蔽掉一半的神经元(每层)来训练网络权重 3. 训练结束后,得将所得所有权重进行一定程度的放大,如50%的丢弃率,则最后把所有的权重扩大至2倍,使得在使用所有网络神经元之后,输出值与训练时的值保持一致。
    另外,对于自正则化网络,如损失函数为SELU的网络,应该使用AlphaDropout

    keras.layers.Dropout(rate=0.5)
    # 只在训练时生效,训练结束后不再操作
    
  • Monte Carlo Dropout[2016]
    采用蒙特卡洛采样,可以不用重新训练或重新建立新模型,就可以提升经过Dropout处理生成网络的表现。
    具体操作:对于训练所得的模型,预测结果时,在Dropout层发挥作用的情况下(那么每次预测结果都不一样),用模型对数据重复预测(例如100次),然后取平均值,使得结果更加准确。不过引入了一个超参数——采样的样本数,其数值选取属于一个权衡过程,值小计算快,值大准确度高
    可通过子类化Dropout实现MCDropout层 ```python y_probas = np.stack([model(X_test_scaled, training=True) for sample in range(100)]) y_probas = y_probas.mean(axis=0)

MCDropout

class MCDropout(keras.layers.Dropout): def call(self, inputs): return super().call(inputs, training=True)



- 
Max-Norm
<br />另一种正则化手段,对于每个神经元的输入连接,限制其权重值,使得![](https://g.yuque.com/gr/latex?%7C%7Cw%7C%7C_2%20%5Cle%20r#card=math&code=%7C%7Cw%7C%7C_2%20%5Cle%20r),其中![](https://g.yuque.com/gr/latex?r#card=math&code=r)是引入的超参数,降低![](https://g.yuque.com/gr/latex?r#card=math&code=r)可以增大正则化程度
```python
keras.layers.Dense(100, activation='elu', kernel_initializer='he_normal', kernel_constraint=keras.constraints.max_norm(1.))
  • 其它:数据增强、过早终止、正交化(orthogonalization)

提升训练速度的方法

  1. 选择好的连接层初始化策略
  2. 选择好的激活函数
  3. 使用批量正规化
  4. 使用迁移学习(通过现有预训练网络,或者通过辅助任务、非监督学习进行构建预训练网络)
  5. 更换优化器