243 行 microGPT:把“训练 + 推理”拆到骨头里

你看到的这份 microgpt.py,厉害之处不在"短",而在"狠"。它把 GPT 这套体系里最核心的那条链路------数据 → token → 前向 → loss → 反向 → 参数更新 → 采样生成------压缩成一个人可以完整读完、在脑子里跑通的实现。

这篇文章按"从输入到输出"的顺序,把它拆成 8 个层级。你读完会获得两种东西:一套 GPT 的可执行直觉,一套看任何 Transformer 代码都能对齐的坐标系。


1)这份代码到底"实现了什么 GPT"

microGPT 做的是一个字符级(char-level)的小 GPT,常用 names 数据集训练,目标是学会生成"像名字的字符串"。这意味着:

  • 词表是字符集合,不是 BPE/WordPiece

  • 模型规模很小,能在 CPU 上跑

  • 实现强调算法链路完整性,性能和规模不在目标里

你可以把它当成"可执行的 Transformer 教科书"。它覆盖了 GPT 的核心结构与训练推理闭环,覆盖不了工业级大模型那套工程系统(GPU 张量库、自动微分、并行、混合精度、KV cache 优化、分布式通信等)。


2)数据与 Tokenizer:GPT 的第一道"语义-数字翻译"

你截图里那段代码就在干这件事:把文本翻译成整数序列。

2.1 训练样本:docs

names 数据集每行一个名字,最后得到:

  • docs: list[str],样本列表

  • shuffle 打乱,避免训练顺序偏置

2.2 词表:chars

复制代码
chars = ['<BOS>'] + sorted(set(''.join(docs)))
vocab_size = len(chars)

关键点有两个:

  • set(''.join(docs)) 把全数据集出现过的字符都收集出来,形成词表

  • <BOS> 是序列起始符,训练与生成时用来让模型知道"从头开始"

2.3 映射表:stoi / itos

复制代码
stoi = {ch:i for i, ch in enumerate(chars)}
itos = {i:ch for i, ch in enumerate(chars)}
BOS = stoi['<BOS>']

这就是 tokenizer 的全部:字符 ⇄ 整数。

这一步的意义很"物理":神经网络只吃数字。语言这层外壳必须被剥离成离散 token,模型才能开始学习统计规律。


3)训练数据如何喂给模型:从"文本样本"变成"监督信号"

GPT 的训练目标是:给定前缀,预测下一个 token

对于一个名字 "emma",加上 <BOS> 后对应 token 序列可能是:

<BOS> e m m a

训练会构造监督对齐:

  • 输入序列:<BOS> e m m

  • 目标序列:e m m a

也就是把序列整体右移一格,形成 "x → y"。

这件事在所有 GPT 训练里都成立。差别只在 token 的粒度与序列长度。


4)模型参数长什么样:Transformer 被拆成"几块矩阵"

microGPT 的核心设计思路是:用最少的数据结构表示 Transformer。你通常会看到几类参数:

1)Embedding(词向量表)

  • token_embedding_table: [vocab_size, n_embd]

  • 把 token id 映射成向量

2)Position Embedding(位置向量表)

  • position_embedding_table: [block_size, n_embd]

  • 给每个位置一个可学习向量,让模型区分"第 1 个字"和"第 10 个字"

3)Attention 的 Q/K/V 投影矩阵

  • Wq, Wk, Wv: [n_embd, head_size] 或合并形式

  • 把输入向量映射到 query/key/value

4)Attention 输出投影矩阵

  • Wo: [n_embd, n_embd]

5)MLP(前馈网络)

  • W1, b1W2, b2 两层线性,中间一个非线性(常见 tanh/relu)

6)最终输出层(语言模型头)

  • Wlm: [n_embd, vocab_size]

  • 把隐藏状态投影回词表维度,得到每个 token 的 logits

这些矩阵合在一起,就构成了一个完整 GPT Block 的最小表达。


5)前向传播:从 token 到 logits 的整条路径

前向传播是推理的核心,也是训练的第一步。它会把输入 token 序列 idx 变成每个位置对下一个 token 的预测分布。

典型路径长这样:

5.1 token + position

x = tok_emb[idx] + pos_emb[pos]

这一步把"字"与"位置"融进一个向量空间。没有 position embedding,注意力机制无法感知顺序。

5.2 自注意力(Self-Attention)

注意力做的事可以用一句话描述:

每个位置产生一个 query,去和所有历史位置的 key 做相似度,得到权重,再对 value 做加权求和。

数学上:

  • q = x Wq

  • k = x Wk

  • v = x Wv

  • att = softmax(q k^T / sqrt(d))

  • out = att v

5.3 因果遮罩(causal mask)

GPT 训练与推理必须满足"只看过去"。所以注意力矩阵会强制:

  • 当前位置只能 attend 到 <= 当前 的位置

  • att[i, j] = -inf for j > i(softmax 后变成 0)

这就是 GPT 与 BERT 的结构分水岭:GPT 的注意力是单向因果的。

5.4 残差 + 归一化(实现可能简化)

工业实现会有 LayerNorm + Residual,microGPT 可能用更原始的形式表达,核心目的在于保持信号稳定,便于训练。

5.5 MLP

x = x + MLP(x)

MLP 提供非线性表达能力,注意力提供信息路由能力,两者组合才是 Transformer 的完整威力。

5.6 输出 logits

logits = x Wlm

logits 的维度是 [T, vocab_size],每个时间步都对应一个词表分布的未归一化分数。


6)Loss:把"预测"变成可优化的标量

训练目标是最大化正确 token 的概率,等价于最小化交叉熵。

对于某个位置的 logits 向量 z

  • p = softmax(z)

  • loss = -log p[target_id]

训练会对所有时间步求平均,得到一个标量 loss。

这一步非常关键,因为它把"语言建模"这个任务变成一个数值优化问题。只有 loss 是标量,反向传播才有明确方向。


7)反向传播:这份代码最"硬核"的地方

真正让人佩服的是:它在没有任何深度学习框架的情况下,仍然实现了梯度计算并更新参数。

这里你要抓住一个核心事实:

训练=前向图 + 反向链式法则 + 参数更新

PyTorch 之类框架帮你做了"计算图与自动微分"。microGPT 选择手写。

7.1 softmax 的梯度

softmax + 交叉熵的组合有一个著名的简化:

dlogits = p; dlogits[target] -= 1

再除以 batch/sequence 的平均系数。

这一步让反向传播非常直接,避免手动推导完整的 softmax Jacobian。

7.2 矩阵乘法的梯度

对于 y = xW

  • dW = x^T dy

  • dx = dy W^T

Transformer 的大部分梯度都在重复这条规则。

7.3 Attention 的梯度

注意力里最麻烦的是:

  • att = softmax(scores)

  • out = att v

  • scores = q k^T / sqrt(d) + mask

每一块都要往回传梯度,softmax 需要处理数值稳定,mask 需要确保未来位置梯度为 0。

microGPT 的写法通常会把中间缓存保存下来(比如 q/k/v、att、softmax 输出),让反向阶段能读到这些中间量完成链式回传。

这种"前向缓存 + 反向用缓存"是手写反传的通用模式。


8)参数更新:最朴素的 SGD

训练最后一步是:

param -= lr * grad

代码短到可怕,意义也简单到可怕:

  • 梯度给方向

  • 学习率给步长

  • 更新重复很多步,模型就会拟合数据分布

工业级训练会用 AdamW、weight decay、梯度裁剪、学习率调度、混合精度等。microGPT 用最朴素的方式把"优化闭环"跑通。


9)推理生成:从概率分布里"采样未来"

推理阶段的关键动作是:拿到 logits,转成概率,采样一个 token,拼回上下文。

9.1 只取最后一个位置的 logits

因为我们在生成下一个 token:

  • 输入是当前上下文

  • 输出只关心最后一步的预测

9.2 softmax + sampling

  • p = softmax(logits_last)

  • next_id = random.choices(range(vocab_size), weights=p)[0]

这一步决定生成风格:

  • 直接 argmax 会变得更确定、更重复

  • 采样会更发散、更像自然语言

9.3 自回归循环

next_id append 回输入序列,继续跑下一步,直到生成长度够了或生成到终止符(字符级数据集通常没有 EOS,会用长度控制)。

这就是 GPT 的"自回归本质":模型每次只预测一步,文本是一步一步长出来的。


10)你那句"普实无华搞定 GPT"如何改成更硬的表述

你这句本身很有传播力,适合社媒。技术文章里可以写得更锋利一点:

  • 这份代码用标准库实现了 GPT 的训练与推理闭环,完整覆盖 tokenizer、Transformer 前向、交叉熵损失、手写反向传播与采样生成。

  • 代码规模很小,强调可读性与概念完整性,计算效率与工业可扩展性不在目标里。

  • 它把 GPT 从"神秘系统"还原成"可推导、可运行、可修改"的算法骨架,读完之后看任何框架版实现都能迅速对齐结构。


11)读完之后你该怎么看这份代码

如果你打算把它变成"自己的能力",建议用三次阅读把它吃透:

第一次只看数据与生成流程:从 docsstoi/itossample(),建立闭环直觉。

第二次只看 attention:找到 q/k/v、mask、softmax、加权求和,画一张数据流图。

第三次只看 backward:把每个矩阵乘的梯度写到纸上,和代码逐行对齐。

你做完第三次,你就拥有了"拆任何 Transformer 实现"的通用技能。

相关推荐
向量引擎17 小时前
别再问GPT-5.3怎么还没记忆了!你的AI还没装“海马体”?揭秘让OpenClaw起飞的幕后大佬:高性能向量引擎实战指南(附Clawdbot保姆级配置)
人工智能·gpt·aigc·agi·api调用
老金带你玩AI1 天前
DeepSeek V4春节炸场,三大黑科技让Claude和GPT都坐不住了
人工智能·科技·gpt
程序员佳佳1 天前
炸裂!为了流畅调用 GPT-5.3 和 Sora2,我用“向量引擎”重构了核心服务,CTO 直呼内行(附 OpenClaw 保姆级配置)
gpt·重构
程序员佳佳1 天前
别再被GPT-5.3和Sora2吊打了!这篇OpenClaw+向量引擎实战,教你徒手捏个超级中转站(内附硬核配置)
人工智能·gpt·aigc·api·ai编程
骇城迷影2 天前
从零构建 GPT 分词器
linux·服务器·pytorch·gpt·深度学习·神经网络
向量引擎2 天前
向量引擎OpenClaw配置实战:让GPT-5.2跑得比隔壁老王的特斯拉还快
gpt·aigc·api·ai编程·ai写作·key·agi
烁烁闪闪烁烁2 天前
Claude Code 完全入门指南
大数据·开发语言·人工智能·gpt·ai编程·claude·cursor
松涛和鸣3 天前
73、IMX6ULL Linux按键驱动实战:从GPIO轮询到设备树中断+等待队列
linux·服务器·数据库·驱动开发·单片机·gpt
赛博鲁迅3 天前
面对 GPT-5.3 与 Claude 4.6:OpenClaw 异步 Agent 框架的工程化演进
人工智能·gpt·aigc·ai编程·ai-native