从文本到回答:一个中文 GPT 的训练流程

最近在学习 GPT_teacher-3.37M-cn 这个教学项目。它用约 337 万个参数实现了一个可以在普通电脑上训练、推理的中文 GPT。本文先不深入数学公式和模型内部细节,而是从全局视角整理:一份中文问答数据,经过哪些步骤,最终变成一个能够生成回答的模型。


目录

  1. 为什么从一个小 GPT 项目开始
  2. GPT 到底在学习什么
  3. 完整训练流程
  4. 模型训练完成后如何回答问题
  5. 项目代码与训练环节的对应关系
  6. 教学项目与真实大模型的区别
  7. 几个容易混淆的概念
  8. 总结

1. 为什么从一个小 GPT 项目开始

平时所说的大语言模型往往有几十亿甚至上千亿个参数,训练数据、计算资源和工程系统都非常庞大,很难由个人从头跑一遍。

GPT_teacher-3.37M-cn 把规模缩小到了教学级别:

  • 模型只有 4 层 Transformer,约 337 万个参数;
  • 上下文长度为 128 个 token;
  • 使用约 600 条中文问答数据;
  • 分词、训练、验证、推理和 Web 演示都包含在同一个项目中。

它和真正的通用大模型在能力上相差很远,但保留了 GPT 的核心工作方式。因此,学习它的价值不在于训练出一个“小号 ChatGPT”,而在于亲手跑通一个语言模型从数据到回答的完整闭环。


2. GPT 到底在学习什么

GPT 的核心任务可以概括成一句话:

根据前面已经出现的 token,预测下一个 token。

例如训练数据中有这样一组问答:

1
2
用户:什么是注意力机制?
助手:注意力机制通过计算相关性分配权重。

模型并不是一次性生成整句话,而是依次学习:

1
2
3
4
“助手:”后面应该出现什么?
“助手:注意”后面应该出现什么?
“助手:注意力机制”后面应该出现什么?
……

对每一个位置,模型都会给词表中的所有 token 计算一个分数,再把分数转换成概率。训练的过程,就是不断纠正这些预测,让正确 token 的概率越来越高。

一开始,模型参数是随机的,输出也接近随机。经过大量重复训练后,参数逐渐记录下数据中的语言规律和问答模式,模型才开始生成像样的内容。


3. 完整训练流程

整个项目可以先简化成下面这条主线:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
准备问答数据

训练并确定分词器

把文本转换成 token id

构造模型输入和正确答案

模型预测下一个 token

计算预测误差(Loss)

反向传播并更新模型参数

在验证集上检查效果并保存模型

3.1 准备训练数据

本项目使用 JSONL 格式保存问答数据,每一行是一组 promptcompletion

1
{"prompt": "什么是注意力机制?", "completion": "注意力机制通过计算相关性分配权重。"}

项目把它整理成统一的对话格式:

1
2
<bos>用户:什么是注意力机制?
助手:注意力机制通过计算相关性分配权重。<eos>

其中 <bos> 表示序列开始,<eos> 表示序列结束。统一格式很重要,因为模型不仅要学习答案内容,还要学习“问题结束后应该开始回答”这种结构。

3.2 用分词器把文字变成数字

神经网络不能直接处理字符串,它接收的是数字组成的张量。因此,文本要先经过分词器:

1
中文文本 → token → token id → 模型

分词器决定文本如何切分,以及每个 token 对应哪个整数。模型的输入词嵌入层和输出层都依赖分词器的词表大小,所以必须先确定分词器,再创建和训练模型。

分词器的结构、BPE 合并规则和词表绑定关系比较复杂,我单独整理在下一篇文章中:分词器(Tokenizer)通俗详解

3.3 构造输入和训练目标

假设分词后的序列是:

1
[A, B, C, D]

要训练“预测下一个 token”,输入和目标就要错开一位:

1
2
输入: [A, B, C]
目标: [B, C, D]

模型看到 A 时预测 B,看到 A、B 时预测 C,以此类推。

在这个问答项目中,模型会读取“用户问题 + 助手前缀 + 答案”,但问题部分不会参与 Loss 计算,真正被评分的是答案部分。这样可以把训练重点放在“如何根据问题生成回答”上。

3.4 模型进行前向计算

token id 首先经过词嵌入层,变成模型可以计算的向量,然后依次经过多层 Transformer,最后输出每个位置对整个词表的预测分数。

可以暂时把模型内部理解成:

1
2
3
4
5
6
7
token id

词嵌入:把编号转换成向量

Transformer:结合当前位置之前的内容处理信息

输出层:预测下一个 token

项目内部还使用了因果注意力、RoPE、RMSNorm、GQA 和 SwiGLU 等结构。这些技术分别解决位置、信息关联、训练稳定性和计算效率等问题,但不影响当前对主流程的理解:模型读取已有 token,输出下一个 token 的预测结果。

3.5 计算 Loss

模型的预测不一定正确,因此需要把预测结果和训练数据中的正确 token 进行比较。这个差距就是 Loss(损失)。

  • 正确 token 的预测概率越高,Loss 越小;
  • 正确 token 的预测概率越低,Loss 越大。

Loss 是训练过程中的“评分标准”。但它只是一个统计指标,Loss 下降表示模型对当前数据的预测在改善,不等于模型已经具备通用知识或推理能力。

3.6 反向传播和参数更新

得到 Loss 后,程序通过反向传播计算:模型中的每个参数应该向哪个方向调整,才能让预测误差减小。

优化器再根据这些信息更新参数。一次“预测 → 计算 Loss → 反向传播 → 更新参数”就是一个基本训练循环。这个过程会重复很多次:

1
2
3
4
5
6
7
预测错误

计算错误来自哪些参数

小幅调整参数

再次预测

因此,模型训练不是把问答文本原封不动写入某个文件,而是把数据中的规律逐步编码进大量数值参数中。

3.7 验证和保存模型

除了训练数据,项目还准备了验证数据。验证时只检查模型表现,不更新参数,用来观察模型对未直接参与当前训练的数据是否也有效。

项目会定期计算验证 Loss:

  • 验证 Loss 变小,说明模型还在改善;
  • 长时间不再改善,可能已经没有继续训练的必要;
  • 训练 Loss 持续下降、验证 Loss 反而上升,通常意味着模型开始过度记忆训练数据。

训练过程中会保存表现最好的模型权重为 best.pt。以后进行推理时,真正加载的是这个权重文件,而不是重新读取训练数据来现场查找答案。


4. 模型训练完成后如何回答问题

训练和推理使用的是同一种能力:预测下一个 token。区别是训练时有正确答案用来纠错,推理时没有正确答案,只能使用模型自己的预测继续生成。

1
2
3
4
5
6
7
8
9
输入用户问题

预测回答的第一个 token

把新 token 拼回已有内容

继续预测下一个 token

遇到 <eos> 或达到长度限制后停止

这叫做自回归生成。我们看到的是一次生成出的完整句子,但模型内部实际上是一个 token、一个 token 地往后写。

推理时还可以使用温度、Top-k、Top-p、重复惩罚等参数控制如何从候选 token 中选择。不过这些参数只能改变输出的稳定性和随机性,不能让模型凭空获得训练中没有学到的知识。


5. 项目代码与训练环节的对应关系

理解全局流程后,再看项目目录就会清晰很多:

文件 作用
data/*.jsonl 保存训练、验证和测试问答
src/build_tokenizer.py 根据语料训练 BPE 分词器
src/tokenizer.py 加载分词器,完成编码和解码
src/data.py 拼接问答、错位构造目标、补齐序列
src/model.py 定义 GPT 模型结构和前向计算
src/train.py 执行训练、验证、保存权重
src/infer.py 加载权重并逐 token 生成回答
src/evaluate.py 用测试问题验收模型效果
run.py 串联分词器、训练和 Web 演示

这些文件不是互相独立的功能,而是一条数据流水线上的不同环节。


6. 教学项目与真实大模型的区别

这个项目直接使用少量中文问答,从随机参数开始训练一个小型 GPT。它适合展示训练原理,但不能代表工业界训练通用大模型的全部过程。

真实的大语言模型通常会经历:

  1. 预训练:使用海量文本学习语言、知识和基本能力,核心任务仍然是预测下一个 token。
  2. 指令微调:使用高质量问答和任务数据,让模型学会按照人类指令回答。
  3. 对齐:进一步利用人类偏好或规则,让回答更有用、更安全、更符合预期。

而本项目更接近把“从头训练”和“问答格式训练”合在一起完成。由于参数和数据都很少,它主要能学习有限的问答模式,不具备真正通用大模型的知识覆盖和泛化能力。

二者的规模不同、阶段不同,但最底层的主线仍然一致:文本经过分词后进入 Transformer,模型预测下一个 token,再通过误差不断更新参数。


7. 几个容易混淆的概念

7.1 分词器不是 GPT 模型

分词器负责文本和 token id 之间的转换;GPT 模型负责根据 token 之间的关系进行预测。分词器有自己的训练过程,但它不是通过反向传播训练的神经网络。

7.2 模型文件不是训练数据的压缩包

best.pt 保存的是模型结构中各个参数训练后的数值,不是可以直接还原成原始问答的数据库。模型可能记住部分数据,但更准确的理解是:它通过参数拟合训练数据中的统计规律。

7.3 训练 Loss 低不代表模型什么都会

小模型可能把训练问题记得很好,却无法回答换一种说法的问题。判断模型效果不能只看训练 Loss,还要结合验证集、测试集以及未见过的问题。

7.4 参数量不是能力的唯一来源

更大的模型通常具有更强的表达能力,但最终效果还取决于数据的数量、质量、覆盖范围、训练方法和计算资源。对本项目而言,少量、高质量且有一定变化的问答数据,比单纯把某个配置调大更有意义。


8. 总结

把整个项目压缩成一句话:

先用分词器把文本转换成 token id,再让 GPT 根据已有 token 预测下一个 token;训练时通过正确答案计算 Loss、反向传播并更新参数,推理时则不断把预测结果接回输入,最终生成完整回答。

目前可以先记住下面这条主线:

1
数据 → 分词 → 构造样本 → 模型预测 → Loss → 反向传播 → 保存权重 → 推理生成

后续再分别深入分词器、注意力机制、Transformer、Loss 和推理策略时,每一个概念都能放回这条主线中理解,而不是变成彼此孤立的知识点。