禁止转载
问题
常见的标量版反向传播公式从推导上易于理解,但对代码实现不友好
所以这里用向量化的形式写出反向传播。 具体要求:
- 向量化写法,避免出现
∑,只采用矩阵的四则运算和逐元素运算
- 带有L2正则项
- 直接写一个batch,避免一个一个样本的写
- 多分类交叉熵损失
说明
- 上述这些要求的优势在于可以直接转化为向量化的计算代码
- 在pytorch、numpy等框架中,向量化的计算比用循环累加要快非常多
- 本文参考了矩阵求导术-上,强推!本文采用的符号规范与之一致,如果没有看过直接看本文也没有问题
网络结构
- 网络结构必须定义明确,说明清楚
- 输入数据为
X,Y
- 网络一共三层,结构为:
fc1:X→F1relu:F1→G1fc2:G1→F2
- 输入层维度为
D,隐层维度为
H,输出层维度为
C. 所以每一层的参数形状为
W1∈RH×DW2∈RC×Hb1∈RH×1b2∈RC×1
- 记一次batch样本数为
N,所以输入层、隐层、类别标注的形状为
X∈RD×NF1,G1∈RH×NF2,Y∈RC×N
这里
Y采用了one-hot编码
- 我用了列向量写法,如果你习惯于行向量,那么可以参考我贴在文末的手稿
前向传播
F1=W1X+b111×N(1)
G1=max(0,F1)(2)
F2=W2G1+b211×N(3)
其中
1a×b表示形状为
a×b的全1矩阵
记
L1为经验损失项
L1=[ln(11×CeF2)−11×C(F2⊙Y)]1N×1(4)
其中“
⊙”表示逐元素乘法,要求参与运算的两个矩阵维度相同
本文所有的指数函数
e⋅和对数函数
ln(⋅)都为逐元素函数
取均值的操作
N1L1放到了后文引入,为了方便求导书写
式(4)的正确性稍加解释:
对于样本
i,
L1i=−ln∑y=1CeF2yieF2yii=ln(∑y=1CeF2yi)−F2yii=ln(11×CeF2⋅i)−11×C(F2⋅i⊙Y⋅i).
其中
yi∈{1,2,...,N},表示样本
i的类别。只要注意到
Y⋅i是one-hot编码,该推导不难成立
由
L1=∑i=1NL1i,得式(4),可以自行验证
[
- 一段冗笔,可以不看:
如果记
Y^为经过softmax后的网络输出,
Y^∈RC×N,那么
Y^=eF2⊙1C×CeF21
其中
A1表示把
A逐元素取倒数,
L1又可写成
L1=−ln(11×C(Y^⊙Y))1N×1
这样
L1就避免了直接使用
F2,但是该式到式(4)的正确性不易看出,该式也不利于后续求导
不过这两式易于构建计算图,方便代码实现
后续推导仍然使用式(4)
]
记
L2表示正则损失项,
L21为
W1的损失,
L22为
W2的损失
L2=L21+L22=11×H(W1⊙W1)1D×1+11×C(W2⊙W2)1H×1(5)
L=N1L1+λL2(6)
式(1)~式(6)完整介绍了前向传播的向量化写法
下一节将介绍反向传播的向量化写法,高潮要来了呀,准备好
反向传播
首先简单介绍两个知识点
- Jacobian辨识:如果
df=tr(AdX),那么
∂X∂f=AT
即如果想求
∂X∂f,只要能把
df写成
tr(AdX)的形式,那么就能把
∂X∂f辨识出来,为
AT
- 迹的一些性质,很重要,这些技巧后文会用
- 矩阵乘法/逐元素乘法交换:
tr{AT(B⊙C)}=tr{(A⊙B)TC}
- 轮换对称性:
tr{AB}=tr{BA}
- 转置迹不变:
tr{A}=tr{AT}
- 微分内提:
dtr{A}=tr{dA}
另外,我们记
A1表示把
A逐元素取倒数
开始吧
经验损失项
dL=N1dL1+λdL2(7)
dL1=[d[ln(11×CeF2)]−11×C(dF2⊙Y)]1N×1=[d[(11×CeF2)]⊙11×CeF21−11×C(dF2⊙Y)]1N×1=[(11×C(eF2⊙dF2))⊙11×CeF21−11×C(dF2⊙Y)]1N×1=tr{[(11×C(eF2⊙dF2))⊙11×CeF21−11×C(dF2⊙Y)]1N×1}=tr{[(11×C(eF2⊙dF2))⊙11×CeF21]1N×1}−tr{11×C(dF2⊙Y)1N×1}=tr{1N×1[(11×C(eF2⊙dF2))⊙11×CeF21]}−tr{1N×C(dF2⊙Y)}=tr{[11×N⊙(11×C(eF2⊙dF2))]T11×CeF21}−tr{(1C×N⊙dF2)TY)}=tr{[11×C(eF2⊙dF2)]T11×CeF21}−tr{dF2TY}=tr{(dF2T⊙eF2T)(1C×111×CeF21)}−tr{dF2TY}=tr{dF2T[eF2⊙(1C×111×CeF21)]}−tr{dF2TY}=tr{[eF2⊙(1C×111×CeF21)−Y]TdF2}
一波推导,Jacobian辨识得
∂F2∂L1=eF2⊙(1C×111×CeF21)−Y(8)
接下来,开始反向传播,由式(3)
dL1=tr{∂F2∂L1TdF2}=tr{∂F2∂L1T(dW2G1+W2dG1+db211×N)}=tr{G1∂F2∂L1TdW2}+tr{∂F2∂L1TW2dG1}+tr{11×N∂F2∂L1Tdb2}
Jacobian辨识得
∂W2∂L1=∂F2∂L1G1T(9)
∂b2∂L1=∂F2∂L11N×1(10)
∂G1∂L1=W2T∂F2∂L1(11)
式(9)和式(10)给出了经验损失项关于
W2和
b2的参数更新公式
我们继续反向传播,由式(2)
dL1=tr{∂G1∂L1dG1}+...(G1无关项)=tr{∂G1∂L1[I(F1>0)⊙dF1]}+...=tr{[∂G1∂L1T⊙I(F1>0)]TdF1}+...
Jacobian辨识得
∂F1∂L1=∂G1∂L1⊙I(F1>0)(12)
由式(1)
dL1=tr{∂F1∂L1TdF1}+...(F1无关项)=tr{∂F1∂L1T(dW1X+db111×N)}+...=tr{X∂F1∂L1TdW1}+tr{11×N∂F1∂L1Tdb1}+...
Jacobian辨识得
∂W1∂L1=∂F1∂L1XT(13)
∂b1∂L1=∂F1∂L11N×1(14)
式(13)和式(14)给出了经验损失项关于
W1和
b1的参数更新公式。注意这两式和
W2,b2更新公式(9)和(10)之间的相似性。更多层反向传播公式是可以归纳出来的
正则损失项
对于
L2
dL21=11×H(W1⊙dW1)1D×1+11×H(dW1⊙W1)1D×1=2tr{11×H(W1⊙dW1)1D×1}=2tr{1D×H(W1⊙dW1)}=2tr{(1H×D⊙W1)TdW1)}=tr{2W1TdW1}
由Jacobian辨识得
∂W1∂L21=2W1(15)
同理
∂W2∂L21=2W2(16)
可以看出L2参数项对参数的影响与用标量写法推导是一致的
L1参数项的向量化推导类似
后记
- 写了一天……
- 矩阵求导是非常强大的工具,而且求出来的表达式可以直接转化成代码实现
- 类似的方法可以推导SVM合页损失、Logistic回归、线性回归等的参数更新公式,可以留作练习(想起来之前面试让我求最小二乘的梯度,我想推向量写法,没推出来就很气,无奈勉强写了标量写法)
- 上述方法始终保持了标量对矩阵求导,反向传播即是把矩阵微分不断打开的过程
- 在矩阵求导术(下)中,给出了一种矩阵对矩阵求导的技法,似乎是可以写出另一种链式法则,这里我存有一个疑问:
如果输出结果为标量(例如loss),是否用矩阵求导术(上)的方法更为方便?因为(下)中方法要先把矩阵拍开,而后再合上?
- 我用手稿写了一份行向量的版本,仅供参考