神经网络求导

根据本文内容用 Numpy 实现的一个前馈神经网络 https://github.com/massquantity/DNN_implementation

本篇本来是想写神经网络反向传播算法,但感觉光写这个不是很完整,所以就在前面将相关的求导内容一并补上。所谓的神经网络求导,核心是损失函数对线性输出 z(z=Wa+b) 求导,即反向传播中的 δ=Lz ,求出了该值以后后面的对参数求导就相对容易了。



Jacobian 矩阵

函数 f:RnRm ,则 Jacobian 矩阵为:

fx=[f1x1f1x2f1xnf2x1f2x2f2xnfmx1fmx2fmxn]Rm×n

(fx)ij=fixj


神经网络中的激活函数多为对应元素运算 ( 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)x1f(x1)x2f(x1)xkf(x2)x1f(x2)x2f(x2)xkf(xk)x1f(xk)x2f(xk)xk]=[f(x1)000f(x2)000f(xk)]=diag(f(x))Rk×k




Sigmoid 激活函数

Sigmoid 函数的形式为:

σ(z)=11+ez(0,1)

其导数为:

σ(z)=(1+ez)(1+ez)2=ez(1+ez)2=ez1+ez11+ez=σ(z)(1σ(z))

若输入为 K 维向量 z=[z1,z2,...,zK]T ,根据上文的定义,其导数为

σ(z)=[σ(z1)(1σ(z1))000σ(z2)(1σ(z2))000σ(zk)(1σ(zk))]=diag(σ(z)(1σ(z)))




Tanh 激活函数

Tanh 函数可以看作是放大并平移的 Sigmoid 函数,但因为是零中心化的 (zero-centered) ,通常收敛速度快于 Sigmoid 函数,下图是二者的对比:

tanh(z)=ezezez+ez=21+e2z1=2σ(2z)1(1,1)

其导数为:

tanh(z)=(ez+ez)2(ezez)2(ez+ez)2=1tanh2(z)




Softplus 激活函数

Softplus 函数可以看作是 ReLU 函数的平滑版本,形式为:

softplus(z)=log(1+ez)

而其导数则恰好就是 Sigmoid 函数:

softplus(z)=ez1+ez=11+ez


另外:

softplus(z)softplus(z)=log1+ez1+ez=logez(ez+1)ez+1=z




Softmax 激活函数

softmax 函数将多个标量映射为一个概率分布,其形式为:

yi=softmax(zi)=ezik=1Cezk

yi 表示第 i 个输出值,也可表示属于类别 i 的概率, i=1Cyi=1

首先求标量形式的导数,即第 i 个输出对于第 j 个输入的偏导:

yizj=ezik=1Ceakzj

其中 ezizj 求导要分情况讨论,即:

ezizj={ezi,ifi=j0,ifij

那么当 i=j 时:

(1.1)yizj=ezik=1Cezkeziezj(k=1Cezk)2=ezik=1Cezkezik=1Cezkezjk=1Cezk=yiyiyj

ij 时:

(1.2)yizj=0eziezj(k=1Cezk)2=yiyj

于是二者综合:

(1.3)yizj=1{i=j}yiyiyj

其中 1{i=j}={1,ifi=j0,ifij


softmax 函数的输入为K 维向量 z=[z1,z2,...,zK]T 时,转换形式为 RKRK

y=softmax(z)=1k=1Kezk[ez1ez2ezK]

其导数同样为 Jabocian 矩阵 ( 同时利用 (1.1)(1.2) 式 ):

yz=[y1z1y1z2y1zKy2z1y2z2y2zKyKz1yKz2yKzK]=[y1y1y1y1y2y1yKy2y1y2y2y1y2yKyKy1yKy2yKyKyK]=diag(y)yyT=diag(softmax(z))softmax(z)softmax(z)T




交叉熵损失函数

交叉熵损失有两种表示形式,设真实标签为 y ,预测值为 a

(一) y 为标量,即 yR ,则交叉熵损失为:

L(y,a)=j=1k1{y=j}logaj

(二) y 为one-hot向量,即 y=[0,0...1...0]TRk ,则交叉熵损失为:

L(y,a)=j=1kyjlogaj




交叉熵损失函数 + Sigmoid激活函数

已知 L(y,a)=j=1kyjlogajaj=σ(zj)=11+ezj ,求 Lzj

Lzj=Lajajzj=yj1σ(zj)σ(zj)(1σ(zj))=σ(zj)1=ajyj




交叉熵损失函数 + Softmax激活函数

已知 L(y,a)=i=1kyilogaiaj=softmax(zj)=ezjc=1Cezc ,求 Lzj

Lzj=i=1kLaiaizj=i=jLajajzj+ijLaiaizj=yjajajzjijyiaiaizj=yjajaj(1aj)+ijyiaiaiaj运用 (1.1)和(1.2) 式=yj+yjaj+ijyiaj=ajyj

若输入为 K 维向量 z=[z1,z2,...,zk]T ,则梯度为:

Lz=ay=[a10aj1ak0]


另外运用对数除法运算,上面的求导过程可以简化:

L(y,a)=i=1kyilogai=i=1kyilogezicezc=i=1kyizi+yilogcezc

Lzi=yi+ezicezc=aiyi








神经网络反向传播算法

通常所谓的“学习”指的是通过最小化损失函数进而求得相应参数的过程,神经网络中一般采用梯度下降来实现这个过程,即:

θ=θαθL(θ)

用神经网络中的常用参数符号替换,并用矩阵形式表示:

W(l)=W(l)αLW(l)b(l)=b(l)αLb(l)

其中 (l) 表示第 l 层。


导数是梯度的组成部分,通常采用数值微分的方法近似下式:

f(x)=limh0f(x+h)f(x)h

f(x) 表示函数 f(x)x 处的斜率,但是由于运算时 h 不可能无限接近于零,上式容易引起数值计算问题,所以实际中常采用中心差分来近似:

f(x)=limh0f(x+h)f(xh)2h


这样整个梯度的计算可以用以下代码实现:

import numpy as np

def numerical_gradient(f, x):   # 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(xh) ,假设神经网络中有100万个参数,则需要计算200万次损失函数。如果是使用 SGD,则是每个样本计算200万次,显然是不可承受的。所以才需要反向传播算法这样能够高效计算梯度的方法。



接下来先定义神经网络中前向传播的式子 (l 表示隐藏层, L 表示输出层):


(2.1)z(l)=W(l)a(l1)+b(l)(2.2)a(l)=f(z(l))(2.3)y^=a(L)=f(z(L))(2.4)L=L(y,y^)


现在我们的终极目标是得到 LW(l)Lb(l) ,为了计算方便,先来看各自的分量 LWjk(l)Lbj(l)
这里定义 δj(l)=Lzj(l) , 根据 (2.1) 式使用链式法则: (2.5)LWjk(l)=Lzj(l)zj(l)Wjk(l)=δj(l)Wjk(l)(iWji(l)ai(l1))=δj(l)ak(l1)(2.6)Lbj(l)=Lzj(l)zj(l)bj(l)=δj(l)

所以接下来的问题就是求 δj(l) :


(1) 对于输出层 L

(2.7)δjL=Lzj(L)=Laj(L)aj(L)zj(L)=Laj(L)f(zj(L))

(2) 对于隐藏层 l ,由 (2.1) 式可知:

{z1(l+1)=jW1j(l+1)aj(l)+b1(l+1)z2(l+1)=jW2j(l+1)aj(l)+b2(l+1)zk(l+1)=jWkj(l+1)aj(l)+bk(l+1)

可见 aj(l) 对于 z(l+1) 的每个分量都有影响,使用链式法则时需要加和每个分量,下图是一个形象表示:


所以下面求 δj(l) 时会用 k 加和:

δj(l)=Lzj(l)=(kLzk(l+1)zk(l+1)aj(l))aj(l)zj(l)=(kLzk(l+1)(jWkj(l+1)aj(l)+bk(l+1))aj(l))aj(l)zj(l)(2.8)=(kδk(l+1)Wkj(l+1))f(zj(l))



将上面的 (2.5)(2.8) 式写成矩阵形式,就得到了传说中反向传播算法的四大公式:

δ(L)=Lz(L)=a(L)Lf(z(L))δ(l)=Lz(l)=((W(l+1))Tδ(l+1))f(z(l))LW(l)=δ(l)(a(l1))T=[δ1(l)a1(l1)δ1(l)a2(l1)δ1(l)ak(l1)δ2(l)a1(l1)δ2(l)a2(l1)δ2(l)ak(l1)δj(l)a1(l1)δj(l)a2(l1)δj(l)ak(l1)]Lb(l)=δ(l)


δ(l) 的计算可以直接套用上面损失函数 + 激活函数的计算结果。




反向传播算法 + 梯度下降算法流程

(1) 前向传播阶段:使用下列式子计算每一层的 z(l)a(l) ,直到最后一层。

z(l)=W(l)a(l1)+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) 计算梯度: LW(l)=δ(l)(a(l1))TLb(l)=δ(l)


(3) 参数更新

W(l)=W(l)αLW(l)b(l)=b(l)αLb(l)







Reference

  1. 神经网络反向传播算法
  2. How the backpropagation algorithm works
  3. The Softmax function and its derivative
  4. Notes on Backpropagation
  5. Computational Graph & Backpropagation





/

posted @   massquantity  阅读(3011)  评论(1编辑  收藏  举报
编辑推荐:
· 从 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的设计差异
点击右上角即可分享
微信分享提示