本文是对一篇国外的讲解Softmax的文章的翻译。
Softmax函数的输入是一个N维的向量,向量元素是任意实数,输出也是一个N维的向量,但是有如下性质:
Softmax函数是这样一个映射 S ( a ) : R N → R N S(a): \mathbb{R}^N \xrightarrow{} \mathbb{R}^N S(a):RNRN
S ( a ) : [ a 1 a 2 . . . a N ] → [ S 1 S 2 . . . S N ] S(a): \begin{bmatrix} a_1 \\ a_2 \\ ... \\ a_N\end{bmatrix} \xrightarrow{} \begin{bmatrix} S_1 \\ S_2 \\ ... \\ S_N \end{bmatrix} S(a):⎣⎢⎢⎡a1a2...aN⎦⎥⎥⎤⎣⎢⎢⎡S1S2...SN⎦⎥⎥⎤
并且对于每个元素,用的是如下公式
S j = e a j ∑ i = 1 N e a k , ∀ j ∈ 1... N S_j= \frac{e^{a_j}}{\sum_{i=1}^N e^{a_k}}, \forall_j \in 1...N Sj=∑i=1Neakeaj,∀j∈1...N
我们很容易看到, S j S_j Sj永远是正数(因为式中全是对 e e e求幂),并且由于在分母中包含了分子与其它正数求和,所以 0 < S j < 1 0
举个例子,向量[1.0, 2.0, 3.0]
通过Softmax变成了[0.09, 0.24, 0.67]
。元素顺序不变,并且和为1。假如是[1.0, 2.0, 5.0]
通过Softmax变成了[0.02, 0.05, 0.93]
,同样是顺序不变,且和为1。可以注意到,0.93
比另外2个数字大很多,而且在和为1的情况下,0.93
已经占了绝大多数。直观地来讲,Softmax函数是“温柔”版的最大值函数,不同的是,Softmax没有直接取一个最大值,而是将所有元素变成各自占 100 % 100\% 100% 的不同比例,元素占比越大,Softmax最终的输出值就越大,当然其他元素得出的值就越小。[1]
Softmax的两个性质使得它非常适合用概率来解释,这也让Softmax在机器学习领域大有用途。在多分类任务中,我们希望给每个输入做一个标注,标注它属于每个类的概率是多大。
如果我们有N个类别,目标就是找到一个N维向量,每个元素代表这个样本是此类别的概率,这个向量元素求和是1。听起来是不是比较熟悉?
我们可以把Softmax解释成如下所示:
S j = P ( y = j ∣ a ) S_j=P(y=j|a) Sj=P(y=j∣a)
其中, y y y代表输出的类别,范围是 1... N 1...N 1...N, a a a是一个N维向量。有一个最基础的例子就是在多分类逻辑回归中,我们会把一个输入向量 x x x和一个权重向量 w w w做点积,然后这个点积被输入到Softmax中去计算概率。这种结构后面会讲到
我们可以发现,从概率学角度看,Softmax用于模型参数的最大似然估计是最优的,但是这超出了本文范围,具体可以看Deep Learning的第5章。
在开始对Softmax进行求导之前,我们先看看如何对向量求导。
Softmax本质上是一个向量函数,输入输出都是向量,换句话说,Softmax有多个输入和多个输出,所以我们不能直接就问“Softmax的导数是什么”,我们应该这么问:
如果你觉得这个听起来很复杂,不要担心,向量微积分就是用来干这事的!我们要找的是这些偏导数:
∂ S i ∂ a j ~\\ \frac{\partial{S_i}}{\partial{a_j}}\\ ∂aj∂Si
这是 第 i 个 输 出 关 于 第 j 个 输 入 求 偏 导 第i个输出关于第j个输入求偏导 第i个输出关于第j个输入求偏导,我们把这个记作: D j S i D_jS_i DjSi。
由于Softmax是 R N → R N \mathbb{R}^N \xrightarrow{} \mathbb{R}^N RNRN的函数,如果整体求导,我们实际上求的是一个 雅 可 比 矩 阵 ( J a c o b i a n M a t r i x ) 雅可比矩阵(Jacobian \ Matrix) 雅可比矩阵(Jacobian Matrix):
D S = [ D 1 S 1 ⋯ D N S 1 ⋮ ⋱ ⋮ D N S 1 ⋯ D N S N ] DS= \begin{bmatrix} D_1 S_1 & \cdots & D_N S_1 & \\ \vdots & \ddots & \vdots \\ D_N S_1 & \cdots & D_N S_N \end{bmatrix} DS=⎣⎢⎡D1S1⋮DNS1⋯⋱⋯DNS1⋮DNSN⎦⎥⎤
在机器学习的文章中,“梯度”常被用来代替“导数”。严格地说,“梯度”是仅针对于标量而定义的(比如机器学习中的损失函数),但对于像Softmax这样的向量函数来说,用“梯度”是不准确的,“雅可比矩阵”才是对向量函数的导数的名称,但是为了方便描述,大多数场合我也直接用“导数”来描述。
让我们开始计算 D j S i ( 任 意 i 和 j ) D_jS_i(任意i和j) DjSi(任意i和j):
D j S i = ∂ S i ∂ a j = ∂ e a j ∑ k = 1 N e a k ∂ a j \begin{aligned} D_jS_i &= \frac{\partial{S_i}}{\partial{a_j}} \\ &= \frac{\partial{ \frac{e^{a_j}}{\sum_{k=1}^N e^{a_k}}{} }}{\partial{a_j}} \end{aligned} DjSi=∂aj∂Si=∂aj∂∑k=1Neakeaj
我们使用一下分式函数求导公式:
对 于 f ( x ) = g ( x ) h ( x ) f ′ ( x ) = g ′ ( x ) h ( x ) − h ′ ( x ) g ( x ) h 2 ( x ) 对于f(x)=\frac{g(x)}{h(x)} \\ ~\\ f'(x)=\frac{g'(x)h(x) - h'(x)g(x)}{h^2(x)} \\ 对于f(x)=h(x)g(x) f′(x)=h2(x)g′(x)h(x)−h′(x)g(x)
我们让 g i = e a i , h i = ∑ k = 1 N e a k g_i=e^{a_i},h_i=\sum_{k=1}^Ne^{a_k} gi=eai,hi=∑k=1Neak ,注意无论 h i h_i hi 关于哪个 a j a_j aj 求偏导结果都是 e a j e^{a_j} eaj,然而对于 g i g_i gi 却不是这样, g i g_i gi 对 a j a_j aj 求偏导当且仅当 i = j i=j i=j 的时候才是 e a j e^{a_j} eaj ,因为只有 i = j i=j i=j 时 g i g_i gi 中才包含 a j a_j aj ,否则偏导就是 0。
回到 D j S i D_jS_i DjSi 的求导,为了简化, ∑ \sum ∑ 代表 ∑ k = 1 N e a k \sum_{k=1}^Ne^{a_k} ∑k=1Neak
如果 i = j i=j i=j:
∂ exp ( a j ) ∑ k = 1 N exp ( a k ) ∂ a j = e a i ∑ − e a j e a i ∑ 2 = e a i ∑ ∑ − e a j ∑ = S i ( 1 − S j ) \begin{aligned} \frac{\partial{ \frac{\exp(a_j)}{\sum_{k=1}^N \exp(a_k)}{} }}{\partial{a_j}} &= \frac{e^{a_i} \sum - e^{a_j}e^{a_i}}{\sum^2} \\ ~\\ &=\frac{e^{a_i}}{\sum} \frac{\sum - e^{a_j}}{\sum} \\ ~\\ &= S_i(1-S_j) \\ \end{aligned} ∂aj∂∑k=1Nexp(ak)exp(aj) =∑2eai∑−eajeai=∑eai∑∑−eaj=Si(1−Sj)
最终的式子用 S i S_i Si 自己表示了 S i S_i Si 自己的导数,当要求导的函数包含 e x e^x ex 时,这是常用的一种技巧。
类似地,我们处理 i ≠ j i\neq j i=j 的情况:
∂ exp ( a j ) ∑ k = 1 N exp ( a k ) ∂ a j = 0 − e a j e a i ∑ 2 = − e a j ∑ e a i ∑ = − S j S i \begin{aligned} \frac{\partial{ \frac{\exp(a_j)}{\sum_{k=1}^N \exp(a_k)}{} }}{\partial{a_j}} &= \frac{0 - e^{a_j}e^{a_i}}{\sum^2} \\ ~\\ &= - \frac{e^{a_j}}{\sum} \frac{e^{a_i}}{\sum} \\ ~\\ &= -S_j S_i \\ \end{aligned} ∂aj∂∑k=1Nexp(ak)exp(aj) =∑20−eajeai=−∑eaj∑eai=−SjSi
总结一下:
f ( x ) = { S i ( 1 − S j ) i = j − S j S i i ≠ j f(x)=\left\{ \begin{aligned} & S_i(1-S_j) & i=j\\ & -S_j S_i & i\neq j \\ \end{aligned} \right. f(x)={Si(1−Sj)−SjSii=ji=j
我比较喜欢这种分开的形式,但是如果有比程序员在公式表达方面更准确的人的话,那一定是数学家们。这就是为什么你会在不同文章里看到对同一个公式的不同表达,但都是等价的。最常见的就是用了克罗内克函数:
δ i j = { 1 i = j 0 i ≠ j \delta_{ij}=\left\{ \begin{aligned} & 1 & i=j\\ & 0 & i\neq j \\ \end{aligned} \right. δij={10i=ji=j
合到一起就写成
D j S i = S i ( δ i j − S j ) D_jS_i=S_i(\delta_{ij} - S_j) DjSi=Si(δij−Sj)
这些公式说的都是同一件事,不同的文章还有其他一些写法:
这些简写形式在我们想计算更复杂的、基于Softmax的导数的其它导数的时候变得很有用,否则我们就需要每次都重述一遍,很麻烦。
代码如下
import numpy as np
def softmax(x):
"""计算向量 x 的 softmax."""
exps = np.exp(x)
return exps / np.sum(exps)
尝试一下吧:
In [146]: softmax([1, 2, 3])
Out[146]: array([ 0.09003057, 0.24472847, 0.66524096])
但是当我们输入比较大的数值时,会发生错误(溢出):
In [148]: softmax([1000, 2000, 3000])
Out[148]: array([ nan, nan, nan])
Numpy使用的浮点数的数值范围是有限的,对于float64
,最大可表示数的数量级是 1 0 308 10^{308} 10308 ,Softmax中的 e x e^x ex 导致很容易溢出,即使 x x x 看起来没多大。
避免这个问题的一个好方法是将输入归一化,使其不要太大或太小,通过观察,我们可以像下面这样使用任意常数 C C C:
S j = e a j ∑ i = 1 N e a k = C e a j ∑ i = 1 N C e a k S_j = \frac{e^{a_j}}{\sum_{i=1}^N e^{a_k}} = \frac{Ce^{a_j}}{\sum_{i=1}^N Ce^{a_k}} Sj=∑i=1Neakeaj=∑i=1NCeakCeaj
然后我们把 C C C 放到指数上面,我们得到:
S j = e a j + l n ( C ) ∑ i = 1 N e a k + l n ( C ) S_j = \frac{e^{a_j+ln(C)}}{\sum_{i=1}^N e^{a_k+ln(C)}} Sj=∑i=1Neak+ln(C)eaj+ln(C)
因为 C C C 是任意数字,所以我们可以写成:
S j = e a j + D ∑ i = 1 N e a k + D ~\\ S_j = \frac{e^{a_j+D}}{\sum_{i=1}^N e^{a_k+D}} Sj=∑i=1Neak+Deaj+D
其中 D D D 是任意常数,这个公式和原本的 S j S_j Sj 是一样的,所以我们需要挑选一个 D D D 使我们的计算在数值上更好(不要溢出)。一个比较好的选择就是对输入向量 x x x 取其中的最大元素:
D = − m a x ( a 1 , a 2 , ⋯ , a N ) D=-max(a_1, a_2, \cdots, a_N) D=−max(a1,a2,⋯,aN)
假设输入本身彼此之间不太远的话,这样可以把输入值全部“平移”到一个接近于零的范围。重要之处在于,这样把它们都变成了负数(当然最大的那个元素变成了0)。负的大指数会让式子的值最终变到0而不是无穷大,所以我们能更好地避免Nan。
代码如下
def stablesoftmax(x):
"""稳定的Softmax,防止溢出"""
shiftx = x - np.max(x)
exps = np.exp(shiftx)
return exps / np.sum(exps)
现在代入数字试试:
In [150]: stablesoftmax([1000, 2000, 3000])
Out[150]: array([ 0., 0., 1.])
Softmax的常用于机器学习中,特别是在logistic回归中的Softmax层,我们将Softmax应用于完全连接层(矩阵乘法)的输出。
在这个图中,我们有一个有 n n n 个特征的输入,和 T T T 个可能的输出类。使用权值矩阵 W W W 将 x x x 转换为带有 T T T 元素的向量(在机器学习中称为“Logits”),使用Softmax函数将Logits“坍塌”为一个概率向量,表示 x x x 属于 T T T 输出类中的每一个的概率。
我们如何计算这个“Softmax层”(全连接矩阵乘法后跟Softmax)的导数?当然是用链式法则!网上有很多关于这个公式的推导,但我想从基本原理入手,仔细地将多元链式法则应用到涉及到的函数的雅可比矩阵中。
在我们开始之前,有一点很重要:你可能认为 x x x 是自变量,需要计算它的导数,但其实不是。事实上在机器学习中,我们要找到最优的权值矩阵 W W W ,因此我们每一步梯度下降都要更新 W W W ,因此,我们要计算这一层关于 W W W 的导数。
我们先把这个式子写成向量函数的复合形式。首先我们做了一次矩阵乘法,记为 g ( W ) g(W) g(W) ,因为输入是 W N × T W_{N \times T} WN×T 输出是 T T T ,所以是这样的结果 : R N T → R T \mathbb{R}^{N T}\xrightarrow{} \mathbb{R}^T RNTRT
接下来是Softmax,如果我们把对数向量表示为 λ \lambda λ ,我们就有 S ( λ ) : R T → R T S(\lambda):\mathbb{R}^{T}\xrightarrow{} \mathbb{R}^T S(λ):RTRT 。
综上我们就有了一个复合函数:
P ( W ) = S ( g ( w ) ) = ( S ∘ g ) ( W ) \begin{aligned} P(W) &= S(g(w)) \\ &=(S \circ g)(W) \end{aligned} P(W)=S(g(w))=(S∘g)(W)
应用链式法则,得到 P ( W ) P(W) P(W) 的雅可比矩阵:
D g = [ D 1 g 1 ⋯ D N T g 1 ⋮ ⋱ ⋮ D 1 g T ⋯ D N T g T ] Dg= \begin{bmatrix} D_1 g_1 & \cdots & D_{NT} g_1 & \\ \vdots & \ddots & \vdots \\ D_1 g_T & \cdots & D_{NT} g_T \end{bmatrix} Dg=⎣⎢⎡D1g1⋮D1gT⋯⋱⋯DNTg1⋮DNTgT⎦⎥⎤
在某种意义上,权值矩阵 W W W 被 线性化(linearize) 为一个长度为 N × T N \times T N×T 的向量,如果您熟悉多维数组在内存的样子,那么应该很容易理解它是如何实现的。在我们的例子中,我们可以做的就是按行(row)的顺序将其线性化,即第一行算在一起的,然后是第二行诸如此类。在数学上, W i j W_{ij} Wij 将会得到雅可比矩阵中的第 ( i − 1 ) N + j (i-1)N+j (i−1)N+j 列的数字,至于 D g Dg Dg ,让我们回想一下 g 1 g_1 g1是什么:
g 1 = W 11 x 1 + W 12 x 2 + ⋯ + W 1 N x N g1=W_{11}x_1 + W_{12}x_2 + \cdots + W_{1N}x_N g1=W11x1+W12x2+⋯+W1NxN
所以:
D 1 g 1 = x 1 D 2 g 1 = x 2 ⋯ D N g 1 = x N D N + 1 g 1 = 0 D N T g 1 = 0 \begin{aligned} \\ D_1 g_1 &= x_1 \\ D_2 g_1 &= x_2 \\ \cdots \\ D_N g_1 &= x_N \\ D_{N+1} g_1 &= 0 \\ D_{NT} g_1 &= 0 \\ \end{aligned}\\ D1g1D2g1⋯DNg1DN+1g1DNTg1=x1=x2=xN=0=0
如果我们用相同的办法计算 g 2 ⋯ g T g_2 \cdots g_T g2⋯gT ,我们就得到了雅可比矩阵:
D g = [ x 1 x 2 ⋯ x N ⋯ 0 0 ⋯ 0 ⋮ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋮ 0 0 ⋯ 0 ⋯ x 1 x 2 ⋯ x N ] Dg= \begin{bmatrix} x_1 & x_2 & \cdots & x_N & \cdots & 0 & 0 & \cdots & 0\\ \vdots & \ddots & \ddots & \ddots & \ddots & \ddots & \ddots & \ddots & \vdots \\ 0 & 0 & \cdots & 0 & \cdots & x_1 & x_2 & \cdots &x_N \end{bmatrix} Dg=⎣⎢⎡x1⋮0x2⋱0⋯⋱⋯xN⋱0⋯⋱⋯0⋱x10⋱x2⋯⋱⋯0⋮xN⎦⎥⎤
换个角度看,如果我们按照 W i j W_{ij} Wij 分开看,就得到:
D i j g t = ∂ ( W t 1 x 1 + W t 2 x 2 + ⋯ + W t N x N ) ∂ W i j = { x j i = t 0 i ≠ t ~\\ \begin{aligned} D_{ij} g_t &= \frac{\partial{(W_{t1}x_1 + W_{t2}x_2 + \cdots + W_{tN}x_N)}}{\partial{W_{ij}}} \\ &= \left\{ \begin{aligned} & x_j & i=t \\ & 0 & i \neq t \\ \end{aligned} \right. \end{aligned} Dijgt=∂Wij∂(Wt1x1+Wt2x2+⋯+WtNxN)={xj0i=ti=t
这一项对应的是雅可比矩阵的第 t t t 行,第 ( i − 1 ) N + j (i-1)N+j (i−1)N+j 列。
最终 D S DS DS 和 D g Dg Dg 再做一次点积运算就得到了Softmax的完整的雅可比矩阵。注意 P ( W ) : R N T → R T P(W): \mathbb{R}^{N T}\xrightarrow{} \mathbb{R}^T P(W):RNTRT 符合雅可比矩阵的维度,因为 D S DS DS 是 T × T T \times T T×T 的而且 D g Dg Dg 是 T × N T T \times NT T×NT ,所以它们点积就是 T × N T T \times NT T×NT。
在文献中,你会看到Softmax层的导数的一个非常简短的推导。这很好,因为所涉及的两个函数简单且众所周知。如果我们仔细计算 D S DS DS 内的一行和 D g Dg Dg 内的一列的点积:
D i j P t = ∑ k = 1 T D k S t ⋅ D i j g k D_{ij} P_t = \sum_{k=1}^T D_k S_t \cdot D_{ij} g_k DijPt=k=1∑TDkSt⋅Dijgk
D g Dg Dg 大部分是零,所以最终结果更简单。当且仅当 i = k i=k i=k 时, D i j g k D_{ij} g_k Dijgk 才不是0,所以:
D i j P t = D i S t x j = S t ( δ t i − S i ) x j \begin{aligned} \\ D_{ij} P_t &= D_i S_t x_j \\ &= S_t (\delta_{ti} - S_i) x_j \\ \end{aligned} \\ DijPt=DiStxj=St(δti−Si)xj
所以完全有可能计算Softmax层的导数而不需要实际的雅可比矩阵乘法,这避免了昂贵的矩阵乘法。由于全连接层的雅可比矩阵是稀疏的,这样就避免了大部分的计算。
也就是说,我仍然觉得很重要的一点是展示这个导数是如何从复合函数雅可比矩阵的基本原理中产生的。这种方法的优点是,它适用于更复杂的复合函数,更复杂的复合函数意味着其中每个元素的导数的“封闭形式”如果用其他方法计算要困难得多。
我们已经看到Softmax函数是如何被用作神经网络的一部分,以及如何使用多元链式法则来计算它的导数。我们同样有必要看看与Softmax一起用于训练网络的一个损失函数:交叉熵(Cross Entropy)。
交叉熵有一个有趣的概率和信息理论解释,但这里我只关注他的运作特性。
对于两个离散的概率分布 p p p 和 q q q ,它们的交叉熵就是:
x e n t ( p , q ) = − ∑ k p ( k ) l n ( q ( k ) ) xent(p,q) = - \sum_{k} p(k) ln(q(k)) xent(p,q)=−k∑p(k)ln(q(k))
k k k 代表这个分布所定义的随机变量的所有可能值,在我们的例子中有 T T T 个输出类,所以 k = 1 , 2 ⋯ T k=1,2 \cdots T k=1,2⋯T 。
如果我们从Softmax的输出 P P P 开始, P P P 其实是一个概率分布[2],另一个概率分布是真实的分类输出(也就是标签Label),通常用 Y Y Y 表示, Y Y Y 是一个 1 × T 1 \times T 1×T 的one-hot向量,其中除了一个元素是1之外,所有元素都是0,值为1的元素表示模型预测的类别。
基于上面所述的情景,交叉熵损失公式就是:
x e n t ( Y , P ) = − ∑ k = 1 T Y ( k ) l n ( P ( k ) ) xent(Y,P) = - \sum_{k=1}^T Y(k) ln(P(k)) xent(Y,P)=−k=1∑TY(k)ln(P(k))
k k k 代表所有可能的输出类, P ( k ) P(k) P(k) 为模型预测此输入属于这个类的概率, Y ( k ) Y(k) Y(k) 是真实情况下,此输入属于这个类别的概率。我们用 y y y 标记 Y ( k ) Y(k) Y(k) 中唯一的、值为1的元素的下标。因为 k ≠ y k \neq y k=y 时, Y ( k ) = 0 Y(k)=0 Y(k)=0 ,所以公式被化简为:
x e n t ( P ) = − l n ( P ( k ) ) xent(P) = - ln(P(k)) xent(P)=−ln(P(k))
交叉熵的雅可比矩阵是一个 1 × T 1 \times T 1×T 的行向量,由于输入的 P P P 也是 1 × T 1 \times T 1×T 的,故:
D x e n t ( P ) = [ D 1 x e n t D 2 x e n t ⋯ D T x e n t ] Dxent(P) = \begin{bmatrix} D_1 xent & D_2 xent & \cdots & D_T xent \\ \end{bmatrix} Dxent(P)=[D1xentD2xent⋯DTxent]
现在回想一下, P P P 可以用输入的权值表示: P ( W ) = S ( g ( W ) ) P(W)=S(g(W)) P(W)=S(g(W)),所以:
x e n t ( W ) = ( x e n t ∘ P ) ( W ) = x e n t ( P ( W ) ) xent(W) = (xent \circ P)(W) = xent(P(W)) xent(W)=(xent∘P)(W)=xent(P(W))
我们可以再用链式法则来求xent的梯度:
D x e n t ( W ) = D ( x e n t ∘ P ) ( W ) = D x e n t ( P ( W ) ) ⋅ D P ( W ) Dxent(W) = D(xent \circ P)(W) = Dxent(P(W)) \cdot DP(W) Dxent(W)=D(xent∘P)(W)=Dxent(P(W))⋅DP(W)
我们来检验一下雅可比矩阵的维数是否成立。我们已经计算了 D P ( W ) T × N T DP(W)_{T \times NT} DP(W)T×NT 和 D x e n t ( P ( W ) ) 1 × T Dxent(P(W))_{1 \times T} Dxent(P(W))1×T 所以结果雅可比矩阵是 D x e n t ( W ) 1 × N T Dxent(W)_{1 \times NT} Dxent(W)1×NT,结果成立,因为整个网络只有一个标量值输出(交叉熵损失)以及一个 N × T N \times T N×T 的权值输入 W W W。
这里,有一个直接的方法化简 D x e n t ( W ) Dxent(W) Dxent(W),因为矩阵乘法中的很多元素最终消去了。注意 x e n t ( P ) xent(P) xent(P) 只依赖于 P P P 的第 y y y 个元素,所以整个雅可比矩阵只有 D y x e n t ≠ 0 D_yxent \neq 0 Dyxent=0 :
D x e n t ( P ) = [ 0 0 D y x e n t ⋯ 0 ] Dxent(P) = \begin{bmatrix} 0 & 0 & D_yxent & \cdots & 0 \\ \end{bmatrix} Dxent(P)=[00Dyxent⋯0]
其中 D y x e n t = − 1 P y D_yxent = - \frac{1}{P_y} Dyxent=−Py1 。
回到完整的雅可比矩阵,我们把 D x e n t ( P ) Dxent(P) Dxent(P) 乘以 D P ( W ) DP(W) DP(W) 的每一列来得到结果行向量中的每一个元素,回想一下,行向量表示整个权值矩阵 W W W 以行主序被“线性化”。下面公式中 D i j D_{ij} Dij 表示行向量的第 ( i − 1 ) N + j (i-1)N+j (i−1)N+j 个元素:
D i j x e n t ( W ) = ∑ k = 1 T D k x e n t ( P ) ⋅ D i j P k ( W ) D_{ij}xent(W) = \sum_{k=1}^T D_k xent(P) \cdot D_{ij} P_k(W) Dijxent(W)=k=1∑TDkxent(P)⋅DijPk(W)
因为 D k x e n t ( P ) D_k xent(P) Dkxent(P) 只有第 y y y 个元素是非零的,化简得到如下(代入上面化简的Softmax导数):
D i j x e n t ( W ) = D y x e n t ( P ) ⋅ D i j P y ( W ) = − 1 P y ⋅ S y ( δ y i − S i ) x j = ( S i − δ y i ) x j \begin{aligned}\\ D_{ij}xent(W) &= D_y xent(P) \cdot D_{ij} P_y(W) \\ &= - \frac{1}{P_y} \cdot S_y (\delta_{yi} - S_i)x_j \\ &= (S_i - \delta_{yi})x_j \\ \end{aligned}\\ Dijxent(W)=Dyxent(P)⋅DijPy(W)=−Py1⋅Sy(δyi−Si)xj=(Si−δyi)xj
根据定义, P y = S y P_y=S_y Py=Sy,所以得到:
D i j x e n t ( W ) = − 1 S y ⋅ S y ( δ y i − S i ) x j = − ( δ y i − S i ) x j = ( S i − δ y i ) x j \begin{aligned}\\ D_{ij}xent(W) &= - \frac{1}{S_y} \cdot S_y (\delta_{yi} - S_i)x_j \\ &= - (\delta_{yi} - S_i)x_j \\ &= (S_i - \delta_{yi})x_j \\ \end{aligned}\\ Dijxent(W)=−Sy1⋅Sy(δyi−Si)xj=−(δyi−Si)xj=(Si−δyi)xj
再一次,即使在这种情况下,最终的结果是漂亮、干净,它不是必须这样,计算 D i j x e n t ( W ) D_{ij}xent(W) Dijxent(W) 的公式最终可能是一个相当复杂的求和(或求和的求和)。雅可比矩阵乘法的技巧也许用不上,因为计算机可以为我们做所有的求和,我们所要做的就是计算各个函数的雅可比矩阵,这通常比较简单因为它们用于更简单的非复合函数,这就是链式法则的魅力和效用。
[1] 为了更好地使用示例输入和Softmax输出,Michael Nielsen的在线书籍有一个很好的交互式Javascript可视化。
[2] 花一点时间回顾一下,根据定义,Softmax函数的输出确实是有效的离散概率分布。