MicroGPT 极简解析:200行代码读懂大语言模型本质

“你不能真正理解一个东西,除非你能把它简化到最本质的形式。” — Andrej Karpathy

原文出处:本文基于 Andrej Karpathy 的 MicroGPT 项目 进行解读和整理。

什么是 MicroGPT?

想象有人用 200 行 Python 代码,不借助任何外部工具,从零搭建了一个能"说话"的 AI。这就是 MicroGPT —— AI 大神 Andrej Karpathy 的最新"艺术作品"。

它的目标很简单:把 GPT 简化到无法再简化的程度,让任何人都能一眼看明白大语言模型到底在干什么。


MicroGPT 整体流程图

在开始详解每个部分之前,先看一下 MicroGPT 的完整工作流程:

flowchart TD A[原始数据
32000个人名] --> B[Tokenizer
文字→数字] B --> C[神经网络
预测下一个token] C --> D[损失计算
预测vs真实] D --> E[Autograd
反向传播算梯度] E --> F[Adam优化器
更新参数] F --> C style A fill:#e1f5ff style B fill:#fff4e1 style C fill:#e8f5e9 style D fill:#ffebee style E fill:#f3e5f5 style F fill:#fff8e1

训练阶段:循环执行 ①→②→③→④→⑤→①,重复 20,000 次

推理阶段:只需 ①→②,循环生成直到遇到结束标记


第一部分:Dataset(数据集)—— AI 的"食物"

本质是什么?

AI 需要学习材料,就像学生需要课本。

大白话解释

MicroGPT 使用了一个极简数据集:32000 个英文人名,每行一个:

emma
olivia
ava
isabella
sophia
...

Karpathy 选择这个名字数据集是有深意的:

  • 规则简单:人名有固定的字母组合规律
  • 容易理解:每个人都能判断"kamon"听起来像不像一个名字
  • 足够复杂:能展示出 AI 学习"模式"的能力

核心思想

AI 的本质是统计机器。你给它看足够多的例子,它就能学会隐藏在数据背后的规律,然后生成类似的全新内容。

训练完成后,MicroGPT 能生成这样的"新名字":

kamon, karai, keylen, vialan, karia...

这些名字世界上可能根本不存在,但它们听起来像真正的名字。这就是 AI 的魔力:不是记忆,而是创造


第二部分:Tokenizer(分词器)—— AI 的"翻译官"

本质是什么?

把人类文字转换成 AI 能理解的数字,再把数字翻译回文字。

大白话解释

神经网络只认识数字,不认识字母。所以我们需要一个"翻译官":

MicroGPT 的做法(极简版):

  • 收集所有出现过的字符(a-z)
  • 给每个字符分配一个编号:a=0, b=1, c=2…
  • 额外加一个特殊符号 BOS(Beginning of Sequence)= 26
"emma" → [4, 12, 12, 0]  (e=4, m=12, m=12, a=0)

核心思想

这些数字本身没有任何含义!0 代表 ‘a’ 只是人为约定。重要的是:每个符号都是一个独立的、离散的 token

Karpathy 特别强调:这些数字换成表情包也行,它们只是不同类别的标签。

为什么需要 BOS?

BOS(序列开始标记)就像一个"哨兵":

  • 告诉模型:“一个新的名字开始了”
  • 也告诉模型:“这个名字结束了”

训练时,每个名字变成:[BOS, e, m, m, a, BOS]

模型学到:看到 BOS 就开始生成新名字,再看到 BOS 就停下来。

Tokenizer 工作流程

flowchart LR A[输入文字emma] --> B{字符级编码} B --> C[e → 4] B --> D[m → 12] B --> E[m → 12] B --> F[a → 0] C & D & E & F --> G[[数字序列【4,12,12,0】 ]] H[BOS标记 26] --> I[包装序列 【26,4,12,12,0,26】] G --> I style A fill:#e1f5ff style I fill:#e8f5e9

关键理解:文字和数字只是不同形式的标签,神经网络只认数字。


第三部分:Autograd(自动微分)—— AI 的"自我纠错系统"

本质是什么?

让 AI 知道自己哪里错了,以及如何改正。

大白话解释

这是 MicroGPT 中最精妙的部分。Karpathy 用 60 行代码 实现了一个微型 PyTorch!

核心概念:Value 类

class Value:
    def __init__(self, data):
        self.data = data  # 当前数值(正向传播算出)
        self.grad = 0     # 梯度(反向传播算出)

每个 Value 就像一个有记忆的数字:

  • data:这个数的当前值
  • grad:如果这个数变化一点点,最终目标会变化多少

举个生活例子

假设你在调收音机的音量(目标:找到最佳音量):

  • 正向传播:转动旋钮,听到声音 → 这就是 data
  • 反向传播:感觉声音太大/太小,决定往哪个方向调、调多少 → 这就是 grad

怎么实现的?

Value 类重载了所有数学运算符(+, -, *, /, pow, log, exp…):

def __mul__(self, other):
    # 乘法:a * b = c
    result = Value(self.data * other.data)
    # 记录:如果 c 变了,a 要承担多少责任?
    # 答案是 b(因为 d(a*b)/da = b)
    result._local_grads = (other.data, self.data)
    return result

反向传播的本质

def backward(self):
    # 从终点往回走
    for node in reversed(topological_order):
        # 把责任分摊给每个输入
        for child, local_grad in zip(children, local_grads):
            child.grad += local_grad * node.grad

Karpathy 用一句话总结:链式法则只是乘法,拓扑排序只是确保顺序正确。

核心思想

深度学习训练 = ** millions 次试错**

每次试错后,Autograd 告诉每个参数:“你闯的祸有多大?下次怎么改?”

这就是 AI “学习"的真相:不是理解,而是统计意义上的参数调优

Autograd 反向传播流程

flowchart BT A[损失 Loss] --> B[梯度 = 1] B --> C[输出层] C -->|链式法则| D[Transformer层] D -->|链式法则| E[Embedding层] E -->|链式法则| F[模型参数] G[正向传播
计算预测值] -.-> A style A fill:#ffebee style F fill:#e8f5e9 style G fill:#e1f5ff

核心原理:链式法则就像「责任追究」——从最终结果往回推,每个环节承担多少责任。


第四部分:神经网络架构 —— AI 的"大脑结构”

本质是什么?

一组数学函数,把输入的数字变成输出的概率。

大白话解释

GPT 的"大脑"由三个核心组件构成:

1. Token Embedding(词嵌入)—— “查字典”

# 27 个 token,每个用一个 48 维向量表示
self.wte = [Value(random.randn(48)) for _ in range(27)]

本质:把整数 token ID 变成一个「语义向量」。

想象:每个词都有一个「个性档案」,48 个数字描述这个词的「性格特征」。

2. Position Embedding(位置编码)—— “记住顺序”

# 32 个位置,每个位置用一个 48 维向量
self.wpe = [Value(random.randn(48)) for _ in range(32)]

本质:告诉模型"这个词出现在第几个位置"。

为什么重要?因为 “狗咬人” 和 “人咬狗” 是完全不同的意思,但 token 完全一样!位置信息让它们区分开。

3. Transformer Block(变形金刚模块)—— “注意力机制”

这是 GPT 的核心创新。MicroGPT 实现了一个简化版:

Self-Attention(自注意力)的本质:

  • 每个词都问其他词:“你们和我有关系吗?”
  • 通过计算「相似度」决定注意力权重
  • 然后「加权求和」得到新的表示
"猫坐在垫子上"

"垫子" 看周围:
- 和"猫"关系强(猫坐的地方)
- 和"坐"关系中等(动作的对象)
- 和"上"关系强(位置词)

最终,"垫子"的新表示 = 融合了这些相关信息的向量

4. 输出层 —— “猜下一个词”

经过所有处理,模型输出一个 27 维的向量,经过 softmax 变成概率分布:

P(下一个词是 'a') = 0.15
P(下一个词是 'b') = 0.03
P(下一个词是 BOS) = 0.42  ← 最可能
...

核心思想

预测下一个词 = 理解语言

如果模型能准确预测 “I love” 后面应该接 “you” 而不是 “苹果”,说明它真的"理解"了英语。

神经网络数据流

flowchart LR A[Token IDs【26,4,12,12,0】] --> B[Token Embedding查表得向量] C[位置 0,1,2,3,4] --> D[Position Embedding 位置编码] B & D --> E[相加 语义+位置] E --> F[Transformer Block 注意力机制] F --> G[输出层 Softmax] G --> H[概率分布 预测下一个token] style A fill:#e1f5ff style H fill:#e8f5e9 style F fill:#fff4e1

关键理解:模型不是直接预测文字,而是预测「下一个 token 的概率分布」。


第五部分:Adam Optimizer(优化器)—— AI 的"教练"

本质是什么?

决定每次参数调整的步长和方向。

大白话解释

训练神经网络就像下山:

  • 目标:找到山谷最低点(损失最小)
  • 问题:山很陡、雾很大,你看不清路
  • Adam:一个聪明的向导,根据历史经验调整步伐

梯度下降 vs Adam

简单梯度下降

param = param - learning_rate * param.grad

问题:梯度大时步子太大(冲过头),梯度小时步子太小(太慢)。

Adam 改进

  1. 动量(Momentum):记住之前的方向,保持惯性
  2. 自适应学习率:自动调整每个参数的步伐
# Adam 核心(简化版)
m = beta1 * m + (1-beta1) * grad  # 一阶矩(动量)
v = beta2 * v + (1-beta2) * grad**2  # 二阶矩(自适应)
param = param - lr * m / sqrt(v)

核心思想

学习是一个动态过程,不能一视同仁:

  • 有些参数变化剧烈 → 要「刹刹车」
  • 有些参数总是很小 → 要「加加速」
  • 有些方向一致 → 要「保持惯性」

Adam 就是调节这些的「智能教练」。

Adam 优化流程

flowchart TD A[计算梯度 grad] --> B{自适应学习率} B --> C[一阶矩 m
动量累积] B --> D[二阶矩 v
梯度平方累积] C --> E[更新公式
param -= lr * m / sqrt(v)] D --> E E --> F[更新后的参数] style A fill:#ffebee style E fill:#fff4e1 style F fill:#e8f5e9

核心思想:不同参数需要不同的学习速度,Adam 自动调节每个参数的「步伐大小」。


第六部分:训练循环 —— AI 的"寒窗苦读"

本质是什么?

让 AI 看例子、算误差、调参数,重复 millions 次。

大白话解释

for step in range(max_steps):
    # 1. 随机抓一批数据
    batch = random_sample(docs)
    
    # 2. 前向传播:预测
    logits = model(batch)
    
    # 3. 算损失:预测 vs 真实的差距
    loss = cross_entropy(logits, targets)
    
    # 4. 反向传播:谁该为错误负责
    loss.backward()
    
    # 5. 更新参数:改错
    optimizer.step()
    
    # 6. 清零梯度,准备下一轮
    model.zero_grad()

训练在干什么?

想象你在教小孩认字:

  1. 给他看一个字 “猫”
  2. 他猜 “狗”
  3. 你说:“错了,是猫”
  4. 他调整大脑连接:“原来长这样的是猫”
  5. 重复 millions 次

AI 训练完全一样,只是「调整大脑连接」变成了「调整数字参数」。

MicroGPT 的训练数据

  • 32,000 个人名
  • 训练 20,000 步
  • 每步看 32 个名字

最终损失从 ~3.3 降到 ~1.9,模型学会了「人名的统计规律」。

训练循环详细流程

flowchart TD Start([开始训练]) --> A[随机抓取一批数据] A --> B[Tokenize
文字转数字] B --> C[前向传播
模型预测] C --> D[计算损失
预测vs真实] D --> E{损失是否
足够小?} E -->|否| F[反向传播
计算梯度] F --> G[Adam优化
更新参数] G --> H[清零梯度] H --> I{训练步数
达标?} I -->|否| A I -->|是| End([结束训练]) E -->|是| End style Start fill:#e1f5ff style End fill:#e8f5e9 style F fill:#ffebee style G fill:#fff4e1

MicroGPT 训练规模:32,000 个名字 × 20,000 步 = 约 6 亿次参数更新


第七部分:推理循环 —— AI 的"说话方式"

本质是什么?

一个词一个词地「接龙」,直到说完。

大白话解释

训练好的模型怎么用?很简单:

# 从 BOS 开始
tokens = [BOS]

while True:
    # 1. 模型预测下一个词的概率
    probs = model(tokens)
    
    # 2. 按概率随机采样
    next_token = sample(probs)
    
    # 3. 加到序列中
    tokens.append(next_token)
    
    # 4. 如果采样到 BOS,说明名字结束了
    if next_token == BOS:
        break

# 把 tokens 转换回文字
name = tokenizer.decode(tokens)

为什么能生成合理的内容?

因为训练时,模型学会了:

  • “k” 后面常跟 “a”、“i”、“o”
  • 名字通常 3-8 个字母
  • 元音和辅音要交替

所以它采样时,大概率会生成听起来像名字的东西。

温度的作用

# temperature = 1.0: 按原概率采样
# temperature < 1.0: 更"确定",选概率最高的
# temperature > 1.0: 更"随机",可能出奇怪结果

低温 = 保守(总是"emma") 高温 = 创意(可能"xyz123")

推理生成流程

flowchart TD Start([开始生成]) --> A[输入 BOS
开始标记] A --> B[前向传播
获取概率分布] B --> C[按概率采样
选择下一个token] C --> D[添加到序列] D --> E{是否是
BOS结束标记?} E -->|否| B E -->|是| F[Detokenize
数字转文字] F --> End([输出生成的名字]) style Start fill:#e1f5ff style End fill:#e8f5e9 style B fill:#fff4e1 style C fill:#f3e5f5

关键理解:生成是「重复预测+采样」的过程,直到遇到结束标记。

温度的作用

flowchart LR A[温度参数] --> B{T < 1} A --> C{T = 1} A --> D{T > 1} B --> E[保守
总是选最可能的] C --> F[平衡
按原概率采样] D --> G[创意
可能出奇怪结果] style E fill:#e8f5e9 style F fill:#fff4e1 style G fill:#ffebee

例子

  • T=0.1(低温):总是 “emma”, “olivia” —— 安全但无聊
  • T=1.0(中温):“kamon”, “karai” —— 合理且多样
  • T=2.0(高温):“xyz123”, “qqqqq” —— 可能不符合规则

🔄 训练 vs 推理:流程对比

flowchart TB subgraph 训练阶段[训练阶段 —— 学习时间] T1[输入数据] --> T2[前向预测] T2 --> T3[计算损失] T3 --> T4[反向传播
算梯度] T4 --> T5[Adam优化
改参数] T5 --> T2 end subgraph 推理阶段[推理阶段 —— 工作时间] I1[输入提示] --> I2[前向预测] I2 --> I3[采样输出] I3 --> I4{结束?} I4 -->|否| I2 I4 -->|是| I5[返回结果] end 训练阶段 -.->|训练完成后
参数固定| 推理阶段 style 训练阶段 fill:#fff4e1 style 推理阶段 fill:#e8f5e9 style T4 fill:#ffebee style T5 fill:#f3e5f5

关键区别

  • 训练:需要「试错-纠错」循环,目的是学习参数
  • 推理:只用前向传播,目的是生成结果,参数不变

🤔 最后思考:MicroGPT 告诉我们什么?

1. 复杂的东西本质是简单的

ChatGPT 和 MicroGPT 在原理上没有本质区别,只是规模差了 10000 倍。

2. AI 没有理解,只有统计

MicroGPT 生成 “kamon” 时,它并不知道这是一个名字,它只是按照统计规律「接龙」而已。

3. 规模就是一切

小模型只能做小游戏(生成名字),大模型才能做复杂任务(写文章、编代码)。但底层机制是一样的

4. 深度学习 = 积木游戏

每个组件都很简单(矩阵乘法、softmax、attention),但组合起来就产生了「智能」。


📚 参考来源

原文及代码

相关学习资源


感谢 Andrej Karpathy 为我们揭开 AI 的神秘面纱。 🙏

这 200 行代码告诉我们:最深刻的理解,往往来自最简单的实现。