3.5 预训练目标与训练流程小结:从数据到损失到 checkpoint

4 阅读6分钟

基于《大规模语言模型:从理论到实践(第2版)》第3–4章串讲

爆款小标题:原书第3–4章串讲:数据管线、损失函数与分布式训练如何配合


为什么这一节重要

前面几节分别讲了预训练数据从哪来、分布式怎么并、指令微调与 RLHF 怎么做。但要真正「把预训练跑起来」,还需要把数据管线(从原始语料到模型能吃的 input_ids)、损失函数(下一 token 预测与交叉熵)和训练循环(前向—反向—梯度同步—优化器更新—checkpoint)串成一条清晰的线。本节基于原书第 2 章语言模型目标、第 3 章数据与第 4 章分布式实践,做一次串讲,让你能说出「从原始 txt 到一次优化器更新」经过哪些步骤,以及预训练损失应如何正确计算与监控。


学习目标

学完本节,你将能够:

  • 说清预训练目标:写出自回归语言模型的训练目标(下一 token 预测、交叉熵损失),并说明与「上下文长度」「batch size」「梯度累积」的关系。
  • 描述数据管线:从「原始语料」到「可训练的 batch」列出至少 3 个必经步骤(如清洗、分片、tokenize、打包),并说明每步目的;理解多卡时数据如何分片与不重复。
  • 理解训练循环与 checkpoint:说明单步训练包含前向、损失、反向、梯度同步(分布式)、优化器更新;checkpoint 通常需保存模型、优化器状态与步数等(原书第 4 章 DeepSpeed 实践)。

一、预训练目标:下一 token 预测与交叉熵(原书第 2–3 章)

语言模型目标:对序列 (x_1, x_2, \ldots, x_T),自回归语言模型建模条件概率 (P(x_t \mid x_1, \ldots, x_{t-1}))。训练时最大化序列的对数似然,等价于最小化交叉熵

[ \mathcal{L} = -\frac{1}{T} \sum_{t=1}^{T} \log P(x_t \mid x_1, \ldots, x_{t-1}) ]

即对序列中每个位置(或每个非 padding 位置)的下一 token 预测算交叉熵,再求平均。通常按 token 平均(或按序列平均),这样长序列与短序列在 loss 中的权重可调(按 token 平均时长序列自然权重大)。原书第 2 章与第 3 章对目标与数据有对应说明。

与上下文长度、batch 的关系:上下文长度决定了每个样本的 token 数;batch size 与梯度累积步数共同决定有效 batch size(即一次优化器更新看到的 token 数 = batch_size × seq_len × grad_accum_steps)。预训练时常用较大有效 batch(如数百万 token per step),通过梯度累积与多卡数据并行实现。

注意:损失应对所有非 padding 位置计算,而不是只对「最后一个 token」;否则长程依赖得不到充分训练。实现时需用 mask 把 padding 位置从 loss 中排除。


二、数据管线:从原始文本到 batch(原书第 3–4 章)

步骤 1:清洗与去重(见 3.1 节)

原始语料经过清洗(格式、质量、安全)与去重(文档级或近似去重),得到「干净文本」流或文件。

步骤 2:分片与 tokenize

  • 分片:把长文档切成固定长度(如 2048、4096)的片段,或按段落/句子边界切再拼接至约定长度;可重叠可不重叠,依实现而定。
  • Tokenize:用词表与 tokenizer(如 BPE、SentencePiece)把文本转为 input_ids(整数序列)。注意要使用与模型一致的 tokenizer 与词表。

步骤 3:打包成 batch

  • 将多条序列组成 batch;若长度不一,可 padding 到同一长度packing(把多条短序列拼成一条长序列,用边界标记分隔)。Padding 时需在后续 loss 计算中 mask 掉 padding 位置。
  • 分布式:数据并行时,每个进程应看到不同的数据分片(通过 DistributedSampler 或等价机制),保证同一 batch 内不重复、不同 step 间覆盖全量数据。

原书第 3–4 章对数据加载与多卡数据分片有说明;DeepSpeed 等框架通常与 PyTorch DataLoader 和 Sampler 配合使用。


三、训练循环与 checkpoint(原书第 4 章)

单步流程

  1. 前向:输入 input_ids(及可选的 attention_mask、position_ids),模型输出 logits。
  2. 损失:对 logits 与 target(通常为 input_ids 右移一位)算交叉熵,并对非 padding 位置平均。
  3. 反向:loss.backward(),得到梯度。
  4. 梯度同步:若为数据并行,AllReduce 或 ReduceScatter 聚合各卡梯度。
  5. 优化器更新:optimizer.step(),更新参数;optimizer.zero_grad() 准备下一步。

Checkpoint:为便于恢复与评估,checkpoint 通常保存:模型权重(或分片)、优化器状态(若用 ZeRO 则可能分片)、当前步数/epoch、以及可选的 RNG 状态。原书第 4 章 DeepSpeed 实践部分给出了保存与加载的示例。


四、工程实战要点

1. 预训练时有效 batch 与显存

有效 batch = batch_size × seq_len × grad_accum_steps × n_gpus(数据并行时)。先小规模跑通(如 2K 长度、小 batch),再逐步放大;显存不足时增大梯度累积、减小单卡 batch 或启用 ZeRO。有效 batch 过小可能导致训练不稳定或收敛慢,过大则需相应调大学习率或 warmup 步数;业界常用数百万 token/step 量级,具体依模型规模与数据量而定。

2. 数据与脚本版本化

数据版本、tokenizer 版本与训练脚本应一起记录,便于复现与排查「换数据后效果变差」等问题。建议在 checkpoint 或实验记录中保存:数据路径与版本、tokenizer 配置、超参数、以及训练命令,便于后续对比与回溯。

3. loss 监控与异常检测

训练过程中应监控 loss 曲线:若 loss 不下降、震荡剧烈或突然上升,可能是学习率过大、数据异常或梯度爆炸。可配合 gradient norm 监控,异常时及早停止并排查。分布式训练时,各卡的 loss 应基本一致(因数据不同会有小幅差异),若某卡 loss 明显偏离,可能是数据分片或通信问题。


五、常见误区与避坑指南

误区一:预训练损失只盯「最后一个 token」

应对序列所有非 padding 位置算损失。避坑:实现时用 attention_mask 或 labels_mask 排除 padding,保证 loss 正确。

误区二:数据并行时每卡数据一样

每卡应看到不同分片。避坑:使用正确的 DistributedSampler 或等价机制,并确认数据不重复、不遗漏。


六、小结与衔接

本节串讲了预训练目标(下一 token 交叉熵)、数据管线(清洗—分片—tokenize—打包)与训练循环(前向—损失—反向—同步—更新—checkpoint),便于把原书第 3–4 章与前面 3.1–3.4 连成完整训练视角。下一模块将进入RAG 与检索:如何用外部检索增强生成、减少幻觉并注入领域与时效知识(原书第 9 章)。


课后思考题

  1. 若上下文长度为 2048、batch size 为 32,梯度累积 4 步,则一次优化器更新对应的「有效 token 数」是多少?
  2. 从「原始 txt 语料」到「模型能吃的 input_ids」,请列出你认为必须的 3 个步骤,并简要说明每步的目的。