通过从零构建一个名为micrograd
的微型自动求导引擎,来深入、直观地理解神经网络的训练核心——反向传播算法。
第一阶段:核心概念与导数直观理解
Micrograd 简介:
它是一个自动梯度(Autograd)引擎,其核心功能是实现反向传播(Backpropagation)。
反向传播是高效计算损失函数相对于神经网络所有权重和偏置的梯度(Derivatives)的算法。梯度指明了调整参数以减小损失的方向。
这是 PyTorch、JAX 等现代深度学习框架的数学核心。
核心演示:
Value
对象:micrograd
中的基本数据单元,可以构建数学表达式图。前向传播 (Forward Pass):从输入开始,通过计算图计算出最终的输出值。
反向传播 (Backward Pass):在最终输出上调用
.backward()
,micrograd
会自动、递归地应用链式法则(Chain Rule),计算出最终输出对每一个输入和中间变量的梯度。
1 | class Value: |
导数的意义:
导数(梯度)衡量的是一种敏感度。例如,
a.grad = 138
意味着,如果输入a
的值发生一个微小的正向变动,最终输出会以138倍的斜率增加。神经网络训练就是利用这个敏感度信息来微调参数,使得损失函数的输出向着减小的方向移动。
构建
Value
类:为了构建计算图,
Value
对象不仅存储数据,还记录了_prev
(它的前驱节点/子节点)和_op
(生成它的运算符号)。通过重载Python的
__add__
和__mul__
等方法,使得Value
对象可以像普通数字一样进行运算,并自动构建计算图。使用
draw_dot
函数可以可视化这个计算图,清晰地看到数据流。
第二阶段:手动反向传播与链式法则
这是理解反向传播最核心的部分。
梯度初始化:
在
Value
对象中增加grad
属性,初始为0。对于最终的输出节点(例如
L
),其grad
被初始化为1.0
,因为任何变量对自身的导数都是1 (dL/dL = 1
)。
手动计算梯度:
从后向前,逐个节点计算梯度。
加法节点 (+):像一个“路由器”,它将上游传来的梯度原封不动地分配给它的所有输入。因为
d(a+b)/da = 1
,所以梯度传递时乘以1,保持不变。乘法节点 (*):像一个“交换机”。对于
c = a * b
,dc/da = b
,dc/db = a
。因此,上游传来的梯度会分别乘以另一个输入的值,再传递给当前输入。a.grad += L.grad * b.data
,b.grad += L.grad * a.data
。链式法则 (Chain Rule):是这一切的数学基础。如果
z
依赖于y
,y
依赖于x
,那么dz/dx = dz/dy * dy/dx
。反向传播就是链式法则在整个计算图上的递归应用。梯度累加: 如果一个变量在计算图中被多次使用,它的梯度需要累加(+=),而不是直接赋值。这是一个非常关键且容易出错的细节。
1 | def __add__(self,other): |
**第三阶段:自动化反向传播与构建神经网络 **
自动化
_backward
函数:为每一种运算(加、乘、Tanh激活函数等)定义一个局部的
_backward
函数。这个函数知道如何将输出的梯度传播给输入。例如,Tanh的导数是
1 - tanh(x)²
。它的_backward
函数就会将上游梯度乘以1 - self.data²
再传递下去。
拓扑排序 (Topological Sort):
- 为了确保反向传播按正确的顺序(从输出到输入)进行,需要对计算图进行拓扑排序。这保证了在计算一个节点的梯度时,所有依赖它的下游节点的梯度都已经计算完毕。
完整的
backward()
方法:- 这个方法封装了整个流程:1. 拓扑排序;2. 初始化最终节点的梯度为1;3. 反向遍历排序后的列表,依次调用每个节点的
_backward
函数。
- 这个方法封装了整个流程:1. 拓扑排序;2. 初始化最终节点的梯度为1;3. 反向遍历排序后的列表,依次调用每个节点的
1 |
|
构建神经网络模块:
Neuron (神经元):一个基本的计算单元,包含一组权重
w
和一个偏置b
。它对输入执行w*x + b
的线性变换,然后通过一个非线性的激活函数(如Tanh)。Layer (层):由多个独立的神经元组成。
MLP (多层感知机):将多个层按顺序堆叠起来,构成一个完整的神经网络。
1 | import random |
1 |
|
1 |
|
**第四阶段:完整的神经网络训练流程 **
训练循环 (Training Loop) 是神经网络学习的核心,包含以下五个步骤:
前向传播 (Forward Pass):将输入数据喂给神经网络,计算出预测值
ypred
。计算损失 (Compute Loss):使用一个损失函数(如均方误差MSE)来衡量预测值
ypred
和真实目标y_true
之间的差距。损失是一个标量,代表了模型当前的“糟糕”程度。梯度清零 (Zero Grad):极其重要的一步! 在每次反向传播之前,必须将所有参数(权重和偏置)的梯度重置为0。因为梯度是累加的,如果不清零,之前的梯度会干扰本次的计算。
反向传播 (Backward Pass):调用
loss.backward()
,计算出损失对网络中每一个参数的梯度。参数更新 (Update Parameters):根据梯度信息更新每一个参数。公式为:
参数.data += -学习率 * 参数.grad
。学习率 (Learning Rate):一个超参数,控制每次更新的步长。
负号表示我们希望向着梯度下降的方向移动,从而最小化损失。
通过成千上万次重复这个循环,神经网络的参数会被微调,使得损失逐渐降低,模型的预测能力越来越强。
**总结 **
神经网络的本质: 它们是可微调的复杂数学表达式。
训练的本质: 通过梯度下降算法,找到一组最优的参数(权重和偏置),使得损失函数最小化。
核心引擎: 反向传播是高效计算梯度的关键,它使得在巨大的参数空间中进行梯度下降成为可能。
Micrograd 与 PyTorch: Micrograd 的API设计与PyTorch高度相似,理解了Micrograd的原理,就能更好地理解PyTorch等工业级框架的内部工作机制。
**神经网络是一个巨大的数学表达式,我们通过反向传播计算梯度,再通过梯度下降来最小化损失,最终让这个表达式学会我们希望它完成的任务。
希望这份笔记能够帮助你拨开神经网络的迷雾,真正理解深度学习的魅力所在。