参考原文:https://blog.****.net/wfei101/article/details/80807749
https://blog.****.net/fsdfasfawre/article/details/80586123
本文通过一个简单的例子来看softmax的梯度求导过程.
1 softmax函数
softmax用于多分类过程中,它将多个神经元的输出,映射到(0,1)区间内,可以看成概率来理解,从而来进行多分类!
假设我们有一个数组z=(3,1,−3)T,那么这个元素的softmax值就是yi=∑jezjezi.
更形象的如下图表示:

注意: 要求编写python代码,完成图示的计算.
2 softmax相关求导
当我们对分类的Loss进行改进的时候,我们要通过梯度下降,每次优化一个step大小的梯度,这个时候我们就要求Loss对每个权重矩阵的偏导,然后应用链式法则。在这个过程中,你会发现用了softmax函数之后,梯度求导过程非常非常方便!
下面我们举出一个简单例子,原理一样,目的是为了帮助大家容易理解!

我们能得到下面公式:
z1=w11o1+w12o2+w13o3
z2=w21o1+w22o2+w23o3
z3=w31o1+w32o2+w33o3
其中,z4,z5,z6分别代表结点4,5,6的输出,o1,o2,o3代表是结点1,2,3往后传的输入.
那么我们可以经过softmax函数得到:
a1=ez1+ez2+ez3ez1,
a2=ez1+ez2+ez3ez2,
a3=ez1+ez2+ez3ez3.
好了,我们的重头戏来了,怎么根据求梯度,然后利用梯度下降方法更新梯度!
要使用梯度下降,肯定需要一个损失函数,这里我们使用交叉熵作为我们的损失函数,为什么使用交叉熵损失函数,不是这篇文章重点,后面有时间会单独写一下为什么要用到交叉熵函数(这里我们默认选取它作为损失函数)
交叉熵函数形式如下:
Loss=−i∑yilnai
其中y代表我们的真实值,a代表我们softmax求出的值。i代表的是输出结点的标号!在上面例子,i就可以取值为1,2,33三个结点(当然我这里只是为了简单,真实应用中可能有很多结点).
参数的形式在该例子中,总共分为w11,w12,w13,w21,w22,w23,w31,w32,w33这些,那么比如我要求出w11,w12,w13的偏导,就需要将Loss函数求偏导传到结点4,然后再利用链式法则继续求导即可,举个例子此时求w41的偏导为:
∂w11∂Loss==∂a1∂Loss∂z1∂a1∂w11∂z1−a11∂z1∂a1o1
其中,(z1=w11o1+w12o2+w13o3),则关键求出∂z1∂a1,w21,⋯,w33等参数的偏导同理可以求出.
这里分为俩种情况:
如果j=i:
∂zi∂aj===∂zi∂(∑kezkezj)(∑kezk)2(ezj)′⋅∑kezk−ezj⋅ezi∑kezkezj−∑kezkezj⋅∑kezkezi=aj(1−ai)
j=i对应例子里就是如下图所示:
比如我选定了j为1,那么就是说我现在求导传到1结点这!
从而,∂w11∂Loss=−a11∂z1∂a1o1=−a11a1(1−a1)o1=−(1−a1)o1.
3 softmax和交叉熵的原理
一般情况下,在神经网络中,最后一个输出层的节点个数与分类任务的目标数相等。假设最后的节点数为N,那么对于每一个样例,神经网络可以得到一个N维的数组作为输出结果,数组中每一个维度会对应一个类别。在最理想的情况下,如果一个样本属于k,那么这个类别所对应的的输出节点的输出值应该为1,而其他节点的输出都为0,即[0,0,1,0,….0,0],这个数组也就是样本的Label,是神经网络最期望的输出结果,交叉熵就是用来判定实际的输出与期望的输出的接近程度!
3.1 Softmax回归处理
神经网络的原始输出不是一个概率值,实质上只是输入的数值做了复杂的加权和与非线性处理之后的一个值而已,那么如何将这个输出变为概率分布?
这就是Softmax层的作用,假设神经网络的原始输出为y1,y2,⋯,yn那么经过Softmax回归处理之后的输出为:
softmax(yi)=j=1∑neyjeyi,
而单个节点的输出变成的一个概率值,经过Softmax处理后结果作为神经网络最后的输出。
3.2 交叉熵的原理
交叉熵刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。假设概率分布p为期望输出,概率分布q为实际输出,H(p,q)为交叉熵,则:
H(p,q)=−x∑p(x)logq(x)
举个例子: 假设N=3,期望输出为p=(1,0,0),实际输出q1=(0.5,0.2,0.3),q2=(0.8,0.1,0.1),那么:
H(p,q1)=−(1×log0.5+0×log0.2+0×log0.3=0.3,
H(p,q2)=−(1×log0.8+0×log0.1+0×log0.1)=0.1.
除此之外,交叉熵还有另一种表达形式,还是使用上面的假设条件:
H(p,q)=−x∑(p(x)logq(x)+(1−p(x))log(1−q(x)))
其结果为:H(p,q1)=0.55,H(p,q2)=0.19.
以上的所有说明针对的都是单个样例的情况,而在实际的使用训练过程中,数据往往是组合成为一个batch来使用,所以对用的神经网络的输出应该是一个m*n的二维矩阵,其中m为batch的个数,n为分类数目,而对应的Label也是一个二维矩阵,还是拿上面的数据,组合成一个batch=2的矩阵:
q=(0.50.80.20.10.30.1),q=(110000).
所以交叉熵的结果应该是一个列向量(根据第一种方法):
H(p,q)=(0.30.1).
而对于一个batch,最后取平均为0.2。
3.3 softmax的求导:
假设有n个输入(z1,z2,⋯,zn),将这n个输入到softmax函数得到n个输出(a1,a2,⋯,an). (a1,a2,⋯,an)分别对(z1,z2,⋯,zn)求偏导,得到雅克比矩阵如下:
∂z∂a=⎝⎜⎜⎜⎛∂z1∂a1∂z1∂a2⋮∂z1∂an∂z2∂a1∂z2∂a2⋮∂z2∂an⋯⋯⋱⋯∂zn∂a1∂zn∂a2⋮∂zn∂an⎠⎟⎟⎟⎞
这里softmax是一个Rn→Rn的向量函数,a=softmax(z).
严格来说,梯度只是为标量函数来定义的,例如机器学习中的损失函数;对于像softmax这样的向量函数,说是“梯度”是不准确的;雅可比是一个向量函数的全部的导数,大多数情况下我们会说“导数”。
由上面的例子知道如果i=j,,则∂zj∂ai=ai(1−aj).
如果i̸=j,则
∂zj∂ai=∂zj∂∑k=1nezkezi=Σ20−eziezj=−ΣeziΣezj=−aiaj
综上所述:
∂zj∂ai={ai(1−aj)−aiaji=ji̸=j
如果引入克罗内克函数δij={10i=ji̸=j,则上式可以表示为:
∂zj∂ai=ai(δij−aj).
在有的文献中,使用”1”作为函数名而不是克罗内克δ,则
∂zj∂ai=ai(1(i=j)−aj).
最终,我们可以得到损失函数Loss对各参数z1,z2,⋯,zn的偏导数.
首先, 根据复合函数求导法则:
∂zi∂Loss=j=1∑n∂aj∂Loss∂zi∂aj
∂aj∂Loss=∂aj∂(−∑kyklnaj)=−yjaj1.
又因为∂zi∂aj={aj(1−ai)−aiaji=ji̸=j
,我们把上面的组合起来得到,
∂zi∂Loss====(−j=1∑nyjaj1)∂zi∂aj−aiyiai(1−ai)+j̸=i∑ajyjaiaj−yi+aiyi+j̸=i∑aiyj−yi+aij=1∑nyj=−yi+ai
最后的结果看起来简单了很多,最后,针对分类问题,我们给定的结果yi最终只会有一个类别是1,其他类别都是0,因此,对于分类问题,这个结果等于:
∂zi∂Loss=ai−yi=ai−1,
即,我们算得的梯度就是神经元的输出-1.