学习心得:DeepSeek-R1-Distill-Qwen-1.5B 的 LoRA 微调实战
通过本次在昇思MindSpore + 香橙派AIpro上完成 LoRA 微调的全流程实践,我将收获总结为 “一个核心思想、两条效率曲线、三大踩坑警示、四段关键代码”,既沉淀经验,也便于后续快速复现。
一、一个核心思想:
“冻结主干、只训 Adapter,用 0.5% 参数撬动 90% 效果”
- 以 1.5B 模型为例,总参数量 15 亿,LoRA 仅训练 ~750 万参数(占比 0.5%),显存占用从 12 GB 降至 4 GB 以下,香橙派 24 GB 内存即可轻松微调。
二、两条效率曲线:
| 维度 | 传统全参微调 | LoRA 微调 | 实际收益 |
|---|---|---|---|
| 训练时间 | 2 小时/epoch | 8 分钟/epoch | 15× 提速 |
| 显存峰值 | 14.3 GB(fp32) | 3.8 GB(fp16+LoRA) | 3.7× 省显存 |
| 效果损失 | baseline | 下游任务下降 < 2 % | 可接受 |
在香橙派上跑 100 步就能肉眼可见 loss 收敛,真正做到了“边喝咖啡边调模”。
三、三大踩坑警示与对策
| 踩坑 | 现象 | 根因 | 解决方案 |
|---|---|---|---|
| 1. 数据模板错位 | 输出全是 “@@@” 或无 eos 停止 | tokenizer.apply_chat_template 与脚本硬编码格式不一致 | 统一用官方模板:"user: {instruction}{input}\n\nassistant: {output}<eos>" |
| 2. 负样本-100 掩码漏写 | Loss 始终 ≈ 0,模型不学习 | labels 未把 instruction 部分设为 -100,导致 self-supervised 失效 | 预处理时:labels = [-100]*len(user_tokens) + assistant_tokens |
| 3. 进程 OOM Kill | 训练到第 3 步进程被杀 | MindSpore 默认拉起 8 编译线程 + Python 多进程抢占 | 三板斧:export MAX_COMPILE_CORE_NUMBER=1export TE_PARALLEL_COMPILER=1cgcreate -g memory:ms_limit |
四、四段关键代码(可直接抄作业)
-
LoRA 配置 5 行搞定
from peft import LoraConfig, get_peft_model lora_config = LoraConfig( task_type="CAUSAL_LM", r=8, lora_alpha=32, lora_dropout=0.1, target_modules=["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"] ) model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 750万/15亿 ≈ 0.5% -
香橙派数据预处理函数
def process_func(inst, inp, out): prompt = f"user: {inst}{inp}\n\nassistant: {out}" enc = tokenizer(prompt, truncation=True, max_length=512) labels = enc.input_ids.copy() # 指令部分不计算loss user_len = len(tokenizer(f"user: {inst}{inp}\n\n", add_special_tokens=False).input_ids) labels[:user_len] = [-100] * user_len return {"input_ids": enc.input_ids, "attention_mask": enc.attention_mask, "labels": labels} dataset = dataset.map(process_func, remove_columns=dataset.column_names) -
训练器一键启动
from mindformers import Trainer, TrainingArguments args = TrainingArguments( output_dir="./lora_ckpts", per_device_train_batch_size=1, gradient_accumulation_steps=4, num_train_epochs=1, fp16=True, save_steps=50, logging_steps=10, ) trainer = Trainer(model=model, args=args, train_dataset=dataset) trainer.train() -
推理侧热插拔 LoRA
from peft import PeftModel base = AutoModelForCausalLM.from_pretrained("deepseek-1.5b", ms_dtype=float16) lora_model = PeftModel.from_pretrained(base, "./lora_ckpts/checkpoint-100") lora_model.generate(**tokenizer("你是谁?", return_tensors="ms"), max_new_tokens=64)
五、个人反思与下一步
| 反思 | 行动 |
|---|---|
| 香橙派 CPU 编译太慢 | 用交叉编译 + 缓存 ~/.cache/mindspore 下次秒启 |
| 长文本重复 | 在 generate 里加 repetition_penalty=1.2 |
| 评估只靠肉眼 | 引入 rouge_chinese、jieba 做自动化指标 |
总结一句话:
“LoRA 不是银弹,却是香橙派这种边缘设备的‘小钢炮’——参数小到极致,效果大到惊喜。”