通用大模型懂天下事,但不懂你的业务:客服话术对不上、输出格式飘忽、行业术语乱用。微调就是把”懂行”这件事写进模型权重里。这篇以 DeepSeek 的开源蒸馏模型为例,用 LLaMA-Factory 走一遍从整理数据到产出可部署模型的完整流程,全部步骤在一块消费级显卡上能跑完。

这篇解决什么问题

先划清边界:DeepSeek 官方 API 目前不提供微调接口,所以微调走的是开源权重路线。对个人开发者,现实的选择是 DeepSeek-R1 蒸馏系列——DeepSeek-R1-Distill-Qwen-1.5B / 7B / 14B / 32B,权重 MIT 协议可商用,底座来自 Qwen2.5 系列,能力保留了大成,尺寸又小到消费级显卡装得下。

微调和 RAG 怎么选,一句话:知识频繁更新、要求可溯源,用 RAG;说话风格、固定格式、任务行为要贴业务,用微调。两者不冲突,常见做法是微调打底、RAG 供料。

环境与版本

项目要求说明
显卡NVIDIA,显存 12GB 起步(7B + QLoRA 量级),24GB 更从容显存不够先降 batch 和上下文长度
Python3.10+与 PyTorch 生态匹配
CUDA12.x【待补:实际版本】跟显卡驱动走
微调框架LLaMA-Factory【待补:实际版本号】支持主流开源模型的 LoRA / QLoRA
模型DeepSeek-R1-Distill-Qwen-7B(fp16 权重约 15GB,4bit 加载后显存压力小得多)HuggingFace 或国内镜像下载

7B 用 QLoRA(4bit 量化 + LoRA)微调是消费级显卡的标准玩法:LoRA 只训练插进去的低秩小矩阵,显存和磁盘开销都是模型本体的零头。

分步骤走一遍

第一步:定任务,攒数据

微调的天花板是数据。先确定一个窄任务——比如”按固定话术回复物流催单”,再围绕它收集几百条问答对【待补:实际数据条数与来源】。新手最常见的错误是拿三五十条杂乱数据开训,那练不出任何稳定行为。

整理成 alpaca 格式的 JSON,每条包含 instruction、input、output 三个字段:

[
  {
    "instruction": "把下面的客服问题归类:退款、物流、投诉、咨询。",
    "input": "我上周买的鞋码数不对,想换一双。",
    "output": "咨询"
  },
  {
    "instruction": "按公司话术回复用户的物流催单问题。",
    "input": "快递三天没动了,怎么办?",
    "output": "您好,已为您加急查询物流状态,预计 24 小时内同步最新进展,请留意短信通知。"
  }
]

第二步:安装 LLaMA-Factory

git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"

把数据放进 data/ 目录,并在 data/dataset_info.json 里登记一个数据集名,训练配置里引用这个名字:

{
  "my_customer_service": {
    "file_name": "my_customer_service.json"
  }
}

第三步:写训练配置

建一个 r1_lora_sft.yaml,QLoRA 微调 7B 蒸馏模型的典型配置:

### 模型
model_name_or_path: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
trust_remote_code: true

### 微调方式:LoRA + 4bit 量化(QLoRA)
stage: sft
do_train: true
finetuning_type: lora
lora_target: all
lora_rank: 16
lora_alpha: 32
quantization_bit: 4

### 数据
dataset: my_customer_service
template: TEMPLATE_NAME   # 【待补:查 LLaMA-Factory 模板列表,选 DeepSeek-R1 蒸馏模型对应的模板】
cutoff_len: 1024

### 训练参数
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 1.0e-4
num_train_epochs: 3
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
gradient_checkpointing: true

### 输出
output_dir: saves/r1-distill-7b-lora
logging_steps: 10
save_steps: 500
plot_loss: true

关键参数的取舍逻辑:lora_rank: 16 对窄任务够用,任务复杂可升到 32 或 64;learning_rate: 1.0e-4 是 LoRA 的常见起点,出问题先往低调;gradient_checkpointing: true 用时间换显存,12GB 显卡的救命开关。

第四步:启动训练

llamafactory-cli train r1_lora_sft.yaml

训练中的正常信号:loss 一路下行并在后段趋平;显存峰值稳定不涨【待补:实际训练时长、峰值显存与最终 loss】。plot_loss: true 会在输出目录生成 loss 曲线图,收工后先看图再下结论。

第五步:合并导出,对话验证

训练完得到的是 LoRA 适配器,合并回基座模型才能独立部署:

model_name_or_path: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
adapter_name_or_path: saves/r1-distill-7b-lora
template: TEMPLATE_NAME   # 与训练时保持一致
export_dir: models/r1-distill-7b-sft
export_size: 4
export_device: cpu
llamafactory-cli export r1_merge.yaml
# 合并完先用命令行聊两句验货
llamafactory-cli chat --model_name_or_path models/r1-distill-7b-sft --template TEMPLATE_NAME

验证满意后,转成 GGUF 量化包,就能丢给 Ollama 常驻运行,部署流程见Ollama 跑 DeepSeek 一篇

训练过程 loss 曲线与显存占用监控

常见坑

坑一:模板选错,模型开始胡言乱语。 每个模型家族有自己的对话模板,训练时 template 配错了,数据会被拼成模型没见过的格式,输出自然混乱。表现是 loss 降了但生成内容前言不搭后语。解法:查 LLaMA-Factory 内置模板列表,选蒸馏模型对应的那个,训练、合并、对话三处保持一致【待补:确认对应模板名】。

坑二:思维链被洗掉。 R1 蒸馏模型的输出自带 <think>...</think> 推理段,如果你的训练数据全是直答、不带思维链,微调会把这个习惯覆盖掉,模型推理能力跟着退化。解法二选一:数据里保留思维链;或者干脆换普通 instruct 底座(比如直接微调 Qwen2.5-7B-Instruct),别在推理模型上做纯直答微调。

坑三:显存 OOM。 12GB 显卡跑 7B QLoRA 也有翻车可能。按顺序降:per_device_train_batch_size 降到 1 → cutoff_len 砍半 → 确认 gradient_checkpointing 已开 → 还不行就把 quantization_bit 保持 4 的同时降 lora_rank

坑四:学过头。 小数据集训太多轮,模型过拟合到只会背训练集,换个问法就露馅。窄任务 2~3 个 epoch 是常见起点,loss 还在高位的另说;验证时专门准备几条训练集里没出现过的问题。

最终效果

微调成功的判定标准很朴素:拿训练集外的问题问它,输出风格、格式、话术与训练数据一致,且没有出现”整段复读训练样本”的过拟合迹象。量化验收:

  • loss 曲线收敛【待补:贴最终 loss 与曲线截图】;
  • 微调前后对同一组业务问题的输出对比【待补:贴真实对比样例】;
  • 用 20 条训练集外问题做小评测,符合预期格式的比例【待补:实测 %】。

相关阅读