机器学习之反向传播与计算图实验
一.反向传播与计算图实验1.实验工具与依赖导入库 / 模块作用sympy符号计算库用于符号化求导、验证链式法则numpy as np数值计算库实现数值求导、验证反向传播结果matplotlib.pyplot数据可视化库绘制计算图、展示链式变化matplotlib.widgets交互式组件用于实验中的输入与交互lab_utils_backprop课程自定义工具提供计算图绘制函数from sympy import * import numpy as np import re %matplotlib widget import matplotlib.pyplot as plt from matplotlib.widgets import TextBox, Button import ipywidgets as widgets from lab_utils_backprop import *2.计算图与链式法则基础1核心概念计算图将复杂的数学表达式拆分为多个简单节点如加法、乘法、平方通过节点间的数据流表示计算过程简化反向传播的导数计算。2示例 1J(23w)^2 的反向传播a前向传播将表达式拆分为两个节点节点 1a 2 3w节点 2J a^2当 w3 时w 3 a 2 3 * w J a ** 2 print(fa {a}, J {J})输出a 11, J 121b反向传播分步求导步骤 1求符号计算Ja^2导数为 2a数值验证a_epsilon a 0.001 J_epsilon a_epsilon ** 2 k (J_epsilon - J) / 0.001 print(fdJ/da ≈ {k}) # 输出 ≈ 22即 2×11步骤 2求符号计算a23w导数为 3数值验证w_epsilon w 0.001 a_epsilon 2 3 * w_epsilon k (a_epsilon - a) / 0.001 print(fda/dw ≈ {k}) # 输出 ≈ 3步骤 3链式法则合并c计算图填充结果蓝色方框前向值第一个方框填11第二个方框填121绿色方框导数\(\frac{\partial a}{\partial w}3\)\(\frac{\partial J}{\partial a}22\)\(\frac{\partial J}{\partial w}66\)3示例 2简单神经网络的计算图与反向传播a表达式与节点拆分给定参数w-2, b8, x2, y1目标函数J 1/2(a - y)^2, a wx b将表达式拆分为 4 个节点节点 1c wx节点 2a c b节点 3d a - y节点 4J 1/2d^2b 前向传播计算w, b, x, y -2, 8, 2, 1 c w * x a c b d a - y J 0.5 * (d ** 2) print(fc{c}, a{a}, d{d}, J{J})输出c-4, a4, d3, J4.53. 反向传播分步求导与链式法则1从右往左求导节点 4\(J \frac{1}{2}d^2\)导数\(\frac{\partial J}{\partial d} d\)代入 \(d3\)得 \(\frac{\partial J}{\partial d}3\)节点 3\(d a - y\)导数\(\frac{\partial d}{\partial a} 1\)由链式法则得\(\frac{\partial J}{\partial a} \frac{\partial J}{\partial d} \times \frac{\partial d}{\partial a} 3 \times 1 3\)节点 2\(a c b\)导数\(\frac{\partial a}{\partial c} 1\)\(\frac{\partial a}{\partial b} 1\)由链式法则得\(\frac{\partial J}{\partial c} \frac{\partial J}{\partial a} \times \frac{\partial a}{\partial c} 3 \times 1 3\)\(\frac{\partial J}{\partial b} \frac{\partial J}{\partial a} \times \frac{\partial a}{\partial b} 3 \times 1 3\)节点 1\(c wx\)导数\(\frac{\partial c}{\partial w} x\)代入 \(x2\)得 \(\frac{\partial c}{\partial w}2\)由链式法则得\(\frac{\partial J}{\partial w} \frac{\partial J}{\partial c} \times \frac{\partial c}{\partial w} 3 \times 2 6\)2计算图填充结果蓝色方框前向值\(cwx-4\)\(acb4\)\(da-y3\)\(J\frac{1}{2}d^24.5\)绿色方框导数\(\frac{\partial c}{\partial w}2\)\(\frac{\partial J}{\partial w}6\)\(\frac{\partial a}{\partial c}1\)\(\frac{\partial J}{\partial c}3\)\(\frac{\partial a}{\partial b}1\)\(\frac{\partial J}{\partial b}3\)\(\frac{\partial d}{\partial a}1\)\(\frac{\partial J}{\partial a}3\)\(\frac{\partial J}{\partial d}3\)d 增加 \(0.001\)J 增加约 \(3×0.0010.003\)四、核心知识点总结1. 计算图的作用将复杂导数计算拆解为简单节点每个节点的导数计算独立且易验证清晰展示链式法则的传递过程直观理解反向传播中梯度如何从损失函数传递到各参数。2. 反向传播的关键步骤前向传播按计算图从左到右计算所有节点的输出值反向传播从损失函数节点开始按链式法则从右往左计算每个节点的梯度参数更新利用参数的梯度更新参数值梯度下降。3. 链式法则的本质梯度在计算图中按 “路径” 传递每经过一个节点就乘以该节点的局部导数最终得到参数的梯度。二.反向传播与链式法则实验笔记续一、前向传播完整计算流程1. 输入与参数定义# 输入和参数 x 2 w -2 b 8 y 12. 分步前向计算# 分步计算每个节点的值 c w * x # 节点1乘法 a c b # 节点2加法 d a - y # 节点3减法 J d**2 / 2 # 节点4平方除以2 print(fJ{J}, d{d}, a{a}, c{c})输出结果c -4a 4d 3J 4.5二、反向传播从损失函数开始从右往左求导反向传播的核心逻辑从损失函数节点开始按链式法则从右往左依次计算每个节点的局部导数再乘上右侧节点传来的梯度。步骤 1节点 4\(J \frac{1}{2}d^2\)求 \(\frac{\partial J}{\partial d}\)1符号求导\(\frac{\partial J}{\partial d} d\)2数值验证微小变化法d_epsilon d 0.001 J_epsilon d_epsilon**2 / 2 k (J_epsilon - J) / 0.001 # 差商近似导数 print(fdJ/dd ≈ {k})输出dJ/dd ≈ 3.0004999999997395和符号求导结果d3一致。步骤 2节点 3\(d a - y\)求 \(\frac{\partial J}{\partial a}\)1先求局部导数 \(\frac{\partial d}{\partial a}\)符号求导\(\frac{\partial d}{\partial a} 1\)数值验证a_epsilon a 0.001 d_epsilon a_epsilon - y k (d_epsilon - d) / 0.001 print(fdd/da ≈ {k})输出dd/da ≈ 1.000000000000334和符号结果一致。2链式法则合并\(\frac{\partial J}{\partial a} \frac{\partial J}{\partial d} \times \frac{\partial d}{\partial a} d \times 1 d\)当 \(d3\) 时\(\frac{\partial J}{\partial a} 3\)。数值验证a_epsilon a 0.001 d_epsilon a_epsilon - y J_epsilon d_epsilon**2 / 2 k (J_epsilon - J) / 0.001 print(fdJ/da ≈ {k})输出dJ/da ≈ 3.00050000000006277和链式法则结果一致。步骤 3节点 2\(a c b\)求 \(\frac{\partial J}{\partial c}\) 和 \(\frac{\partial J}{\partial b}\)1局部导数计算\(\frac{\partial a}{\partial c} 1\)\(\frac{\partial a}{\partial b} 1\)2链式法则合并\(\frac{\partial J}{\partial c} \frac{\partial J}{\partial a} \times \frac{\partial a}{\partial c} 3 \times 1 3\)\(\frac{\partial J}{\partial b} \frac{\partial J}{\partial a} \times \frac{\partial a}{\partial b} 3 \times 1 3\)步骤 4节点 1\(c w \times x\)求 \(\frac{\partial J}{\partial w}\)1局部导数计算符号求导\(\frac{\partial c}{\partial w} x\)在本例子中\(x2\)所以 \(\frac{\partial c}{\partial w} 2\)。2链式法则合并\(\frac{\partial J}{\partial w} \frac{\partial J}{\partial c} \times \frac{\partial c}{\partial w} 3 \times 2 6\)数值验证w_epsilon w 0.001 J_epsilon ((w_epsilon * x b) - y)**2 / 2 k (J_epsilon - J) / 0.001 print(fdJ/dw ≈ {k})输出dJ/dw ≈ 6.001999999999619和链式法则结果一致。三、反向传播的通用步骤总结前向传播按计算图从左到右计算所有节点的输出值保存每个节点的中间结果反向传播初始化从损失函数节点开始初始化梯度为 1或根据损失函数直接求导从右往左遍历节点计算当前节点对输入的局部导数用局部导数乘上右侧节点传来的梯度得到当前节点输入的梯度参数更新最终得到的参数梯度如 \(\frac{\partial J}{\partial w}\)、\(\frac{\partial J}{\partial b}\)用于梯度下降更新参数。四、关键知识点补充1. 链式法则的本质梯度在计算图中按 “路径” 传递每经过一个节点就乘以该节点的局部导数最终得到参数的梯度。例如\(\frac{\partial J}{\partial w} \frac{\partial J}{\partial d} \times \frac{\partial d}{\partial a} \times \frac{\partial a}{\partial c} \times \frac{\partial c}{\partial w}\)2. 数值验证的意义用 “微小变化法” 验证导数是为了直观理解导数的定义导数是 “输入微小变化时输出的变化率”也可以验证链式法则的正确性。3. 为什么不反向传播到输入 x在这个实验中x 是固定的输入数据不是需要优化的参数因此我们只需要计算损失函数对参数 w 和 b 的梯度不需要计算对 x 的梯度。5.完整代码from sympy import * import numpy as np # 1. 输入与参数 x 2 w -2 b 8 y 1 # 2. 前向传播 c w * x a c b d a - y J d**2 / 2 print(f前向结果J{J}, d{d}, a{a}, c{c}) # 3. 反向传播分步求导 # 节点4J 0.5*d^2 def dJ_dd(d): return d print(fdJ/dd {dJ_dd(d)}) # 节点3d a - y def dd_da(): return 1 dJ_da dJ_dd(d) * dd_da() print(fdJ/da {dJ_da}) # 节点2a c b def da_dc(): return 1 def da_db(): return 1 dJ_dc dJ_da * da_dc() dJ_db dJ_da * da_db() print(fdJ/dc {dJ_dc}, dJ/db {dJ_db}) # 节点1c w*x def dc_dw(x): return x dJ_dw dJ_dc * dc_dw(x) print(fdJ/dw {dJ_dw})