系列开篇,从房价回归讲起,把机器学习最基本的动作「拟合一个函数」讲透:模型结构、损失函数、梯度下降、向量矩阵、神经网络为何要叠非线性、自动微分怎么把求导交给机器。这些都是地基,末尾埋下贯穿全系列的脊柱:一切语言模型都在做同一件事,估计「下一个词的概率分布」。
这是《从预测一个词开始》系列的第一篇。整个系列的文章(Chen 注:主要参考 Andriy Burkov 的《The Hundred-Page Language Models Book: Hands-On with PyTorch》,2025 年出版。原书延续作者上一本《The Hundred-Page Machine Learning Book》的极简路线:用尽量少的篇幅讲清楚一个领域最核心的东西,聚焦语言模型,并覆盖从 n-gram 到大语言模型微调对齐的完整链路。)会把现代语言模型讲清楚。
一切语言模型都在做同一件事:估计「下一个词的概率分布」,即 。这也是本系列文章的核心主线。n-gram 用计数估、RNN 用循环状态估、Transformer 用注意力估,训练是在拟合这个分布,采样是在从这个分布里取样。后面几篇会反复回指这句话。
但在谈「下一个词的分布」之前,得先把更基本的问题铲平:机器学习本身在干什么?本篇文章不含任何语言模型的内容,却是唯一一处把模型、损失、梯度下降、神经网络这些地基词汇讲透的地方。语言模型说到底只是一种特殊的函数,用来学习特殊的分布,但它依然要用这套地基去训练。这一篇就把地基铲平,末尾埋下脊柱的第一个锚点。
机器学习要解决的问题,本质上可以写成一个函数:
是输入, 是输出, 是从输入到输出的映射规则。机器学习不凭空发明这个 ,而是先收集一批 例子,再从数据里「选出」一个足够好的 。
拿一个具体问题落地:用房子的面积估算房价。数据集是一批 (面积, 房价) 对,比如 ,面积单位是平方米,房价单位是千。给定一个新房子:面积 250 平方米,我们想要 能给出一个合理的房价。
穷举所有可能的函数不现实,所以先钦定一个结构,只在这个结构里找最优参数。最简单的结构是线性函数:
这里 叫权重(weight), 叫偏置(bias);本书统一用这两个词,不用「斜率」、「截距」等说法。确定 只需要确定 和 这两个数。
和 可以取无穷多组值,需要一个标准来衡量哪组最好。最自然的标准是让 的预测尽量贴近真实房价。给定一个例子 ,模型的预测是 ,定义为(squared error):
预测完全准确时误差为 0;偏差越大,误差越大;平方保证误差不会因为「高估」和「低估」而互相抵消抹平(Chen 注:误差 $\hat{y}_i-y_i$ 本身带符号:高估为正、低估为负。若不平方直接求和,一个时而高估、时而低估的坏模型可能正负抵消,算出很小的平均误差,掩盖真实偏差。平方把符号抹掉,只留下偏离幅度,才能如实衡量整体预测质量。)。
把整个数据集 ( 是样本数)的平均误差记作一个关于 的函数 :
这个 就是损失函数(loss function),具体在这里就是均方误差(mean squared error,MSE)。数据集里的 都已知,唯二的未知量是 和 。我们要找的最优参数 ,就是让 最小的那组取值。
是关于 的二次函数,微积分保证它只有一个最小值。求最优值的标准做法:对每个变量求偏导,因为函数在极值点处,切线是水平的,因此令偏导数等于 0:
拿一个三个样本的具体数据集练手:,,。代入 (1.3) 式:
是三层函数的复合(composition):先算三个关于 的线性函数 (如 ),再各自平方得到 ,最后取平均得到 。求偏导要用到(chain rule):复合函数 对 的导数,等于 对 的导数乘上 对 的导数。配合(求和的导数等于导数的求和)与(常数提出来不影响求导)两条规则,逐层展开:
令两式为零、化简、解方程组,得到 ,。也就是说,这三个房子的数据支持的最优模型是 。代回式 1.3 算出训练损失 ,其平方根 ,即平均预测误差约。
到这里,监督学习的四步流程已经完整走了一遍:一是收集数据集;二是定义模型结构(如 );三是定义损失函数(如式 1.3);四是最小化损失。本篇第 1、2 节其实只做了第一到到第三步和「手解」版的第四步。语言模型的参数动辄十亿百亿,手解无从谈起。第 5 节的梯度下降,就是第四步在真实规模下唯一可行的做法。
估房价光靠面积不够,还得看卧室数、建成年份等。假设用面积和卧室数两个特征的话,输入就变成了一个特征向量:
本系列文章约定向量用小写斜粗体字母(如 、),矩阵用大写斜粗体字母(如 、),标量用斜体字母(如 、)。两个特征的权重也要打包成向量 ,线性模型写成向量形式:
是点积(dot product),定义为 , 是输入维度。点积把两个同维向量压缩成一个标量,是「加权求和」这个动作最紧凑的写法。
特征一多,参数个数就会膨胀到人手解不动的规模,而且高维空间里也没法用眼睛判断数据是否符合线性假设。矩阵把这套运算进一步打包:一个 行 列的矩阵 ,可以把一整层神经网络的权重集中表示,配合矩阵乘法、矩阵-向量乘法,用 BLAS、cuBLAS 这类优化过的线性代数库高效计算,这正是神经网络能训练得起来的工程基础:把「逐个数循环相乘再求和」的操作,交给专门为矩阵运算做过硬件加速的库去执行。
线性模型有一个硬伤:多个线性函数复合起来,结果还是线性。设 ,,代入化简:
就是个新常数, 依然是 的线性函数。不管叠多少层纯线性变换,表达能力都不会超过一层。要跳出这个天花板,得在每层线性变换后插入一个固定的非线性函数,称为激活函数(activation):
三个常见选择:

把带激活函数的单元逐层堆叠、彼此嵌套,就搭出了神经网络:。每个非线性单元叫一个(artificial neuron),信息从输入到输出单向流动、不含循环的网络叫(feedforward neural network,FNN);一层里每个神经元都连接下一层所有神经元,叫(fully connected layer)或(multilayer perceptron,MLP)。
用简单的两层网络举例:输入是二维向量 ,第一层用矩阵 ()和向量 把 变成三维输出:
第二层用 ()和标量 ,把 变成最终输出:
这套「矩阵乘向量再加激活函数」的模板,逐层叠加、层数越深、每层单元越多,模型就越能拟合复杂的高维数据,包括自然语言这种典型的高维数据。本书第 3 章讲的循环神经网络(recurrent neural network,RNN)会在这套前馈结构里加入循环连接,让网络能处理任意长度的序列;这是本系列第 4 篇的正题,这里先记住「前馈」和「循环」的分野。
神经网络通常层数深、非线性函数套得多,损失函数早就不是能手解的二次函数了。这时候要用梯度下降(gradient descent)算法,通过迭代一步步逼近最小值。这也是包括语言模型在内几乎所有现代神经网络的训练方式。
换一个更贴近「分类」的例子来讲梯度下降:二分类(binary classification),判断一封邮件是否为垃圾邮件。数据集 ,。模型定义为:
这个模型叫逻辑回归(logistic regression),尽管名字里有「回归」,但它做的是分类:sigmoid 把线性输出压缩到 ,可以读作「属于正类的概率」。
对应的损失函数是二元交叉熵(binary cross-entropy),也叫逻辑损失(logistic loss):
验证两个极端:预测完全正确()时,损失为 ;预测完全相反()时,损失趋向 。惩罚的方向是对的:越接近真相损失越小,越离谱损失越大。
要用梯度下降最小化这个损失,需要对每个参数求偏导。把 ,损失是三层复合:。用链式法则逐层展开,权重 和偏置 上的偏导数出乎意料地简洁:
把梯度打包成一个向量 (包含所有参数的偏导数),梯度下降的迭代规则就是:
是学习率(learning rate),一个人为设定的超参数(hyperparameter,不是学出来的,是训练前手动设的),控制每步走多远。之所以用「减去梯度」而不是「加上」:梯度指向损失上升最快的方向,要最小化损失,就该往梯度的反方向走。 太小则收敛慢,太大则可能在最小值附近来回震荡甚至发散。这条权衡,训练任何神经网络(包括语言模型)时都要面对。
完整流程的五个步骤:一是用当前参数算预测值;二是用式(1.11)算梯度;三是按学习率更新参数;四是算新的损失;五是重复步骤一到步骤四,直到损失收敛或达到预定迭代次数(步数,step)。
前两节手推了线性回归和逻辑回归这两次梯度,已经能感觉到了繁琐。真实的神经网络参数动辄百万、十亿计,手推梯度不现实。自动微分(automatic differentiation,简称 autograd)解决了这个问题:框架在执行前向计算时自动记录每一步运算,需要梯度时直接沿着记录反向套用链式法则,全程不需要人写一条求导公式。
用 PyTorch 实现上面的逻辑回归:
import torch
import torch.nn as nn
import torch.optim as optim
model = nn.Sequential(
nn.Linear(n_inputs, n_outputs), # 对应 (1.8) 式里的 w·x+b
nn.Sigmoid() # 对应 (1.8) 式里的 σ
)nn.Linear(n_inputs, n_outputs) 就是式(1.8)里 的封装。数据、模型、损失函数、优化器一起搭起来:
inputs = torch.tensor([
[22, 25], [25, 35], [47, 80], [52, 95], [46, 82], [56, 90],
[23, 27], [30, 50], [40, 60], [39,
inputs 是一个 的矩阵,即 12 个样本、每个样本 2 个特征(年龄、收入),对应用来预测是否会购买某商品。训练循环只需四行:
for step in range(500):
optimizer.zero_grad() # 清空上一步残留的梯度
loss = criterion(model(inputs), labels) # 前向传播,算损失
loss.backward() # 反向传播,自动求梯度
optimizer.step() # 按梯度更新参数loss.backward() 这一行,就是自动微分登场的地方:PyTorch 在 model(inputs) 执行前向传播时,已经在背后搭好了一张计算图(computational graph),记录了每一步运算;backward() 沿着这张图反向套用链式法则,一次性把所有参数的梯度算出来,写进各参数的 .grad 属性。数据从输入到输出流动的过程叫前向传播(forward pass),沿着计算图反向求梯度的过程叫反向传播(backward pass),这个词也是深度学习最核心的算法名字。
nn.Sequential 里的层,训练循环这四行完全不用动。这正是自动微分最实际的好处:写模型的人不用为每种新结构手推一份新的求导公式,框架帮你把这件事一次性解决了。第 3 篇之后我们会看到,无论 n-gram 之后的 RNN 还是 Transformer,用的都是同一套 loss.backward()。这一篇没有出现任何「语言」或「词」,因为它讲的是所有监督学习共享的地基:模型是一个被数据选出来的函数。先钦定结构,再用损失函数衡量好坏,最后用梯度下降(在能手解的小问题上,直接解方程)找出最优参数;向量和矩阵是把这套计算压缩、加速的表示法;神经网络靠叠非线性摆脱了「复合线性还是线性」的天花板;自动微分让「求梯度」这件全书反复出现的动作,从人力劳动变成了框架自动完成的例行操作。
这些概念本身不专属于语言模型,但语言模型要解决的问题,同样可以写成 的模样,只是 和 不再是面积和房价,而是文本。下一篇要做的第一件事,就是回答一个具体得多的问题:文字要怎么变成数,才能塞进这一篇讲的这套机器(向量、矩阵、损失函数、梯度下降)里?答案会从分词开始:把一段文字切成词或子词,一路走到词嵌入,也就是把每个词映射成一个向量。而这一篇里 那个笼统的「拟合一个函数」,到下一篇会第一次演化成本系列真正的主角:拟合一个分布 。语言模型要学的,就是给定上文,下一个词该是什么的概率分布。这根脊柱,会贯穿本系列剩下的六篇。