单卡蒸馏 Claude Opus 4.6 到 Qwen3.5-27B:保姆级教程
原文链接:https://mp.weixin.qq.com/s/a-7HD4clgAu6bsX3ag9oyQ 版权声明:本文版权归原文作者所有,仅供参考学习
🔥 手把手教你炼丹!单卡 3090 蒸馏 Claude-4.6-Opus 到 Qwen3.5-27B
🧪 炼丹配方:Unsloth + LoRA + SFT + train_on_responses_only 💰 炼丹成本:一张 3090 + 几小时电费 + 一杯咖啡的时间 🎯 炼丹成果:让 27B 小模型学会 Claude Opus 的"深度思考"
写在前面
花几百块 API 费才能用上的 Claude Opus 4.6 推理能力,其实可以蒸馏到本地模型里。
思路很简单:让 Claude 把解题过程写成思维链数据,然后拿这些数据训练 Qwen3.5-27B。学术上叫知识蒸馏,说白了就是「抄学霸笔记」。
整个流程只需要一张 3090 + 几小时,从环境搭建到本地部署,这篇全覆盖。
1️⃣ 硬件要求
最低配置 vs 推荐配置
| 项目 | 最低配置 🥉 | 推荐配置 🥇 |
|---|---|---|
| GPU | 1× RTX 3090 (24GB) | 1× A100 (80GB) |
| 内存 | 32GB | 64GB+ |
| 磁盘 | 100GB SSD | 200GB+ NVMe SSD |
| CUDA | 12.1+ | 12.4+ |
最低一张 3090,门槛不高。
三种训练方式
| 方式 | 显存需求 | 适合谁 | 类比 |
|---|---|---|---|
| QLoRA (4-bit) 🌟 | ~18GB | 穷学生、独立开发者 | 用微波炉做饭 |
| LoRA (16-bit) | ~55GB | 有 A100 的大户 | 用燃气灶炒菜 |
| Full Fine-tuning | ~120GB+ | 土豪公司 | 开五星级后厨 |
本教程以 QLoRA (4-bit) 为主线,一张 3090 就够。A100 用户把 load_in_4bit 改成 False 即可。
2️⃣ 环境安装
配环境的程序员坑我都踩过了,跟着复制粘贴就行。
Step 1:创建虚拟环境
conda create -n distill python=3.11 -y
conda activate distill建议别在 base 环境里搞,依赖冲突很烦。
Step 2:安装 PyTorch
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121CUDA 12.4 用户(推荐):
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124不知道自己 CUDA 版本?跑一下 nvidia-smi,右上角写着呢~
Step 3:安装 Unsloth
pip install unslothUnsloth 让训练速度快 2 倍、显存省 60%。没有它,3090 跑不动 27B 模型。
Step 4:安装其他依赖
pip install datasets accelerate bitsandbytes trl peft huggingface_hubStep 5:验证一下
import torch
print(f'🔥 PyTorch: {torch.__version__}')
print(f'🎮 CUDA: {torch.cuda.is_available()}')
print(f'💪 GPU: {torch.cuda.get_device_name(0)}')
print(f'🧠 显存: {torch.cuda.get_device_properties(0).total_mem / 1024**3:.1f} GB')
print('✅ 环境就绪,可以开始炼丹了!')看到 ✅ 环境就绪 就说明成功了。
Step 6:登录 HuggingFace
huggingface-cli login去 https://huggingface.co/settings/tokens 拿你的 token,粘贴到终端里就行。
🇨🇳 国内用户注意:如果下载模型巨慢,先跑这行设置镜像:
export HF_ENDPOINT=https://hf-mirror.com3️⃣ 下载基座模型
Unsloth 提供了预量化版本,开箱即用。
QLoRA 方案(你大概率用这个):unsloth/Qwen3.5-27B-unsloth-bnb-4bit
训练代码会自动下载,不用手动操作。不过建议提前下载到本地,省得训练时断网重来 👇
提前下载到本地(推荐)
from huggingface_hub import snapshot_download
snapshot_download('unsloth/Qwen3.5-27B-unsloth-bnb-4bit',
local_dir='./models/Qwen3.5-27B-4bit')
print('✅ 模型下载完成!')4-bit 版约 15GB,不会太久。LoRA 16-bit 用户用 unsloth/Qwen3.5-27B,约 55GB。
4️⃣ 准备数据集
这一步是整个蒸馏的灵魂——你得给模型高质量的思维链数据,让它学会 Claude 怎么一步步推导的。
数据集清单
Jackrong 用了 3 份数据集,合计约 3,280 条:
| 数据集 | 数量 | 一句话介绍 |
|---|---|---|
| 📚 nohurry/Opus-4.6-Reasoning-3000x-filtered | ~2,326 条 | 主力数据集,Claude 的深度推理轨迹 |
| 🧠 TeichAI/claude-4.5-opus-high-reasoning-250x | ~250 条 | 高难度硬核推理样本 |
| 🔧 Jackrong/Qwen3.5-reasoning-700x | ~700 条 | 补充多样性,防止模型偏科 |
总共才 3,280 条数据就蒸馏出了很强的效果,数据质量远比数量重要。
数据长什么样?
每条数据都是这样的 messages 格式:
{
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "请解释量子纠缠的原理..."},
{"role": "assistant", "content": "<think>\nLet me analyze this carefully:\n1. First...\n2. The key insight...\n3. Therefore...\n</think>\n\n量子纠缠是...(最终答案)"}
]
}🔑 关键是 <thinking> 标签——Claude 的内心推理过程。我们要让 Qwen 学会的就是这种结构化思维。
一键下载 & 合并
from datasets import load_dataset, concatenate_datasets
print('📥 正在下载学霸笔记...')
ds1 = load_dataset('nohurry/Opus-4.6-Reasoning-3000x-filtered', split='train')
print(f' 📚 数据集 1: {len(ds1)} 条 ✅')
ds2 = load_dataset('TeichAI/claude-4.5-opus-high-reasoning-250x', split='train')
print(f' 🧠 数据集 2: {len(ds2)} 条 ✅')
ds3 = load_dataset('Jackrong/Qwen3.5-reasoning-700x', split='train')
print(f' 🔧 数据集 3: {len(ds3)} 条 ✅')
combined = concatenate_datasets([ds1, ds2, ds3])
combined.save_to_disk('./data/combined_opus_reasoning')
print(f'🎉 合并完成!共 {len(combined)} 条学霸笔记,已保存到 ./data/')5️⃣ 训练代码(核心)
这是整篇教程最重要的部分。创建文件 train.py,把下面的代码依次粘贴进去:
Part 1:配置区(改这里就行,其他不用动)
# ============================================================
# 🔥 train.py - Claude-4.6-Opus 蒸馏 Qwen3.5-27B
# 使用方法:改完配置区,直接 python train.py
# 预计时间:3090 约 2-4 小时,4090 约 1-2 小时
# ============================================================
from unsloth import FastLanguageModel
from datasets import load_dataset, concatenate_datasets
from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported
# ╔══════════════════════════════════════════════════════════╗
# ║ 🎛️ 配置区 ║
# ║ 只需要改这里!其他代码不用动! ║
# ╚══════════════════════════════════════════════════════════╝
# --- 🖥️ 模型配置 ---
MODEL_NAME = "unsloth/Qwen3.5-27B-unsloth-bnb-4bit" # 3090 用这个
# MODEL_NAME = "unsloth/Qwen3.5-27B" # A100 土豪用这个
MAX_SEQ_LENGTH = 4096 # 序列长度(显存不够就改 2048)
LOAD_IN_4BIT = True # True = QLoRA(省显存), False = LoRA(需大显存)
# --- 🧬 LoRA 配置 ---
LORA_R = 64 # LoRA Rank,原作者就是用的 64
LORA_ALPHA = 64 # 一般跟 r 保持一致
LORA_DROPOUT = 0 # Unsloth 优化过了,放心填 0
# --- 📊 训练超参数 ---
NUM_EPOCHS = 3 # 训练 3 轮(多了容易过拟合)
BATCH_SIZE = 2 # 每批 2 条(显存小就改 1)
GRAD_ACCUM = 4 # 梯度累积 4 步(等效 batch = 8)
LEARNING_RATE = 2e-4
WARMUP_STEPS = 10
OUTPUT_DIR = "./output"
LOGGING_STEPS = 53090 用户上面的配置直接用就行,不用改。
Part 2:加载模型
# ==================== 📦 加载基座模型 ====================
print("🚀 正在加载模型,请稍候...")
print(" (第一次运行会自动下载,可能需要几分钟)")
model, tokenizer = FastLanguageModel.from_pretrained(
model_name=MODEL_NAME,
max_seq_length=MAX_SEQ_LENGTH,
dtype=None, # 自动检测最佳精度
load_in_4bit=LOAD_IN_4BIT,
)
print(f"✅ 模型加载完成:{MODEL_NAME}")Part 3:挂上 LoRA 适配器
# ==================== 🧬 配置 LoRA ====================
model = FastLanguageModel.get_peft_model(
model,
r=LORA_R,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", # 注意力模块
"gate_proj", "up_proj", "down_proj"], # FFN 模块
lora_alpha=LORA_ALPHA,
lora_dropout=LORA_DROPOUT,
bias="none",
use_gradient_checkpointing="unsloth", # 省 30% 显存的魔法
random_state=3407,
use_rslora=False,
loftq_config=None,
)
model.print_trainable_parameters()
# 你会看到类似:trainable params: 83M || all params: 27B || 0.31%Part 4:加载数据集 & 格式化
# ==================== 📚 加载学霸笔记 ====================
print("📥 加载数据集中...")
ds1 = load_dataset("nohurry/Opus-4.6-Reasoning-3000x-filtered", split="train")
ds2 = load_dataset("TeichAI/claude-4.5-opus-high-reasoning-250x", split="train")
ds3 = load_dataset("Jackrong/Qwen3.5-reasoning-700x", split="train")
dataset = concatenate_datasets([ds1, ds2, ds3])
print(f"📊 共加载 {len(dataset)} 条训练数据")
# ==================== 🔄 格式化数据 ====================
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = []
for convo in convos:
text = tokenizer.apply_chat_template(convo, tokenize=False, add_generation_prompt=False)
texts.append(text)
return {"text": texts}
dataset = dataset.map(formatting_prompts_func, batched=True)
print(f"✅ 数据格式化完成!")Part 5:配置训练器 + 核心技巧
# ==================== 🎯 配置训练器 ====================
from unsloth import train_on_responses_only
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
args=TrainingArguments(
output_dir=OUTPUT_DIR,
num_train_epochs=NUM_EPOCHS,
per_device_train_batch_size=BATCH_SIZE,
gradient_accumulation_steps=GRAD_ACCUM,
learning_rate=LEARNING_RATE,
warmup_steps=WARMUP_STEPS,
lr_scheduler_type="cosine",
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
optim="adamw_8bit",
logging_steps=LOGGING_STEPS,
save_strategy="steps",
save_steps=100,
save_total_limit=3,
weight_decay=0.01,
max_grad_norm=1.0,
seed=3407,
report_to="none",
),
dataset_text_field="text",
max_seq_length=MAX_SEQ_LENGTH,
packing=False, # 蒸馏场景关闭 packing
)⭐ 核心技巧:train_on_responses_only
trainer = train_on_responses_only(
trainer,
instruction_part="<|im_start|>user\n",
response_part="<|im_start|>assistant\n",
)
print("✅ 训练器配置完成!")
print(f" 等效 Batch Size: {BATCH_SIZE * GRAD_ACCUM}")
print(f" 训练轮数:{NUM_EPOCHS}")
print(f" LoRA Rank: {LORA_R}")train_on_responses_only 一句话总结:只让模型学"怎么答",不学"怎么问",把注意力集中在 <thinking> 思维链和最终回答上。这是用少量数据就能出效果的关键。
Part 6:开始训练
# ==================== 🔥 开始训练 ====================
print("🔥 开始训练...")
trainer_stats = trainer.train()
print(f"🎉 训练完成!最终 Loss: {trainer_stats.training_loss:.4f}")
print(f"📊 总步数:{trainer_stats.global_step}")Part 7:保存模型
# ==================== 💾 保存炼丹成果 ====================
model.save_pretrained("./output/lora_adapter")
tokenizer.save_pretrained("./output/lora_adapter")
print("✅ LoRA 适配器已保存到 ./output/lora_adapter")可选:推到 HuggingFace 分享你的模型:
model.push_to_hub("your-username/my-opus-distill", token="hf_xxx")6️⃣ 启动训练
等待训练
直接运行:
conda activate distill
python train.py后台运行(推荐,防止 SSH 断开翻车):
方式一:nohup
nohup python train.py > train.log 2>&1 &
tail -f train.log方式二:tmux(强烈推荐)
tmux new -s distill
python train.py用 Ctrl+B, D 脱离会话(训练继续跑),之后用 tmux attach -t distill 回来看。
一定要用 tmux 或 nohup,SSH 断了就白跑了。
训练时间参考
| 你的显卡 | 预计时间 |
|---|---|
| RTX 3090 | 2-4 小时 |
| RTX 4090 | 1-2 小时 |
| A100 80GB | 1-2 小时 |
怎么看训练是否正常?
Loss 日志大致是这样的走势:
Step 5 | Loss: 2.34 ← 刚开始,偏高正常
Step 50 | Loss: 0.85 ← 开始收敛
Step 200 | Loss: 0.51 ← 趋于稳定
最终 | Loss: 0.4~0.8 ← 理想范围- 持续下降 → 正常
- 降到 0 → 过拟合了,减少 epochs 或加数据
- 纹丝不动 → 检查数据格式和学习率
7️⃣ 模型导出
训练完拿到的是 LoRA 适配器(约 100MB),还需要导出成可部署的格式。
方式一:合并为完整模型(通用部署)
model.save_pretrained_merged("./output/merged_model", tokenizer, save_method="merged_16bit") # 约 55GB
print("✅ 完整模型已保存!")方式二:导出 GGUF(用 Ollama 跑起来)⭐ 推荐
model.save_pretrained_gguf("./output/gguf", tokenizer, quantization_method="q4_k_m")
print("✅ GGUF 模型已导出!可以用 Ollama 跑了!")量化方式怎么选?
| 格式 | 大小 | 显存 | 精度 | 推荐度 |
|---|---|---|---|---|
| Q4_K_M | ~16GB | ~16.5GB | ⭐⭐⭐ | 🏆 首选 |
| Q5_K_M | ~19GB | ~20GB | ⭐⭐⭐⭐ | 有余量就用 |
| Q8_0 | ~28GB | ~29GB | ⭐⭐⭐⭐⭐ | 追求精度 |
方式三:推到 HuggingFace 开源(可选)
model.push_to_hub_gguf("your-username/Qwen3.5-27B-Opus-Distilled-GGUF",
tokenizer, quantization_method="q4_k_m", token="hf_xxx")8️⃣ 推理测试
看看训练效果如何。
方式一:用 Unsloth 直接推理
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="./output/lora_adapter",
max_seq_length=4096, dtype=None, load_in_4bit=True,
)
FastLanguageModel.for_inference(model)
messages = [
{"role": "system", "content": "You are a helpful assistant that thinks step by step."},
{"role": "user", "content": "请用 Python 实现一个线程安全的单例模式,并解释每一步的设计考量"},
]
inputs = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to("cuda")
outputs = model.generate(input_ids=inputs, max_new_tokens=4096, temperature=0.6, top_p=0.95, top_k=20)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))如果回复里出现了 <thinking> 结构化思维链,说明蒸馏成功了。
方式二:用 Ollama 一键起飞(日常使用推荐)🚀
Step 1:安装 Ollama
curl -fsSL https://ollama.com/install.sh | shStep 2:创建 Modelfile
FROM ./output/gguf/unsloth.Q4_K_M.gguf
PARAMETER temperature 0.6
PARAMETER top_p 0.95
PARAMETER top_k 20
PARAMETER num_ctx 32768
SYSTEM "You are a helpful assistant that thinks step by step."Step 3:创建并运行模型
ollama create my-opus-distill -f Modelfile
ollama run my-opus-distill到这里你就有了一个本地版的「类 Claude Opus」,不花 API 钱。29-35 tok/s,262K 上下文,支持 Claude Code 和 OpenCode。
推理参数速查表
| 场景 | temperature | top_p | 建议 max_tokens | 适用情况 |
|---|---|---|---|---|
| 💬 日常对话 | 1.0 | 0.95 | 32,768 | 聊天、创意写作 |
| 💻 写代码 | 0.6 | 0.95 | 32,768 | 精确逻辑、Web 开发 |
| 🧮 竞赛难题 | 1.0 | 0.95 | 81,920 | 数学/编程竞赛 |
🔧 附录:常见问题
CUDA out of memory:把 MAX_SEQ_LENGTH 改成 2048,BATCH_SIZE 改成 1,确认 LOAD_IN_4BIT = True。
Loss 不下降:检查数据集有没有 messages 字段,chat template 是否正确,学习率调小到 5e-5 试试。
推理没有 <thinking> 思维链:temperature 不要设 0(用 0.6 或 1.0),max_new_tokens 至少 4096,system prompt 加上 "think step by step"。
导出 GGUF 报错:先 pip install llama-cpp-python,然后先 merge 成 16-bit 再转 GGUF。
📎 资源链接汇总
写在最后
大功告成 🎉
回顾一下:用 3,280 条数据 + 一张 3090 + 几小时,把 Claude Opus 4.6 的推理能力迁移到了 Qwen3.5-27B 里。
蒸馏模型当然有局限——没有多模态能力,边界场景可能翻车——但在纯文本推理、代码、数学方面,确实能打。
觉得有用的话欢迎转发。有问题评论区见。
基于 Jackrong 和 TeichAI 的开源工作整理,感谢 Unsloth 团队。
#蒸馏 #Qwen3.5 #Claude-Opus #LoRA #Unsloth #本地部署
版权声明:本文版权归原文作者所有 原文链接:https://mp.weixin.qq.com/s/a-7HD4clgAu6bsX3ag9oyQ