文章

从零看懂 MicroGPT:给程序员的“无数学负担”大模型入门

用可视化和工程直觉讲清 Token、Softmax、交叉熵、反向传播、注意力与训练循环,帮助没有数学基础的程序员真正理解 GPT。

从零看懂 MicroGPT:给程序员的“无数学负担”大模型入门

很多程序员第一次接触大模型时,都会有一种感受:

  • 代码大概看得懂;
  • 公式一出现就开始“失焦”;
  • 一堆术语(logits、softmax、cross-entropy、backprop)像黑魔法。

如果你也这样,这篇就是为你写的。

本文基于 Andrej Karpathy 的 200 行教学脚本(通常被称为 microGPT 思路)来讲,但我会尽量用程序员视角解释,不要求你有机器学习和高数基础。

你可以把它当成一篇“把 GPT 拆开看内部零件”的图解说明书。


1. 先定一个最小目标:让模型学会“起名字”

这个教学模型的训练数据很简单:3.2 万个人名,一行一个,例如:

  • emma
  • olivia
  • sophia
  • isabella

模型的任务也很简单:

看前面的字符,预测下一个字符。

训练完后,它可能生成:

  • kamon
  • karai
  • anna
  • anton

这些名字不一定真实存在,但“像人名”。

这件事和 ChatGPT 的关系是:

  • 在名字任务里,单位是字符;
  • 在 ChatGPT 里,单位是 token(词片段);
  • 本质都是“根据上下文,做下一个 token 预测”

2. 机器不认识字母,只认识数字

神经网络只处理数值,所以第一步是把字符映射成整数。

例如:

  • a-z 对应 0-25
  • 再加一个特殊符号 BOS(序列开始/结束)对应 26

于是 emma 会变成:

1
2
[BOS, e, m, m, a, BOS]
[26, 4, 12, 12, 0, 26]

这里要记住一件非常重要的事:

token id 只是“编号”,不表示大小关系。

4 并不比 2“更大更强”,就像数据库主键,只有区分作用,没有数学语义。


3. GPT 的核心训练任务:Next Token Prediction

emma 为例,训练样本会被拆成多个“输入→目标”:

  1. [BOS],预测 e
  2. [BOS, e],预测 m
  3. [BOS, e, m],预测 m
  4. [BOS, e, m, m],预测 a
  5. [BOS, e, m, m, a],预测 BOS

这就是滑动窗口训练。对 ChatGPT 也是同一套逻辑。

你可以把它想成代码补全:

  • IDE 看你前面写了什么;
  • 给出后续最可能的候选。

GPT 只是把这个过程做到了超大规模。


4. 从“原始分数”到“概率”:Softmax 在做什么

模型每一步会输出一组分数(logits),每个候选 token 一个分数。

这些分数可能是负数、正数、很大、很小,不满足概率要求。于是要经过 softmax:

  1. 对每个分数做 exp
  2. 再除以总和
  3. 得到一组 0~1 且和为 1 的概率

可以把它当成“归一化投票”:

  • 分数高的候选拿更多票;
  • 分数差距会被指数函数放大。

常见实现里会先减去最大值再 exp,这是数值稳定技巧:

  • 数学上结果不变;
  • 工程上避免 exp(1000) 这种溢出。

5. 怎么衡量模型“猜错得有多离谱”:交叉熵

我们只关心一件事:

模型给“正确答案”分配了多大概率?

损失函数用 -log(p)

  • p=0.9,损失很小;
  • p=0.01,损失很大。

直觉上:

  • 猜对且自信,奖励高(loss 低);
  • 猜错且自信,重罚(loss 高)。

这对训练非常关键,因为它会强烈纠正“自信但错误”的预测。


6. 反向传播:不是魔法,是自动算导数的流水线

很多人怕反向传播,是因为“导数”这两个字。

你可以先这样理解:

它在回答:每个参数改一点点,最终 loss 会变好还是变坏,变化幅度多少?

在 microGPT 里,每次运算(加法、乘法、exp、log)都是计算图中的一个节点。每个节点保存:

  • 前向结果(data)
  • 反向梯度(grad)
  • 局部导数规则

backward() 的流程:

  1. 先拓扑排序,确定反向顺序;
  2. 从损失节点开始(梯度设为 1);
  3. 按链式法则把梯度传回去;
  4. 如果某变量走过多条路径,梯度会累加。

这和你在 PyTorch 里写 loss.backward() 是同一件事,只是这里是标量版本,便于看透底层。


7. Embedding:把离散 ID 变成可学习向量

token id 只是整数编号,模型需要的是“可计算的向量”。

做法:

  • 在 token embedding 表里查一行(比如 16 维);
  • 在 position embedding 表里再查一行;
  • 两个向量相加,得到当前位置输入。

为什么要位置向量?

因为“a 在开头”和“a 在结尾”的语义角色不同。模型要知道顺序。

可以把 embedding 理解成每个 token 的“可训练特征画像”,训练过程中会不断调整。


8. Attention:让当前位置读取“历史上下文”

这是 Transformer 最核心的机制。

每个位置会生成三组向量:

  • Query:我想找什么
  • Key:我这里有什么
  • Value:如果你关注我,我提供什么信息

流程是:

  1. 用当前 Query 去和历史 Key 做点积,得到相关性分数;
  2. softmax 变成注意力权重;
  3. 按权重对历史 Value 加权求和。

多头注意力(multi-head)就是并行做多套“检索视角”:

  • 有的头可能偏向最近字符;
  • 有的头可能关注 BOS;
  • 有的头可能学到元音/辅音模式。

同时还有因果掩码(causal mask)

当前位置只能看过去,不能偷看未来。

这保证了自回归生成的正确性。


9. GPT 一层里到底发生了什么

简化后可以记成:

  1. 输入向量
  2. 归一化(RMSNorm)
  3. 注意力
  4. 残差相加
  5. 再归一化
  6. MLP(升维→ReLU→降维)
  7. 再残差相加
  8. 线性投影输出 logits

其中两个组件尤其“工程关键”:

  • 残差连接:给梯度提供捷径,避免深层网络训练时梯度消失;
  • 归一化:稳定激活值范围,减少训练发散。

一句话:

  • Attention 负责“和上下文沟通”;
  • MLP 负责“当前位置内部思考”。

10. 训练循环:重复 1000 次的参数微调

一次训练迭代基本是:

  1. 取一个名字并 token 化;
  2. 每个位置前向计算并得到 loss;
  3. 平均 loss;
  4. backward() 得全部参数梯度;
  5. 用 Adam 更新参数;
  6. 梯度清零,进入下一轮。

为什么初始 loss 大约是 3.3

因为词表大小是 27,随机猜中概率约 1/27-log(1/27)≈3.3

随着训练推进,loss 下降到约 2.37,生成结果会从乱码变得“像名字”。


11. 推理(生成)阶段:其实非常直接

训练完后,生成流程是:

  1. 输入 BOS
  2. 前向得到下一 token 概率
  3. 按概率采样一个 token
  4. 把新 token 喂回去
  5. 直到采到 BOS 或达到最大长度

这里有个很实用的旋钮:temperature

  • <1.0:分布更尖锐,更保守、更稳定;
  • =1.0:按模型原分布采样;
  • >1.0:更发散,更多样,也更容易胡说。

在名字任务里,0.5 往往比较自然。


12. 从 microGPT 到 ChatGPT:核心几乎没变

最值得建立的认知是:

大模型的“算法骨架”并不神秘,复杂度主要来自规模和工程化。

本质差异主要是:

  • 数据量:几万名字 → 万亿 token
  • 词表:字符级 27 → 子词级十万左右
  • 参数量:几千 → 千亿级
  • 计算:CPU 标量对象 → GPU 张量并行
  • 训练:单机几分钟 → 多机多卡数周到数月

但核心循环仍然是:

Tokenize → Embed → Attend/MLP → Next-token 概率 → Loss → Backward → 参数更新。


13. 给程序员的学习路线(不走“公式地狱”)

如果你想真正入门 LLM,我建议按这个顺序:

  1. 先跑通一个最小实现(microGPT 或 nanoGPT 教学版)
  2. 手改超参数并观察现象(维度、层数、学习率、温度)
  3. 画出一次前向/反向的数据流(哪怕是纸笔)
  4. 再回头看数学定义(softmax、交叉熵、链式法则)
  5. 最后迁移到框架实现(PyTorch + tensor)

顺序不要反:

  • 先有系统直觉,
  • 再补数学严谨性,

你会轻松很多。


14. 一个最短总结

如果你今天只记住三句话:

  1. GPT 做的事就是根据上下文预测下一个 token
  2. 训练的事就是让正确 token 的概率更高(loss 更低)
  3. 反向传播的事就是算清每个参数该往哪个方向微调

理解了这三点,再大的模型也只是“同一算法在更大规模上的工程实现”。


如果你愿意,下一篇我可以继续写:

  • 如何从这个最小版本迁移到 PyTorch(tensor 版);
  • 如何把字符级模型改成中文分词版本;
  • 如何用可视化工具观察 attention 头到底在关注什么。
本文由作者按照 CC BY 4.0 进行授权