本篇本来是想写神经网络反向传播算法,但感觉光写这个不是很完整,所以就在前面将相关的求导内容一并补上。所谓的神经网络求导,核心是损失函数对线性输出 z(z=Wa+b) 求导,即反向传播中的 δ=∂L∂z ,求出了该值以后后面的对参数求导就相对容易了。
Jacobian 矩阵
函数 f:Rn→Rm ,则 Jacobian 矩阵为:
∂f∂x=⎡⎢
⎢
⎢
⎢
⎢
⎢
⎢
⎢⎣∂f1∂x1∂f1∂x2⋯∂f1∂xn∂f2∂x1∂f2∂x2⋯∂f2∂xn⋮⋮⋱∂fm∂x1∂fm∂x2⋯∂fm∂xn⎤⎥
⎥
⎥
⎥
⎥
⎥
⎥
⎥⎦∈Rm×n
即 (∂f∂x)ij=∂fi∂xj
神经网络中的激活函数多为对应元素运算 ( element-wise ) ,设输入为K维向量 x=[x1,x2,...,xK]T , 输出为K维向量 z=[z1,z2,...,zK]T ,则激活函数为 z=f(x) ,即 zi=[f(x)]i=f(xi) ,则其导数按 Jacobian 矩阵的定义为一个对角矩阵:
∂f(x)∂x=⎡⎢
⎢
⎢
⎢
⎢
⎢
⎢
⎢
⎢⎣∂f(x1)∂x1∂f(x1)∂x2⋯∂f(x1)∂xk∂f(x2)∂x1∂f(x2)∂x2⋯∂f(x2)∂xk⋮⋮⋱∂f(xk)∂x1∂f(xk)∂x2⋯∂f(xk)∂xk⎤⎥
⎥
⎥
⎥
⎥
⎥
⎥
⎥
⎥⎦=⎡⎢
⎢
⎢
⎢
⎢⎣f′(x1)0⋯00f′(x2)⋯0⋮⋮⋱00⋯f′(xk)⎤⎥
⎥
⎥
⎥
⎥⎦=diag(f′(x))∈Rk×k
Sigmoid 激活函数
Sigmoid 函数的形式为:
σ(z)=11+e−z∈(0,1)
其导数为:
σ′(z)=−(1+e−z)′(1+e−z)2=−−e−z(1+e−z)2=e−z1+e−z⋅11+e−z=σ(z)(1−σ(z))
若输入为 K 维向量 z=[z1,z2,...,zK]T ,根据上文的定义,其导数为
σ′(z)=⎡⎢
⎢
⎢
⎢
⎢⎣σ(z1)(1−σ(z1))0⋯00σ(z2)(1−σ(z2))⋯0⋮⋮⋱00⋯σ(zk)(1−σ(zk))⎤⎥
⎥
⎥
⎥
⎥⎦=diag(σ(z)⊙(1−σ(z)))
Tanh 激活函数
Tanh 函数可以看作是放大并平移的 Sigmoid 函数,但因为是零中心化的 (zero-centered) ,通常收敛速度快于 Sigmoid 函数,下图是二者的对比:
tanh(z)=ez−e−zez+e−z=21+e−2z−1=2σ(2z)−1∈(−1,1)
其导数为:
tanh′(z)=(ez+e−z)2−(ez−e−z)2(ez+e−z)2=1−tanh2(z)
Softplus 激活函数
Softplus 函数可以看作是 ReLU 函数的平滑版本,形式为:
softplus(z)=log(1+ez)
而其导数则恰好就是 Sigmoid 函数:
softplus′(z)=ez1+ez=11+e−z
另外:
softplus(z)−softplus(−z)=log1+ez1+e−z=logez(e−z+1)e−z+1=z
Softmax 激活函数
softmax 函数将多个标量映射为一个概率分布,其形式为:
yi=softmax(zi)=ezi∑Ck=1ezk
yi 表示第 i 个输出值,也可表示属于类别 i 的概率, C∑i=1yi=1。
首先求标量形式的导数,即第 i 个输出对于第 j 个输入的偏导:
∂yi∂zj=∂ezi∑Ck=1eak∂zj
其中 ezi 对 zj 求导要分情况讨论,即:
∂ezi∂zj={ezi,ifi=j0,ifi≠j
那么当 i=j 时:
∂yi∂zj=ezi∑Ck=1ezk−eziezj(∑Ck=1ezk)2=ezi∑Ck=1ezk−ezi∑Ck=1ezkezj∑Ck=1ezk=yi−yiyj(1.1)
当 i≠j 时:
∂yi∂zj=0−eziezj(∑Ck=1ezk)2=−yiyj(1.2)
于是二者综合:
∂yi∂zj=1{i=j}yi−yiyj(1.3)
其中 1{i=j}={1,ifi=j0,ifi≠j
当 softmax 函数的输入为K 维向量 z=[z1,z2,...,zK]T 时,转换形式为 RK→RK :
y=softmax(z)=1∑Kk=1ezk⎡⎢
⎢
⎢
⎢⎣ez1ez2⋮ezK⎤⎥
⎥
⎥
⎥⎦
其导数同样为 Jabocian 矩阵 ( 同时利用 (1.1) 和 (1.2) 式 ):
∂y∂z=⎡⎢
⎢
⎢
⎢
⎢
⎢
⎢
⎢⎣∂y1∂z1∂y1∂z2⋯∂y1∂zK∂y2∂z1∂y2∂z2⋯∂y2∂zK⋮⋮⋱∂yK∂z1∂yK∂z2⋯∂yK∂zK⎤⎥
⎥
⎥
⎥
⎥
⎥
⎥
⎥⎦=⎡⎢
⎢
⎢
⎢
⎢⎣y1−y1y1−y1y2⋯−y1yK−y2y1y2−y2y1⋯−y2yK⋮⋮⋱−yKy1−yKy2⋯yK−yKyK⎤⎥
⎥
⎥
⎥
⎥⎦=diag(y)−yyT=diag(softmax(z))−softmax(z)softmax(z)T
交叉熵损失函数
交叉熵损失有两种表示形式,设真实标签为 y ,预测值为 a :
(一) y 为标量,即 y∈R ,则交叉熵损失为:
L(y,a)=−k∑j=11{y=j}logaj
(二) y 为one-hot向量,即 y=[0,0...1...0]T∈Rk ,则交叉熵损失为:
L(y,a)=−k∑j=1yjlogaj
交叉熵损失函数 + Sigmoid激活函数
已知 L(y,a)=−k∑j=1yjlogaj, aj=σ(zj)=11+e−zj ,求 ∂Lzj :
∂L∂zj=∂L∂aj∂aj∂zj=−yj1σ(zj)σ(zj)(1−σ(zj))=σ(zj)−1=aj−yj
交叉熵损失函数 + Softmax激活函数
已知 L(y,a)=−k∑i=1yilogai, aj=softmax(zj)=ezj∑Cc=1ezc ,求 ∂L∂zj :
∂L∂zj=k∑i=1∂L∂ai∂ai∂zj=∑i=j∂L∂aj∂aj∂zj+∑i≠j∂L∂ai∂ai∂zj=−yjaj∂aj∂zj−∑i≠jyiaiaizj=−yjajaj(1−aj)+∑i≠jyiaiaiaj运用 (1.1)和(1.2) 式=−yj+yjaj+∑i≠jyiaj=aj−yj
若输入为 K 维向量 z=[z1,z2,...,zk]T ,则梯度为:
∂L∂z=a−y=⎡⎢
⎢
⎢
⎢
⎢
⎢
⎢
⎢⎣a1−0⋮aj−1⋮ak−0⎤⎥
⎥
⎥
⎥
⎥
⎥
⎥
⎥⎦
另外运用对数除法运算,上面的求导过程可以简化:
L(y,a)=−k∑i=1yilogai=−k∑i=1yilogezi∑cezc=−k∑i=1yizi+yilog∑cezc
∂L∂zi=−yi+ezi∑cezc=ai−yi
神经网络反向传播算法
通常所谓的“学习”指的是通过最小化损失函数进而求得相应参数的过程,神经网络中一般采用梯度下降来实现这个过程,即:
θ=θ−α⋅∂∂θL(θ)
用神经网络中的常用参数符号替换,并用矩阵形式表示:
W(l)=W(l)−α∂L∂W(l)b(l)=b(l)−α∂L∂b(l)
其中 (l) 表示第 l 层。
导数是梯度的组成部分,通常采用数值微分的方法近似下式:
f′(x)=limh→0f(x+h)−f(x)h
f′(x) 表示函数 f(x) 在 x 处的斜率,但是由于运算时 h 不可能无限接近于零,上式容易引起数值计算问题,所以实际中常采用中心差分来近似:
f′(x)=limh→0f(x+h)−f(x−h)2h
这样整个梯度的计算可以用以下代码实现:
import numpy as np
def numerical_gradient(f, x):
h = 1e-4
grad = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
idx = it.multi_index
temp = x[idx]
x[idx] = temp + h
fxh1 = f(x)
x[idx] = temp - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = temp
it.iternext()
return grad
由于数值微分对每个参数都要计算 f(x+h) 和 f(x−h) ,假设神经网络中有100万个参数,则需要计算200万次损失函数。如果是使用 SGD,则是每个样本计算200万次,显然是不可承受的。所以才需要反向传播算法这样能够高效计算梯度的方法。
接下来先定义神经网络中前向传播的式子 (l 表示隐藏层, L 表示输出层):
z(l)=W(l)a(l−1)+b(l)a(l)=f(z(l))^y=a(L)=f(z(L))L=L(y,^y)(2.1)(2.2)(2.3)(2.4)
现在我们的终极目标是得到
∂L∂W(l) 和
∂L∂b(l) ,为了计算方便,先来看各自的分量
∂L∂W(l)jk 和
∂L∂b(l)j 。
这里定义
δ(l)j=∂L∂z(l)j , 根据
(2.1) 式使用链式法则:
∂L∂W(l)jk=∂L∂z(l)j∂z(l)j∂W(l)jk=δ(l)j∂∂W(l)jk(∑iW(l)jia(l−1)i)=δ(l)ja(l−1)k∂L∂b(l)j=∂L∂z(l)j∂z(l)j∂b(l)j=δ(l)j(2.5)(2.6)
所以接下来的问题就是求 δ(l)j :
(1) 对于输出层 L :
δLj=∂L∂z(L)j=∂L∂a(L)j∂a(L)j∂z(L)j=∂L∂a(L)jf′(z(L)j)(2.7)
(2) 对于隐藏层 l ,由 (2.1) 式可知:
⎧⎪
⎪
⎪
⎪
⎪
⎪
⎪⎨⎪
⎪
⎪
⎪
⎪
⎪
⎪⎩z(l+1)1=∑jW(l+1)1ja(l)j+b(l+1)1z(l+1)2=∑jW(l+1)2ja(l)j+b(l+1)2⋮z(l+1)k=∑jW(l+1)kja(l)j+b(l+1)k
可见 a(l)j 对于 z(l+1) 的每个分量都有影响,使用链式法则时需要加和每个分量,下图是一个形象表示:
所以下面求 δ(l)j 时会用 k 加和:
δ(l)j=∂L∂z(l)j=⎛⎝∑k∂L∂z(l+1)k∂z(l+1)k∂a(l)j⎞⎠∂a(l)j∂z(l)j=⎛⎜
⎜
⎜
⎜
⎜
⎜⎝∑k∂L∂z(l+1)k∂(∑jW(l+1)kja(l)j+b(l+1)k)∂a(l)j⎞⎟
⎟
⎟
⎟
⎟
⎟⎠∂a(l)j∂z(l)j=(∑kδ(l+1)kW(l+1)kj)f′(z(l)j)(2.8)
将上面的 (2.5)∼(2.8) 式写成矩阵形式,就得到了传说中反向传播算法的四大公式:
δ(L)=∂L∂z(L)=∇a(L)L⊙f′(z(L))δ(l)=∂L∂z(l)=((W(l+1))Tδ(l+1))⊙f′(z(l))∂L∂W(l)=δ(l)(a(l−1))T=⎡⎢
⎢
⎢
⎢
⎢
⎢
⎢⎣δ(l)1a(l−1)1δ(l)1a(l−1)2⋯δ(l)1a(l−1)kδ(l)2a(l−1)1δ(l)2a(l−1)2⋯δ(l)2a(l−1)k⋮⋮⋱δ(l)ja(l−1)1δ(l)ja(l−1)2⋯δ(l)ja(l−1)k⎤⎥
⎥
⎥
⎥
⎥
⎥
⎥⎦∂L∂b(l)=δ(l)
而 δ(l) 的计算可以直接套用上面损失函数 + 激活函数的计算结果。
反向传播算法 + 梯度下降算法流程
(1) 前向传播阶段:使用下列式子计算每一层的 z(l) 和 a(l) ,直到最后一层。
z(l)=W(l)a(l−1)+b(l)a(l)=f(z(l))^y=a(L)=f(z(L))L=L(y,^y)
(2) 反向传播阶段:
(2.1) 计算输出层的误差: δ(L)=∇a(L)L,⊙f′(z(L))
(2.2) 由后一层反向传播计算前一层的误差: δ(l)=((W(l+1))Tδ(l+1))⊙f′(z(l))
(2.3) 计算梯度: ∂L∂W(l)=δ(l)(a(l−1))T , ∂L∂b(l)=δ(l)
(3) 参数更新:
W(l)=W(l)−α∂L∂W(l)b(l)=b(l)−α∂L∂b(l)
Reference
- 神经网络反向传播算法
- How the backpropagation algorithm works
- The Softmax function and its derivative
- Notes on Backpropagation
- Computational Graph & Backpropagation
/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异