最后更新于
最后更新于
ReLU(Rectified Linear Unit), 整流线性单元, 是现在常见的激活函数, 并且有多种优化版本. 函数本身十分简单:
对应的导数为:
对应的函数图和导数图:
导数在正半轴恒为1, 因此解决了因为链式求导这部分的梯度消失问题, 但仍然存在梯度消失问题, 原因见下
计算简单, 网络效率高
每层的输出具有稀疏性
为了理解其产生的原因, 从后向传播开始. 假设使用一个非常简单的线性损失函数:
同时对于当前层的前向传播有:
其实不仅仅对于简单的线性损失函数, 对于其他常用的损失函数仍然会得到如上的只有两种值的梯度, 0是永恒的, 1可能会变化为其他的数值.
另外, 大的学习率往往会引起或加剧这个问题. 梯度更新的步幅过大, 使得网络中的神经元转为dying neuron. 因此在训练ReLU为激活函数的网络时, 最好使用较小的学习率.
Leaky ReLU是为了解决Dying ReLU问题而进行的改进. 不再有饱和区, 负轴的输出值不再为0, 整个函数是由两端线性函数拼接而成.
相对于ReLU, 优点有:
有负值输出, zero-centered, 能帮助网络向正确的方向推动权重和偏置变化
缺点:
实际操作中, 并没有明显优于ReLU
ELU(Exponential Linear Units)函数, 也是为了解决ReLU的Dying ReLU问题而进行的改进.
与Leaky ReLU相比, 负半轴由直线变成了曲线. 优缺点类似于Leaky ReLU, 但存在计算量大的问题(幂计算). 并且也需要指定超参数.
PReLU的提出是结合CNN的. PReLU函数的定义如下
这样的确引入了额外的网络参数, 但由于每一层增加了相应通道数的参数, 相比于原本的参数量是可以忽略不计的.
在Keras
的实现中, PReLU默认会对输出的每个位置都设定一个独立的可训练斜率参数, 例如对于2D-CNN网络, 当前层的输出为(batch, height, width, channels)
, 默认每个通道的每个位置的斜率参数都是独立的. 如果想实现通道内共享同一斜率参数, 可以在定义时使用shared_axes
参数指定共享参数的维度, 指定为[1, 2]
即每个通道共享同一参数.
最大的缺点为dying ReLU问题, 即在训练过程中, 网络的部分参数对于几乎任何输入, 神经元的输出和梯度都为0, 神经元处于僵死的状态. 随着训练的推进, 每层都有僵死的神经元, 而层数的推进造成僵死现象叠加愈发严重, 最终也无法训练深层的网络. 可以参考: .
其中是上一层的输出, 当前层的输入, 即是输入到当前层ReLU激活函数的内容. 这样对应的梯度为:
而当前层参数对应的梯度为:
当梯度为0时, 对应的参数因此就不会得到更新, 整个网络也就没有在学习. 而使得梯度为0就要求的值对于任何输入一直小于0, 而这常常是由于某些input batch使得网络的参数值变得很小, 使得的输出小于0, 而一旦陷入这个问题, 就很难再出来了, 因为网络得不到更新.
参考:
小于0时的输出与梯度不再为0, 解决dying ReLu问题
引入了超参数, 通常取0.01, 需要人工调参设定
PReLU(Parametric Rectified Linear Unit)在形式上与上面两种改进的ReLU都类似. Leaky ReLU与ELU都需要人工指定超参数, 但PReLU将超参数作为可训练的参数, 避免了对激活函数中超参数的调参.
输入到激活函数中的表示第个输出通道, 其实对应更常用的. 是控制这一通道负半轴的斜率参数, 在训练中更新. 因此不同的输出通道对应着不同的斜率参数.
参数的更新策略与普通参数相同, 对于每步训练, 首先得到对应的导数:
是损失对激活函数输出值的偏导, 而激活函数的输出对输入的导数为:
导数公式中指的是对应层输出中的所有位置得到的值的累加. 最后, 如同普通参数一样更新:
PReLU来自论文.