---
tags: [SFT, FSDP, 大模型训练]
doc_type: knowledge-note
abstract: "以Wordle理解监督微调、标签掩码、TorchTitan、分片训练与评测闭环。"
creation: 2026-10-15
返回目录: "[[计算机系/昇腾与CANN/大模型与算子专题学习导航|大模型与算子专题学习导航]]"
下一篇: "[[计算机系/昇腾与CANN/Wordle强化学习与GRPO|Wordle强化学习与GRPO]]"
前置知识: "[[计算机系/昇腾与CANN/大语言模型与推理系统|大语言模型与推理系统]]"
后续学习: ["[[计算机系/昇腾与CANN/Wordle强化学习与GRPO|Wordle强化学习与GRPO]]"]
---

# SFT 监督微调与 FSDP 训练

前置：[模型结构](大语言模型与推理系统.md) · 后续：[Wordle 强化学习与 GRPO](Wordle强化学习与GRPO.md)

## 1. 训练到底改变什么

训练通过数据或环境反馈计算学习信号，再沿梯度更新参数，改变模型的输出分布。

```text
取 batch → forward → loss → backward → 梯度累计/裁剪 → optimizer.step → 评测
```

Forward 产生 logits 和中间激活，Backward 用链式法则将梯度写入参数的 `.grad`。**真正修改参数的是 `optimizer.step()`**。梯度还需按训练流程清零，避免意外累计。

输入 `input_ids` 常为 `[B,S]`，输出 logits 常为 `[B,S,V]`。$B$ 是 batch，$S$ 是序列长度，$V$ 是词表大小。多头注意力张量可用 `[B,H,S,D]` 理解，但 Q 和 KV 的头数不必相同。

| 阶段 | 数据与信号 | 主要目标 |
| --- | --- | --- |
| 预训练 | 大量文本中的下一 token 目标 | 学习语言和知识模式 |
| SFT | 高质量示范与监督标签 | 指令、格式、风格和任务行为 |
| RL | 采样轨迹与环境奖励 | 改善结果、策略与多步决策 |
| 推理评测 | 固定参数与测试任务 | 检查能力与性能 |

有可靠示范时，SFT 往往是明确的起点；过程难标注而结果能打分时，可以考虑 RL。RL 需要探索和奖励设计，不会自动优于 SFT。

## 2. Wordle：先学会合法行动

目标是在最多 6 次猜测中找出隐藏的五字母单词，输出协议为：

```xml
<guess>CRANE</guess>
```

| 反馈 | 含义 |
| --- | --- |
| G | 字母和位置正确 |
| Y | 字母存在，但位置不对 |
| X | 当前字母实例没有匹配到剩余目标字母 |

处理重复字母时，灰色反馈不能一律解释为“目标中完全没有这个字母”，需要考虑已经匹配的次数。根据历史反馈排除候选是策略目标；是否强制每次猜测遵守全部已知约束，取决于环境规则。

以目标 `SMILE` 为例，`CRANE` 可以提供排除信息并锁定结尾 E，`SLATE` 进一步确定 S/E 并提示 L 的位置，最后猜 `SMILE`。

SFT 学习合法格式与多轮回复模式，RL 再优化选择什么词、如何利用反馈、怎样减少猜测轮次。格式正确只是参与任务的门槛，不等于高胜率。

## 3. 对话数据到监督标签

![SFT数据处理](.assets/大模型与算子专题/SFT数据处理.png)

处理链路为 Chat Template、Tokenize、Label Mask、Batch。角色边界和特殊 token 必须与基座模型匹配。

| 角色 | 输入是否保留 | 标签处理 |
| --- | --- | --- |
| system / user | 保留，作为条件上下文 | 常设为 `-100`，不直接计入交叉熵 |
| assistant | 保留 | 对目标 token 计算监督损失 |
| padding | 按 batch 补齐 | 通常不计入损失，并配合 attention mask |

`-100` 是常用交叉熵忽略索引。**忽略损失不等于删除输入，也不等于屏蔽注意力。**模型仍根据用户消息生成回答。

设 $\mathcal A$ 是参与监督的目标位置：

$$
\mathcal L_{\mathrm{SFT}}=-\frac1{|\mathcal A|}\sum_{t\in\mathcal A}\log p_\theta(x_t\mid x_{<t})
$$

实现时要核对下一 token 的错位对齐，避免将同一位置输入误当成要预测的目标。标签 shift 可能由模型内部完成，也可能由数据处理完成，不能重复移动。

示例数据中的 `prompt` 保存多轮训练轨迹，`completion` 用于评测答案；这属于该数据管道的约定，并非所有 SFT 框架都这么定义。Packed SFT 将多个样本拼接时，还需要隔离样本间注意力边界，不能只拼 token 并设置 loss mask。

## 4. TorchTitan 与 NPU 适配

TorchTitan 管理模型定义、分布式策略、数据处理、优化器、混合精度、checkpoint 与性能日志。TorchTitan-NPU 通过适配层接入 NPU 算子、编译和并行增强。

`model_registry` 关联模型构造、权重加载、分片方法和 loss。启动时的 `MODULE` 决定加载哪个模型模块，`CONFIG` 选择训练配置。

SFT 示例环境为 CANN `9.0.0`、PyTorch `2.12.0`、`torch_npu 2.12.0rc1`。它与 RL 示例的 PyTorch `2.9.0` 是两套版本组合，应分别核对，不要直接混装。

仓库准备流程：

```bash
git clone https://gitcode.com/cann/torchtitan-npu.git
cd torchtitan-npu
pip install -r requirements.txt
pip install -e .
```

调试训练：

```bash
NGPU=2 CONFIG=sft_qwen3_1_7b_wordle_debug \
bash scripts/run_train.sh
```

Debug 模式先验证数据、前向、反向、参数更新与 checkpoint 链路。完成日志说明流程走通，不代表质量已经达标。设备状态与软件包版本应分别检查，`npu-smi info` 不能代替所有组件的版本核对。

## 5. 为什么需要 FSDP

训练显存除权重外，还包括梯度、优化器状态、激活、通信缓冲和临时内存。

对于 1.7B 参数，BF16 权重本体约 3.4 GB；若梯度同为 BF16，再约 3.4 GB。Adam 的两个 FP32 状态约 13.6 GB，若还保留一份 FP32 主权重，再增加约 6.8 GB。是否保留主权重、梯度采用什么类型，取决于实现。

这些是十进制 GB 估算，不能直接标成 GiB。激活大小还与 batch、序列长度和重计算策略有关，因此模型文件能放进设备并不代表训练状态也能放下。

### 分片与通信

![FSDP通信](.assets/大模型与算子专题/FSDP通信.png)

FSDP 将参数、梯度和优化器状态分片保存。计算某层时，通过 AllGather 临时取得所需参数，计算后按策略释放或重新分片；反向阶段可再次收集参数，并用 ReduceScatter 聚合、分片梯度，各卡更新自己负责的状态。

这不是把一层的矩阵计算简单切成 TP。FSDP 的主要目的之一是减少每卡常驻训练状态，仍需为临时完整参数、通信与激活留出空间。

若用粗略预算 $M_{state}$ 表示可分片状态、$M_{act}$ 表示未分片激活，$D$ 为分片数：

$$
M_{\mathrm{per\ device}}\approx M_{state}/D+M_{act}+M_{temporary}
$$

所以两卡 FSDP 不会让所有显存占用都直接减半。Activation Checkpointing 通过反向重算部分前向结果减少激活存储，是另一种“计算换显存”的策略。

## 6. 基线配置与梯度累计

```python
training = TrainingConfig(
    local_batch_size=2,
    global_batch_size=64,
    seq_len=1024,
    max_norm=1.0,
    steps=20,
)

optimizer.lr = 1e-5
min_lr_factor = 1.0
```

这是短基线配置：长度上限 1024、梯度范数上限 1.0、20 次优化步骤、恒定学习率。20 步可以用来检查格式学习，但不能保证任意数据都收敛，也不能推出短训练永远不需要 warmup。

若 `local_batch_size` 是每卡每 microstep 的样本数、global batch 是全体数据并行副本每次更新的总量：

$$
\text{累计步数}=\frac{\text{global batch}}{\text{local batch}\times\text{数据并行度}}
$$

两卡数据并行下，$64/(2\times2)=16$。直接计算 $64/2=32$ 只在数据并行度为 1 或该配置另有 batch 语义时成立。TP/PP 的卡数不能不加区分地乘入数据并行度。

示例将 TP、PP、CP、EP 设为 1，启用自动 FSDP 分片：

```python
data_parallel_shard_degree = -1
tensor_parallel_degree = 1
pipeline_parallel_degree = 1
context_parallel_degree = 1
expert_parallel_degree = 1
```

`-1` 在此配置中表示按设备数自动选择。并行维度应根据模型和负载选取，不是开启越多越好。

## 7. 数据、训练与导出

基座权重使用 Qwen3-1.7B，示例路径为 `assets/hf/Qwen3-1.7B`；Wordle 数据来自 `willcb/V3-wordle`，需转换为管道接受的 parquet 格式。

在匹配版本的项目根目录运行：

```bash
NGPU=2 \
MODULE=torchtitan_npu.models.qwen3 \
CONFIG=sft_qwen3_1_7b_wordle \
bash scripts/run_train.sh \
  --checkpoint.last_save_in_hf \
  --dataloader.dataset_path ./assets/data/wordle
```

关注 loss、grad_norm、吞吐和 checkpoint。一次示例中 loss 从 1.25 降至 0.49、梯度范数从 140.5 降到约 3、吞吐约 400 token/s，这些是观察样例，不是所有机器和数据的验收阈值。

导出后应能重新加载 HF 权重，tokenizer 和模板也要齐全。文件存在不能证明模型可用于后续推理。

## 8. 评测闭环

通过兼容推理服务接入 `vf-eval` 多轮环境，分别观察格式、猜中率、部分正确和猜测效率。

![SFT评测对照](.assets/大模型与算子专题/SFT评测对照.png)

图中的格式指标从 0.2 到 1.0、综合奖励从 0.04 到 0.40，说明该实验的格式和交互改善。`format_reward` 的归一化值与奖励中的加权贡献可能不同，不能将格式指标 1.0 与加权格式奖励 0.2 混为一个量。

图中 `num_turns≈7` 与游戏最多 6 次猜测不在同一显然口径，可能涉及消息或环境轮次统计，需检查评测器定义，不能解释成允许猜 7 次。

公平比较 base 与 SFT 时，固定任务、测试集、种子、模板和生成参数。应检查 checkpoint 可 reload、指标有限且合理、训练与测试不泄漏，以及格式改善是否伴随真实任务改善。

## 9. 复习与实践

先跑 debug，再完成短基线，保存日志与权重，最后用同一评测配置比较 base/SFT。训练完成后可以解释：

- `backward()` 与 `optimizer.step()` 分别做什么。
- loss mask 与 attention mask 为什么不同。
- 为什么 FSDP 仍需临时完整参数与激活空间。
- 为什么多卡梯度累计需要考虑数据并行度。
- 为什么 loss 下降不能直接证明游戏策略更优。

参考：[TorchTitan-NPU](https://gitcode.com/cann/torchtitan-npu)、[SFT 训练流程](https://gitcode.com/cann/cann-learning-hub/tree/test/tutorials/sft_training_pipeline)。
