基于千问的白盒蒸馏操作教学文档

本文档带你从零开始完成白盒 Logits 蒸馏:环境搭建 → 数据集准备 → 教师 SFT → 合并 → ASFT 蒸馏 → 学生合并 → 推理对比。

理论背景与实测记录见 基于千问的白盒蒸馏 SOP;黑盒蒸馏(API 数据)。


目录

  1. 开始之前
  2. [第一步:克隆 LLaMA-Factory](#第一步:克隆 LLaMA-Factory)
  3. 第二步:安装依赖
  4. 第三步:下载模型(download_models.py)
  5. 第四步:准备数据集(prepare_dataset.py)
  6. 第五步:估算训练时间(estimate_time.py)
  7. 第六步:冒烟验证(可选但推荐)
  8. [第七步:正式四步训练(30 epoch)](#第七步:正式四步训练(30 epoch))
  9. 第八步:推理对比(run_inference_compare.py)
  10. 一键脚本(run_full_pipeline.sh)
  11. [YAML 配置(全部 8 个)](#YAML 配置(全部 8 个))
  12. [常见问题 FAQ](#常见问题 FAQ)
  13. [附录:项目资产全文(Python + YAML)](#附录:项目资产全文(Python + YAML))

第0步:开始之前

0.1 硬件与平台

项目 最低要求 实测配置
GPU Step1/2 ≥16GB;Step3 双模型 ≥24GB RTX 3090 24GB
数据盘 建议 ≥100GB(50GB 盘需精细清理) AutoDL 50GB(实测紧张)
内存 ≥16GB 默认即可
Python 3.10+ 3.12
PyTorch 2.x + CUDA 2.7.0+cu128

0.2 你要完成什么

#mermaid-svg-0jqebTqZNlzS8z8I{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-0jqebTqZNlzS8z8I .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0jqebTqZNlzS8z8I .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0jqebTqZNlzS8z8I .error-icon{fill:#552222;}#mermaid-svg-0jqebTqZNlzS8z8I .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0jqebTqZNlzS8z8I .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0jqebTqZNlzS8z8I .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0jqebTqZNlzS8z8I .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0jqebTqZNlzS8z8I .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0jqebTqZNlzS8z8I .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0jqebTqZNlzS8z8I .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0jqebTqZNlzS8z8I .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0jqebTqZNlzS8z8I .marker.cross{stroke:#333333;}#mermaid-svg-0jqebTqZNlzS8z8I svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0jqebTqZNlzS8z8I p{margin:0;}#mermaid-svg-0jqebTqZNlzS8z8I .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0jqebTqZNlzS8z8I .cluster-label text{fill:#333;}#mermaid-svg-0jqebTqZNlzS8z8I .cluster-label span{color:#333;}#mermaid-svg-0jqebTqZNlzS8z8I .cluster-label span p{background-color:transparent;}#mermaid-svg-0jqebTqZNlzS8z8I .label text,#mermaid-svg-0jqebTqZNlzS8z8I span{fill:#333;color:#333;}#mermaid-svg-0jqebTqZNlzS8z8I .node rect,#mermaid-svg-0jqebTqZNlzS8z8I .node circle,#mermaid-svg-0jqebTqZNlzS8z8I .node ellipse,#mermaid-svg-0jqebTqZNlzS8z8I .node polygon,#mermaid-svg-0jqebTqZNlzS8z8I .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0jqebTqZNlzS8z8I .rough-node .label text,#mermaid-svg-0jqebTqZNlzS8z8I .node .label text,#mermaid-svg-0jqebTqZNlzS8z8I .image-shape .label,#mermaid-svg-0jqebTqZNlzS8z8I .icon-shape .label{text-anchor:middle;}#mermaid-svg-0jqebTqZNlzS8z8I .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0jqebTqZNlzS8z8I .rough-node .label,#mermaid-svg-0jqebTqZNlzS8z8I .node .label,#mermaid-svg-0jqebTqZNlzS8z8I .image-shape .label,#mermaid-svg-0jqebTqZNlzS8z8I .icon-shape .label{text-align:center;}#mermaid-svg-0jqebTqZNlzS8z8I .node.clickable{cursor:pointer;}#mermaid-svg-0jqebTqZNlzS8z8I .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0jqebTqZNlzS8z8I .arrowheadPath{fill:#333333;}#mermaid-svg-0jqebTqZNlzS8z8I .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0jqebTqZNlzS8z8I .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0jqebTqZNlzS8z8I .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0jqebTqZNlzS8z8I .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0jqebTqZNlzS8z8I .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0jqebTqZNlzS8z8I .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0jqebTqZNlzS8z8I .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0jqebTqZNlzS8z8I .cluster text{fill:#333;}#mermaid-svg-0jqebTqZNlzS8z8I .cluster span{color:#333;}#mermaid-svg-0jqebTqZNlzS8z8I 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-0jqebTqZNlzS8z8I .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0jqebTqZNlzS8z8I rect.text{fill:none;stroke-width:0;}#mermaid-svg-0jqebTqZNlzS8z8I .icon-shape,#mermaid-svg-0jqebTqZNlzS8z8I .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0jqebTqZNlzS8z8I .icon-shape p,#mermaid-svg-0jqebTqZNlzS8z8I .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0jqebTqZNlzS8z8I .icon-shape .label rect,#mermaid-svg-0jqebTqZNlzS8z8I .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0jqebTqZNlzS8z8I .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0jqebTqZNlzS8z8I .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0jqebTqZNlzS8z8I :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} prepare_dataset.py

42,677 条中文指令
Step1 教师 LoRA SFT

Qwen3-4B
Step2 合并教师
Step3 ASFT 蒸馏

Qwen3-1.7B + ref_model
Step4 合并学生

白盒 vs 黑盒: 黑盒用 API 文本当标签;白盒用教师 forward 的 Logits 做 KL 对齐(LLaMA-Factory 的 use_asft_loss)。

0.3 最终目录结构

复制代码
/root/autodl-tmp/Distill2/
├── SOP.md                      # 理论 + 实测记录
├── TUTORIAL.md                 # 本文档
├── download_models.py          # 下载教师/学生基座
├── prepare_dataset.py          # 下载并导出本地数据集
├── estimate_time.py            # 30 epoch 耗时估算
├── run_inference_compare.py      # 三路推理对比
├── validation_prompt.txt         # 从数据集第 0 条导出的验证 prompt
├── logs_*.log                    # 各步日志
├── models/                       # ModelScope 模型缓存 (~11GB)
└── LLaMA-Factory/
    ├── data/alpaca_gpt4_zh.json
    ├── examples/train_lora/
    │   ├── qwen3_4b_teacher_lora.yaml        # 冒烟
    │   ├── qwen3_4b_teacher_lora_full.yaml   # 正式 30 epoch
    │   ├── qwen3_1.7b_distill_lora.yaml      # 冒烟(含 ASFT)
    │   └── qwen3_1.7b_distill_lora_full.yaml # 正式 30 epoch
    ├── examples/merge_lora/
    │   ├── qwen3_4b_teacher_merge.yaml       # Step2 冒烟合并
    │   ├── qwen3_4b_teacher_merge_full.yaml  # Step2 正式合并
    │   ├── qwen3_1.7b_student_merge.yaml     # Step4 冒烟合并
    │   └── qwen3_1.7b_student_merge_full.yaml
    └── saves/                    # 训练产出
        └── benchmark/tmp/        # 微基准 train_results.json

第一步:克隆 LLaMA-Factory

bash 复制代码
mkdir -p /root/autodl-tmp/Distill2
cd /root/autodl-tmp/Distill2

source /etc/network_turbo   # AutoDL 访问 GitHub 前必做
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git

检查点: 存在 LLaMA-Factory/src/llamafactory/


第二步:安装依赖

bash 复制代码
cd /root/autodl-tmp/Distill2/LLaMA-Factory
pip install -e ".[torch,metrics]"
pip install addict   # ModelScope MsDataset 需要
llamafactory-cli version
问题 解决
torchaudio 与 torch 版本不匹配 pip install "torchaudio==2.7.0" --index-url https://download.pytorch.org/whl/cu128(须与当前 torch 主版本一致)
OMP 警告 Invalid value for OMP_NUM_THREADS export OMP_NUM_THREADS=1(勿设为 0

第三步:下载模型(download_models.py)

3.1 脚本作用

download_models.pyModelScope 下载两个基座模型到本地,避免训练时联网。

变量 Model ID 角色
TEACHER_ID Qwen/Qwen3-4B-Instruct-2507 教师(4B Instruct)
STUDENT_ID Qwen/Qwen3-1.7B 学生(1.7B Thinking 基座)
LOCAL_DIR /root/autodl-tmp/Distill2/models 缓存根目录

3.2 逐行说明

python 复制代码
from modelscope import snapshot_download

调用魔搭下载 API;cache_dir 下会生成 models/Qwen--.../snapshots/master 嵌套路径。

python 复制代码
for mid in (TEACHER_ID, STUDENT_ID):
    path = snapshot_download(mid, cache_dir=LOCAL_DIR)

循环下载;教师约 7.6GB,学生约 3.8GB,合计 ~35 分钟(视网速)。

3.3 运行

bash 复制代码
source /etc/network_turbo
cd /root/autodl-tmp/Distill2
python3 download_models.py

检查点: 打印两条 OK: 路径。后续 YAML 中使用:

复制代码
教师: .../Qwen--Qwen3-4B-Instruct-2507/snapshots/master
学生: .../Qwen--Qwen3-1.7B/snapshots/master

第四步:准备数据集(prepare_dataset.py)

4.1 为什么需要本地 JSON

实测:LLaMA-Factory 加载 Hub 名 alpaca_gpt4_zh默认走 HuggingFace ,AutoDL 无加速时常报 Network is unreachable。因此本教程强制导出本地副本

4.2 脚本作用

prepare_dataset.py 完成三件事:

  1. 从 ModelScope(失败则 HuggingFace)加载 llamafactory/alpaca_gpt4_zh
  2. 导出为 LLaMA-Factory/data/alpaca_gpt4_zh.json
  3. dataset_info.json 注册 alpaca_gpt4_zh_local,并写入 validation_prompt.txt

4.3 关键函数

load_hub_dataset() --- 优先 ModelScope,失败回退 HF:

python 复制代码
ds = MsDataset.load(HUB_ID, split="train")  # 需 pip install addict

ensure_registered() --- 向 dataset_info.json 追加:

json 复制代码
"alpaca_gpt4_zh_local": { "file_name": "alpaca_gpt4_zh.json" }

训练 YAML 中写 dataset: alpaca_gpt4_zh_local 即可离线加载。

export_validation_prompt(ds) --- 取第 0 条样本的 instruction(+input)写入 validation_prompt.txt,供第八步推理对比。内容来自真实数据,非手写。

4.4 运行

bash 复制代码
source /etc/network_turbo   # 首次下载需要
pip install addict
python3 prepare_dataset.py

检查点:

  • 打印 exported 42677 records
  • 存在 validation_prompt.txt(实测为:保持健康的三个提示。

第五步:估算训练时间(estimate_time.py)

5.1 为什么先估算

30 epoch × 全量 42,677 条在 RTX 3090 上约 9~10 天。正式开跑前应心里有数,并确保磁盘、实例不会中途释放。

5.2 脚本原理

estimate_time.py 根据优化步数外推:

复制代码
steps_per_epoch = ceil(样本数 / (batch_size × grad_accum))
                = ceil(42677 / 4) = 10,670
total_steps     = 10,670 × 30 = 320,100
耗时            = total_steps / train_steps_per_second

速度来源按优先级分三种:

模式 触发条件 说明
微基准 传入 --micro-samples N 现场跑 N 条、ceil(N/4) 步训练,实测 train_steps_per_second(最准,约 2~3 分钟)
冒烟外推 第六节已跑完 读取 saves/qwen3-4b/lora/teacher-sft/train_results.jsonsaves/qwen3-1.7b/lora/distill/train_results.json
内置参考值 无上述文件且未开微基准 使用 RTX 3090 参考速度(教师 0.71、蒸馏 0.778 steps/s)

冒烟 YAML 全文见本文档 [YAML 配置](#YAML 配置) 一节,文件路径:

5.3 运行(正式训练前必做)

方式 A:快速估算(无需 GPU 训练,默认)

bash 复制代码
cd /root/autodl-tmp/Distill2
python3 estimate_time.py --epochs 30

无第六节冒烟产物时,脚本自动使用内置参考速度,不会 因缺少 train_results.json 报错。

方式 B:微基准(本机实测,推荐)

bash 复制代码
cd /root/autodl-tmp/Distill2/LLaMA-Factory
python3 estimate_time.py --epochs 30 --micro-samples 50

也可在根目录执行(脚本会调用 LLaMA-Factory 下的 YAML 与 llamafactory-cli):

bash 复制代码
cd /root/autodl-tmp/Distill2
python3 estimate_time.py --epochs 30 --micro-samples 50

微基准产物写入 LLaMA-Factory/saves/benchmark/tmp/{teacher,distill}/train_results.json

5.4 实测外推结果(RTX 3090,2026-07-08)

内置参考值 / 冒烟外推(教师 0.71、蒸馏 0.778 steps/s):

阶段 优化步数 预估耗时
Step1 教师 LoRA 320,100 ~5.2 天(125h)
Step3 ASFT 蒸馏 320,100 ~4.8 天(114h)
Step2+4 合并 --- ~1 分钟
合计 --- ~10.0 天(240h)

微基准实测(--micro-samples 50,教师 0.689、蒸馏 0.857 steps/s):

阶段 优化步数 预估耗时
Step1 教师 LoRA 320,100 ~5.4 天(129h)
Step3 ASFT 蒸馏 320,100 ~4.3 天(104h)
Step2+4 合并 --- ~1 分钟
合计 --- ~9.7 天(233h)

另计:首次全量 tokenize 预处理 +10~30 分钟

5.5 参数说明

参数 含义
--epochs 30 训练轮数
--samples 42677 样本数(可改小做子集实验)
--micro-samples 50 可选,先跑微基准再估算(50 与冒烟规模一致)

第六步:冒烟验证

在投入约 10 天之前,用 50 条 / 1 epoch 验证四步链路(约 5 分钟)。8 个 YAML 完整正文见 [第十一节 YAML 配置](#8 个 YAML 完整正文见 第十一节 YAML 配置),本节冒烟使用其中前 4 个:

步骤 YAML
Step1 教师 LoRA examples/train_lora/qwen3_4b_teacher_lora.yaml
Step2 合并教师 examples/merge_lora/qwen3_4b_teacher_merge.yaml
Step3 白盒蒸馏 examples/train_lora/qwen3_1.7b_distill_lora.yaml
Step4 合并学生 examples/merge_lora/qwen3_1.7b_student_merge.yaml

6.1 执行命令

bash 复制代码
cd /root/autodl-tmp/Distill2/LLaMA-Factory
export OMP_NUM_THREADS=1

llamafactory-cli train examples/train_lora/qwen3_4b_teacher_lora.yaml
llamafactory-cli export examples/merge_lora/qwen3_4b_teacher_merge.yaml
llamafactory-cli train examples/train_lora/qwen3_1.7b_distill_lora.yaml
llamafactory-cli export examples/merge_lora/qwen3_1.7b_student_merge.yaml

6.2 检查点

复制代码
saves/qwen3-4b/lora/teacher-sft/train_results.json
saves/qwen3-1.7b/lora/distill/train_results.json

检查点: Step3 日志出现 Created reference model from saves/qwen3-4b/merged-teacher


第七步:正式四步训练(30 epoch)

7.1 磁盘准备(50GB 盘必看)

合并教师需额外 ~7.6GB。建议开跑前:

bash 复制代码
df -h /root/autodl-tmp   # 确保 Step2 前 ≥8GB 可用
# 可删除旧冒烟产物、merged-student 等(Step4 会重建)
rm -rf LLaMA-Factory/saves/qwen3-1.7b/merged-student

磁盘策略: Step3 完成后可删 merged-teacher 再合并学生(见 SOP 9.2)。

7.2 分步执行(推荐,便于断点续训)

8 个 YAML 完整正文见 [第十一节 YAML 配置](#8 个 YAML 完整正文见 第十一节 YAML 配置) ,本节正式训练使用后 4 个(*_full.yaml):

步骤 YAML
Step1 教师 30 epoch examples/train_lora/qwen3_4b_teacher_lora_full.yaml
Step2 合并教师 examples/merge_lora/qwen3_4b_teacher_merge_full.yaml
Step3 蒸馏 30 epoch examples/train_lora/qwen3_1.7b_distill_lora_full.yaml
Step4 合并学生 examples/merge_lora/qwen3_1.7b_student_merge_full.yaml
bash 复制代码
cd /root/autodl-tmp/Distill2/LLaMA-Factory
export OMP_NUM_THREADS=1

# Step1 教师(输出 -> saves/qwen3-4b/lora/teacher-sft-full)
llamafactory-cli train examples/train_lora/qwen3_4b_teacher_lora_full.yaml \
  2>&1 | tee ../logs_full_step1_teacher.log

# Step2 合并教师(输出 -> saves/qwen3-4b/merged-teacher)
llamafactory-cli export examples/merge_lora/qwen3_4b_teacher_merge_full.yaml \
  2>&1 | tee ../logs_full_step2_merge_teacher.log

# Step3 蒸馏(输出 -> saves/qwen3-1.7b/lora/distill-full)
llamafactory-cli train examples/train_lora/qwen3_1.7b_distill_lora_full.yaml \
  2>&1 | tee ../logs_full_step3_distill.log

# Step4 合并学生(输出 -> saves/qwen3-1.7b/merged-student)
llamafactory-cli export examples/merge_lora/qwen3_1.7b_student_merge_full.yaml \
  2>&1 | tee ../logs_full_step4_merge_student.log

7.3 冒烟 vs 正式 YAML 差异

字段 冒烟 正式 full
max_samples 50 删除(全量)
num_train_epochs 1.0 30.0
output_dir teacher-sft / distill teacher-sft-full / distill-full
save_steps 100 1000
adapter_name_or_path(merge) teacher-sft / distill teacher-sft-full / distill-full

7.4 断点续训

若 Step1 中断,在 YAML 中添加:

yaml 复制代码
resume_from_checkpoint: saves/qwen3-4b/lora/teacher-sft-full/checkpoint-XXXX

第八步:推理对比(run_inference_compare.py)

8.1 脚本作用

run_inference_compare.py同一条 validation_prompt.txt,依次加载三个模型并生成回答,避免显存同时加载多模型 OOM。

顺序 模型 加载方式
1 基座 Qwen3-1.7B 直接 from_pretrained(STUDENT_BASE)
2 教师 Qwen3-4B+LoRA PeftModel.from_pretrained(基座, TEACHER_LORA)
3 蒸馏学生 from_pretrained(STUDENT_MERGED)

8.2 关键逻辑

python 复制代码
def generate(model, tokenizer, user_content, max_new_tokens=200):
    messages = [{"role": "user", "content": user_content}]
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    ...
    outputs = model.generate(..., do_sample=False)  # 贪心解码,便于对比

每轮推理后 del model; torch.cuda.empty_cache() 释放显存。

8.3 运行

bash 复制代码
cd /root/autodl-tmp/Distill2
python3 run_inference_compare.py
# 指定合并路径(可选)
python3 run_inference_compare.py --student-merged LLaMA-Factory/saves/qwen3-1.7b/merged-student

如何解读结果:

  • 教师 应输出结构化中文列表(与 Alpaca 数据风格一致)
  • 蒸馏学生 正式 30 epoch 后应更接近教师;冒烟 1 epoch 可能仍带 Thinking 模型思维链

一键脚本(run_full_pipeline.sh)

run_full_pipeline.sh 尚未提供;可按下列顺序手动执行,或自行封装为脚本。每步日志建议 teelogs_full_*.log

bash 复制代码
# 脚本内容摘要
python3 estimate_time.py --epochs 30 --micro-samples 50  # 微基准估算(可选 --epochs 30 快速估算)
llamafactory-cli train ...teacher_lora_full.yaml
llamafactory-cli export ...teacher_merge_full.yaml
llamafactory-cli train ...distill_lora_full.yaml
llamafactory-cli export ...student_merge_full.yaml
bash 复制代码
cd /root/autodl-tmp/Distill2
# nohup bash run_full_pipeline.sh > logs_full_pipeline.log 2>&1 &
# tail -f logs_full_pipeline.log

注意: 全流程约 9~10 天,务必使用 nohup/后台实例,并监控磁盘。


YAML 配置(全部 8 个)

以下为项目创建的 8 个 YAML 完整正文,与 LLaMA-Factory/examples/ 下磁盘文件一致。第六节冒烟用前 4 个,第七节正式训练用后 4 个。

微基准说明: estimate_time.py --micro-samples 使用冒烟训练 YAML(qwen3_4b_teacher_lora.yamlqwen3_1.7b_distill_lora.yaml),会临时将 num_train_epochs 替换为 max_steps

冒烟训练 / 合并(4 个)

qwen3_4b_teacher_lora.yaml --- Step1 教师

路径:LLaMA-Factory/examples/train_lora/qwen3_4b_teacher_lora.yaml

yaml 复制代码
### model
model_name_or_path: /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-4B-Instruct-2507/snapshots/master
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
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: 100
plot_loss: true
overwrite_output_dir: true
save_only_model: false
report_to: none

### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 4
learning_rate: 1.0e-4
num_train_epochs: 1.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000
resume_from_checkpoint: null
qwen3_4b_teacher_merge.yaml --- Step2 合并教师

路径: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: /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-4B-Instruct-2507/snapshots/master
adapter_name_or_path: saves/qwen3-4b/lora/teacher-sft
template: qwen3
trust_remote_code: true

### export
export_dir: saves/qwen3-4b/merged-teacher
export_size: 5
export_device: cpu
export_legacy_format: false
qwen3_1.7b_distill_lora.yaml --- Step3 白盒蒸馏

路径:LLaMA-Factory/examples/train_lora/qwen3_1.7b_distill_lora.yaml

yaml 复制代码
### model
model_name_or_path: /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-1.7B/snapshots/master
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
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: 100
plot_loss: true
overwrite_output_dir: true
save_only_model: false
report_to: none

### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 4
learning_rate: 1.0e-4
num_train_epochs: 1.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000
resume_from_checkpoint: null
qwen3_1.7b_student_merge.yaml --- Step4 合并学生

路径: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: /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-1.7B/snapshots/master
adapter_name_or_path: saves/qwen3-1.7b/lora/distill
template: qwen3
trust_remote_code: true

### export
export_dir: saves/qwen3-1.7b/merged-student
export_size: 2
export_device: cpu
export_legacy_format: false

正式训练 / 合并(4 个)

qwen3_4b_teacher_lora_full.yaml --- Step1 教师 30 epoch

路径:LLaMA-Factory/examples/train_lora/qwen3_4b_teacher_lora_full.yaml

yaml 复制代码
### model
model_name_or_path: /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-4B-Instruct-2507/snapshots/master
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
cutoff_len: 2048
preprocessing_num_workers: 16
dataloader_num_workers: 4

### output
output_dir: saves/qwen3-4b/lora/teacher-sft-full
logging_steps: 10
save_steps: 1000
plot_loss: true
overwrite_output_dir: true
save_only_model: false
report_to: none

### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 4
learning_rate: 1.0e-4
num_train_epochs: 30.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000
resume_from_checkpoint: null
qwen3_4b_teacher_merge_full.yaml --- Step2 合并教师

路径:LLaMA-Factory/examples/merge_lora/qwen3_4b_teacher_merge_full.yaml

yaml 复制代码
### Note: DO NOT use quantized model or quantization_bit when merging lora adapters

### model
model_name_or_path: /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-4B-Instruct-2507/snapshots/master
adapter_name_or_path: saves/qwen3-4b/lora/teacher-sft-full
template: qwen3
trust_remote_code: true

### export
export_dir: saves/qwen3-4b/merged-teacher
export_size: 5
export_device: cpu
export_legacy_format: false
qwen3_1.7b_distill_lora_full.yaml --- Step3 蒸馏 30 epoch

路径:LLaMA-Factory/examples/train_lora/qwen3_1.7b_distill_lora_full.yaml

yaml 复制代码
### model
model_name_or_path: /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-1.7B/snapshots/master
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
cutoff_len: 2048
preprocessing_num_workers: 16
dataloader_num_workers: 4

### output
output_dir: saves/qwen3-1.7b/lora/distill-full
logging_steps: 10
save_steps: 1000
plot_loss: true
overwrite_output_dir: true
save_only_model: false
report_to: none

### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 4
learning_rate: 1.0e-4
num_train_epochs: 30.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000
resume_from_checkpoint: null
qwen3_1.7b_student_merge_full.yaml --- Step4 合并学生

路径:LLaMA-Factory/examples/merge_lora/qwen3_1.7b_student_merge_full.yaml

yaml 复制代码
### Note: DO NOT use quantized model or quantization_bit when merging lora adapters

### model
model_name_or_path: /root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-1.7B/snapshots/master
adapter_name_or_path: saves/qwen3-1.7b/lora/distill-full
template: qwen3
trust_remote_code: true

### export
export_dir: saves/qwen3-1.7b/merged-student
export_size: 2
export_device: cpu
export_legacy_format: false

逐字段说明

qwen3_1.7b_distill_lora.yaml 为例:

字段 含义
model_name_or_path 本地模型路径 教师 4B Instruct / 学生 1.7B 基座
stage: sft 监督微调阶段 蒸馏也在 SFT 框架内完成
finetuning_type: lora LoRA 只训练低秩适配器,省显存
lora_rank: 8 秩 8 越大容量越大、越慢
use_asft_loss: true 白盒蒸馏 损失 = CE + α·KL(teacher logits)
asft_alpha: 0.1 KL 权重 对应理论笔记中的 α
ref_model saves/qwen3-4b/merged-teacher 必须指向合并后的教师
dataset alpaca_gpt4_zh_local 本地 JSON,离线可用
template: qwen3 聊天模板 4B Instruct 可试 qwen3_nothink;1.7B Thinking 基座用 qwen3
cutoff_len: 2048 最大序列长 超长样本截断;estimate_time.py 微基准在此注入 max_samples
max_samples: 50 冒烟样本数 正式 full 版删除此行
output_dir teacher-sft / distill 冒烟输出目录;正式版改为 *-full
per_device_train_batch_size: 1 微批次 显存紧张保持 1
gradient_accumulation_steps: 4 梯度累积 等效 batch=4;与 estimate_time.py 外推公式一致
num_train_epochs 1.0(冒烟)/ 30.0(正式) 训练轮数;微基准时脚本替换为 max_steps
bf16: true 混合精度 3090 推荐开启

常见问题 FAQ

Q1:训练报 HF Hub unreachable?

A:确认 YAML 使用 alpaca_gpt4_zh_local,并重新运行 prepare_dataset.py

Q2:合并报 No space left on device?

A:df -h 检查;删除旧 merged 产物或扩容;Step3 后删 merged-teacher 再合并学生。

Q3:蒸馏和普通 SFT 一样?

A:检查 Step3 是否 use_asft_loss: true 且日志有 Created reference model

Q4:30 epoch 太久怎么办?

A:先用 max_samples: 5000 + 3 epoch 验证趋势;或子集 + 正式 epoch 折中。

Q5:学生回答全是思维链?

A:Qwen3-1.7B 为 Thinking 基座;可换 Instruct 学生,或 template: qwen3,并跑满 30 epoch。

Q6:estimate_time.pytrain_results.json 不存在?

A:旧版脚本会直接崩溃。当前版本已支持内置参考值;或加 --micro-samples 50 跑微基准;或先完成第六节冒烟生成 train_results.json

Q7:llamafactory-cli 报 torchaudio / libcudart 错误?

A:安装与 torch 同主版本的 torchaudio(如 pip install "torchaudio==2.7.0" --index-url https://download.pytorch.org/whl/cu128)。


完整命令速查

bash 复制代码
cd /root/autodl-tmp/Distill2
source /etc/network_turbo

python3 download_models.py
pip install addict && python3 prepare_dataset.py
python3 estimate_time.py --epochs 30                    # 快速估算
python3 estimate_time.py --epochs 30 --micro-samples 50  # 微基准(更准确)

cd LLaMA-Factory
export OMP_NUM_THREADS=1
llamafactory-cli train examples/train_lora/qwen3_4b_teacher_lora_full.yaml
llamafactory-cli export examples/merge_lora/qwen3_4b_teacher_merge_full.yaml
llamafactory-cli train examples/train_lora/qwen3_1.7b_distill_lora_full.yaml
llamafactory-cli export examples/merge_lora/qwen3_1.7b_student_merge_full.yaml

cd ..
python3 run_inference_compare.py

附录:项目资产全文(Python + YAML)

本附录收录项目自有的 Python 脚本训练/合并 YAML 的完整内容(不含数据集 JSON、模型权重、saves/ 中间产物等)。

Python 脚本在根目录与 LLaMA-Factory/ 下各有一份内容相同的副本 ;下文以根目录路径为准,副本路径将 Distill2/ 换为 Distill2/LLaMA-Factory/ 即可。

download_models.py

路径:download_models.py(副本:LLaMA-Factory/download_models.py

python 复制代码
"""从 ModelScope 下载教师/学生基座模型。"""
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"

if __name__ == "__main__":
    for mid in (TEACHER_ID, STUDENT_ID):
        print(f"=== Downloading {mid} ===")
        path = snapshot_download(mid, cache_dir=LOCAL_DIR)
        print(f"OK: {path}")

prepare_dataset.py

路径:prepare_dataset.py(副本:LLaMA-Factory/prepare_dataset.py

python 复制代码
"""预下载 alpaca_gpt4_zh 并导出为 LLaMA-Factory 本地 JSON。"""
import json
from pathlib import Path

HUB_ID = "llamafactory/alpaca_gpt4_zh"
OUT_DIR = Path("/root/autodl-tmp/Distill2/LLaMA-Factory/data")
OUT_FILE = OUT_DIR / "alpaca_gpt4_zh.json"
VALIDATION_PROMPT = Path("/root/autodl-tmp/Distill2/validation_prompt.txt")
DATASET_INFO = OUT_DIR / "dataset_info.json"
REG_KEY = "alpaca_gpt4_zh_local"


def load_hub_dataset():
    try:
        from modelscope.msdatasets import MsDataset

        ds = MsDataset.load(HUB_ID, split="train")
        print("source: modelscope")
        return ds
    except Exception as exc:
        print("modelscope failed:", exc)
        from datasets import load_dataset

        ds = load_dataset(HUB_ID, split="train")
        print("source: huggingface")
        return ds


def ensure_registered():
    info = json.loads(DATASET_INFO.read_text(encoding="utf-8"))
    if REG_KEY not in info:
        info[REG_KEY] = {"file_name": "alpaca_gpt4_zh.json"}
        DATASET_INFO.write_text(json.dumps(info, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
        print(f"registered {REG_KEY} in dataset_info.json")
    else:
        print(f"{REG_KEY} already registered")


def export_validation_prompt(ds):
    row = ds[0]
    prompt = row["instruction"]
    if row.get("input"):
        prompt = prompt + "\n" + row["input"]
    VALIDATION_PROMPT.write_text(prompt, encoding="utf-8")
    print(f"wrote validation prompt -> {VALIDATION_PROMPT}")


def main():
    ds = load_hub_dataset()
    records = [dict(ds[i]) for i in range(len(ds))]
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    OUT_FILE.write_text(json.dumps(records, ensure_ascii=False), encoding="utf-8")
    print(f"exported {len(records)} records -> {OUT_FILE}")
    ensure_registered()
    export_validation_prompt(ds)


if __name__ == "__main__":
    main()

estimate_time.py

路径:estimate_time.py(副本:LLaMA-Factory/estimate_time.py

python 复制代码
"""根据小规模实测或冒烟指标,估算 30 epoch 全量训练耗时。"""
from __future__ import annotations

import argparse
import json
import math
import os
import subprocess
import sys
from pathlib import Path

LF_ROOT = Path("/root/autodl-tmp/Distill2/LLaMA-Factory")
FULL_SAMPLES = 42_677
# 2026-07-08 RTX 3090 冒烟实测(50 条 / 1 epoch);无冒烟产物时作 fallback
DEFAULT_TEACHER_STEPS_PER_SEC = 0.71
DEFAULT_DISTILL_STEPS_PER_SEC = 0.778

TEACHER_SMOKE_RESULTS = LF_ROOT / "saves/qwen3-4b/lora/teacher-sft/train_results.json"
DISTILL_SMOKE_RESULTS = LF_ROOT / "saves/qwen3-1.7b/lora/distill/train_results.json"


def load_smoke_metrics() -> dict | None:
    """读取第六节冒烟产物;不存在则返回 None(第五节可在冒烟前运行)。"""
    if not TEACHER_SMOKE_RESULTS.is_file() or not DISTILL_SMOKE_RESULTS.is_file():
        return None
    teacher = json.loads(TEACHER_SMOKE_RESULTS.read_text())
    distill = json.loads(DISTILL_SMOKE_RESULTS.read_text())
    return {"teacher": teacher, "distill": distill}


def steps_per_epoch(num_samples: int, batch: int = 1, grad_accum: int = 4) -> int:
    return math.ceil(num_samples / (batch * grad_accum))


def estimate_from_metrics(num_samples: int, epochs: int, steps_per_sec: float) -> dict:
    steps = steps_per_epoch(num_samples) * epochs
    seconds = steps / max(steps_per_sec, 1e-9)
    return {
        "num_samples": num_samples,
        "epochs": epochs,
        "optimizer_steps": steps,
        "seconds": seconds,
        "hours": seconds / 3600,
    }


def run_micro_benchmark(samples: int, yaml_path: Path) -> float:
    """跑 max_samples=N、max_steps=ceil(N/4) 的微基准,返回 steps/sec。"""
    import tempfile

    base = yaml_path.read_text()
    micro_steps = max(1, math.ceil(samples / 4))
    patched = base
    for key in ("max_samples: 50", "max_samples: 100", "num_train_epochs: 1.0"):
        patched = patched.replace(key, "")
    if "max_samples:" not in patched:
        patched = patched.replace(
            "cutoff_len: 2048",
            f"cutoff_len: 2048\nmax_samples: {samples}",
        )
    patched = patched.replace("num_train_epochs: 30.0", f"max_steps: {micro_steps}")
    if "max_steps:" not in patched:
        patched = patched.replace("num_train_epochs: 1.0", f"max_steps: {micro_steps}")

    with tempfile.NamedTemporaryFile("w", suffix=".yaml", delete=False) as f:
        f.write(patched)
        tmp = Path(f.name)

    out_dir = LF_ROOT / "saves/benchmark/tmp"
    patched = patched.replace(
        "output_dir: saves/qwen3-4b/lora/teacher-sft",
        f"output_dir: {out_dir / 'teacher'}",
    ).replace(
        "output_dir: saves/qwen3-1.7b/lora/distill",
        f"output_dir: {out_dir / 'distill'}",
    )
    tmp.write_text(patched)

    cmd = ["llamafactory-cli", "train", str(tmp)]
    print("running micro benchmark:", " ".join(cmd))
    env = os.environ.copy()
    env["OMP_NUM_THREADS"] = "1"
    subprocess.run(cmd, cwd=LF_ROOT, check=True, env=env)

    bench_subdir = "teacher" if "teacher" in yaml_path.stem else "distill"
    result_file = out_dir / bench_subdir / "train_results.json"
    if not result_file.is_file():
        raise RuntimeError(f"benchmark finished but {result_file} not found")
    data = json.loads(result_file.read_text())
    return float(data["train_steps_per_second"])


def fmt_hours(h: float) -> str:
    if h < 1:
        return f"{h * 60:.1f} 分钟"
    if h < 24:
        return f"{h:.1f} 小时"
    return f"{h / 24:.1f} 天 ({h:.1f} 小时)"


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--epochs", type=int, default=30)
    parser.add_argument("--samples", type=int, default=FULL_SAMPLES)
    parser.add_argument("--micro-samples", type=int, default=0, help=">0 时先跑微基准再估算")
    args = parser.parse_args()

    if args.micro_samples > 0:
        print("=== 微基准(较慢,但更准确)===")
        t_sps = run_micro_benchmark(args.micro_samples, LF_ROOT / "examples/train_lora/qwen3_4b_teacher_lora.yaml")
        d_sps = run_micro_benchmark(args.micro_samples, LF_ROOT / "examples/train_lora/qwen3_1.7b_distill_lora.yaml")
        teacher_sec = t_sps
        distill_sec = d_sps
    else:
        metrics = load_smoke_metrics()
        if metrics is not None:
            print("=== 使用冒烟指标外推(第六节已跑完)===")
            teacher_sec = metrics["teacher"]["train_steps_per_second"]
            distill_sec = metrics["distill"]["train_steps_per_second"]
        else:
            print("=== 使用内置默认速度外推(尚未跑第六节冒烟)===")
            print(f"  教师 steps/s: {DEFAULT_TEACHER_STEPS_PER_SEC}(RTX 3090 参考值)")
            print(f"  蒸馏 steps/s: {DEFAULT_DISTILL_STEPS_PER_SEC}(RTX 3090 参考值)")
            print("  完成第六节冒烟后可重新运行本脚本,将自动读取你的实测速度。")
            teacher_sec = DEFAULT_TEACHER_STEPS_PER_SEC
            distill_sec = DEFAULT_DISTILL_STEPS_PER_SEC

    teacher = estimate_from_metrics(args.samples, args.epochs, teacher_sec)
    distill = estimate_from_metrics(args.samples, args.epochs, distill_sec)
    merge_sec = 60  # 两次 export 各约 30-45s

    total_hours = teacher["hours"] + distill["hours"] + merge_sec / 3600

    print("\n--- 估算结果 ---")
    print(f"数据集规模: {args.samples:,} 条 × {args.epochs} epoch")
    print(f"Step1 教师 LoRA: {teacher['optimizer_steps']:,} steps, {fmt_hours(teacher['hours'])}")
    print(f"Step3 白盒蒸馏:  {distill['optimizer_steps']:,} steps, {fmt_hours(distill['hours'])}")
    print(f"Step2+4 合并:   ~{merge_sec}s")
    print(f"合计训练+合并:  {fmt_hours(total_hours)}")
    print("\n注意: 首次全量 tokenize 预处理额外 +10~30 分钟;磁盘需预留 merged-teacher ~7.6GB。")


if __name__ == "__main__":
    main()

run_inference_compare.py

路径:run_inference_compare.py(副本:LLaMA-Factory/run_inference_compare.py

python 复制代码
"""对同一条 validation_prompt 比较基座 / 教师 LoRA / 蒸馏学生。"""
import argparse
import gc
from pathlib import Path

import torch
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

STUDENT_BASE = "/root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-1.7B/snapshots/master"
TEACHER_BASE = "/root/autodl-tmp/Distill2/models/models/Qwen--Qwen3-4B-Instruct-2507/snapshots/master"
TEACHER_LORA = "/root/autodl-tmp/Distill2/LLaMA-Factory/saves/qwen3-4b/lora/teacher-sft"
STUDENT_MERGED = "/root/autodl-tmp/Distill2/LLaMA-Factory/saves/qwen3-1.7b/merged-student"
PROMPT_FILE = Path("/root/autodl-tmp/Distill2/validation_prompt.txt")


def generate(model, tokenizer, user_content: str, max_new_tokens: int = 200) -> str:
    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)
    with torch.no_grad():
        outputs = model.generate(**inputs, max_new_tokens=max_new_tokens, do_sample=False)
    return tokenizer.decode(outputs[0][inputs.input_ids.shape[1] :], skip_special_tokens=True).strip()


def run_once(label, loader):
    print(f"\n{'=' * 20} {label} {'=' * 20}")
    model, tokenizer = loader()
    text = PROMPT_FILE.read_text(encoding="utf-8").strip()
    print(generate(model, tokenizer, text)[:800])
    del model, tokenizer
    gc.collect()
    torch.cuda.empty_cache()


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--student-merged", default=STUDENT_MERGED)
    args = parser.parse_args()

    run_once(
        "基座 Qwen3-1.7B",
        lambda: (
            AutoModelForCausalLM.from_pretrained(
                STUDENT_BASE, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True
            ),
            AutoTokenizer.from_pretrained(STUDENT_BASE, trust_remote_code=True),
        ),
    )
    run_once(
        "教师 Qwen3-4B + LoRA",
        lambda: (
            PeftModel.from_pretrained(
                AutoModelForCausalLM.from_pretrained(
                    TEACHER_BASE, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True
                ),
                TEACHER_LORA,
            ),
            AutoTokenizer.from_pretrained(TEACHER_BASE, trust_remote_code=True),
        ),
    )
    run_once(
        "蒸馏学生 merged",
        lambda: (
            AutoModelForCausalLM.from_pretrained(
                args.student_merged, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True
            ),
            AutoTokenizer.from_pretrained(args.student_merged, trust_remote_code=True),
        ),
    )


if __name__ == "__main__":
    main()

YAML 配置(8 个)

完整正文见正文 [第十一节 YAML 配置(全部 8 个)](#第十一节 YAML 配置(全部 8 个))

文件 路径 用途
qwen3_4b_teacher_lora.yaml examples/train_lora/ Step1 冒烟
qwen3_4b_teacher_merge.yaml examples/merge_lora/ Step2 冒烟合并
qwen3_1.7b_distill_lora.yaml examples/train_lora/ Step3 冒烟蒸馏
qwen3_1.7b_student_merge.yaml examples/merge_lora/ Step4 冒烟合并
qwen3_4b_teacher_lora_full.yaml examples/train_lora/ Step1 正式 30 epoch
qwen3_4b_teacher_merge_full.yaml examples/merge_lora/ Step2 正式合并
qwen3_1.7b_distill_lora_full.yaml examples/train_lora/ Step3 正式蒸馏
qwen3_1.7b_student_merge_full.yaml examples/merge_lora/ Step4 正式合并
相关推荐
战族狼魂1 小时前
每日一课:算法系统学习路线
人工智能·算法·大模型·大语言模型
七牛云行业应用1 小时前
Codex Desktop 怎么安装:Windows、macOS 全平台完整教程
人工智能·windows·macos
Token炼金师1 小时前
提词的艺术:Few-shot、CoT、Function Calling、ReAct 与自洽性 —— Prompt 工程六技
人工智能·llm·prompt·react·function call·cot·few-show
阿乔外贸日记1 小时前
快速了解东帝汶市场 基础经济概况汇总
大数据·人工智能
格子生意经1 小时前
GEO系统实战:破解流量瓶颈的技术方案与选型指南
人工智能
懂AI的老郑1 小时前
面对小众场景的目标检测联合智能体零样本检测可行性分析
人工智能·yolo·架构
学习日记5251 小时前
【提示词工程 02 理解大模型】
人工智能·ai·prompt
明哥聊AI1 小时前
AI视频生成技术全景:Sora2、Veo3、可灵3.0背后的Diffusion架构深度解析
人工智能·架构·音视频
Token炼金师1 小时前
知识的外挂:分块、Embedding、Rerank、GraphRAG 与多路融合 —— RAG 检索增强六脉
人工智能·深度学习·llm·embedding·chunk·graphrag·rerank