从零构建神经网络框架:多层感知机、梯度下降与反向传播原理精讲(generative-ai-for-beginners 机器学习基础篇)
本篇技术指南以仓库 own_framework.md(及其捷克语译本 translations/cs/15-rag-and-vector-databases/data/own_framework.md)为骨架,系统讲解机器学习的形式化定义、损失函数、梯度下降与随机梯度下降,以及多层感知机(MLP)背后的链式法则与反向传播(backprop)原理。文中还会结合仓库内同目录的 perceptron.md 与 frameworks.md 两篇姊妹文档,还原“单层感知机 → 自研模块化框架 → 业界框架 TensorFlow/PyTorch”的完整进阶路线。读完你将能透彻理解深度学习中“前向计算 + 反向求导”的运作机制,并具备手写一个可训练多层感知机的小型 Python 框架、进而完成 MNIST 手写数字分类任务的全部知识储备。
这篇文档在仓库中的定位:既是理论讲义,又是 RAG 演示语料
在阅读正文前,有必要说明这份文档在仓库中的物理位置与两种用途,这对理解它的写作风格和内容边界很有帮助:
- 学习载体:文件位于第 15 课 RAG 与向量数据库的知识数据目录下,与 perceptron.md、frameworks.md 共同组成一套迷你“神经网络入门”系列资料。三份文档按内容衔接:先介绍单层感知机(二元分类、感知机训练法则),再进入本篇“在感知机基础上扩展出模块化框架、支持多类分类/回归/非线性可分”,最后讨论真实深度学习框架与过拟合。中文语境下阅读时,应把三篇当作一组连续讲义看待。
- RAG 示例语料:本仓库的第 15 课用 notebook-rag-vector-databases.ipynb 演示“读取一批 Markdown 文档 → 分块 → 向量化入库 → 检索问答”的完整 RAG 流程,而这三份
data/*.md文件正是 notebook 中实际读取、分块并索引的文档集合(该 notebook 内将它们逐一读入 DataFrame,path 字段分别为data/frameworks.md、data/own_framework.md、data/perceptron.md)。也就是说,本篇文章所讲的神经网络理论,在本仓库中还承担着“可供检索的知识正文”这一角色。
本篇承接的上一讲是单层感知机——一个做二元分类的线性模型。单层感知机的局限性很明显:它只能分开“线性可分”的两类数据。本讲将把这个模型推广成一个更灵活的框架,使得我们能做到三件事:
- 在二分类之外支持多类别分类(multi-class classification);
- 在分类之外还能求解回归问题(regression);
- 分离**非线性可分(not linearly separable)**的类别。
同时,我们会在 Python 中亲手搭建一个模块化的自研框架,用它来拼装不同的神经网络架构。下面从数学形式化讲起。
机器学习问题的形式化:数据集、模型与损失函数
为了让“训练神经网络”这件事可计算、可优化,第一步是把它写成严格的形式化问题。设:
- X 为训练数据集;
- Y 为对应的标签集合;
- 目标是从中学到一个模型 f,使得它的预测尽可能准确。
“预测有多准”需要用损失函数(loss function) ℒ 来度量,训练的本质就是在参数空间里找到让损失最小的那个 f。针对不同任务,仓库讲义给出了最常用的几种损失函数:
| 任务类型 | 损失函数 | 说明 |
|---|---|---|
| 回归(预测一个连续数值) | 绝对误差 `Σᵢ | f(x⁽ⁱ⁾)−y⁽ⁱ⁾ |
| 回归(预测一个连续数值) | 平方误差 Σᵢ(f(x⁽ⁱ⁾)−y⁽ⁱ⁾)² |
差的平方求和,放大较大误差的惩罚 |
| 分类 | 0-1 损失 | 预测错了记 1、对了记 0,其均值本质上等价于模型的准确率(accuracy) |
| 分类 | 逻辑损失(logistic loss) | 相对 0-1 损失是平滑可微的代理损失,便于梯度优化 |
在单层感知机中,模型 f 被定义成一个线性函数 f(x)=wx+b,其中 w 是权重矩阵,x 是输入特征向量,b 是偏置(bias)向量。而在更复杂的神经网络架构中,这个函数会变成逐层嵌套的复合函数(见后文“多层感知机”一节)。
在分类任务中,我们通常希望网络的输出直接就是“属于每个类别的概率”。要把任意实数输出规整成概率值(即对输出做归一化),最常用的手段是 softmax 函数 σ。加上 softmax 之后,模型函数就写为 f(x)=σ(wx+b)。
在上面 f 的定义里,w 和 b 合称为模型的参数 θ=⟨w,b⟩。给定数据集 ⟨X,Y⟩,就可以把“整个数据集上的总误差”写成参数 θ 的函数——误差越小,说明当前的参数组合越好。
✅ 神经网络训练的目标,就是通过改变参数 θ 来最小化误差。 这句话是理解后续所有优化算法的总纲:误差是 θ 的函数,训练 = 优化这个函数。
梯度下降优化:沿着误差曲面最陡的方向下山
面对“最小化误差函数”这个优化问题,最经典的手段是梯度下降(gradient descent)。核心思想非常直观:
- 损失函数 ℒ 关于参数有导数;在多维情况下这个导数被称为梯度(gradient),它指向误差增长最快的方向;
- 想让误差下降,就让参数朝梯度的反方向移动一小步;
- 反复执行,直到误差收敛到(局部)极小附近。
形式上可以写成下面两步:
- 先用随机值初始化参数
w⁽⁰⁾, b⁽⁰⁾; - 然后反复迭代更新,每次移动的方向与梯度相反、步长由学习率 η 控制:
w⁽ⁱ⁺¹⁾ = w⁽ⁱ⁾ − η·∂ℒ/∂wb⁽ⁱ⁺¹⁾ = b⁽ⁱ⁾ − η·∂ℒ/∂b
学习率 η 决定每一次沿反梯度方向迈出的步子有多大,是整个训练过程中最关键的调节旋钮之一。这一步更新规则在姊妹文档 perceptron.md 中同样以 w⁽ᵗ⁺¹⁾ = w⁽ᵗ⁾ − η∇E(w) 的形式出现,那里 η 被正式命名为学习率、∇E(w) 被称为 E 的梯度——可见感知机训练与深度网络训练共享同一套优化框架。
从理论严谨性看,每一步更新都应该基于整个数据集来计算梯度(毕竟损失是对所有训练样本求和得到的)。但在真实世界中,一次性遍历全量数据计算梯度代价过高,于是实践中普遍的做法是:从数据集中取一小部分,称为小批量(minibatches),基于这一子集计算梯度。由于每次抽取的子集都是随机的,梯度带上了随机性,这种方法也因此被称为随机梯度下降(SGD,stochastic gradient descent)。小批量策略同时带来了工程上的好处:每次迭代只处理一小块数据,内存开销可控,且算法能在更早的迭代步数内看到多个不同子集,训练整体上更高效、更常能在损失曲面中“跳出”不利区域。
多层感知机:用非线性激活堆叠出表达能力更强的模型
回到能力边界的问题:如上一讲所述,单层网络只能对“线性可分”的类别做划分。要想建模更复杂的关系(比如异或这种经典非线性可分情形),就需要把多层网络组合起来。
数学上,这意味着函数 f 不再是一步线性变换,而是按多个步骤依次计算:
z₁ = w₁x + b₁z₂ = w₂α(z₁) + b₂f = σ(z₂)
其中 α 是非线性激活函数(non-linear activation function),σ 是输出端的 softmax,而全部待训练参数收敛为 θ=⟨w₁,b₁,w₂,b₂⟩。
这里有一个值得展开说明的关键点:为什么两层之间必须夹一个非线性的 α?如果去掉 α、直接把两个线性变换叠在一起,w₂(w₁x+b₁)+b₂ 化简后仍然只是一个线性函数——再多层也等价于一层,模型表达能力不会有任何提升。正是非线性激活函数的引入,才使得复合函数可以拟合弯曲的决策边界,从而分离非线性可分的类别。文档把它抽象成“α(z₁)”的嵌套形式,就是提醒读者:前一层输出要先过激活、再进下一层。
链式法则与反向传播:误差信息如何逐层“回流”
层数增加后,梯度下降的目标没变,变难的是梯度的计算——损失 ℒ 与深层参数 w₁、w₂ 之间隔着多层复合运算,无法直接一步求导。此时要用到微积分里的链式法则(chain rule)。对上述两隐藏层的结构,梯度可以展开为:
∂ℒ/∂w₂ = (∂ℒ/∂σ)(∂σ/∂z₂)(∂z₂/∂w₂)∂ℒ/∂w₁ = (∂ℒ/∂σ)(∂σ/∂z₂)(∂z₂/∂α)(∂α/∂z₁)(∂z₁/∂w₁)
✅ 链式法则是计算损失函数关于各参数导数的核心工具。 每一层只需提供“局部导数”,把从损失到该参数的路径上的所有局部导数相乘即可。
请注意上面两个式子:最左端 (∂ℒ/∂σ) 与 (∂σ/∂z₂) 是相同的公共因子。这意味着我们完全不必为每个参数各算一遍完整求导,而可以采取一个高效得多的策略:从损失函数出发,沿着计算图从后向前逐层把“误差相对于本层输出的导数”累积下来并复用。这种让梯度从输出端向输入端层层回传、从而高效训练多层感知机的方法,正是著名的反向传播(backpropagation),简称 backprop。
换个角度理解反向传播的直觉:前向阶段每一层都记住了自己的输入输出;反向阶段则像在“反向再走一遍网络”——每一层拿到上一层传来的梯度,用本地的权重与激活导数把梯度继续向前传,同时顺手算出自己参数的梯度,供 SGD 更新使用。由于所有共享前缀被复用,反向传播的计算开销与前向几乎同量级,这是它能撑起现代深度学习的根本原因。
关于反向传播更精细的推导(包括各层的局部导数到底长什么样、如何组织求导顺序),原文档明确提示“在 notebook 示例中会展开更详细讲解”,读者在做下面“动手实践”部分时可以边写代码边对照本节公式验证。
从公式到代码:自研框架的最小模块化骨架
光有数学还不够——文档在开篇就承诺“在 Python 中开发一个模块化自研框架,允许我们拼装不同架构”。姊妹文档 frameworks.md 把自研框架要解决的两件事讲得很清楚:
- 张量运算:能对张量做乘法、加法,并计算 sigmoid、softmax 等函数——在 Python 里
numpy就能胜任; - 梯度计算:为所有表达式求出梯度以执行梯度下降——这正是自研框架最费功夫的部分。
frameworks.md 里特别强调:在“上一节(即本讲)自研的框架”中,我们必须在每一个模块的 backward 方法里手工编写全部导数函数来执行反向传播。这实际上给了我们一个非常清晰的模块化设计线索:把网络拆成一层层对象,每层实现“前向”与“反向”两个方法。结合 perceptron.md 给出的感知机训练循环(随机抽正负样本、计算输出、按误差方向更新权重),可以重构出如下最小骨架(示意代码,用于说明文档所描述的分层思想):
class Layer:
"""网络中的一个层/模块:负责前向计算与反向传播"""
def forward(self, x):
"""前向:记录输入输出,返回本层输出"""
raise NotImplementedError
def backward(self, grad_output):
"""反向:接收来自上游的梯度,计算并返回对输入的梯度"""
raise NotImplementedError
基于上述约定,一个可训练的多层感知机大体需要四部分能力:
- 模块容器:把若干 Layer 串成序列,前向时依次调用
forward,反向时按逆序调用backward,恰好实现“误差从损失函数沿计算图向输入方向回流”; - 可导激活/输出函数:把 σ(softmax) 与 α(非线性激活) 封装为 Layer,各自在
backward中实现自己的局部导数; - 损失模块:按任务选用平方误差(回归)或 0-1/逻辑损失(分类),并作为反向传播的“起点”,输出
∂ℒ/∂输出; - SGD 训练循环:随机抽取 minibatch → 前向算损失 → 反向收集每个参数上的梯度 → 按
w = w − η·∂ℒ/∂w更新所有参数,重复直至收敛。
这样组织的好处与文档目标完全一致:想换网络结构时,只需重新组合 Layer 的序列;想换任务时,只需替换损失与输出模块——这就是“模块化框架”的含义。同时,由于每个 backward 都要手写导数,亲手实现一遍会让你对“梯度从何而来”获得远超调库的深刻理解。
需要提醒的是:本仓库的第 15 课目录里随附的是三份理论 Markdown 讲义,原讲义中配套的 OwnFramework 动手 notebook 以“进入该 notebook 逐步实现”为挑战引导;在实际练习时,可依据上文骨架并结合 perceptron.md 中现成的 Python 训练代码自行搭建与验证。
为什么还要学习真实框架:从手写 backward 到自动微分
理解了自研框架的繁琐之处(每个模块的导数都要手写),也就自然明白为什么业界会发展出 TensorFlow、PyTorch 这样的成熟框架。frameworks.md 对此做了对照:
| API 层级 | TensorFlow 系 | PyTorch 系 | 特点 |
|---|---|---|---|
| 底层 API | TensorFlow | PyTorch | 显式构建计算图,支持对图自动求导;可推到 GPU/TPU 上并行计算 |
| 高层 API | Keras | PyTorch Lightning | 把网络视为层序列,搭模型、调用 fit 即可训练 |
底层框架帮我们自动完成本讲中手写的“链式求导”,并能在 GPU、TPU 等专用计算单元上并行执行海量矩阵运算——这是手写框架难以企及的。高层 API 则进一步把网络抽象为“层的序列”,几行代码即可搭出典型网络。文档同时强调:两类 API 可以混用,例如用底层 API 自定义一个新层,再把它放进高层 API 构建的大网络里,或用高层 API 定义结构后自写底层训练循环。理解这一点后回头看本讲的自研框架,你会意识到它的 Layer 抽象、backward 设计与真实框架的设计哲学是同构的——从零手写一遍,是为了之后用框架时心中有数。
动手任务与学习闭环
本讲在原课程设计里以“构建自己的神经网络库”收尾,并配套了完整的练习体系:
- 小结(本讲):完成自研神经网络库,并用它求解一个简单的二维分类问题;
- 🚀 挑战:在 OwnFramework notebook 中亲手实现并训练自己的多层感知机框架,近距离观察现代神经网络的运转细节;建议从实现一个带
forward/backward的两层网络开始,逐步扩展; - 复习与自学:反向传播是 AI/ML 领域的通用算法,值得深入钻研其推导(建议结合上一节“链式法则”的公式逐层验算一遍);
- 作业(Assignment):运用本讲构建的框架完成 MNIST 手写数字分类——将 28×28 像素的手写数字图展平成输入向量,构建输入层→隐藏层→softmax 输出层的多层感知机,用 SGD + 反向传播训练,最终按 0-1 损失(即准确率)评估模型在 10 个数字类别上的分类能力。这一任务完整覆盖了文档开篇承诺的三项能力:多类别分类、非线性决策边界、模块化框架拼装。
小结:从一条公式到一套自研框架
本文围绕 own_framework.md 展开了一条清晰的认知链路:用损失函数把学习问题形式化 → 用梯度下降/SGD 做参数优化 → 用多层+非线性激活突破表达力上限 → 用链式法则/反向传播高效求解深层梯度 → 用“前向+反向”的模块化 Layer 封装成自研框架 → 借 MNIST 作业完成端到端验证。它与 perceptron.md(单层起点)、frameworks.md(真实框架与过拟合)首尾相接,在仓库第 15 课中还作为 RAG 演示的检索语料被 notebook-rag-vector-databases.ipynb 实际使用。理解多层感知机与反向传播,是后续理解更复杂的生成式模型训练机制、乃至任何深度学习框架底层行为的第一块基石。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00