3.一文看懂反向传播:从单个神经元到 PyTorch 自动求导

张开发
2026/4/10 4:29:12 15 分钟阅读

分享文章

3.一文看懂反向传播:从单个神经元到 PyTorch 自动求导
反向求导多层次对应一个神经单个神经元场景学习这一篇的前提是已经学会了梯度算法和线性结构算法不明白的可以去看我之前的文章。前面看不懂的直接跳转到 “ 反向传播的流程 ”底层的数学算法z 是中间变量 u 的函数u 是自变量 w权重的函数因此 z 通过 u 间接依赖 w。公式核心逻辑为单路相乘对权重 w 求偏导时仅存在 z→u→w 一条路径如果嵌套的跟多那就继续导将路径上的偏导数直接相乘即可无其他分支无需相加。注意这一篇文章只考虑单个神经元场景多个场景在下一篇完成。第一张图片是多个神经元的场景第二章是本文中对应的底层的运用逻辑我们知道对一个函数的求导就是求这个函数的最值。大模型的运用就是面对海量的训练数据找到一个最贴近输入一个x输出一个正确的y值。这样我们的大模型在没有对应的y值时也能通过之前我们训练的函数计算出预算出。所以训练的过程中我们要不断的求导直到找到最合适的 w 和 b 。类比人类的大脑如图看不懂的直接看下一张图树突就是我们的影响因数 w 和 b 但是树突不可能只有一个是有很多个的。轴突他输出信息的一个过程在我们的反向传播可以类比成激活函数他的作用就是放在我们的函数模型太过单一这里的意思就是嵌套另外一个函数上面的类比图只类比了一层一组 w 和 b树突对应 激活函数轴突。现在来看多层对应单个神经元的图反向传播的流程好的现在就根据这个来解说反向传播的流程我们先假设值 w1 b1 w2 b2 w3 b3,激活函数是在每一个算完 y wx b 对 fy 再进行嵌套的一个函数。正向运算一步一步的计算得到一个 y_forward 值。将 y_forward 与真正的 y_true 进行相减去得到损失值 loss 1/2*( y_forward - y_true )^2。为什么要*1/2这是因为我们方便计算倒数自己规定的因为导一下就没有常数了这个不影响因为反向传播会自己寻找最合适的 w 和 b进行平方是为了保证非负数。经理过这一步了我们就得到了 w1 b1 w2 b2 w3 b3 y_forward loss这几个值接下来我们就要进行反向求导一步一步的得到最佳 w 和 b分别对w1 b1 w2 b2 w3 b3进行求导注意了我们求导的函数是 loss 1/2*( y_forward - y_true )^2。求导公式如图求导之后我们得到了w1 b1 w2 b2 w3 b3 他们的导数然后用跟新公式对他们进行跟新。如此反复我们就能得到一个最佳的 w1 b1 w2 b2 w3 b3值。代码展示三层四层就是多两个参数思路代码是一样的# 这个库里面有自动帮我们计算的求导的方法就不用自己去手动计算了importtorch# 定义数据x_data[0.0,1.0,2.0,3.0]y_data[1.0,3.0,5.0,7.0]# 第1层参数w1torch.tensor([0.1],requires_gradTrue)# 第1层权重b1torch.tensor([0.0],requires_gradTrue)# 第1层偏置# 第2层参数w2torch.tensor([0.1],requires_gradTrue)# 第2层权重b2torch.tensor([0.0],requires_gradTrue)# 第2层偏置# 第3层参数输出层w3torch.tensor([0.1],requires_gradTrue)# 第3层权重b3torch.tensor([0.0],requires_gradTrue)# 第3层偏置# 定义学习率lr0.1# 定义遍历次数epochs2000# 定义期望函数defforward(x):# 第一层z1w1*xb1# a1 torch.sigmoid(z1) # 激活函数 这里知道了是线性结构就不用激活函数了如果要的话就嵌套进去就好了注意几个传参就是了# 第二层z2w2*z1b2# a2 torch.sigmoid(z2) # 激活函数这里知道了是线性结构就不用激活函数了如果要的话就嵌套进去就好了注意几个传参就是了# 第三层输出y_predw3*z2b3returnz1,z2,y_pred# 定义损失函数defloss_n(y_pred,y_true):return0.5*(y_pred-y_true)**2# 定义变量方便找到最小的损失值best_lossfloat(inf)# 先设成无穷大方便后面比较best_w10best_b10best_w20best_b20best_w30best_b30# 开始遍历计算forepochinrange(epochs):total_loss0forx,yinzip(x_data,y_data):# 先将原始数据转化成可以计算的形式# 因为 PyTorch 的运算和自动求导主要针对 tensorx_tensortorch.tensor([x])y_tensortorch.tensor([y])# 拿到原始数据z1,z2,y_predforward(x_tensor)# 开始计算求导,用链式法则把每一层参数的梯度都求出来lossloss_n(y_pred,y_tensor)loss.backward()# 梯度跟新公式withtorch.no_grad():w1-lr*w1.grad b1-lr*b1.grad w2-lr*w2.grad b2-lr*b2.grad w3-lr*w3.grad b3-lr*b3.grad# 记录损失,.item()是取里面的值因为他是tensor对象的数据total_lossloss.item()# 清零w1.grad.zero_()b1.grad.zero_()w2.grad.zero_()b2.grad.zero_()w3.grad.zero_()b3.grad.zero_()# 平均损失值total_losstotal_loss/len(x_data)# 找最佳的参数iftotal_lossbest_loss:best_losstotal_loss# 找到最小的参数best_w1w1.item()best_b1b1.item()best_w2w2.item()best_b2b2.item()best_w3w3.item()best_b3b3.item()ifepoch%1000:# 打印平均损失值print(fepoch:{epoch}| loss:{total_loss})print(fw1:{w1.item()}| b1:{b1.item()}| w2:{w2.item()}| b2:{b2.item()}| w3:{w3.item()}| b3:{b3.item()})# 打印最终值print(最终参数)print(w1 ,w1.item(),b1 ,b1.item())print(w2 ,w2.item(),b2 ,b2.item())print(w3 ,w3.item(),b3 ,b3.item())# 打印最佳值print(最佳参数)print(w1 ,best_w1,b1 ,best_b1)print(w2 ,best_w2,b2 ,best_b2)print(w3 ,best_w3,b3 ,best_b3)w_totalw1*w2*w3 b_totalw3*w2*b1w3*b2b3print(最终结果)print(w ,w_total.item(),b ,b_total.item())

更多文章