理论笔记: 蒸馏原理与公式详见
/root/autodl-tmp/Distillation.md,本 SOP 聚焦第六节「四步实战」的工程落地。手把手教学: 逐步操作与脚本说明见 <TUTORIAL.md>
互补项目:
/root/autodl-tmp/Distill/已实现黑盒蒸馏 (API 生成数据 → SFT);本目录 Distill2 实现白盒蒸馏(Logits 对齐)。
一、理论背景与白盒蒸馏原理
1. 核心目标
知识蒸馏(Knowledge Distillation)通过迁移大模型(教师 Teacher )的知识,训练出推理更快、资源占用更小的模型(学生 Student)。
白盒蒸馏的前提:教师模型结构开源可见,可获取 forward 过程中的 Logits 或中间层特征,作为比硬标签更丰富的监督信号。
2. 白盒蒸馏的三类方法
| 类型 | 核心 | 本 SOP 是否采用 |
|---|---|---|
| 响应式 (Response-Based) | 匹配最终输出 Logits 分布 | 是(本 SOP 采用) |
| 特征式 (Feature-Based) | 对齐中间层特征图(FitNet 等) | 否 |
| 关系式 (Relation-Based) | 对齐样本间/层间关系矩阵(RKD 等) | 否 |
3. 响应式蒸馏的损失函数
经典 Hinton 蒸馏损失:
Total Loss = α ⋅ L distill + ( 1 − α ) ⋅ L student \text{Total Loss} = \alpha \cdot L_{\text{distill}} + (1-\alpha) \cdot L_{\text{student}} Total Loss=α⋅Ldistill+(1−α)⋅Lstudent
- L distill L_{\text{distill}} Ldistill :对教师/学生 Logits 施加温度 T T T 软化后,计算 KL 散度
- p ( z i , T ) = exp ( z i / T ) ∑ j exp ( z j / T ) p(z_i, T) = \dfrac{\exp(z_i/T)}{\sum_j \exp(z_j/T)} p(zi,T)=∑jexp(zj/T)exp(zi/T)
- T T T 越大,分布越平滑,学生能学到更多类间关系
- L student L_{\text{student}} Lstudent :学生预测与数据集硬标签的 交叉熵 (CE)
- α \alpha α:蒸馏项权重,通常 0.1--0.5
KL 散度(离散):
D K L ( P ∥ Q ) = ∑ x P ( x ) log P ( x ) Q ( x ) D_{KL}(P \| Q) = \sum_x P(x) \log \frac{P(x)}{Q(x)} DKL(P∥Q)=x∑P(x)logQ(x)P(x)
4. 与黑盒蒸馏(Distill/)的对比
#mermaid-svg-7eiNmDIuFgzpaKzH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7eiNmDIuFgzpaKzH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7eiNmDIuFgzpaKzH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7eiNmDIuFgzpaKzH .error-icon{fill:#552222;}#mermaid-svg-7eiNmDIuFgzpaKzH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7eiNmDIuFgzpaKzH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7eiNmDIuFgzpaKzH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7eiNmDIuFgzpaKzH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7eiNmDIuFgzpaKzH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7eiNmDIuFgzpaKzH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7eiNmDIuFgzpaKzH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7eiNmDIuFgzpaKzH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7eiNmDIuFgzpaKzH .marker.cross{stroke:#333333;}#mermaid-svg-7eiNmDIuFgzpaKzH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7eiNmDIuFgzpaKzH p{margin:0;}#mermaid-svg-7eiNmDIuFgzpaKzH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7eiNmDIuFgzpaKzH .cluster-label text{fill:#333;}#mermaid-svg-7eiNmDIuFgzpaKzH .cluster-label span{color:#333;}#mermaid-svg-7eiNmDIuFgzpaKzH .cluster-label span p{background-color:transparent;}#mermaid-svg-7eiNmDIuFgzpaKzH .label text,#mermaid-svg-7eiNmDIuFgzpaKzH span{fill:#333;color:#333;}#mermaid-svg-7eiNmDIuFgzpaKzH .node rect,#mermaid-svg-7eiNmDIuFgzpaKzH .node circle,#mermaid-svg-7eiNmDIuFgzpaKzH .node ellipse,#mermaid-svg-7eiNmDIuFgzpaKzH .node polygon,#mermaid-svg-7eiNmDIuFgzpaKzH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7eiNmDIuFgzpaKzH .rough-node .label text,#mermaid-svg-7eiNmDIuFgzpaKzH .node .label text,#mermaid-svg-7eiNmDIuFgzpaKzH .image-shape .label,#mermaid-svg-7eiNmDIuFgzpaKzH .icon-shape .label{text-anchor:middle;}#mermaid-svg-7eiNmDIuFgzpaKzH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7eiNmDIuFgzpaKzH .rough-node .label,#mermaid-svg-7eiNmDIuFgzpaKzH .node .label,#mermaid-svg-7eiNmDIuFgzpaKzH .image-shape .label,#mermaid-svg-7eiNmDIuFgzpaKzH .icon-shape .label{text-align:center;}#mermaid-svg-7eiNmDIuFgzpaKzH .node.clickable{cursor:pointer;}#mermaid-svg-7eiNmDIuFgzpaKzH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7eiNmDIuFgzpaKzH .arrowheadPath{fill:#333333;}#mermaid-svg-7eiNmDIuFgzpaKzH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7eiNmDIuFgzpaKzH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7eiNmDIuFgzpaKzH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7eiNmDIuFgzpaKzH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7eiNmDIuFgzpaKzH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7eiNmDIuFgzpaKzH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7eiNmDIuFgzpaKzH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7eiNmDIuFgzpaKzH .cluster text{fill:#333;}#mermaid-svg-7eiNmDIuFgzpaKzH .cluster span{color:#333;}#mermaid-svg-7eiNmDIuFgzpaKzH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7eiNmDIuFgzpaKzH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7eiNmDIuFgzpaKzH rect.text{fill:none;stroke-width:0;}#mermaid-svg-7eiNmDIuFgzpaKzH .icon-shape,#mermaid-svg-7eiNmDIuFgzpaKzH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7eiNmDIuFgzpaKzH .icon-shape p,#mermaid-svg-7eiNmDIuFgzpaKzH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7eiNmDIuFgzpaKzH .icon-shape .label rect,#mermaid-svg-7eiNmDIuFgzpaKzH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7eiNmDIuFgzpaKzH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7eiNmDIuFgzpaKzH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7eiNmDIuFgzpaKzH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Distill2 白盒
教师 LoRA SFT
alpaca_gpt4_zh
合并教师权重
学生 LoRA + Logits 对齐
ref_model=教师
合并学生权重
Distill 黑盒
DeepSeek-R1 API
文本输出
学生 Full SFT
| 维度 | Distill/ 黑盒 | Distill2/ 白盒 |
|---|---|---|
| 教师来源 | 闭源 API(DeepSeek-R1) | 本地开源模型(Qwen3-4B) |
| 监督信号 | 教师生成的文本(软标签文本) | 教师 forward 的 Logits 概率分布 |
| 是否需要先训教师 | 否(API 直接调用) | 是(教师须先 SFT 再蒸馏) |
| 训练框架 | LLaMA-Factory Full SFT | LLaMA-Factory LoRA + ASFT |
| 技术门槛 | 较低(本质是 SFT) | 较高(需加载双模型、配置 ref_model) |
核心逻辑(来自笔记第六节): 「教师模型先基于业务数据进行 SFT 微调,再拿着这个懂业务的教师去教小模型。」若直接用未微调的基础模型当教师,特定业务场景下蒸馏效果会大打折扣。
二、任务配置总览
本节参数直接对应 Distillation.md 第六节。
| 配置项 | 值 |
|---|---|
| 蒸馏方法 | 白盒 - 响应式(Logits 对齐) |
| 教师模型 | Qwen/Qwen3-4B-Instruct-2507 |
| 学生模型 | Qwen/Qwen3-1.7B(LLaMA-Factory 内置别名 Qwen3-1.7B-Thinking) |
| 数据集 | alpaca_gpt4_zh(公开中文指令集,Alpaca 格式) |
| 训练方式 | LoRA 微调 |
| 训练轮数 | 30 Epoch(教师、学生各 30) |
| 工程框架 | LLaMA-Factory(独立 clone 至 Distill2/LLaMA-Factory) |
与笔记的差异说明:
Distillation.md第六节 示例使用定制identity_modify.json;本 SOP 选用 LLaMA-Factory 已内置注册的公开数据集alpaca_gpt4_zh,避免手写样本,同时完整走通「下载 → 校验 → 训练 → 蒸馏」链路。若你的业务有私有语料,可替换为同格式的自建 JSON,四步流程不变。
规模建议:
| 阶段 | max_samples |
num_train_epochs |
目的 |
|---|---|---|---|
| 冒烟 | 50 | 1 | 验证数据链路与四步流程 |
| 正式 | 不设置(全量 ~4.8 万条) | 30 | 对齐笔记第六节参数 |
三、环境准备
1. 工作目录与网络加速
工作目录: /root/autodl-tmp/Distill2
AutoDL 网络加速(访问 GitHub / Hugging Face / ModelScope 前执行):
bashsource /etc/network_turbo开启后 pip 源等非 GitHub/HF 资源可能变慢,建议仅在 clone / 下载模型时使用。
2. 克隆并安装 LLaMA-Factory
bash
mkdir -p /root/autodl-tmp/Distill2
cd /root/autodl-tmp/Distill2
source /etc/network_turbo
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"
llamafactory-cli version # 验证安装
实测问题与解决:
| 问题 | 现象 | 解决方案 |
|---|---|---|
| torchaudio 版本冲突 | OSError: libcudart.so.13: cannot open shared object file |
pip install "torchaudio==2.8.0" --index-url https://download.pytorch.org/whl/cu128 |
| OMP 警告 | libgomp: Invalid value for environment variable OMP_NUM_THREADS |
可忽略;或 export OMP_NUM_THREADS=1 |
3. 下载教师与学生模型
在 ModelScope 搜索并确认 Model ID 后,执行:
python
# 可在 python 交互环境或临时脚本中运行
from modelscope import snapshot_download
TEACHER_ID = "Qwen/Qwen3-4B-Instruct-2507"
STUDENT_ID = "Qwen/Qwen3-1.7B"
LOCAL_DIR = "/root/autodl-tmp/Distill2/models"
teacher_path = snapshot_download(TEACHER_ID, cache_dir=LOCAL_DIR)
student_path = snapshot_download(STUDENT_ID, cache_dir=LOCAL_DIR)
print("Teacher:", teacher_path)
print("Student:", student_path)
ModelScope 典型缓存路径(配置 YAML 时使用完整绝对路径):
/root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-4B-Instruct-2507/snapshots/master
/root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-1.7B/snapshots/master
下文 YAML 中以
<TEACHER_PATH>、<STUDENT_PATH>代指上述实际路径。
4. 准备训练数据集(公开集 alpaca_gpt4_zh)
选用 Alpaca-GPT4-Data-ZH 的中文子集,经 LLaMA-Factory 维护为 Hub 数据集,适合 Qwen3 中文 Instruct 模型的 SFT 与白盒蒸馏。
| 属性 | 值 |
|---|---|
| LLaMA-Factory 注册名 | alpaca_gpt4_zh |
| HuggingFace | llamafactory/alpaca_gpt4_zh |
| ModelScope | llamafactory/alpaca_gpt4_zh |
| 格式 | Alpaca(instruction / input / output) |
| 规模 | 约 4.8 万条 |
#mermaid-svg-qbcj77y8PsTmOVfV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qbcj77y8PsTmOVfV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qbcj77y8PsTmOVfV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qbcj77y8PsTmOVfV .error-icon{fill:#552222;}#mermaid-svg-qbcj77y8PsTmOVfV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qbcj77y8PsTmOVfV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qbcj77y8PsTmOVfV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qbcj77y8PsTmOVfV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qbcj77y8PsTmOVfV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qbcj77y8PsTmOVfV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qbcj77y8PsTmOVfV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qbcj77y8PsTmOVfV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qbcj77y8PsTmOVfV .marker.cross{stroke:#333333;}#mermaid-svg-qbcj77y8PsTmOVfV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qbcj77y8PsTmOVfV p{margin:0;}#mermaid-svg-qbcj77y8PsTmOVfV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qbcj77y8PsTmOVfV .cluster-label text{fill:#333;}#mermaid-svg-qbcj77y8PsTmOVfV .cluster-label span{color:#333;}#mermaid-svg-qbcj77y8PsTmOVfV .cluster-label span p{background-color:transparent;}#mermaid-svg-qbcj77y8PsTmOVfV .label text,#mermaid-svg-qbcj77y8PsTmOVfV span{fill:#333;color:#333;}#mermaid-svg-qbcj77y8PsTmOVfV .node rect,#mermaid-svg-qbcj77y8PsTmOVfV .node circle,#mermaid-svg-qbcj77y8PsTmOVfV .node ellipse,#mermaid-svg-qbcj77y8PsTmOVfV .node polygon,#mermaid-svg-qbcj77y8PsTmOVfV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qbcj77y8PsTmOVfV .rough-node .label text,#mermaid-svg-qbcj77y8PsTmOVfV .node .label text,#mermaid-svg-qbcj77y8PsTmOVfV .image-shape .label,#mermaid-svg-qbcj77y8PsTmOVfV .icon-shape .label{text-anchor:middle;}#mermaid-svg-qbcj77y8PsTmOVfV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qbcj77y8PsTmOVfV .rough-node .label,#mermaid-svg-qbcj77y8PsTmOVfV .node .label,#mermaid-svg-qbcj77y8PsTmOVfV .image-shape .label,#mermaid-svg-qbcj77y8PsTmOVfV .icon-shape .label{text-align:center;}#mermaid-svg-qbcj77y8PsTmOVfV .node.clickable{cursor:pointer;}#mermaid-svg-qbcj77y8PsTmOVfV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qbcj77y8PsTmOVfV .arrowheadPath{fill:#333333;}#mermaid-svg-qbcj77y8PsTmOVfV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qbcj77y8PsTmOVfV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qbcj77y8PsTmOVfV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qbcj77y8PsTmOVfV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qbcj77y8PsTmOVfV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qbcj77y8PsTmOVfV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qbcj77y8PsTmOVfV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qbcj77y8PsTmOVfV .cluster text{fill:#333;}#mermaid-svg-qbcj77y8PsTmOVfV .cluster span{color:#333;}#mermaid-svg-qbcj77y8PsTmOVfV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qbcj77y8PsTmOVfV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qbcj77y8PsTmOVfV rect.text{fill:none;stroke-width:0;}#mermaid-svg-qbcj77y8PsTmOVfV .icon-shape,#mermaid-svg-qbcj77y8PsTmOVfV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qbcj77y8PsTmOVfV .icon-shape p,#mermaid-svg-qbcj77y8PsTmOVfV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qbcj77y8PsTmOVfV .icon-shape .label rect,#mermaid-svg-qbcj77y8PsTmOVfV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qbcj77y8PsTmOVfV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qbcj77y8PsTmOVfV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qbcj77y8PsTmOVfV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Hub 公开数据集
alpaca_gpt4_zh
确认 dataset_info.json
内置注册
预下载到本地缓存
抽样预览与字段校验
LLaMA-Factory
max_steps=1 加载冒烟
Step1~4 训练/蒸馏
以下子步骤均需执行 (冒烟时可限制 max_samples,但不可跳过下载与校验)。
4.1 确认数据集已在 LLaMA-Factory 中注册
clone 完成后检查 data/dataset_info.json 是否含以下条目(官方仓库默认已有,无需手写注册):
bash
cd /root/autodl-tmp/Distill2/LLaMA-Factory
python3 -c "
import json
info = json.load(open('data/dataset_info.json'))
assert 'alpaca_gpt4_zh' in info, '缺少 alpaca_gpt4_zh 注册项'
print(json.dumps(info['alpaca_gpt4_zh'], indent=2, ensure_ascii=False))
"
预期输出含 hf_hub_url 与 ms_hub_url:
json
{
"hf_hub_url": "llamafactory/alpaca_gpt4_zh",
"ms_hub_url": "llamafactory/alpaca_gpt4_zh"
}
4.2 预下载数据集到本地缓存
AutoDL 环境优先走 ModelScope;若 MS 不可用再回退 HuggingFace。
bash
source /etc/network_turbo
cd /root/autodl-tmp/Distill2/LLaMA-Factory
方式 A(推荐,ModelScope):
python
from modelscope.msdatasets import MsDataset
ds = MsDataset.load("llamafactory/alpaca_gpt4_zh", split="train")
print("下载完成,样本数:", len(ds))
方式 B(回退,HuggingFace Datasets):
python
from datasets import load_dataset
ds = load_dataset("llamafactory/alpaca_gpt4_zh", split="train")
print("下载完成,样本数:", len(ds))
首次下载耗时数分钟,缓存默认位于
~/.cache/modelscope或~/.cache/huggingface/datasets。后续训练会直接命中缓存。
4.3 抽样预览与字段校验
确认字段完整、无大量空 output,并保存一条验证 prompt 供 Step 4 推理对比(从真实数据中读取,非手写):
python
# 接续 4.2,变量 ds 已加载
required = {"instruction", "output"}
for i in range(3):
row = ds[i]
assert required.issubset(row.keys()), f"样本 {i} 缺少必要字段"
print(f"--- sample {i} ---")
print("instruction:", row["instruction"][:120])
print("input:", (row.get("input") or "")[:80])
print("output:", row["output"][:120])
# 写入验证 prompt,供第四节推理使用
val = ds[0]
prompt = val["instruction"]
if val.get("input"):
prompt = prompt + "\n" + val["input"]
with open("/root/autodl-tmp/Distill2/validation_prompt.txt", "w", encoding="utf-8") as f:
f.write(prompt)
print("验证 prompt 已写入 Distill2/validation_prompt.txt")
print("全量样本数:", len(ds))
检查点: 终端无报错;validation_prompt.txt 已生成。
4.4 LLaMA-Factory 数据加载冒烟(max_steps: 1)
在正式四步训练前,用极小训练步数 验证框架能正确读取 Hub 数据集并完成 tokenize → dataloader → forward。创建临时配置 examples/train_lora/smoke_data_load.yaml:
yaml
### model
model_name_or_path: <STUDENT_PATH>
trust_remote_code: true
### method
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 8
lora_target: all
### dataset
dataset: alpaca_gpt4_zh_local
template: qwen3_nothink
cutoff_len: 2048
max_samples: 5
overwrite_cache: true
### output
output_dir: saves/smoke/data-load
overwrite_output_dir: true
report_to: none
### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 1
learning_rate: 1.0e-4
max_steps: 1
bf16: true
bash
cd /root/autodl-tmp/Distill2/LLaMA-Factory
llamafactory-cli train examples/train_lora/smoke_data_load.yaml
检查点: 日志出现 Running training 且 1 step 正常结束;无 Dataset ... not found 或字段映射错误。
4.5 可选:导出本地 JSON 副本(离线复现)
若需在无网环境复现,可将 Hub 数据原样导出为本地文件(内容来自下载结果,非手写):
python
ds.select(range(min(50, len(ds)))).to_json(
"data/alpaca_gpt4_zh_smoke.json",
force_ascii=False,
)
print("已导出冒烟子集 -> data/alpaca_gpt4_zh_smoke.json")
若使用本地副本,在 dataset_info.json 增加:
json
"alpaca_gpt4_zh_smoke": {
"file_name": "alpaca_gpt4_zh_smoke.json"
}
训练时将 YAML 中 dataset 改为 alpaca_gpt4_zh_smoke 即可。实测主流程使用 alpaca_gpt4_zh_local(见 4.6)。
4.6 实测推荐:导出本地副本(AutoDL 环境强烈建议)
2026-07-08 冒烟实测: 在未开启
source /etc/network_turbo时,LLaMA-Factory 加载 Hub 数据集默认走 HuggingFace,报错[Errno 101] Network is unreachable。即使 ModelScope 已缓存,训练阶段仍可能触发 HF 请求。
推荐做法(与实测一致): 预下载后导出本地 JSON,并注册为 alpaca_gpt4_zh_local:
python
# 4.2 下载完成后执行(需 pip install addict)
import json
from modelscope.msdatasets import MsDataset
ds = MsDataset.load("llamafactory/alpaca_gpt4_zh", split="train")
records = [dict(ds[i]) for i in range(len(ds))]
with open("/root/autodl-tmp/Distill2/LLaMA-Factory/data/alpaca_gpt4_zh.json", "w", encoding="utf-8") as f:
json.dump(records, f, ensure_ascii=False)
print(len(records), "records exported")
在 data/dataset_info.json 添加:
json
"alpaca_gpt4_zh_local": {
"file_name": "alpaca_gpt4_zh.json"
}
所有训练 YAML 的 dataset 字段改用 alpaca_gpt4_zh_local。 备选方案:训练前 export USE_MODELSCOPE_HUB=1(仍建议本地副本,离线更稳)。
实测样本规模: 全量 42,677 条(非文档估计的 4.8 万,属正常偏差)。
四、四步实战操作
整体流程:
#mermaid-svg-PDRQGN0ryrH0q0uO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PDRQGN0ryrH0q0uO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PDRQGN0ryrH0q0uO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PDRQGN0ryrH0q0uO .error-icon{fill:#552222;}#mermaid-svg-PDRQGN0ryrH0q0uO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PDRQGN0ryrH0q0uO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PDRQGN0ryrH0q0uO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PDRQGN0ryrH0q0uO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PDRQGN0ryrH0q0uO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PDRQGN0ryrH0q0uO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PDRQGN0ryrH0q0uO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PDRQGN0ryrH0q0uO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PDRQGN0ryrH0q0uO .marker.cross{stroke:#333333;}#mermaid-svg-PDRQGN0ryrH0q0uO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PDRQGN0ryrH0q0uO p{margin:0;}#mermaid-svg-PDRQGN0ryrH0q0uO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PDRQGN0ryrH0q0uO .cluster-label text{fill:#333;}#mermaid-svg-PDRQGN0ryrH0q0uO .cluster-label span{color:#333;}#mermaid-svg-PDRQGN0ryrH0q0uO .cluster-label span p{background-color:transparent;}#mermaid-svg-PDRQGN0ryrH0q0uO .label text,#mermaid-svg-PDRQGN0ryrH0q0uO span{fill:#333;color:#333;}#mermaid-svg-PDRQGN0ryrH0q0uO .node rect,#mermaid-svg-PDRQGN0ryrH0q0uO .node circle,#mermaid-svg-PDRQGN0ryrH0q0uO .node ellipse,#mermaid-svg-PDRQGN0ryrH0q0uO .node polygon,#mermaid-svg-PDRQGN0ryrH0q0uO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PDRQGN0ryrH0q0uO .rough-node .label text,#mermaid-svg-PDRQGN0ryrH0q0uO .node .label text,#mermaid-svg-PDRQGN0ryrH0q0uO .image-shape .label,#mermaid-svg-PDRQGN0ryrH0q0uO .icon-shape .label{text-anchor:middle;}#mermaid-svg-PDRQGN0ryrH0q0uO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PDRQGN0ryrH0q0uO .rough-node .label,#mermaid-svg-PDRQGN0ryrH0q0uO .node .label,#mermaid-svg-PDRQGN0ryrH0q0uO .image-shape .label,#mermaid-svg-PDRQGN0ryrH0q0uO .icon-shape .label{text-align:center;}#mermaid-svg-PDRQGN0ryrH0q0uO .node.clickable{cursor:pointer;}#mermaid-svg-PDRQGN0ryrH0q0uO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PDRQGN0ryrH0q0uO .arrowheadPath{fill:#333333;}#mermaid-svg-PDRQGN0ryrH0q0uO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PDRQGN0ryrH0q0uO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PDRQGN0ryrH0q0uO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PDRQGN0ryrH0q0uO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PDRQGN0ryrH0q0uO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PDRQGN0ryrH0q0uO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PDRQGN0ryrH0q0uO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PDRQGN0ryrH0q0uO .cluster text{fill:#333;}#mermaid-svg-PDRQGN0ryrH0q0uO .cluster span{color:#333;}#mermaid-svg-PDRQGN0ryrH0q0uO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PDRQGN0ryrH0q0uO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PDRQGN0ryrH0q0uO rect.text{fill:none;stroke-width:0;}#mermaid-svg-PDRQGN0ryrH0q0uO .icon-shape,#mermaid-svg-PDRQGN0ryrH0q0uO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PDRQGN0ryrH0q0uO .icon-shape p,#mermaid-svg-PDRQGN0ryrH0q0uO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PDRQGN0ryrH0q0uO .icon-shape .label rect,#mermaid-svg-PDRQGN0ryrH0q0uO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PDRQGN0ryrH0q0uO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PDRQGN0ryrH0q0uO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PDRQGN0ryrH0q0uO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Step1
教师 LoRA SFT
30 Epoch
Step2
合并教师权重
Step3
学生 LoRA 蒸馏
30 Epoch
Step4
合并学生 + 验证
第一步:训练教师模型(LoRA SFT)
目标: 在 alpaca_gpt4_zh 上微调 Qwen3-4B-Instruct-2507,得到 4B LoRA 适配器。
创建配置文件 LLaMA-Factory/examples/train_lora/qwen3_4b_teacher_lora.yaml:
yaml
### model
model_name_or_path: <TEACHER_PATH>
trust_remote_code: true
### method
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 8
lora_target: all
### dataset
dataset: alpaca_gpt4_zh_local
template: qwen3_nothink
cutoff_len: 2048
max_samples: 50 # 冒烟;正式训练删除此行
preprocessing_num_workers: 16
dataloader_num_workers: 4
### output
output_dir: saves/qwen3-4b/lora/teacher-sft
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true
save_only_model: false
report_to: none
### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 1.0e-4
num_train_epochs: 1.0 # 冒烟;正式改为 30.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000
冒烟时 YAML 已含 max_samples: 50 与 num_train_epochs: 1.0;正式训练 删除 max_samples 行,并将 num_train_epochs 改为 30.0。
执行训练:
bash
cd /root/autodl-tmp/Distill2/LLaMA-Factory
llamafactory-cli train examples/train_lora/qwen3_4b_teacher_lora.yaml
产出: saves/qwen3-4b/lora/teacher-sft/(含 LoRA adapter 与 checkpoint)
第二步:合并教师 LoRA 权重
目标: 将 LoRA 适配器与基座合并,得到全参数教师模型 ,供第三步作为 ref_model 提供准确软标签。
创建配置文件 LLaMA-Factory/examples/merge_lora/qwen3_4b_teacher_merge.yaml:
yaml
### Note: DO NOT use quantized model or quantization_bit when merging lora adapters
### model
model_name_or_path: <TEACHER_PATH>
adapter_name_or_path: saves/qwen3-4b/lora/teacher-sft
template: qwen3_nothink
trust_remote_code: true
### export
export_dir: saves/qwen3-4b/merged-teacher
export_size: 5
export_device: cpu
export_legacy_format: false
执行合并:
bash
cd /root/autodl-tmp/Distill2/LLaMA-Factory
llamafactory-cli export examples/merge_lora/qwen3_4b_teacher_merge.yaml
产出: saves/qwen3-4b/merged-teacher/(可直接加载的全参数 safetensors 模型)
为何必须合并? 第三步蒸馏需加载冻结的教师模型做 forward 取 Logits;合并后的全参数模型路径可直接作为
ref_model,软标签与业务 SFT 后的教师行为一致。
第三步:执行白盒知识蒸馏(学生 LoRA + Logits 对齐)
目标: 用合并后的教师,在 alpaca_gpt4_zh 上对 Qwen3-1.7B 做 LoRA 蒸馏,得到 1.7B LoRA 适配器。
工程实现说明
LLaMA-Factory 通过 use_asft_loss: true + ref_model 实现 Logits 级对齐:
- 训练时教师模型
eval()冻结,对学生每个 token 位置输出ref_logits - 损失 = CE(硬标签)+
asft_alpha× KL(学生 Logits vs 教师 Logits) - 源码:
src/llamafactory/train/sft/trainer.py(compute_loss中调用asft_loss_func)
⚠️ LoRA 蒸馏必须显式配置 ref_model: 当 finetuning_type: lora 且未指定 ref_model 时,框架会将 ref_model 设为 None,蒸馏不生效(见 trainer_utils.py 中 create_ref_model)。
理论 vs 实现的差异
| 经典 KD(笔记公式) | LLaMA-Factory ASFT |
|---|---|
| 显式温度 T T T 软化 Logits | 当前实现无独立 T T T 参数 |
| α ⋅ L distill + ( 1 − α ) ⋅ L student \alpha \cdot L_{\text{distill}} + (1-\alpha) \cdot L_{\text{student}} α⋅Ldistill+(1−α)⋅Lstudent | CE + asft_alpha × KL(ref_logits) |
| --- | asft_alpha 建议范围 0.05--0.3,默认 0.1 |
创建配置文件 LLaMA-Factory/examples/train_lora/qwen3_1.7b_distill_lora.yaml:
yaml
### model
model_name_or_path: <STUDENT_PATH>
trust_remote_code: true
### method
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 8
lora_target: all
use_asft_loss: true
asft_alpha: 0.1
ref_model: saves/qwen3-4b/merged-teacher
### dataset
dataset: alpaca_gpt4_zh_local
template: qwen3_nothink
cutoff_len: 2048
max_samples: 50 # 冒烟;正式训练删除此行
preprocessing_num_workers: 16
dataloader_num_workers: 4
### output
output_dir: saves/qwen3-1.7b/lora/distill
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true
save_only_model: false
report_to: none
### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 1.0e-4
num_train_epochs: 1.0 # 冒烟;正式改为 30.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000
显存不足时可尝试:
- 减小
per_device_train_batch_size为 1(已是最小) - 增大
gradient_accumulation_steps - 在 method 段添加 DeepSpeed:
deepspeed: examples/deepspeed/ds_z3_offload_config.json
执行蒸馏训练:
bash
cd /root/autodl-tmp/Distill2/LLaMA-Factory
llamafactory-cli train examples/train_lora/qwen3_1.7b_distill_lora.yaml
产出: saves/qwen3-1.7b/lora/distill/(1.7B LoRA 适配器)
第四步:合并学生模型与效果验证
4.1 合并学生 LoRA 权重
创建配置文件 LLaMA-Factory/examples/merge_lora/qwen3_1.7b_student_merge.yaml:
yaml
### Note: DO NOT use quantized model or quantization_bit when merging lora adapters
### model
model_name_or_path: <STUDENT_PATH>
adapter_name_or_path: saves/qwen3-1.7b/lora/distill
template: qwen3_nothink
trust_remote_code: true
### export
export_dir: saves/qwen3-1.7b/merged-student
export_size: 2
export_device: cpu
export_legacy_format: false
bash
cd /root/autodl-tmp/Distill2/LLaMA-Factory
llamafactory-cli export examples/merge_lora/qwen3_1.7b_student_merge.yaml
产出: saves/qwen3-1.7b/merged-student/(约 1.7B 全参数模型,可直接部署为 safetensors / 转 gguf)
4.2 推理验证
方式 A:LLaMA-Factory 交互对话
bash
cd /root/autodl-tmp/Distill2/LLaMA-Factory
llamafactory-cli chat examples/inference/qwen3_lora_sft.yaml
将 chat 配置中的 model_name_or_path 改为 saves/qwen3-1.7b/merged-student,template 保持 qwen3_nothink。
方式 B:Transformers 脚本(使用 4.3 节保存的验证 prompt)
python
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL_PATH = "/root/autodl-tmp/Distill2/LLaMA-Factory/saves/qwen3-1.7b/merged-student"
PROMPT_FILE = "/root/autodl-tmp/Distill2/validation_prompt.txt"
model = AutoModelForCausalLM.from_pretrained(
MODEL_PATH, torch_dtype="auto", device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)
with open(PROMPT_FILE, encoding="utf-8") as f:
user_content = f.read().strip()
messages = [{"role": "user", "content": user_content}]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer([text], return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=256)
print(tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True))
验收要点:
- 使用同一条 来自
alpaca_gpt4_zh的验证 prompt,分别对 基座 1.7B 、合并教师 、蒸馏学生 推理,对比回答质量 - 蒸馏学生在指令遵循、回答完整度上应更接近教师,优于未蒸馏基座
- 教师与学生在同一 prompt 下的 Logits 分布差异,即 Step 3 ASFT 所要对齐的目标
五、目录结构与产物清单
执行完整流程后,Distill2/ 预期结构:
Distill2/
├── SOP.md # 本文档
├── validation_prompt.txt # 4.3 节从数据集导出的验证 prompt
├── models/ # ModelScope 下载缓存
│ └── models/
│ ├── Qwen--Qwen3-4B-Instruct-2507/snapshots/master/
│ └── Qwen--Qwen3-1.7B/snapshots/master/
└── LLaMA-Factory/
├── data/
│ └── dataset_info.json # 内置 alpaca_gpt4_zh 注册
├── examples/
│ ├── train_lora/
│ │ ├── smoke_data_load.yaml # 4.4 数据加载冒烟
│ │ ├── qwen3_4b_teacher_lora.yaml
│ │ └── qwen3_1.7b_distill_lora.yaml
│ └── merge_lora/
│ ├── qwen3_4b_teacher_merge.yaml
│ └── qwen3_1.7b_student_merge.yaml
└── saves/
├── smoke/data-load/ # 4.4 冒烟产出
├── qwen3-4b/
│ ├── lora/teacher-sft/ # Step1 产出
│ └── merged-teacher/ # Step2 产出 → ref_model
└── qwen3-1.7b/
├── lora/distill/ # Step3 产出
└── merged-student/ # Step4 产出 → 最终部署模型
产物路径对照表:
| 步骤 | 产物 | 路径 |
|---|---|---|
| 1 | 教师 LoRA adapter | saves/qwen3-4b/lora/teacher-sft/ |
| 2 | 合并后全参数教师 | saves/qwen3-4b/merged-teacher/ |
| 3 | 学生 LoRA adapter | saves/qwen3-1.7b/lora/distill/ |
| 4 | 合并后全参数学生 | saves/qwen3-1.7b/merged-student/ |
六、硬件与显存估算
| 步骤 | 加载模型 | 建议 GPU 显存 |
|---|---|---|
| Step 1 教师 LoRA SFT | 4B + LoRA | ≥ 16 GB(24 GB 较稳妥) |
| Step 2 合并教师 | 4B(CPU export) | 主要占用内存,GPU 可选 |
| Step 3 白盒蒸馏 | 4B 教师(冻结)+ 1.7B 学生 + LoRA | ≥ 24 GB |
| Step 4 合并学生 | 1.7B(CPU export) | 主要占用内存 |
| Step 4 推理验证 | 1.7B | ≥ 8 GB |
LoRA 显存参考(来自 LLaMA-Factory 社区经验):
| 模型参数量 | LoRA 训练(约) |
|---|---|
| 4B | ~10--16 GB |
| 1.7B | ~6--10 GB |
| 4B + 1.7B 同卡蒸馏 | ~20--28 GB |
云平台: AutoDL(https://www.autodl.com/)、魔搭社区(https://www.modelscope.cn/)
七、故障排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 蒸馏等同普通 SFT,loss 无 KL 效果 | LoRA 未配置 ref_model |
在蒸馏 YAML 中添加 ref_model: saves/qwen3-4b/merged-teacher |
ref_model 路径找不到 |
Step 2 未执行或路径错误 | 确认 merged-teacher/ 存在且含 model.safetensors |
| CUDA OOM(Step 3) | 双模型同卡显存不足 | 减小 batch、启用 DeepSpeed ZeRO-3 offload、或使用更大显存 GPU |
| 数据集加载失败 | Hub 未下载或 dataset_info.json 无注册项 |
重跑 4.2 预下载;执行 4.1 确认 alpaca_gpt4_zh 存在 |
| 模板不匹配 / 生成乱码 | 教师/学生 template 不一致 | 统一使用 qwen3_nothink |
| 合并失败 | 使用了量化模型 | merge 时不要 设置 quantization_bit |
| 蒸馏后效果接近基座 | 教师 SFT 不充分、epoch 不足或 asft_alpha 过小 |
检查 Step 1/3 loss;正式训练用 30 epoch;尝试 asft_alpha: 0.2 |
| torchaudio / CUDA 版本冲突 | pip 拉取了不兼容包 | 见第三节依赖问题表 |
ModuleNotFoundError: addict |
ModelScope MsDataset 依赖缺失 | pip install addict |
| 训练时 HF 数据集 unreachable | 默认走 HuggingFace Hub | 改用 alpaca_gpt4_zh_local 本地 JSON(见 4.6)或 export USE_MODELSCOPE_HUB=1 |
| 合并/export 报 No space left on device | /root/autodl-tmp 仅 50GB,双模型+合并产物占满 |
见第九节磁盘策略;合并前 df -h 确保 ≥8GB 可用 |
八、验收标准与实施检查清单
验收标准
- 教师 LoRA 训练 loss 收敛;合并后(或 LoRA)在验证 prompt 上给出结构化中文回答
- 蒸馏训练 YAML 中
use_asft_loss: true且ref_model指向合并教师(日志含Created reference model) - 学生合并后参数量约 1.7B(
all params: 1,720,574,976) - 基座 / 教师 / 学生三者对比已完成(见
validation_prompt.txt)
实施检查清单
环境:
-
Distill2/LLaMA-Factory已 clone 且llamafactory-cli version正常(v0.9.6.dev0) - 教师、学生模型已下载至
Distill2/models/
数据集(第三节 4.1--4.4,不可跳过):
- 4.1 确认
alpaca_gpt4_zh已在dataset_info.json注册 - 4.2 预下载完成(ModelScope;需
pip install addict) - 4.3 抽样预览通过,
validation_prompt.txt已生成 - 4.4
smoke_data_load.yaml跑通max_steps: 1(train_loss≈2.11) - 4.6 导出
alpaca_gpt4_zh_local本地 JSON(42,677 条)
四步流程(冒烟 2026-07-08):
- Step 1:教师 LoRA SFT(50 samples, 1 epoch, train_loss≈2.54, 18s)
- Step 2:教师合并(首次因磁盘满失败,清理后成功)
- Step 3:ASFT 蒸馏(train_loss≈0.94, 17s, ref_model 加载成功)
- Step 4:学生合并 + 三路推理验证
冒烟(推荐先做):
-
max_samples: 50、num_train_epochs: 1跑通 Step 1 → 4 全流程 - 冒烟通过后再改为 30 Epoch 正式训练(本次未执行)
附录:命令速查
bash
# 环境
cd /root/autodl-tmp/Distill2/LLaMA-Factory
# 4.4 数据加载冒烟
llamafactory-cli train examples/train_lora/smoke_data_load.yaml
# Step 1 教师 LoRA SFT
llamafactory-cli train examples/train_lora/qwen3_4b_teacher_lora.yaml
# Step 2 合并教师
llamafactory-cli export examples/merge_lora/qwen3_4b_teacher_merge.yaml
# Step 3 白盒蒸馏
llamafactory-cli train examples/train_lora/qwen3_1.7b_distill_lora.yaml
# Step 4 合并学生
llamafactory-cli export examples/merge_lora/qwen3_1.7b_student_merge.yaml
九、实测执行记录(冒烟全流程 2026-07-08)
硬件:NVIDIA RTX 3090 24GB · PyTorch 2.8.0+cu128 · LLaMA-Factory 0.9.6.dev0
日志目录:
Distill2/logs_*.log· 配置已写入LLaMA-Factory/examples/
9.1 环境与路径(实测)
| 项目 | 实测值 |
|---|---|
| 教师路径 | /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-4B-Instruct-2507/snapshots/master |
| 学生路径 | /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-1.7B/snapshots/master |
| 本地数据集 | LLaMA-Factory/data/alpaca_gpt4_zh.json(42,677 条,26MB) |
| 验证 prompt | Distill2/validation_prompt.txt → 保持健康的三个提示。 |
9.2 遇到的问题与解决方案
| # | 阶段 | 问题 | 解决方案 |
|---|---|---|---|
| 1 | 4.2 预下载 | ModuleNotFoundError: No module named 'addict' |
pip install addict |
| 2 | Step 1 训练 | HF Hub unreachable,无法加载 alpaca_gpt4_zh |
导出本地 JSON + 注册 alpaca_gpt4_zh_local;YAML 改 dataset: alpaca_gpt4_zh_local |
| 3 | Step 2 合并 | SafetensorError: No space left on device |
清理磁盘:删除 Distill/models、Distill/saves(~5.8GB)、seg-ui.zip(1.2GB);合并前保持 ≥8GB 可用 |
| 4 | Step 4 合并 | 同上磁盘满(教师+学生+双份合并产物) | 蒸馏完成后删除 saves/qwen3-4b/merged-teacher(~7.6GB)再合并学生;或磁盘扩容至 ≥100GB |
| 5 | 推理验证 | 双模型同时加载 OOM 风险 | 逐模型加载 ,每次 torch.cuda.empty_cache();教师用 PeftModel+LoRA,无需保留合并教师 |
| 6 | 学生回答 | 蒸馏后仍输出 `` 思维链 | Qwen3-1.7B 为 Thinking 基座,1 epoch/50 样本冒烟不足以对齐 Instruct 风格;正式训练需 30 epoch 或换 Instruct 学生 |
磁盘规划建议(50GB 数据盘): 无法同时保留「双基座 + 教师合并 + 学生合并」全副本。推荐流程:
#mermaid-svg-5qQVAl4YQeA4uXOd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5qQVAl4YQeA4uXOd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5qQVAl4YQeA4uXOd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5qQVAl4YQeA4uXOd .error-icon{fill:#552222;}#mermaid-svg-5qQVAl4YQeA4uXOd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5qQVAl4YQeA4uXOd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5qQVAl4YQeA4uXOd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5qQVAl4YQeA4uXOd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5qQVAl4YQeA4uXOd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5qQVAl4YQeA4uXOd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5qQVAl4YQeA4uXOd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5qQVAl4YQeA4uXOd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5qQVAl4YQeA4uXOd .marker.cross{stroke:#333333;}#mermaid-svg-5qQVAl4YQeA4uXOd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5qQVAl4YQeA4uXOd p{margin:0;}#mermaid-svg-5qQVAl4YQeA4uXOd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5qQVAl4YQeA4uXOd .cluster-label text{fill:#333;}#mermaid-svg-5qQVAl4YQeA4uXOd .cluster-label span{color:#333;}#mermaid-svg-5qQVAl4YQeA4uXOd .cluster-label span p{background-color:transparent;}#mermaid-svg-5qQVAl4YQeA4uXOd .label text,#mermaid-svg-5qQVAl4YQeA4uXOd span{fill:#333;color:#333;}#mermaid-svg-5qQVAl4YQeA4uXOd .node rect,#mermaid-svg-5qQVAl4YQeA4uXOd .node circle,#mermaid-svg-5qQVAl4YQeA4uXOd .node ellipse,#mermaid-svg-5qQVAl4YQeA4uXOd .node polygon,#mermaid-svg-5qQVAl4YQeA4uXOd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5qQVAl4YQeA4uXOd .rough-node .label text,#mermaid-svg-5qQVAl4YQeA4uXOd .node .label text,#mermaid-svg-5qQVAl4YQeA4uXOd .image-shape .label,#mermaid-svg-5qQVAl4YQeA4uXOd .icon-shape .label{text-anchor:middle;}#mermaid-svg-5qQVAl4YQeA4uXOd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5qQVAl4YQeA4uXOd .rough-node .label,#mermaid-svg-5qQVAl4YQeA4uXOd .node .label,#mermaid-svg-5qQVAl4YQeA4uXOd .image-shape .label,#mermaid-svg-5qQVAl4YQeA4uXOd .icon-shape .label{text-align:center;}#mermaid-svg-5qQVAl4YQeA4uXOd .node.clickable{cursor:pointer;}#mermaid-svg-5qQVAl4YQeA4uXOd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5qQVAl4YQeA4uXOd .arrowheadPath{fill:#333333;}#mermaid-svg-5qQVAl4YQeA4uXOd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5qQVAl4YQeA4uXOd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5qQVAl4YQeA4uXOd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5qQVAl4YQeA4uXOd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5qQVAl4YQeA4uXOd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5qQVAl4YQeA4uXOd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5qQVAl4YQeA4uXOd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5qQVAl4YQeA4uXOd .cluster text{fill:#333;}#mermaid-svg-5qQVAl4YQeA4uXOd .cluster span{color:#333;}#mermaid-svg-5qQVAl4YQeA4uXOd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5qQVAl4YQeA4uXOd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5qQVAl4YQeA4uXOd rect.text{fill:none;stroke-width:0;}#mermaid-svg-5qQVAl4YQeA4uXOd .icon-shape,#mermaid-svg-5qQVAl4YQeA4uXOd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5qQVAl4YQeA4uXOd .icon-shape p,#mermaid-svg-5qQVAl4YQeA4uXOd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5qQVAl4YQeA4uXOd .icon-shape .label rect,#mermaid-svg-5qQVAl4YQeA4uXOd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5qQVAl4YQeA4uXOd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5qQVAl4YQeA4uXOd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5qQVAl4YQeA4uXOd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Step1 教师LoRA
Step2 合并教师
Step3 蒸馏
删除 merged-teacher 释放7.6GB
Step4 合并学生
Step 3 磁盘不足时的备选: 不合并教师,YAML 改用:
yaml
ref_model: <TEACHER_PATH>
ref_model_adapters: saves/qwen3-4b/lora/teacher-sft
9.3 冒烟训练指标
| 步骤 | 配置 | 耗时 | train_loss | 备注 |
|---|---|---|---|---|
| 4.4 数据加载 | max_samples=5, max_steps=1 | 1.1s | 2.11 | 本地 JSON 加载正常 |
| Step 1 教师 SFT | 50 samples, 1 epoch | 18.3s | 2.54 | 13 steps |
| Step 2 教师合并 | CPU export | ~35s | --- | 产出 ~7.6GB(冒烟后已删) |
| Step 3 蒸馏 | 50 samples, 1 epoch, ASFT | 16.7s | 0.94 | 日志:Created reference model from saves/qwen3-4b/merged-teacher |
| Step 4 学生合并 | CPU export | ~16s | --- | 产出 ~3.4GB |
9.4 推理对比(同一条验证 prompt)
Prompt: 保持健康的三个提示。
| 模型 | 回答特征(摘要) |
|---|---|
| 基座 Qwen3-1.7B | 输出 `` 思维链,未直接给出三点列表 |
| 教师 Qwen3-4B+LoRA | 直接输出结构化三点:均衡饮食 / 规律运动 / 充足睡眠,与训练数据风格一致 |
| 蒸馏学生 merged | 冒烟 1 epoch 后仍偏 Thinking 风格,未明显接近教师(需正式 30 epoch) |
结论: 四步链路已跑通;白盒 ASFT 蒸馏工程链路正确(ref_model 加载、loss 收敛)。效果层面 需正式规模训练;另建议评估将学生换为 Instruct 版本或在 Thinking 模型上使用 template: qwen3 而非 qwen3_nothink。
9.5 实测产物清单
Distill2/
├── validation_prompt.txt
├── logs_step1_teacher.log
├── logs_smoke_data_load.log
├── logs_step2_merge_teacher.log
├── logs_step3_distill.log
├── logs_step4_merge_student.log
└── LLaMA-Factory/
├── data/alpaca_gpt4_zh.json # 本地数据集
├── saves/qwen3-4b/lora/teacher-sft/ # 教师 LoRA
├── saves/qwen3-1.7b/lora/distill/ # 学生 LoRA
└── saves/qwen3-1.7b/merged-student/ # 最终可部署模型
参考文档:
- 理论笔记:
/root/autodl-tmp/Distillation.md - 黑盒蒸馏 SOP:
/root/autodl-tmp/Distill/SOP.md - LLaMA-Factory 官方示例:
examples/train_lora/qwen3_lora_sft.yaml、examples/extras/asft/、examples/merge_lora/