5天从零手写反向传播:数学新手的深度学习实战记录

当数学不再是拦路虎
在深度学习的学习路上,很多人都会遇到同一个障碍——数学。神经网络的概念看似直观(数据进去、预测出来),但一旦深入到梯度、导数和反向传播,不少人就会望而却步。
最近一位Reddit用户分享了自己的学习经历,标题写得很直接:「我用5天时间自学了数值梯度和解析梯度(反向传播)」。他坦言自己「数学不太好」,但对神经网络的热爱驱使他决心啃下这块硬骨头。这篇文章不仅是个人成长记录,更是一份对反向传播原理极其清晰的「白话拆解」,值得每一位深度学习入门者参考。

理解导数与斜率:建立梯度下降的直觉
作者花了整整两天来理解导数和「斜率」这个概念。他提到一个有趣的观点:斜率(slope)这个词我们每天都在用,直觉上都懂,但要在数学意义上真正理解它,却出乎意料地困难。
他最终掌握了导数的数值近似公式:
f(a + h) - f(a) / h
这个公式是理解**数值梯度(numerical gradient)**的钥匙。它源自微积分中极限的定义——当h趋近于0时,这个比值就是函数在a点的瞬时变化率。这一数学工具的起源可以追溯到牛顿和莱布尼茨在17世纪建立的微积分基础,核心思想是用「无穷小的变化量之比」来定义瞬时速率。在计算机实现中,由于无法取无穷小,通常选择一个很小的h值(如1e-5或1e-7)来近似。实践中更常用的是中心差分公式 (f(a+h)-f(a-h))/(2h),它的近似精度更高(误差为O(h²)而非O(h)),这是因为中心差分消除了泰勒展开中的一阶误差项。数值梯度虽然精度有限且计算成本高,但它是验证解析梯度实现是否正确的黄金标准——这种验证方法被称为「梯度检查」(gradient checking),是深度学习框架开发中不可或缺的调试手段。在PyTorch、JAX等现代框架的单元测试中,几乎每个新实现的运算层都会用数值梯度作为「测试oracle」:计算解析梯度与数值梯度的相对误差,若小于1e-5则认为实现正确。
其核心思路非常朴素:
- 先跑一遍神经网络,得到预测的损失分数(loss);
- 把某个权重稍微「推动」一点点(bump);
- 再跑一遍网络,得到新的损失;
- 用
(loss1 - loss2) / weight_bump计算斜率。
为什么梯度下降总是做减法
作者形容这是「他学过最酷的东西」:当你根据斜率调整权重时,永远是做减法。
- 如果斜率是负的,说明需要往正方向移动,而「减去一个负数」就等于加法;
- 如果斜率是正的,减去它就会往负方向移动一点。
这就是那个经典的「小球滚下山坡」的比喻——而他是用手一步步算出来的。这种「亲手实现梯度下降」的体验,带来的理解深度远超看视频或读教程。实际上,这个更新规则可以写成 w = w - lr * gradient,其中lr是学习率(learning rate),控制每步移动的幅度。学习率的选择是一门艺术:太大会导致震荡甚至发散(参数在最优点两侧来回跳跃),太小则收敛缓慢(可能需要数百万步才能到达最优)。在实践中,学习率通常从1e-3开始尝试,并配合学习率调度策略(如余弦退火、warmup等)动态调整。现代优化器如Adam(2014年由Kingma和Ba提出)结合了动量(Momentum)和自适应学习率(RMSProp)两种思想:动量帮助优化器越过小的局部极值和鞍点,自适应学习率则为每个参数维护独立的步长,使得频繁更新的参数步长变小、稀疏更新的参数步长变大。这些改进使得深度学习在实践中对初始学习率的选择更加鲁棒。
从数值梯度到反向传播:链式法则的突破
数值梯度虽然直观,但效率极低——每调整一个权重就要重跑整个网络。对于现代动辄拥有数十亿参数的大模型(如GPT-4据估计有超过1万亿参数),如果用数值梯度逐一计算,每一步训练都需要数十亿次前向传播,这在计算上完全不可行。于是作者转向了真正的主角:反向传播(backpropagation),也就是解析梯度(analytical gradient)。
反向传播算法的历史比许多人想象的更为悠久。虽然它在1986年由Rumelhart、Hinton和Williams在Nature发表的论文中广为人知,但其数学基础——链式法则的自动化应用——可以追溯到1960年代控制论领域Bryson和Ho的工作,以及1970年代Werbos的博士论文。算法的核心洞察是:一次前向传播加一次反向传播,即可同时得到所有参数的梯度,计算量仅为前向传播的常数倍(通常约2-3倍),与参数数量无关。
为此他必须自学链式法则(chain rule),并弄懂 dL/dd 这样的记号到底是什么意思。链式法则是微积分中复合函数求导的基本定理:如果 y=f(g(x)),那么 dy/dx = f'(g(x))·g'(x)。在神经网络的语境下,整个网络可以被视为一个巨大的复合函数——输入经过层层变换最终产生损失值。反向传播的本质就是沿着计算图(computational graph)从输出到输入逐层应用链式法则。计算图是一种有向无环图(DAG),每个节点代表一个运算操作,边代表数据流动方向。现代深度学习框架在计算图的实现上分为两种范式:静态图(如早期的TensorFlow 1.x)需要先定义完整的图再执行,适合生产部署和编译优化;动态图(如PyTorch、TensorFlow 2.x的eager mode)允许在运行时逐步构建图,更适合研究和调试。micrograd采用的正是动态图方式——每次执行运算时即时记录操作历史。
每个节点只需要知道「局部梯度」(本节点运算对输入的偏导)和「上游梯度」(从输出传回来的梯度),两者相乘即可得到该节点对最终损失的贡献。这种分解使得复杂网络的梯度计算变得模块化:开发者只需为每种运算定义其局部梯度公式,框架会自动完成链式法则的组合。
他特别指出一个新手常见的误区:dL/dd 并不是「L的导数除以d的导数」,而是一个整体符号,表示L对d的偏导数。Leibniz记号中的'd'代表微分算子,整个符号是一个不可分割的数学记号,表示「L随d变化的速率」,这确实是初学者极易混淆的地方。这种记号由莱布尼茨在17世纪引入,它的优势在于链式法则的表达非常直观——形式上看起来就像分数的「约分」:(dL/dd) × (dd/da) = dL/da,虽然严格来说这不是真正的分数运算,但这种记忆法帮助了无数学习者建立直觉。
神经网络的「顿悟时刻」
在这个过程中,作者产生了一个深刻的领悟:
我们从神经网络中得到如此复杂的逻辑,而底层其实只是加法和乘法。真正解决问题的是我们为其构建的上下文(context)。
这是一个非常本质的洞察。神经网络的「智能」并非来自复杂的运算,而是来自海量简单运算的组合,以及我们如何设计结构去逼近目标函数。从数学角度看,这与「万能近似定理」(Universal Approximation Theorem)相呼应——该定理由George Cybenko在1989年首次针对sigmoid激活函数证明,后被Kurt Hornik等人推广到更一般的激活函数。它证明了具有足够宽度的单隐藏层前馈网络可以以任意精度逼近任意连续函数。然而,这个定理说明的是「存在性」而非「可达性」——它保证理论上存在这样的网络参数配置,但并不告诉你如何通过梯度下降找到它们,也不保证所需的网络宽度在实际中可行。这也解释了为什么深度(多层)网络在实践中远优于浅而宽的网络:深度架构能以指数级更少的参数表达相同的函数复杂度。网络的「上下文」——即架构设计(CNN适合图像的局部特征提取、Transformer适合序列的长距离依赖建模)、损失函数选择(交叉熵适合分类任务、MSE适合回归任务)、数据表示方式(tokenization将文本离散化、embedding将离散符号映射到连续向量空间)——决定了这些简单运算如何组织起来解决特定问题。
他还对比了两种方法的哲学差异:反向传播比数值梯度更优雅,因为你是**「走回」网络内部,向它解释哪些部分导致了高损失**,而不是机械地推动数值、计算斜率。这种「归因」的视角,正是反向传播的精髓所在。
亲手实现micrograd:自动微分引擎的代码实战
作者参考了Andrej Karpathy著名的micrograd教程。micrograd是前特斯拉AI总监、OpenAI联合创始人Karpathy在2020年开源的一个极简自动微分引擎,总共只有约150行Python代码。它的设计哲学是:用最少的代码展示深度学习框架(如PyTorch、TensorFlow)核心引擎的工作原理。
要理解micrograd的意义,需要了解「自动微分」(Automatic Differentiation, AD)这一更广泛的计算机科学概念。自动微分既不同于符号微分(如Mathematica那样产生显式公式,容易导致表达式爆炸),也不同于数值微分(如前文讨论的有限差分法,精度有限)。自动微分利用链式法则将复合函数的导数分解为基本运算的导数组合,在保持机器精度的同时避免表达式膨胀。它有两种模式:前向模式(forward-mode AD)从输入向输出传播导数,一次前向传播得到一个输入变量对所有中间变量的导数;反向模式(reverse-mode AD)从输出向输入传播梯度,一次反向传播得到所有输入变量对一个输出的导数。对于神经网络这种「多输入(数十亿参数)、单输出(标量损失)」的场景,反向模式的计算效率是前向模式的数十亿倍,这就是为什么深度学习统一采用反向传播。
micrograd支持标量级别的自动微分,实现了完整的反向传播,并能在其上构建简单的神经网络进行训练。Karpathy配套录制了一个约2.5小时的YouTube教程,累计观看量超过千万。这个项目之所以具有教育意义,是因为PyTorch等工业框架的autograd引擎虽然支持张量运算和GPU加速,但其核心思想与micrograd完全一致——都是基于动态计算图的反向模式自动微分。PyTorch的每个Tensor在 requires_grad=True 时,其行为本质上就是micrograd中Value类的高维版本。
有意思的是,作者坦言自己并没有看完那个视频——他觉得Karpathy的讲解方式仍然「默认你数学很好」,于是中途放弃,转而靠自己理解链式法则来实现。
最终他写出了自己的 Value 类,一个极简的自动微分引擎:
class Value:
def __init__(self, value, _op="", _children=(), gradient=0):
self.value = value
self.op = _op
self.children = _children
self.gradient = gradient
def __add__(self, other):
return Value(self.value + other.value, _op="+",
_children=(self, other), gradient=0)
def __mul__(self, other):
return Value(self.value * other.value, _op="*",
_children=(self, other), gradient=0)
这段代码的精妙之处在于通过Python的运算符重载(__add__、__mul__),让普通的算术表达式自动构建出计算图。Python的数据模型(data model)允许类通过定义双下划线方法(dunder methods)来自定义运算符行为——当你写 a + b 时,Python实际调用的是 a.__add__(b)。这意味着用户可以像写普通数学表达式一样编写计算代码,而框架在幕后静默地记录了整个计算历史。每个Value对象不仅存储数值结果,还记录了产生它的运算类型和父节点——这些信息正是反向传播所需要的「路径记忆」。这种设计模式在工业框架中被称为「tape-based」自动微分(如同磁带录音机记录声音一样记录运算),PyTorch的autograd引擎正是采用了类似的动态图记录机制。
加法与乘法节点的梯度传播规则
他在 backward() 方法中清晰地实现了两种基本运算的梯度传播规则:
- 加法节点:由于加法对各项是「线性透传」的,L对任一加数的偏导都是1,所以直接把全局梯度乘以1传下去。数学上,若 z = x + y,则 ∂z/∂x = 1,∂z/∂y = 1。这意味着加法节点是梯度的「分配器」——它将收到的梯度原封不动地传给每个输入。
- 乘法节点:L对x的偏导等于y,对y的偏导等于x——也就是「交换两者的值」,再乘以全局梯度。数学上,若 z = x * y,则 ∂z/∂x = y,∂z/∂y = x。这意味着乘法节点是梯度的「缩放器」——它用对方的值来缩放传给每个输入的梯度。
这两条规则虽然简单,却构成了所有复杂运算梯度计算的基础。实际上,现代深度学习框架中的所有运算(矩阵乘法、卷积、注意力机制等)最终都可以分解为加法和乘法的组合,再加上一些非线性激活函数(如ReLU、tanh、sigmoid)的局部梯度规则。例如,ReLU函数 f(x)=max(0,x) 的梯度规则是:当x>0时梯度为1(完整传递),当x≤0时梯度为零(完全阻断)——这个简单的「门控」机制部分解决了深层网络中的梯度消失问题,因为正区间的梯度不会衰减。相比之下,sigmoid和tanh的梯度最大值分别为0.25和1,在多层堆叠时梯度会指数级衰减,这正是2000年代深层网络难以训练的主要原因之一。ReLU的提出(由Nair和Hinton在2010年引入深度学习领域)被认为是开启深度学习「复兴」的关键技术之一,它与GPU计算、大规模数据集、以及Dropout等正则化技术共同构成了2012年AlexNet突破的技术基础。
他用一个经典例子验证了结果:
a = Value(2.0); b = Value(-3.0)
c = Value(10.0); f = Value(-2.0)
e = a * b
d = e + c
L = d * f
L.backward()
# 输出: a=6.0, b=-4.0, c=-2.0, f=4.0, e=-2.0, d=-2.0, L=1
这些数值与Karpathy micrograd中的标准答案完全一致,证明他的理解和实现都是正确的。我们可以手动验证:L = df,所以 dL/dd = f.value = -2.0,dL/df = d.value = e.value + c.value = (-6)+10 = 4.0;d = e+c,所以 dL/de = dL/dc = dL/dd * 1 = -2.0;e = ab,所以 dL/da = dL/de * b.value = -2.0 * (-3.0) = 6.0,dL/db = dL/de * a.value = -2.0 * 2.0 = -4.0。每一步都严格遵循链式法则。这种手动验证的过程本身就是理解反向传播最有效的方式——它将抽象的数学规则具象化为可追踪的数值变化。
诚实的局限与真实的学习价值
作者非常坦诚,他给自己的实现打上了引号——「micrograd」。因为他的版本存在一个已知局限:无法正确处理同一个变量被重复使用的情况。在真实的反向传播中,如果一个节点被多条路径引用,其梯度应当累加(sum),而他的实现是遍历式的,没有做梯度累积。
这个问题在计算图理论中称为「扇出」(fan-out)。当一个节点的输出被多个下游节点使用时,根据多元链式法则(多变量微积分中的全导数公式),该节点的梯度应该是所有路径贡献的总和。例如,如果 z = x + x,正确的梯度是 dz/dx = 2(两条路径各贡献1),而非简单的1。如果用 z = x * x 来看更为明显:dz/dx = 2x(而非仅仅x)。在PyTorch中,这通过 grad.data += local_grad 的累加操作实现,而非赋值覆盖。
这个看似简单的问题实际上涉及到计算图的拓扑排序——反向传播必须按照拓扑逆序进行,确保一个节点的所有下游梯度都已计算完毕后再向上传播,否则就会丢失梯度信息。拓扑排序是图论中的经典算法,它保证有向无环图中的每个节点都在其所有后继节点之前被处理。在micrograd的完整实现中,Karpathy使用了深度优先搜索(DFS)来实现拓扑排序,这也是PyTorch autograd引擎内部使用的策略。
值得一提的是,梯度累积的概念在训练实践中也有直接应用:当GPU显存不足以容纳大batch时,工程师会将大batch拆分为多个小batch(micro-batch),每个小batch计算的梯度进行累积,积累到完整batch大小后再执行一次参数更新。这种训练技巧与计算图中的梯度求和在数学上完全等价,体现了同一原理在不同抽象层面的应用。
他也直言「懒得处理这个」。但这恰恰体现了学习过程的真实性——先把核心机制跑通,再逐步完善,是极为务实的工程学习路径。在软件工程中,这被称为「迭代开发」或「最小可行产品」(MVP)思维:先构建一个能运行的核心版本,验证基本假设,再逐步添加边界情况的处理。
给深度学习入门者的实践启示
这篇分享之所以引发共鸣,是因为它展示了一条可复制的学习路线:
- 别怕数学基础差:从导数和斜率的最基础概念开始,两天足以建立直觉;
- 先数值梯度、后解析梯度:用数值梯度建立「梯度下降」的物理直觉,再理解反向传播的效率优势;
- 动手实现胜过被动观看:当视频讲解不适合你时,不必强求看完,自己推导、用纸笔计算、写代码验证,反而理解更深;
- 接受不完美的实现:一个有局限但能跑通的实现,比一个完美但从未动手的计划更有价值。
值得补充的是,这条学习路线与认知科学中的「建构主义学习理论」高度吻合——该理论由皮亚杰和维果茨基等人奠基,核心观点是学习者通过主动构建知识结构(而非被动接收信息)获得最深层的理解。神经科学研究也支持这一观点:主动解决问题时大脑前额叶皮层和海马体的协同活动远强于被动听讲,形成的记忆痕迹也更加持久。在编程教育领域,这被称为「从零构建」(build from scratch)方法论,其代表性资源包括Karpathy的Neural Networks: Zero to Hero系列(从micrograd到GPT)、fast.ai的自顶向下教学法(先跑通完整项目再深入细节)、3Blue1Brown的可视化数学系列(通过动画建立几何直觉),以及Sebastian Raschka的《Build a Large Language Model (From Scratch)》等。这些资源的共同特点是强调直觉优先于形式化证明,强调动手实现优先于理论背诵。
对于任何被深度学习数学门槛劝退的人来说,这位作者的5天经历都是一剂强心针:真正的障碍往往不是智商或天赋,而是「愿不愿意从最基础的地方开始,一步步啃下去」。事实上,深度学习所需的数学知识远没有想象中那么多——核心只需要:基础微积分(导数、链式法则、偏导数)、线性代数(矩阵乘法、向量运算、特征分解的基本概念)、以及基础概率论(概率分布、条件概率、贝叶斯定理)。这三个领域各学习一到两周的基础内容,就足以理解和实现绝大多数深度学习模型。关键在于「用到什么学什么」——在实现具体模型的过程中遇到不懂的数学概念再去补充,这种目标驱动的学习方式远比系统性地先学完整本教材再动手要高效得多。
相关推荐

AI网络攻防能力逼近临界点:模型研发该踩刹车吗
AI模型的网络攻防能力正逼近关键阈值,能自主发现漏洞、编写exploit甚至执行完整攻击链。本文深入分析放慢研发与加速防御两派观点,探讨能力封锁的博弈困境及系统性治理路径。
fx:极简开源原生编码智能体深度解析
fx:极简开源原生编码智能体深度解析
深度解析fx开源编码智能体,探讨其Tiny、Open、Native三大核心理念,分析极简AI编程工具在可控性、隐私保护和模型无关性方面的独特价值与局限。

ROS成立Physical AI特别兴趣小组,开源机器人生态拥抱具身智能
开源机器人联盟OSRA正式成立Physical AI SIG,推动ROS生态系统整合物理AI能力。本文解析Physical AI特别兴趣小组的目标、路线图及其对机器人开发者的深远影响。