Generative AI 课程实战:手写多层感知机框架,吃透梯度下降与反向传播
本篇文章以 own_framework.md 的内容为骨架,系统讲解机器学习问题的形式化表达、梯度下降(Gradient Descent)与随机梯度下降(SGD)、多层感知机(MLP)的数学结构,以及贯穿全过程的**反向传播(Backpropagation)**原理。读完本文,你将能够理解从单层感知机升级到多层神经网络的完整动机与推导过程,并具备在 Notebook 示例 中自行构建一个模块化神经网络框架、直至解决 MNIST 手写数字分类问题的实战能力。
需要说明的是,本文对应的文档在本仓库中位于 RAG 与向量数据库课程 的 data 目录,它来源于神经网络的系统化讲解,在本课程中作为知识库示例数据被 notebook-rag-vector-databases.ipynb 分块、向量化后用于检索增强生成(RAG)演示——因此理解其技术内核,同时也能让你看懂这些"底层文档"如何被加工成可检索的文本块。
为什么需要从单层感知机走向多层感知机
在上一份资料 perceptron.md 中,我们认识了最简单的神经网络模型——单层感知机(one-layered perceptron),它本质上是一个线性二分类模型,输出为 y(x)=f(wᵀx),其中 f 是阶跃激活函数。
单层感知机的能力上限很明确:它只能分隔线性可分的类别。为了把它扩展成更通用的学习框架,我们希望在以下三个方面获得能力提升:
- 多分类(multi-class classification):把输出从"两个类别"扩展到两个以上;
- 回归(regression):除分类外,还能预测连续的数值;
- 非线性可分:能够把线性不可分的类别也正确区分开。
为此,我们会用 Python 开发一个模块化(modular)的自研框架,用它来组合构造各种不同的神经网络结构。这正是本仓库中 data/frameworks.md 所强调的"从底层理解网络如何工作"的思路:先用底层 API 与张量打交道,再逐步抽象出可复用的框架。
机器学习问题的形式化:从数据集到损失函数
要扩展模型,首先需要把问题本身说清楚。假设我们有训练数据集 X 及其对应的标签 Y,我们的任务是构建一个模型 f,使其预测尽可能准确。预测质量的度量标准是损失函数(loss function) ℒ,不同问题类型对应不同的常用损失:
- 回归问题(预测一个数值):
- 绝对误差(absolute error):∑ᵢ |f(x⁽ⁱ⁾) − y⁽ⁱ⁾|
- 平方误差(squared error):∑ᵢ (f(x⁽ⁱ⁾) − y⁽ⁱ⁾)²
- 分类问题:
- 0-1 损失(0-1 loss):与模型的准确率(accuracy) 本质等价;
- logistic 损失(logistic loss):即对数似然视角下的交叉熵形式,具有处处可微的良好性质,便于梯度优化。
在单层感知机中,函数 f 被定义为线性函数:
f(x) = wx + b
其中 w 是权重(weight)矩阵,x 是输入特征向量,b 是偏置(bias)向量。在不同的网络结构下,函数 f 会取更复杂的形式。
用 softmax 把输出变成概率
在分类任务中,通常希望网络输出的是各个类别对应的概率。要把任意实数转换为概率(即对输出做归一化),我们通常使用 softmax 函数 σ,此时函数变为:
f(x) = σ(wx + b)
softmax 会保证所有类别的输出落在 (0, 1) 区间内且总和为 1,使损失计算和概率解释都成为可能。
参数与训练目标
在上述定义中,w 和 b 被统称为参数 θ = ⟨w, b⟩。给定数据集 ⟨X, Y⟩,我们可以把整个数据集上的总体误差写成参数 θ 的函数。于是得到贯穿整个神经网络的训练信条:
✅ 神经网络训练的目标,就是通过不断改变参数 θ 来最小化误差。
梯度下降与随机梯度下降:参数如何被优化
误差是参数的函数,我们希望在参数空间中"往下走"找到谷底。最经典的方法是梯度下降(gradient descent):计算损失函数对参数的导数(在多维情形下称为梯度 gradient),并朝误差减小的方向更新参数。其形式化过程为:
- 用随机值初始化参数 w⁽⁰⁾、b⁽⁰⁾;
- 反复执行如下更新步骤,直到收敛:
- w⁽ⁱ⁺¹⁾ = w⁽ⁱ⁾ − η · ∂ℒ/∂w
- b⁽ⁱ⁺¹⁾ = b⁽ⁱ⁾ − η · ∂ℒ/∂b
这里的 η 称为学习率(learning rate),它控制每一步沿梯度方向迈出的步长。学习率过大容易震荡发散,过小则收敛缓慢,是训练时最重要的超参数之一。
从全量计算到 minibatch:SGD
理论上,损失是以所有训练样本之和计算的,优化步骤也应当基于整个数据集。但在实践中,我们会取数据集的小批次子集(minibatches),仅基于这批子数据计算梯度。由于每一轮子集都是随机选取的,这种方法被称为随机梯度下降(stochastic gradient descent, SGD)。它一方面大幅降低了单步计算量,另一方面由于采样的随机性反而常常帮助模型跳出局部极小点。绝大多数现代深度学习框架(如 PyTorch、TensorFlow)的默认优化流程都建立在 SGD 及其变体的思想之上。
多层感知机:用层堆叠出更丰富的模型
如前所述,单层网络只能处理线性可分的类别。为了构建更强的模型,我们把多个网络层组合起来。数学上,这相当于让函数 f 呈现更复杂的形态,并分多步计算:
- z₁ = w₁x + b₁
- z₂ = w₂·α(z₁) + b₂
- f = σ(z₂)
其中:
- α 是非线性激活函数(non-linear activation function),例如 sigmoid、tanh 或 ReLU。它给网络引入非线性,是"多层"真正能超越单层表达能力的核心原因——若没有非线性,多层线性变换仍等价于一层线性变换;
- σ 是 softmax 函数,用于最终输出概率;
- 参数集合扩展为 θ = ⟨w₁, b₁, w₂, b₂⟩。
由 data/frameworks.md 可知,构建此类网络需要两类基础能力:一是对张量做乘法、加法并计算 sigmoid、softmax 等函数;二是为所有表达式计算梯度。前者可由 numpy 胜任,后者则是我们自研框架需要解决的核心问题——这正是下一节"反向传播"登场的理由。
反向传播:链式法则驱动梯度流动
多层模型的梯度下降算法本身没有改变,真正的难点在于如何计算梯度。利用链式求导法则(chain differentiation rule),我们可以逐层展开损失对各层参数的导数:
- ∂ℒ/∂w₂ = (∂ℒ/∂σ)(∂σ/∂z₂)(∂z₂/∂w₂)
- ∂ℒ/∂w₁ = (∂ℒ/∂σ)(∂σ/∂z₂)(∂z₂/∂α)(∂α/∂z₁)(∂z₁/∂w₁)
✅ 链式法则被用来计算损失函数对参数的导数。
仔细观察可以发现,上述所有表达式最左侧的部分是相同的(都是 ∂ℒ/∂σ 起头的公共因子)。因此,我们可以从损失函数出发,沿着计算图(computational graph)"从后往前"逐层复用这些中间结果,高效地求出全部导数。这种训练多层感知机的方法被称作反向传播(backpropagation,或简称 backprop)。
理解反向传播的关键在于两点:
- 前向传播按 z₁ → z₂ → f 的顺序计算输出并缓存各层中间量;
- 反向传播从损失开始,按与计算顺序相反的方向把梯度"传回去",逐层更新 w₁、b₁、w₂、b₂。
在原 data/frameworks.md 中亦印证了这一实现方式:在自研框架里,我们需要在 backward 方法中手工编写所有导数函数来执行反向传播。现代框架(TensorFlow、PyTorch)则通过自动微分机制,让你可以对"任意表达式"自动求梯度,并支持把计算推送到 GPU/TPU 等专用计算单元上并行执行——但对于理解原理而言,手工实现一遍 backward 是无可替代的必修课。
动手实践:从二维分类到 MNIST 手写数字识别
理解了上述理论与数学推导后,即可进入编码实践环节。文档对应的完整学习路径是:先实现自己的神经网络"库",然后逐步检验它:
- 二维分类任务:用自研框架解决一个简单的二维分类问题,验证前向计算、损失计算与反向传播代码的正确性;
- 挑战(Challenge):进入配套的 OwnFramework 笔记(notebook),在其中亲手实现一个用于构建和训练多层感知机的框架,你会直观看到现代神经网络内部到底是如何运作的;
- 实验作业(Assignment):运用本讲构建的框架解决 MNIST 手写数字分类问题——把 28×28 的手写图片像素展平为输入特征,经过含隐藏层的多层感知机输出 10 个类别的概率分布。
说明:OwnFramework 笔记来自该系列资料配套的深度学习课程素材。在本仓库中,这三份 Markdown 文本(frameworks.md、own_framework.md、perceptron.md)作为知识库样本被 RAG 课程 notebook 读入并切分为文本块,随后转换成向量嵌入,供"以神经网课文档为知识底座"的聊天机器人在检索时使用——因此本文的内容既是神经网络原理,也是理解 RAG 数据管道"原材料从哪来"的窗口。
小结:三条主线串起整份材料
回顾全文,可以把核心收获归纳为三条主线:
- 问题侧:回归用绝对误差 / 平方误差,分类用 0-1 损失或 logistic 损失,配合 softmax 得到概率输出,训练目标是最小化关于参数 θ 的损失;
- 优化侧:梯度下降以 η·∂ℒ/∂θ 的步长更新参数,随机选取 minibatch 演化为 SGD;
- 结构侧:非线性激活函数 α 让多层堆叠产生真正的表达能力,链式法则与计算图上的反向传播让深层网络的梯度计算高效可行。
自学建议与延伸
反向传播是人工智能与机器学习中最通用的算法之一,值得深入研读。建议结合 data/frameworks.md 中关于过拟合(overfitting) 与偏差-方差权衡(Bias-Variance Tradeoff) 的讨论一并学习——当你在 MNIST 上用自研框架训练多层网络时,训练误差低而验证误差高的现象就是过拟合的直接信号,可以通过增加数据量、降低模型复杂度或引入 Dropout 等正则化手段加以缓解。
完成以上内容后,可回到 第 15 课 RAG 与向量数据库 的完整流程中,把同样的文本数据切块、嵌入并接入检索与向量搜索,体验"从一篇技术文章到可问答的知识库"的完整链路。
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 StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00