本课模式:先准备数据(最关键)→ 再跑训练 → 合并 → 评估前后对比。这是 Lumen L0.2 的完整预演:火星语料 SFT 后,行为真的变了、而且可测量。
📋 本课你将完成
- 能解释为什么 SFT 数据要「质量 > 数量」
- 能用 chat template 正确包裹一条指令数据
- 能解释 LoRA 与 QLoRA 的原理(低秩矩阵 + 4bit 量化)
- 在 Mac 上完成一次 Qwen2.5-3B 的 QLoRA 微调并 fuse
- 做过 SFT 前后对比评估并记入 manifest
步骤 1
环境与内存检查(5 分钟)
未完成🎯 这一步的目标:确认 Mac 内存足够跑 QLoRA 微调 3B。16GB 内存可跑(1.5B 更稳);8GB 请改用 1.5B。
sysctl hw.memsize | awk '{print $2/1073741824 " GB"}'▶ 预期输出(点开对照)
16.0 GB(16GB 或以上 → 放心往下走)
pip3 install mlx-lm▶ 预期输出(点开对照)
Collecting mlx-lm... Successfully installed mlx-lm-0.x.x
如果出现:pip3 报 externally-managed-environment
怎么办:建虚拟环境:python3 -m venv ~/llm-course/venv && source ~/llm-course/venv/bin/activate,再重跑。
怎么办:建虚拟环境:python3 -m venv ~/llm-course/venv && source ~/llm-course/venv/bin/activate,再重跑。
如果出现:不支持当前 Python 版本
怎么办:MLX 要求 Python 3.9+。检查:python3 --version;必要时用 Homebrew 装新版本。
怎么办:MLX 要求 Python 3.9+。检查:python3 --version;必要时用 Homebrew 装新版本。
✅ 检查点:mlx-lm 安装成功(python3 -c 'import mlx_lm' 无报错)。
步骤 2
准备 SFT 数据(最重要,30 分钟)
未完成🎯 这一步的目标:写 20–50 条高质量「指令 → 回答」对。记住铁律:20 条精心编写的示范 > 2000 条复制的噪声。这是全课最花心思的一步,也是决定成败的一步。
mkdir -p ~/llm-course/sft-data && cd ~/llm-course/sft-data▶ 预期输出(点开对照)
(无输出即成功)
nano mars-sft.jsonl▶ 预期输出(点开对照)
(在编辑器里粘贴下面 3 条示例,然后替换成你自己的数据。格式:每行一个 JSON 对象)
{"instruction": "火星的一天是多长?", "output": "火星自转一周约 24 小时 37 分钟,比地球一天长约 40 分钟。"}▶ 预期输出(点开对照)
(保存:Ctrl+O 回车,退出:Ctrl+X。再复制两行换主题:火星种土豆、什么是 Proof)
python3 -c 'import json; [json.loads(l) for l in open("mars-sft.jsonl")]; print("OK")'▶ 预期输出(点开对照)
OK
✅ 检查点:JSONL 每行都是合法 JSON,且包含 instruction + output 字段。
步骤 3
先跑 10 步试训练(5 分钟)
未完成🎯 这一步的目标:用最小步数验证「代码能跑、数据能读」,再正式训练——这是「大跑之前小跑必须已通」的实践。
cd ~/llm-course
mlx_lm.lora --model Qwen/Qwen2.5-3B-Instruct --train --data ./sft-data --iters 10 --batch-size 1 --lora-layers 8 --learning-rate 2e-5▶ 预期输出(点开对照)
Loading pretrained model... Loading datasets... Training: 100%|██| 10/10 ... (看到 Training 进度条即成功;首次会自动下载模型权重约 6GB,稍等)
如果出现:下载模型很慢或失败
怎么办:设置镜像:export HF_ENDPOINT=https://hf-mirror.com,再重跑。
怎么办:设置镜像:export HF_ENDPOINT=https://hf-mirror.com,再重跑。
如果出现:Out of memory / 内存不足
怎么办:改用 1.5B:把模型名换成 Qwen/Qwen2.5-1.5B-Instruct。
怎么办:改用 1.5B:把模型名换成 Qwen/Qwen2.5-1.5B-Instruct。
✅ 检查点:10 步试训练跑完,无报错。
步骤 4
正式微调(40–60 分钟,可挂机)
未完成🎯 这一步的目标:500 步 QLoRA 微调——把火星知识训进模型。训练结束会生成 adapters 目录(LoRA 权重)。
mlx_lm.lora --model Qwen/Qwen2.5-3B-Instruct --train --data ./sft-data --iters 500 --batch-size 2 --lora-layers 8 --learning-rate 2e-5▶ 预期输出(点开对照)
Training: 100%|██| 500/500 ... Iter 500: loss 0.872 ... (loss 逐步下降即是成功。500 步约 40–60 分钟)
mlx_lm.fuse --model Qwen/Qwen2.5-3B-Instruct --adapter-path ./adapters --save-path ./lumen-mini-v01▶ 预期输出(点开对照)
Saving fused model to ./lumen-mini-v01 (生成 lumen-mini-v01 目录,里面是完整的合并后模型)
✅ 检查点:lumen-mini-v01 目录生成,含 model.safetensors。
步骤 5
评估前后对比(15 分钟)
未完成🎯 这一步的目标:用同一组问题测「微调前 vs 微调后」,看行为是否真的变了——这是可证伪的评估,不是凭感觉。
mlx_lm.generate --model Qwen/Qwen2.5-3B-Instruct --prompt "火星的一天是多长?"▶ 预期输出(点开对照)
(原版模型会回答——可能泛泛而谈,甚至答错/编造。记录下回答)
mlx_lm.generate --model ./lumen-mini-v01 --prompt "火星的一天是多长?"▶ 预期输出(点开对照)
(微调后模型应给出准确、符合你训练数据的回答。对比两次回答,写进 manifest)
✅ 检查点:前后对比完成,能说出「微调改变了什么」——至少在你训练过的主题上回答变准了。
🧪 交互实验 · SFT 数据格式检查器
写数据时最常见的错误:缺字段、JSON 语法错、或格式不是「指令 → 回答」。把一行粘贴进来,立即检查。
✅ 绿色 = 格式合法。⚠️ 红色 = 有问题,按提示修改。
❓ 老师答疑(卡住了先看这里)
❓ 为什么只要 20–50 条数据?太少了吧?
SFT 教的是「怎么答」,不是「知识全集」。20–50 条高质量示范足够让模型学会「火星主题的问答格式与内容风格」。想要覆盖更多主题就加数据——但永远别用低质量数据充数,坏数据会教坏模型。
❓ LoRA 和 QLoRA 到底改了什么?
模型原权重全部冻结,只在旁边加两个低秩小矩阵(LoRA),只训练它们(<1% 参数)。QLoRA 额外把原模型压到 4-bit,让 16GB 内存也能装下 3B。所以微调后你「没有改动」Qwen 本体,只是加了一个火星知识的「外挂补丁」——fuse 步骤就是把补丁焊回去。
❓ 为什么要用 chat template?不包行不行?
不行。Qwen 的 chat template 是它的「对话协议」——告诉模型「这行是用户说的、那行该你回答」。不包裹,模型分不清边界,输出会乱。用 mlx_lm 时它自动处理,但你写数据时字段要符合(instruction/output 或 messages)。
❓ 训练 loss 到多少算好?
看趋势不看绝对值。500 步后 loss 通常在 0.5–1.5 之间。更重要的指标是评估对比:微调后的模型在火星问题上答得比原版准,就成功了。
❓ 微调会不会把模型原来的能力搞坏?
有可能(灾难性遗忘)。所以必须做前后对比评估:不仅看新知识学没学到,还要看通用能力掉没掉。掉太多 → 数据量太大或学习率太高,调小重来。
❓ 我没有火星数据,能用别的主题吗?
完全可以。把 instruction/output 换成任意你想教的主题(公司产品问答、个人知识库、特定写作风格……)。管道完全一样——这就是「把模型变成自己的」。
📝 自测清单(全勾才算过)
- 能解释为什么 SFT 数据要「质量 > 数量」
- 能用 chat template 正确包裹一条指令数据
- 能解释 LoRA 与 QLoRA 的原理(低秩矩阵 + 4bit 量化)
- 在 Mac 上完成一次 Qwen2.5-3B 的 QLoRA 微调并 fuse
- 做过 SFT 前后对比评估并记入 manifest
全勾 → 节点 08 / 14 完成。未勾 → 回看对应步骤,别急着往前走。