介绍改进神经网络的基本方法:交叉熵代价函数与softmax。
二次代价函数
之前我们一直使用二次代价函数,貌似在一定程度上也挺work。但其实,当输入值与目标值差距很大时,二次代价函数就不是很恰当了;这是因为当差距很大时,此函数的学习速率会很慢。我们可以通过一个简单的例子观察这种变化:
假设我们只使用一个神经元与一个输出神经元,定义代价函数为:
使用链式法则计算权重和偏置的导数:
假设我们训练输入为 ,目标输出为 ,可以看见此时输入输出差距很大,则带入:
回忆一下 函数:
可以看出,当神经元的输出接近于1时,曲线变得相当平缓,因此 就很小了。这就是学习缓慢的原因。
交叉熵代价函数
因此,我们引入交叉熵代价函数,我们希望这个函数能弥补我们之前遇到的问题:
这个函数的表达式看起来十分晦涩难懂,首先我们来看它为什么能成为一个代价函数。
why cost function
代价函数需要满足非负性。
在求和中,由于 ,因此都是负数,在前面加上一个负号就变为正数。
在神经元输出接近目标值时,代价函数接近于0
我们假设 ,则带入可发现
同样,在 ,也发现
因此,满足这个性质。
交叉熵是非负的,并且在神经元达到很好的正确率时会接近于0,这就是我们想要的代价函数的性质。
why works
接下来我们就要来搞清楚为什么交叉熵代价函数能比二次代价函数更好地避免学习速率下降的问题:
我们对其求权重 的偏导数:
由于我们使用sigmoid
函数,因此
,很容易得到
。带入可约去:
这是一个优美的公式,它告诉我们权重学习的速率受到 的影响,也就是输出中的误差的控制:在误差较大时有更大的学习速度。因此,这个代价函数还避免了像二次代价函数中类似 导致学习缓慢的问题(因为我们把它约去了)。
类似的,我们也可以计算出关于偏置的偏导数,可以验证得到:
再一次验证了交叉熵代价函数的优越性。
这里需要注意:当我们选择不同的代价函数时,也需要选择不同的学习速率,并不能通过相同的学习速率去比较不同的代价函数。
然而,我们比较不同的代价函数在犯错误较大时的学习速度,并不要求学习速率一定一致,因为交叉熵代价函数在犯错误时学习得更快,这个现象并不依赖于如何设置学习速率。
二元熵
我们已经证明了当 或者 时如果有 ,那么交叉熵很小。但如果 可以取[0,1]中的所有数字呢?
可以证明,交叉熵对所有训练输入在 依然有最小值。此时的交叉熵被称为二元熵:
one more thing
我们已经推导了一层神经元时的公式,现在扩展到多层多神经元网络。借用这里的公式,我们知道:
这里, 就消失了,因此避免了学习缓慢的问题。
可以证明,如果输出层使用线性神经元,这时候使用二次代价函数就会是不错的选择。
直观理解
我们知道,交叉熵是来源于信息论,但我们不去追踪那些琐碎的细节,就算是从直观上,我们也能推导得到这个公式:
我们的目的就是在进行反向传播时,计算的关于 和 的偏导能不受 的影响,因此我们可以想象这样一个偏导数(不包含 ):
如果我们选择的代价函数满足这些条件,那么他们就能以简单的方式呈现我们想要的特性:初始误差越大,神经元学习的越快。
由链式法则:
使用
对比可得:
对其进行积分:
这就是对于一个训练样本 对代价函数的贡献,我们很容易推广到所有样本的平均,这就确定了交叉熵的形式。
实现
实现就比理论简单多了,我们只需要定义这样一个函数:
class CrossEntropyCost(object):
@staticmethod
def fn(a, y):
"""Return the cost associated with an output ``a`` and desired output
``y``. Note that np.nan_to_num is used to ensure numerical
stability. In particular, if both ``a`` and ``y`` have a 1.0
in the same slot, then the expression (1-y)*np.log(1-a)
returns nan. The np.nan_to_num ensures that that is converted
to the correct value (0.0).
"""
return np.sum(np.nan_to_num(-y*np.log(a)-(1-y)*np.log(1-a)))
softmax
softmax
是为神经网络定义了一种新的输出层,回想一下,我们之前的神经网络都是使用sigmoid fuction
,而我们将会看见,softmax
方法会有一些特别的好处。
首先定义这个函数,第 个神经元的激活值为:
由表达式可以看见,以前的激活值只是将输出值直接作用于sigmoid function
上,而softmax函数则将激活值与所有神经元的输出值联系在一起。
性质
概率分布函数
我们不难看出,所有神经元的激活值加起来为1:
方程同样保证了输出激活值都是正数–因为指数函数都是正的。
将这两点结合起来,我们可以将softmax funciton
看成是一个概率分布函数。
这样的理解是很直观的,因为在很多问题中,能够将激活值 理解为正确输出为 的概率估计是非常方便的。
而作为对比,如果输出层是sigmoid function
,则没有这样一个关于输出激活值的简单解释。
单调性
现在我们来考虑softmax的单调性。
当 时:
当 时:
因此,增加 的值会提高对应的输出激活值并降低所有其他的输出激活值。
非局部性
sigmoid function
的一个好处是对应的带权输入
,然而,对应softmax active function
,任何特定的输出激活值依赖于所有的带权输入。
逆转softmax层
如果我们已知有一个使用softmax active function
的神经网络,激活值
已知,可以由定义很容易的证明其带权输入为:
其中 是独立于 的常数。
学习缓慢问题
我们目前只定义了一个激活函数,而我们关心这个激活函数与学习速率的关系。
对数似然代价函数
首先定义一个对数似然代价函数(log-likehood):
其中,当 取目标值时为1,其余为0。
对于这个函数,我们可以想象:
- 当网络表现得很好地时,估计的对应概率 与1非常接近,那么代价函数会很小;
- 当网络表现得很糟糕,则对应的概率 与0非常接近,那么代价函数会很大。
所以说对数似然函数也是我们满足期待的代价函数条件。
推导
那么这个代价函数作用在softmax active function
上时,表现如何呢?
对于 ,我们可以求偏导:
同理,我们可以得到 的偏导:
通过推导,我们发现通过这种方法计算反向传播会异常简单。
我们发现,用softmax active fuction
+ log-likehood
代价函数得到的偏导和之前用corss entropy function
+sigmoid function
得到的式子几乎一样。我们后面会发现,这种相似性很有用。
总之,我们验证了使用softmax fuction
+ log-likehood
组合也能避免学习缓慢的问题。
从一种更为通用的视角来看,虽然这两种组合在实际应用中都不错,但softmax fuction
+ log-likehood
更适用于那些需要将输出激活值解释为概率的场景。
why softmax
另外一个值得思考的问题是,为什么这个函数叫柔性最大值?
在Quora上有一个很好地解释,简单来说,就是:
it highlights the largest input and suppresses all the significantly smaller ones.