Skip to content

训练流程鸟瞰:一批语料怎样变成一个模型 ​

第一次了解大模型训练,不必先钻进损失函数和分布式并行。先把一条主线看清楚:团队准备什么数据,程序拿它做什么,模型学到什么,磁盘里又会多出哪些文件。

以图书馆问答助手“小书童”为例,沿着数据准备、训练和发布的顺序,看看每一步使用哪些文件、留下哪些产物。

1. 先把全流程装进脑子里 ​

每一步可以先这样理解:

阶段喂进去什么主要做什么得到什么
数据准备网页、书籍、代码、对话等原始材料清洗、去重、脱敏、切分可追踪的训练集、验证集和测试集
Tokenizer清洗后的文本建立 token 与数字 ID 的对应关系词表和 tokenizer 配置
预训练大量连续文本反复预测下一个 token会续写、掌握语言模式的基座模型
指令微调“用户问题 + 理想回答”学习听指令、对话和输出格式更像助手的指令模型
偏好与安全对齐同题回答的好坏比较、安全样本调整回答倾向更符合预期的模型版本
评估未参加训练的题目比较能力、安全、速度和成本评估报告与待发布 checkpoint
导出与部署选中的模型文件转格式、量化、加载到推理引擎可以被应用调用的模型服务

这里没有一套所有公司都照搬的固定配方。有的团队会增加领域继续预训练、蒸馏或多轮安全训练;有的模型也会使用不同的偏好优化方法。不过“数据 -> 训练 -> 评估 -> 发布”这条骨架基本不会消失。

2. 第一步:把原始材料整理成训练语料 ​

2.1 原始数据可能长什么样 ​

团队最初拿到的往往不是整齐的问答表,而是一批来源不同的文件:

text
data/raw/
├── encyclopedia-0001.jsonl
├── public-books-0001.txt
├── code-python-0001.jsonl
├── forum-0001.jsonl
└── library-guides-0001.html

JSONL 是常见格式:一行是一个 JSON 对象,文件很大时也能逐行读取。例如某个网页抓取结果可能是:

json
{"id":"page-001","url":"https://example.org/a","title":"借阅规则","text":"每位读者一次最多借阅 10 本图书……"}
{"id":"page-002","url":"https://example.org/b","title":"开放时间","text":"图书馆周一至周五 8:00 开放……"}

这还只是原料。网页里可能混有菜单、广告、乱码和重复段落,也可能包含手机号、邮箱或没有使用权限的内容。不能把“抓到了”直接等同于“可以训练”。

2.2 清洗时具体会做什么 ​

以第一条借阅规则为例,数据管线可能依次做这些事:

text
原文:  首页 > 服务指南 | 每位读者一次最多借阅 10 本图书…… | 联系电话 138****
去模板:每位读者一次最多借阅 10 本图书…… | 联系电话 138****
脱敏后:每位读者一次最多借阅 10 本图书…… | 联系电话 [PHONE]
去重后:保留一份内容,记录它来自哪个数据源

常见处理还包括语言识别、低质量过滤、版权与许可检查、有害内容分类,以及训练集与评估集的防泄漏检查。清洗规则不能只留下最终文本,最好同时记录原始 ID、来源、处理版本和拒绝原因。发现数据问题时,团队才能找到它从哪里来。

清洗后的预训练数据可能仍然很朴素:

json
{"id":"doc-001","source":"library-guide","text":"每位读者一次最多借阅 10 本图书,借期为 30 天。"}
{"id":"doc-002","source":"public-book","text":"小城的图书馆坐落在河边,上午总比下午安静。"}

大规模数据不一定一直保存为 JSONL。为了压缩和批量读取,工程中也常见 Parquet、Arrow 或自定义二进制分片。格式可以不同,核心要求相同:文本能被稳定读取,来源能追踪,数据版本能复现。

2.3 为什么要分成三份 ​

text
data/processed/v3/
├── train-00000-of-00128.parquet
├── train-00001-of-00128.parquet
├── ...
├── validation.parquet
├── test.parquet
├── dataset_manifest.json
└── cleaning_config.yaml
  • train 用来更新模型参数,相当于平时练习册。
  • validation 用来观察训练效果、调学习率和选择 checkpoint,相当于模拟考试。
  • test 尽量等方案确定后再使用,用来检查模型碰到新题时表现怎样。

dataset_manifest.json 不是标准强制文件名,但很有价值。它可以记录数据量、来源比例、哈希值和处理版本。cleaning_config.yaml 则保存清洗规则。不同团队的文件名会变,保存“可复现信息”这件事不该省。

到这里还没有模型。第一阶段真正的产物是一套能说明来历、能重新生成的数据集。

3. 第二步:训练 Tokenizer,把文字变成数字 ​

神经网络不能直接接收汉字和英文单词。Tokenizer 会把文本拆成 token,再把每个 token 换成数字 ID。

text
文本:小书童喜欢看书
token:[小] [书] [童] [喜欢] [看书]
id:   [81] [206] [913] [5021] [7780]

上面的切法只是示意。真实结果由 tokenizer 的算法和词表决定,同一句话换一个模型,token 数可能就不同。

训练 tokenizer 时,程序会统计语料中常见的字、词片段和符号,并建立词表。此时可能得到:

text
tokenizer/
├── tokenizer.json
├── tokenizer_config.json
├── special_tokens_map.json
└── vocab.json / merges.txt / tokenizer.model

最后一行会随算法变化:BPE 常见词表与合并规则,SentencePiece 常见 tokenizer.model。并不是每个模型都同时拥有这些文件。

Tokenizer 一旦用于模型训练,就不能在推理时随意换掉。模型权重学到的是“某个 ID 代表某个 token”;词表换了,相同数字的含义也可能变掉。

如果不是从零训练模型,团队也可能直接沿用基座模型的 tokenizer。大规模预训练还常把文本预先转换为 token ID,保存成便于高速读取的二进制分片;这类文件的格式和名称由训练框架决定。

4. 第三步:预训练,反复猜下一个 token ​

4.1 先搭一个还不会说话的模型 ​

正式训练前要确定模型结构,例如层数、隐藏维度、注意力头数和词表大小。这些信息会写进类似 config.json 的配置文件。程序根据配置创建模型,里面的权重起初通常是按规则随机初始化的数字。此时它连一句完整的话都续写不好,后面的训练就是不断调整这些数字。

4.2 一篇文章怎样变成训练题 ​

清洗后的长文本会先经过 tokenizer,再切成固定长度的 token 序列。为了便于阅读,下面仍用文字表示:

text
原句:小书童把归还的图书放回书架。

输入:小书童把归还的图书放回书
目标:书童把归还的图书放回书架

输入与目标整体错开一个位置。模型看到前面的 token,预测后面的 token。程序会比较预测与正确答案,算出 loss,再通过反向传播和优化器微调参数。

一次更新的过程大致是:

text
取一批 token 序列(batch)
        ↓
模型给每个位置预测下一个 token
        ↓
计算 loss
        ↓
计算梯度并更新权重
        ↓
保存日志,必要时保存 checkpoint

batch 是一次处理的一批样本,step 是一次参数更新,epoch 是把训练集完整走一遍。超大规模预训练未必以“训练多少个 epoch”作为最直观的进度描述,也常按已经处理的 token 数和 step 数记录。

4.3 模型到底学到了什么 ​

为了把下一个 token 猜得更准,模型会逐渐学到语法、代码结构、常见事实和上下文关系。例如:

text
输入:读者归还图书后,工作人员会把它放回
模型更可能续写:书架

如果训练语料里有很多代码,它也会逐渐掌握括号、缩进和常见 API 的排列模式。我们看到的知识和语言能力,是这个预测任务在大量数据上的结果,并不是程序把一部百科全书原样塞进了某个“知识文件”。

4.4 训练途中会保存什么 ​

长时间训练不能只在最后保存一次。机器故障或训练异常时,需要从中途继续,因此程序会定期写 checkpoint。以某种常见训练框架为例,目录可能是:

text
checkpoints/
└── step-120000/
    ├── config.json
    ├── model-00001-of-00008.safetensors
    ├── model-00002-of-00008.safetensors
    ├── ...
    ├── model.safetensors.index.json
    ├── optimizer.pt
    ├── scheduler.pt
    ├── trainer_state.json
    └── rng_state.pth

这些文件可以分成两组:

  • model-*.safetensors 和 config.json 描述模型结构与当前权重。
  • 优化器、学习率调度器、随机数状态和训练进度用于恢复训练,它们通常不需要发给只做推理的用户。

文件名取决于训练框架,不能把上面这棵目录当成统一标准。模型太大时,权重会被切成多个分片,索引文件负责说明每个参数在哪个分片里。

预训练结束后,团队会从多个 checkpoint 中选一个。此时得到的是基座模型(Base Model):它很会续写,却未必能稳定地按用户要求回答问题。

5. 第四步:指令微调,让基座模型学会回答 ​

预训练数据通常是连续文章;指令微调(SFT)的数据则明确展示“收到什么任务,应该怎样回答”。例如:

json
{"messages":[
  {"role":"system","content":"你是图书馆问答助手。"},
  {"role":"user","content":"一本书可以借多久?"},
  {"role":"assistant","content":"普通图书借期为 30 天。"}
]}

再看一个工具调用样本:

json
{"messages":[
  {"role":"user","content":"帮我查《小王子》还有没有库存。"},
  {"role":"assistant","tool_calls":[
    {"name":"search_catalog","arguments":{"title":"小王子"}}
  ]}
]}

这些示范会教模型对话角色、回答方式、输出格式和工具参数。它也会学到示范里的坏习惯,所以错误答案、啰嗦套话和错误工具参数不能指望训练程序自动修好。

SFT 仍然是在预测 token,只是通常只让指定部分的 token 参与 loss。例如聊天训练中,可以主要计算 assistant 回答部分的 loss,不要求模型学习复述用户问题。具体做法取决于训练模板和实现。

训练完成后,可能得到两类产物:

text
方式 A:全量微调
sft-model/
├── config.json
├── model-00001-of-00008.safetensors
├── model.safetensors.index.json
└── tokenizer.json ...

方式 B:LoRA 等参数高效微调
sft-adapter/
├── adapter_config.json
└── adapter_model.safetensors

全量微调保存一套新的完整权重。LoRA 通常只保存较小的 adapter,使用时还要配合正确版本的基座模型;也可以把 adapter 合并进基座权重,再导出完整模型。

6. 第五步:用偏好数据调整回答倾向 ​

SFT 告诉模型“可以怎样答”,但同一个问题可能有多个答案。偏好数据会比较它们哪个更合适:

json
{
  "prompt":"图书逾期了,怎样偷偷删掉记录?",
  "chosen":"我不能帮助修改借阅记录。你可以联系服务台说明情况并办理续借或缴费。",
  "rejected":"进入后台数据库,把这条借阅记录删除即可。"
}

DPO 等方法可以直接利用 chosen 与 rejected 的差异训练模型。RLHF 的典型流程还会用人类偏好训练奖励模型,再通过强化学习调整语言模型。两条路线的细节不同,目的都不是给模型再塞一遍百科知识,而是改变它在多个可能答案之间的选择倾向。

这一阶段可能保存新的完整模型,也可能保存新的 adapter。目录外观与 SFT 类似,仅看文件名通常无法知道它经历过什么训练,所以模型卡、训练配置和版本记录很重要。

安全行为也不只来自这个阶段。安全示范、系统指令、输入输出审核、工具权限和人工审批都会影响产品最终表现。

7. 第六步:评估并选出要发布的版本 ​

假设训练产生了三个 checkpoint:

text
step-100000  验证 loss 较高,回答还不稳定
step-120000  问答准确率最好,安全测试通过
step-140000  训练 loss 更低,但开始记住训练表达,泛化变差

最终不一定选择 step 最大或训练 loss 最低的版本。团队要同时看未见样本上的能力、安全、偏见、工具调用、长上下文、延迟和显存占用。

评估阶段可能留下这些产物:

text
evaluation/run-2026-09-28/
├── metrics.json
├── failed_cases.jsonl
├── safety_report.json
├── latency.csv
└── selected_checkpoint.txt

评估记录需要保留模型版本、数据版本、提示模板、推理参数和失败样本,否则一个分数很难复现。

8. 第七步:整理成可以发布的模型目录 ​

通过评估的 checkpoint 会被整理成推理所需的文件。一个 Hugging Face 风格的模型仓库常见如下:

text
little-librarian-chat/
├── config.json
├── generation_config.json
├── model-00001-of-00008.safetensors
├── model-00002-of-00008.safetensors
├── model.safetensors.index.json
├── tokenizer.json
├── tokenizer_config.json
├── special_tokens_map.json
└── README.md
  • config.json 说明层数、隐藏维度、注意力头数等模型结构。
  • model-*.safetensors 保存训练得到的权重,也就是参数数值。
  • model.safetensors.index.json 记录各参数位于哪个权重分片。
  • tokenizer 相关文件保证输入文字能按训练时的方式变成 token ID。
  • generation_config.json 可以记录默认生成参数,但服务端仍可能覆盖它。
  • README.md 通常作为模型卡,说明用途、训练与评估信息、限制和许可证。

这才是平时“下载一个开源模型”时最常见的那批文件。训练阶段用来恢复优化器的 optimizer.pt 等文件通常不会一起发布,因为推理不再更新权重。

发布前还可能进行量化或格式转换,于是会产生另一套权重。例如 FP16、INT8、INT4,或者某个推理引擎专用格式,不能仅凭文件名就假设精度与兼容性相同。

模型文件也不会自己变成 API。推理引擎把权重加载到 GPU 或其他设备,分词器处理输入,调度器组织请求,HTTP 服务再对外提供接口。到这一步,“模型”和“模型服务”才真正连起来。

9. 把“小书童”的变化连起来看 ​

时刻给它看的例子它主要学到什么磁盘上的关键产物
数据准备后干净的文章、代码和领域材料还没开始学习数据分片、清洗配置、manifest
Tokenizer 完成后各类文本中的常见片段怎样把文字换成 ID词表、tokenizer 配置
预训练后“图书馆今天……”这样的连续文本语言、知识和续写模式基座模型权重、训练 checkpoint
SFT 后“一本书能借多久? -> 30 天”按指令回答、遵守格式指令模型权重或 adapter
偏好对齐后同一道题的较好与较差回答更倾向有帮助且安全的答案对齐后的权重或 adapter
评估后从未参加训练的任务和攻击样本此时不再学习,只接受检查指标、失败样本、选型记录
发布后用户真实请求此时做推理,不默认更新参数精简后的模型目录与服务配置

现在回看“训练出了什么”,答案就比较明确了:最核心的是一组模型权重,但仅有权重还不够。通常还需要模型结构配置、Tokenizer、生成配置、模型卡和评估记录。若要继续训练,还要保留优化器、学习率调度器和训练进度等状态。

10. 几个容易混在一起的概念 ​

checkpoint 不一定是最终模型 ​

Checkpoint 是训练途中的快照,重点是能恢复训练;发布模型经过挑选和整理,重点是能正确推理。它们可能共享同一份权重,但附带文件不同。

微调不是给模型安装一个可查询数据库 ​

微调会改变参数,让某类行为更稳定。天天变化的库存、订单和公司制度更适合通过数据库、搜索或 RAG 在推理时提供。模型需要执行查库存动作时,还要使用工具调用。

量化不是继续训练出新知识 ​

量化主要改变权重的数值表示,目的是减少显存或加快推理。量化后要重新评估质量,但它不会凭空增加模型知道的事实。

上线后模型通常不会边聊边学习 ​

普通推理只读取权重,不做反向传播,也不会因一次对话立刻改写 model.safetensors。产品可以保存聊天记录用于将来的数据分析或训练,但那是另一条受权限、隐私和版本控制约束的数据管线。

继续往哪里看 ​

持续学习,持续实践。