第 31 章:从零训练一个小型中文 GPT
本章问题:一个完整的大语言模型——从分词到生成——从头到尾各需要什么?
31.1 你将要造的东西
前 30 章,你走完了从图灵到 ChatGPT 的全部道路。但你可能还有一个合理的感觉——"知道每一块砖是什么,但还没亲手砌过一堵墙。"
这一章就是你的那堵墙。
你将用纯 PyTorch(不需要 HuggingFace、不需要预训练权重、不需要 API key)从零训练一个小型中文 GPT。它只有约 1000 万参数——相比之下 GPT-3 有 1750 亿参数——但它包含一个完整大语言模型的所有核心组件:
训练完成后,它可以用你提供的任何中文文本作为训练数据,学会续写中文——从随机乱码开始,逐步"学会"词、短语、句子结构的统计规律。
这一章比较长,但每一段代码都有用。读完你就是真的造过一个 GPT 了。
31.2 第一步:字符级分词器
大语言模型的第一步是把文本变成模型能理解的数字。对于一个尽可能简单的实验——不需要 BPE、SentencePiece 或 WordPiece。就按字符切:
这个分词器做两件事:
encode("人工智能")→ 一个整数列表,每个整数对应一个字符的 ID。decode([42, 108, 7, 256])→ 原来的文本。
对于一个典型的中文文本,词汇量通常在 3000-6000 个字符之间(覆盖常见汉字、标点和数字)。不需要合并、不需要子词——这在小型实验上完全够用。
31.3 第二步:加载训练数据
你可以用任何中文文本——一本你喜欢的小说(四大名著都是公版)、维基百科中文语料的一段、或者你自己的文章。对于这个实验,10-50 万字符的纯文本就足以看到模型的进化。
如果你没有现成的文本文件——把本章的 markdown 源文件直接拿去当训练数据。让一个 GPT 去学习一本"关于如何训练 GPT"的书——很 meta。
31.4 第三步:构造训练样本
语言建模的训练样本是序列的"前 N 个词→第 N+1 个词"。对于字符级模型——每个训练样本是一段固定长度的字符序列。输入是前 block_size 个字符,标签是后移一位的同一个序列("下一个字符"):
如果 block_size = 64,数据是"人工智能正在以前所未有的速度改变人类社会":
- x = "人工智能正在以前所未有的速度改变人类社"
- y = "工智能正在以前所未有的速度改变人类社会"
模型在每个位置上预测下一个字符。第17章的基本公式在这里完全一样——只不过现在是几千个字符的词汇表,而不是之前的 3 类鸢尾花。
31.5 第四步:搭建 GPT 模型
以下是 MiniGPT 的核心架构——一个 Decoder-only Transformer。每个组件你都在卷三各章中见过,这里把它们拼成一个完整模型:
架构参数解读:
总参数量 ≈ vocab_size × d_model(词嵌入)+ block_size × d_model(位置嵌入)+ 6 × (4 × d_model²)(Transformer 块)+ d_model × vocab_size(输出头)。在 vocab_size 约 4000、d_model=256 时,总计约 1000 万参数——完全可以在普通笔记本电脑的 CPU 上训练。
31.6 第五步:训练循环
困惑度(perplexity, ppl)是 NLP 中常用的指标:它是交叉熵损失的指数。直觉上——如果 ppl = 50,意味着模型在每个字符位置平均有 50 个"差不多合理"的选择。训练过程中 ppl 从几百降到几十——模型越来越"确定"下一个字符应该是什么。
31.7 第六步:运行
把所有拼在一起:
找一个中文 .txt 文件,把路径填入 corpus.txt,运行这个脚本。
CPU 上的预期训练时间:对于 10 万字符的语料、1000 万参数的模型、10 个 epoch——在普通 MacBook 上大约 10-30 分钟。你可能想先减少 epoch 数(比如 3-5)跑一次看效果,然后再加。
31.8 观察进化
训练过程中连续抽样的生成文本是最有信息量的反馈。你会看到类似以下的进化轨迹:
模型从随机乱码→出现高频字符→出现常见词组→出现部分语法结构→出现带引号的对话→出现合理的叙事模式。每一步的"智能感"都在增加——但它从头到尾都在做完全一样的事:预测下一个字符,最大化似然。
这就是 Scaling Law 在微观上的体现——更多的训练迭代(你在第 27 章读到的)让 loss 持续下降,每次下降都伴随着生成质量的质变。你没有改变架构——你只是让梯度多流了几轮。
31.9 本章实验扩展
这个 MiniGPT 骨架可以作为你未来实验的基础台:
- 换语料:训练在武侠小说上→生成武侠味;训练在政府公告上→生成公文味。模型学会的"风格"完全来自训练数据的统计模式。
- 加参数:把 d_model 从 256 升到 512,n_layers 从 6 升到 12——参数从 1000 万升到约 8500 万。你会明显看到更好的生成质量——但训练时间也会变长。
- 换分词器:把字符级分词器换成 BPE(用 HuggingFace tokenizers 库训练一个小 BPE tokenizer)——模型能学会更高层的"子词"语义单元。
- 加温度实验:generation 时分别用 temperature=0.2, 0.8, 1.5——观察"创造性"和"连贯性"的 tradeoff。
- 观察注意力:在每个 TransformerBlock 中抽出 attention 权重,画热图——你会看到模型在不同层、不同头上分别关注了位置、标点、和语义相关的字符。
31.10 本章地图
31.11 本章结语:你造的确实是一个 GPT
你现在造的这个东西——如果你把 d_model 从 256 调到 12288(GPT-3 的尺寸),n_layers 调到 96,把字符级分词器换成 5 万词 BPE tokenizer,在 3000 亿 token 上训练几个月——你就得到了 GPT-3。
架构完全一样。Tokenizer + Embedding + Transformer Blocks + LM Head + 交叉熵。差别只是规模和训练数据量的指数倍放大——这正是第 27 章 Scaling Law 告诉你的故事:更大模型、更多数据、更多算力带来可预测的能力涌现。
但你不需要 3000 亿 token 就能理解这个架构。1000 万参数 + 一本小说就够了。看 loss 从 6 降到 3、看生成从乱码变成语句、看 perplexity 从几百跌到几十——你对大模型的理解会从"理论上知道"变成"体感上确认"。
知道每一块砖是什么,和你亲手砌过一堵墙,是两种完全不同的认知。
下一章:微调——如何把一个已经训练好的大型模型适配到你的特定任务上。
Discussion
留言区 · GitHub-powered comments via Giscus