MiniMind SFT:流程、验证与实验结论
本页定位:在已验证 Pretrain 底座上完成 Full SFT,并记录真实产物、对照验证和失败边界。Pretrain 的环境与底座流程见 MiniMind Pretrain;本页不重复其数据读取和 CUDA 排障细节。
阅读顺序为:目标与前置 → 可复现步骤 → 成功判据 → 产物验证 → 常见失败 → 后续路线。
1. 训练目标与基础概念
目标:记录如何在 Windows + Anaconda + E 盘项目结构 下,把 MiniMind 的 Full SFT 训练成功跑起来,并补充 SFT 的核心概念、默认参数、日志怎么看、训练完成后如何验证权重是否真的被使用。 适合人群:LLM 小白,已经跑通过 Pretrain,准备继续做 SFT。
1.1 本次验证目标
这次 SFT 的目标,不是重新学语言本身,而是:
让已经做过 Pretrain 的模型,更像一个会按指令回答的助手。
如果 SFT 跑通了,通常会看到这些结果:
- 能成功启动
trainer\train_full_sft.py - 能正确读取
pretrain_512.pth - 能正确读取
sft_mini_512.jsonl或别的 SFT 数据 - 训练日志持续输出
step / loss / lr - 最终在
out里得到full_sft_512.pth - 可以用
python eval_llm.py --weight full_sft测试模型
简单理解:
Pretrain:让模型学会“会说话”SFT:让模型学会“按指令说话”
1.2 推荐目录结构
推荐并已验证比较清晰的目录结构如下:
E:\minimind
├─ MiniMind2
│ ├─ config.json
│ ├─ model.safetensors
│ ├─ tokenizer.json
│ ├─ tokenizer_config.json
│ ├─ special_tokens_map.json
│ └─ ...
├─ dataset
│ ├─ pretrain_hq.jsonl
│ ├─ sft_mini_512.jsonl
│ ├─ sft_512.jsonl
│ ├─ sft_1024.jsonl
│ ├─ sft_2048.jsonl
│ ├─ dpo.jsonl
│ └─ ...
├─ checkpoints
├─ out
├─ trainer
│ ├─ train_pretrain.py
│ ├─ train_full_sft.py
│ ├─ train_lora.py
│ ├─ train_dpo.py
│ └─ ...
├─ model
├─ scripts
├─ eval_llm.py
└─ ...
说明:
MiniMind2:基础模型和 tokenizer 目录dataset:训练数据目录out:保存最终权重checkpoints:保存中间断点和 resume 文件trainer:训练脚本目录
1.3 SFT 的作用
1. 最直白的理解
SFT 是 Supervised Fine-Tuning,中文通常叫:
监督微调
它做的事情不是让模型从零开始学语言,而是让模型学会:
- 遇到用户提问时,应该怎么回答
- 什么样的语气更像助手
- 什么样的输出结构更符合指令要求
- 如何更稳定地做翻译、总结、列步骤、问答
2. 为什么先 Pretrain,再 SFT
可以把流程理解成:
Pretrain:学“语言能力”SFT:学“指令跟随”LoRA:在已有模型上做更便宜的小专项微调DPO / GRPO:再往后做偏好优化或强化学习式优化
所以:
SFT 是建立在 Pretrain 权重之上的。
如果没有 Pretrain,SFT 通常不是理想的起点。
1.4 MiniMind 中的 SFT 脚本
MiniMind 里负责 Full SFT 的核心脚本是:
E:\minimind\trainer\train_full_sft.py
这次最重要的事实有两个:
- 默认使用的数据是
sft_mini_512.jsonl - 默认是在
pretrain权重基础上继续训练
按你当前这套环境整理出来的默认参数,大致是:
-save_dir ../out-save_weight full_sft-epochs 2-batch_size 16-learning_rate 1e-6-dtype bfloat16-save_interval 1000-hidden_size 512-num_hidden_layers 8-max_seq_len 340-data_path ../dataset/sft_mini_512.jsonl-from_weight pretrain-from_resume 0
这组默认值的含义是:
- 脚本默认产出的是
full_sft_512.pth - 默认数据是轻量版
sft_mini_512.jsonl - 默认不是从零训,而是接着
pretrain_512.pth继续训
1.5 开始前的检查
开始训练前,至少确认这 4 件事:
1. 你的 pretrain 权重已经存在
例如:
E:\minimind\out\pretrain_512.pth
这是 SFT 默认要加载的底座权重。
2. 模型目录和 tokenizer 已经放好
例如:
E:\minimind\MiniMind2
里面至少应该有:
config.jsonmodel.safetensorstokenizer.jsontokenizer_config.jsonspecial_tokens_map.json
3. 数据集目录已经准备好
至少要有:
E:\minimind\dataset\sft_mini_512.jsonl
如果你想做更完整路线,还要有:
sft_512.jsonlsft_2048.jsonl
4. tokenizer 路径不要乱切
如果你之前为了适配 E 盘目录,已经在 trainer_utils.py 里把 tokenizer 路径改成:
tokenizer_path = r"E:\minimind\MiniMind2"
那就尽量保持训练和推理都使用同一套 tokenizer 文件,不要训练时读 MiniMind2,推理时又换成另一套不一致的 tokenizer。
2. 可复现训练流程
1. 进入环境和项目根目录
conda activate minimind
cd /d E:\minimind
建议固定从仓库根目录启动,这样最不容易出现路径混乱。
2. 确认 Pretrain 权重已经存在
最关键的是确认这类文件已经有了:
E:\minimind\out\pretrain_512.pth
如果没有这个文件,SFT 默认通常无法顺利接着训。
3. 确认模型目录和 tokenizer 可读
如果你已经像 Pretrain 阶段那样,把 tokenizer 路径修到了 MiniMind2,这一步只要保证 MiniMind2 目录文件齐全即可。
如果你没改过脚本,那就要确认脚本默认读取的 tokenizer 路径在你当前目录结构下是成立的。
4. 确认 SFT 数据集存在
最基础的是检查这个文件:
E:\minimind\dataset\sft_mini_512.jsonl
如果你打算先跑官方默认轻量路线,这个文件就够了。
5. 先跑官方默认的快速 SFT 路线
如果你的目标是:
先把 SFT 链路完整跑通
推荐先用官方默认的轻量路线,也就是:
- 数据:
sft_mini_512.jsonl - 底座:
pretrain_512.pth - 产物:
full_sft_512.pth
最稳妥的命令建议把关键路径都写清楚:
conda activate minimind
cd /d E:\minimind
python trainer\train_full_sft.py `
--device cuda:0 `
--data_path E:\minimind\dataset\sft_mini_512.jsonl `
--save_dir E:\minimind\out `
--hidden_size 512 `
--num_hidden_layers 8 `
--max_seq_len 340 `
--from_weight pretrain
如果你只是沿用脚本默认参数,也可以直接:
python trainer\train_full_sft.py --device cuda:0
但更推荐显式写出 data_path 和 save_dir,这样以后回看时更清楚,也能减少相对路径跑偏的风险。
6. 如果想做更完整的 SFT 路线
如果你的目标不是只复现轻量 MiniMind-Zero 风格,而是希望比 sft_mini_512 更强一点,可以继续往下做:
路线 A:先做 sft_512.jsonl
conda activate minimind
cd /d E:\minimind
python trainer\train_full_sft.py `
--device cuda:0 `
--data_path E:\minimind\dataset\sft_512.jsonl `
--save_dir E:\minimind\out `
--hidden_size 512 `
--num_hidden_layers 8 `
--from_weight pretrain
这一步的作用是:
- 比
sft_mini_512使用更完整的 SFT 数据 - 让模型更像真正的问答助手
路线 B:再做更长上下文的 sft_2048.jsonl
如果你已经完成了一轮 full_sft,还想让模型接触更长上下文的数据,可以再继续一轮:
conda activate minimind
cd /d E:\minimind
python trainer\train_full_sft.py `
--device cuda:0 `
--data_path E:\minimind\dataset\sft_2048.jsonl `
--save_dir E:\minimind\out `
--hidden_size 512 `
--num_hidden_layers 8 `
--from_weight full_sft
这里的意思是:
- 第一轮:
pretrain -> full_sft - 第二轮:
full_sft -> 更长上下文 SFT
如果你只是第一次跑通 SFT,不建议一上来就把路线拉太长。最稳的顺序通常是:
- 先把
sft_mini_512跑通 - 再做
sft_512 - 再考虑
sft_2048
7. 如何停止和继续训练
停止训练
训练过程中如果想手动停掉:
Ctrl + C
继续训练
如果你已经生成了类似这样的文件:
E:\minimind\checkpoints\full_sft_512_resume.pth
通常说明脚本已经保存了可继续训练的断点信息。
如果脚本支持 --from_resume 1,可以尝试:
python trainer\train_full_sft.py `
--device cuda:0 `
--data_path E:\minimind\dataset\sft_mini_512.jsonl `
--save_dir E:\minimind\out `
--hidden_size 512 `
--num_hidden_layers 8 `
--max_seq_len 340 `
--from_weight pretrain `
--from_resume 1
3. 成功判据
如果 SFT 成功启动,日志通常会不断出现类似输出:
Epoch:[1/2](100/75921), loss: 2.8xxx, logits_loss: 2.8xxx, aux_loss: 0.0000, lr: 0.00000xxx, epoch_time: xx.xmin
你要重点看这些现象:
Epoch在推进step在增加loss整体有下降趋势lr在变化epoch_time在更新
如果训练结束后出现:
E:\minimind\out\full_sft_512.pth
那就说明主权重已经保存出来了。
3.1 训练日志解读
假设看到一行日志:
Epoch:[2/2](75000/75921), loss: 2.1306, logits_loss: 2.1306, aux_loss: 0.0000, lr: 0.00000010, epoch_time: 0.0min
可以这样理解:
Epoch:[2/2]
- 总共训练 2 个 epoch
- 当前处于第 2 个 epoch
(75000/75921)
- 当前 step 大约在 7.5 万
- 这个数字说明训练已经接近尾声
loss: 2.1306
- 当前总损失
- 不是越接近 0 越神,但整体下降通常是好现象
logits_loss: 2.1306
- 主损失项
- 对语言模型来说通常是最关键的
aux_loss: 0.0000
- 辅助损失
- 当前这次训练里没有额外贡献
lr: 0.00000010
- 当前学习率
- 接近训练末尾时,学习率通常已经衰减得比较小
epoch_time: 0.0min
- 按当前速度估计,本 epoch 即将结束
4. 训练产物
通常至少会看到这几类文件:
1. 主权重
E:\minimind\out\full_sft_512.pth
这是最核心的 SFT 结果。
2. checkpoint
E:\minimind\checkpoints\full_sft_512.pth
更像中间过程的权重快照。
3. resume checkpoint
E:\minimind\checkpoints\full_sft_512_resume.pth
里面通常会包含:
- 模型参数
- 优化器状态
- 当前 step
- 当前 epoch
它的意义是:
下次可以继续训练,而不是从头开始。
历史验证:至少在 2026-03-26 21:50:15,本机已经保存了主权重、checkpoint 和 resume checkpoint。是否使用最新数据集、训练到哪个 step,仍应以对应日志和评测命令为准。
5. 训练完成后的验证
最简单的验证方式:
conda activate minimind
cd /d E:\minimind
python eval_llm.py --weight full_sft
如果启动后出现:
[0] 自动测试
[1] 手动输入
输入 1,就可以自己和模型对话。
如何判断自己真的在用 SFT
你可以做一个对照测试。
用 Pretrain 跑
python eval_llm.py --weight pretrain
用 Full SFT 跑
python eval_llm.py --weight full_sft
然后用同样的问题各问一遍,例如:
请用一句话介绍你自己。
把“今天天气不错”翻译成英文。
请按 1、2、3 列出番茄炒蛋的做法。
如果 full_sft 比 pretrain 更像在按要求回答,那就说明 SFT 已经生效了。
6. 常见问题
1. 以为“能启动对话界面”就等于“肯定在用 SFT”
不是。
eval_llm.py 只是推理脚本,真正决定加载哪份权重的是:
-weight pretrain-weight full_sft
2. 误以为效果一般,就是没训练成功
也不一定。
如果你走的是:
pretrain_hq.jsonl + sft_mini_512.jsonl
那更像官方快速复现的轻量路线,模型能聊,但不一定很稳。
3. 相对路径容易跑偏
像 ../out、../checkpoints、../dataset/... 这种写法,在不同启动目录下可能会解析到意料之外的位置。
最稳妥的办法:
- 固定从
E:\minimind根目录启动 - 关键参数尽量写绝对路径
4. tokenizer 路径前后不一致
如果训练时读取的是 MiniMind2 里的 tokenizer,推理时最好也保持一致,不要混用不一致的词表文件。
5. pynvml warning
如果你看到:
FutureWarning: The pynvml package is deprecated
这通常只是警告,不是这次 SFT 跑不通的核心问题。
7. 推荐命令清单
1. 进入环境
conda activate minimind
cd /d E:\minimind
2. 跑默认轻量 SFT
python trainer\train_full_sft.py `
--device cuda:0 `
--data_path E:\minimind\dataset\sft_mini_512.jsonl `
--save_dir E:\minimind\out `
--hidden_size 512 `
--num_hidden_layers 8 `
--max_seq_len 340 `
--from_weight pretrain
3. 跑更完整的 SFT-512
python trainer\train_full_sft.py `
--device cuda:0 `
--data_path E:\minimind\dataset\sft_512.jsonl `
--save_dir E:\minimind\out `
--hidden_size 512 `
--num_hidden_layers 8 `
--from_weight pretrain
4. 接着做更长上下文 SFT
python trainer\train_full_sft.py `
--device cuda:0 `
--data_path E:\minimind\dataset\sft_2048.jsonl `
--save_dir E:\minimind\out `
--hidden_size 512 `
--num_hidden_layers 8 `
--from_weight full_sft
5. 验证 SFT 权重
python eval_llm.py --weight full_sft
6. 对照测试 Pretrain
python eval_llm.py --weight pretrain
8. 下一步建议
如果你已经把 SFT 跑通,后面推荐按这个顺序推进:
1. 先确认当前 full_sft_512.pth 的真实效果
重点看:
- 是否比
pretrain更像助手 - 是否更听指令
- 是否对简单翻译、问答、列步骤更稳定
2. 如果效果一般,不要先怀疑命令
更优先怀疑的是:
- 用的是
sft_mini_512.jsonl这种轻量数据 - 模型规模只有 25M 左右,本来就有限
- tokenizer 路径前后不一致
3. 如果想明显提升效果
优先做:
sft_512.jsonlsft_2048.jsonldpo.jsonl
4. LoRA 和 DPO 通常接在 full_sft 后面
也就是说:
SFT 不是终点,而是后续 LoRA / DPO 的基础。
9. 参考来源
这份指南主要依据下面两类信息整理:
1. 官方资料
- GitHub:
jingyaogong/minimind - ModelScope:
gongjy/minimind_dataset
2. 你当前这条链路里已经确认过的信息
train_full_sft.py是 SFT 主脚本- 默认数据是
sft_mini_512.jsonl - 默认底座是
pretrain - 产物是
full_sft_512.pth - 你已经成功跑出并测试过
full_sft_512.pth
总结
如果只记住一句话,那就是:
MiniMind 的 Full SFT 是用
trainer\train_full_sft.py读取pretrain_512.pth,结合sft_mini_512.jsonl或更完整的 SFT 数据继续训练,产出full_sft_512.pth,再用python eval_llm.py --weight full_sft验证。
MiniMind 的标准链路是 Tokenizer → Pretrain → Full SFT → LoRA → DPO / GRPO。SFT 建立在 pretrain 权重上,目标是指令跟随和问答格式,不等同于从零训练,也不保证自动获得成熟对话能力。本仓库没有保留完整终端训练日志,因此不虚构 loss、step 或效果指标;验收时按上方命令与 pretrain 对照即可。