深度学习中的矩阵求导基础#
Denominator Layout and Numerator Layout#
在深度学习中,矩阵求导是一个重要的概念。我们通常使用两种布局来表示矩阵求导:分母布局(Denominator Layout)和分子布局(Numerator Layout)。
分母布局(Denominator Layout)
在分母布局中,我们将矩阵的每一行视为一个整体,求导时将整个行作为一个单位来处理。这种布局在处理一些特定类型的矩阵时非常有用,例如在计算雅可比矩阵(矩阵的一阶导,Jacobian Matrix)时。
分子布局(Numerator Layout)
在分子布局中,我们将矩阵的每一列视为一个整体,求导时将整个列作为一个单位来处理。这种布局在处理一些其他类型的矩阵时更为方便,例如在计算海森矩阵(矩阵的二阶导,Hessian Matrix)时。
对于一个函数 y=Wx ,其中 y∈RD1,W∈RD1×D2,x∈RD2 ,其导数为:
∂x∂y=∂x1∂y1∂x2∂y1⋮∂xD2∂y1∂x1∂y2∂x2∂y2⋮∂xD2∂y2⋯⋯⋱⋯∂x1∂yD1∂x2∂yD1⋮∂xD2∂yD1=W⊤ (Denominator Layout)
∂x∂y=∂x1∂y1∂x1∂y2⋮∂x1∂yD1∂x2∂y1∂x2∂y2⋮∂x2∂yD1⋯⋯⋱⋯∂xD2∂y1∂xD2∂y2⋮∂xD2∂yD1=W (Numerator Layout)
矩阵求导的链式法则#
对于一个函数 y=f(g(h(x))) ,如果我们令 q=h(x) ,k=g(q) ,y=f(k) ,那么根据链式法则,我们先以内层为单位排列(分母表达式,Denominator Layout)可以得到:
∂x∂y=∂x∂q⋅∂q∂k⋅∂k∂y
分子表达式(Numerator Layout)下:
∂x∂y=∂k∂y⋅∂q∂k⋅∂x∂q
一个例子#
对于函数 y=(Ax−b)⊤(Ax−b) ,我们可以使用链式法则来计算其导数。
其中 x∈RD,A∈RD1×D ,b∈RD1 ,y∈R 。
令 z=Ax−b ,则有 y=z⊤z 。
根据链式法则,我们可以得到:
(Denominator Layout)∂x∂y=∂x∂z⋅∂z∂y=A⊤⋅(2z)=2A⊤(Ax−b)
(Numerator Layout)∂x∂y=∂z∂y⋅∂x∂z=2z⊤⋅A=2(Ax−b)⊤⋅A
显然 (2(Ax−b)⊤⋅A)⊤=2A⊤(Ax−b) 。
在分母表达式下 2A⊤(Ax−b) 的形状是 2×RD×D1×(RD1×D×RD×1−RD1×1)=RD1×1 ,即为长度为 D1 的列向量。
含有Layer Norm的矩阵求导#
在深度学习中,Layer Norm(层归一化)是一个常用的技术,它在特征维度上进行归一化处理。
先假设输入是一个矩阵 X∈Rm×n ,其中 m 是样本数量,n 是特征数量。
- 对于每个样本,计算每个样本的均值和标准差:
- 均值:μi=n1∑j=1nxij
- 标准差:σi2=n1∑j=1n(xij−μi)2
- 对每个样本进行归一化处理:
- 归一化:x^ij=σi2+ϵxij−μi
- 其中ϵ是一个小常数,用于防止除以零。
- 对归一化后的结果进行缩放和平移:
- 平移:yij=γx^ij+β
- 其中 γ 和 β 是可学习的参数。
例子:#
输入 x 是一维列向量,长度为 D
p=Wxk=max(p,0)y=LN(k)=γ⊙σ2(k)+ξk−μ(k)+β
我们可以知道对于第一个式子 ∂x∂p=W⊤ 。
对于第二个式子p,它是一个列向量,它的值为 max(p,0) ,因此其导数为:
∂p∂k={10if p>0if p≤0
一般我们可以将其表示为一个对角矩阵 diag(1p>0),其中对角线上的元素为 1 或 0,具体取决于 p 的值。
第三个式子就是重头戏了,Layer Norm的导数计算相对复杂,我们假设输入是一个列向量 k ,其均值为 μ(k) ,标准差为 σ2(k) ,则Layer Norm的导数可以表示为:
k−μ(k)=(I−D111⊤)k
其中 11⊤ 是一个全1的矩阵。D的大小是k的长度。
我们令 k−μ(k)=ν ,则有:
q=σ2(k)+ϵk−μ(k)=D1∑i=1D(ki−μ(k))(ki−μ(k))+ξk−μ(k)=D1∑i=1Dνiνi+ξν=∣∣ν∣∣22+DξDν
那么 ∂ν∂q 的雅可比矩阵为:
∂ν∂q=∣∣ν∣∣22+DξD⋅(I−∣∣ν∣∣22+Dξνν⊤)
对于 I−∣∣ν∣∣22+Dξνν⊤我们可以知道如果输入是 RD ,那么输出就是 RD×D 。而它前面的部分就是一个标量了。
如果输入 k 的每一维都相等,则ν的所有分量为零,此时 ∣∣ν∣∣22 为零。为了避免这种情况导致分母为零,我们引入了 ξ 作为正则化项,确保计算的稳定性。不加 ξ 的话它就不是Lipschitz连续函数。关于Lipschitz连续后面会单独写一篇文章介绍。
回到 y=LN(k)=γ⊙σ2(k)+ξk−μ(k)+β ,可以重写为:
y=diag(γ)⋅q+β
根据链式法则,我们可以得到:
∂k∂y=∂k∂ν⋅∂ν∂q⋅∂q∂y=(I−D111⊤)⋅∣∣ν∣∣22+DξD⋅(I−∣∣ν∣∣22+Dξνν⊤)⋅diag(γ)
最终的∂x∂y就是:
∂x∂y=∂x∂p⋅∂p∂k⋅∂k∂y
带入相应结果即可。
含有Loss#
我们知道神经网络需要计算损失函数(Loss Function)来进行训练。
对于下图所示的神经网络,我们可以使用链式法则来计算损失函数对输入的导数。
该神经网络有两个linear层,紧跟其后的是Softmax层和Cross Entropy Loss层。
在前向计算过程中我们要计算
y=W1xh=W2yp=Softmax(h)L=CrossEntropy(p,t)
Softmax和CrossEntropy没有参数,实际上我们只需要计算两个线性层的导数。
注意这里的t是一个one-hot编码的向量,表示真实标签。对于Softmax层和CrossEntropy层导数推导如下:
Softmax函数定义为:pi=∑jehjehi
导数为:∂h∂p=diag(p)−pp⊤
diag(p) 是一个对角矩阵,主对角线是 (p) 的元素。
CrossEntropy Loss定义为:L=−∑itilog(pi)
导数为:∂p∂L=−pt
根据链式法则,我们可以得到:∂h∂L=p−t
∂h∂L=∂h∂p⋅∂p∂L=RCp−t∂W2∂L=RC×1∂h∂L⋅R1×D1y⊤∂y∂L=RD1×CW2⊤⋅RC×1∂h∂L∂W1∂L=∂y∂L⋅x⊤
参数更新#
TODO