摘要:本文记录了我从零开始,使用 ModelScope SWIFT 框架对 Qwen2.5-1.5B 模型进行 LoRA 微调的全过程。通过构建垂直领域的简历抽取数据集,解决了通用大模型在结构化输出(JSON)时指令遵循能力弱的问题,最终实现了 Loss < 0.001 的高精度微调,并完成了模型的合并导出与 API 服务化部署。
1. 项目背景:为什么要做微调?
在日常的“专家标注”工作中,我发现通用大模型(如 GPT-3.5/4 或原始 Qwen)在处理特定格式信息提取时存在两个痛点:
废话多:喜欢输出“好的,这是为您提取的信息...”等自然语言,难以直接用于代码解析。
格式漂移:在零样本(Zero-shot)下,输出的 JSON 结构经常不稳定,Key 值容易变。
作为一名从数据标注向 AI 工程化转型的开发者,我决定通过 SFT(Supervised Fine-Tuning,监督微调),训练一个**“人狠话不多”**的专用模型,让它只输出我想要的 JSON。
2. 技术选型
基座模型:Qwen2.5-1.5B-Instruct
理由:阿里出的开源千问系列,中文能力强,1.5B 参数量极小,免费的 T4 GPU 也能跑得飞起,适合实验。
微调框架:ModelScope SWIFT (v3.x)
理由:魔塔社区官方出品,集成了训练、推理、导出、部署全流程,支持 LoRA,开箱即用。
算力环境:ModelScope PAI-DSW (免费 GPU 实例)。
3. 数据工程:Garbage In, Garbage Out
微调的核心在于数据。基于我的标注经验,我构建了一个名为 resume_ner.jsonl 的数据集。
3.1 数据格式 (Alpaca)
codeJSON
{"system": "你是一个专业的简历助手", "query": "张三,男,1995年出生,毕业于清华大学,精通Python。", "response": "{\"姓名\": \"张三\", \"大学\": \"清华大学\", \"技能\": \"Python\"}"}
{"system": "你是一个专业的简历助手", "query": "李四,女,硕士,曾在字节跳动实习。", "response": "{\"姓名\": \"李四\", \"学历\": \"硕士\", \"公司\": \"字节跳动\"}"}3.2 踩坑与迭代
V1 版本:仅使用了 2 条数据,训练 5 个 Epoch。
结果:模型出现“灾难性遗忘”,依然输出大量自然语言废话。
V2 版本:数据扩充至 10+ 条,覆盖了不同年龄、职业、学历的样本,并严格统一了 JSON 输出规范。
4. 炼丹过程:寻找最佳超参数
4.1 核心命令
使用 swift sft 启动训练,采用 LoRA 算法以节省显存。
codeBash
swift sft \
--model qwen/Qwen2.5-1.5B-Instruct \
--dataset ./my_data.jsonl \
--train_type lora \
--lora_rank 16 \
--lora_alpha 32 \
--learning_rate 2e-4 \
--num_train_epochs 20 \
--max_length 512 \
--output_dir output_v24.2 关键参数调优逻辑
Epoch (训练轮数) 5 -> 20:
针对小样本特定任务,为了让模型形成肌肉记忆,我采用了“过拟合”策略,强制模型收敛。
LoRA Rank 8 -> 16:
增加了 LoRA 适配器的参数量,提升模型捕捉复杂格式映射的能力。
Learning Rate 1e-4 -> 2e-4:
略微调大学习率,加速在特定域的收敛。
4.3 训练结果
Loss (损失值):从初始的 1.22 一路下降至 0.0008。
Token Acc (准确率):最终达到 1.0 (100%)。
(示意图)
5. 推理验证:见证“洗脑”效果
训练完成后,使用 swift infer 加载 LoRA 权重进行测试。
测试 Prompt:
<<< 钱十三,男,40岁,腾讯技术总监,擅长大规模架构。
模型输出 (Output):
codeJSON
{"姓名": "钱十三", "年龄": "40", "公司": "腾讯", "职位": "技术总监", "技能": "大规模架构"}结论:模型完全摒弃了“废话”习惯,输出了纯净的 JSON 格式,完全符合预期。
6. 工程化落地:从 Checkpoint 到 API
模型练好了不能只躺在文件夹里,必须服务化。
6.1 模型合并 (Merge)
将 LoRA 的“补丁”权重与原始模型权重合并,导出一个独立的模型。
codeBash
swift export \
--ckpt_dir /path/to/checkpoint-20 \
--merge_lora true \
--output_dir /mnt/workspace/my_final_model6.2 部署 API (Deploy)
使用 SWIFT 内置的部署工具,启动一个兼容 OpenAI 协议的 API 服务。
codeBash
swift deploy --model /mnt/workspace/my_final_model --port 80006.3 客户端调用 (Python)
编写 Python 脚本模拟业务调用:
codePython
from openai import OpenAI
client = OpenAI(api_key='empty', base_url='http://localhost:8000/v1')
response = client.chat.completions.create(
model=client.models.list().data[0].id,
messages=[
{'role': 'system', 'content': '你是一个专业的简历助手'},
{'role': 'user', 'content': '郑十二,女,博士,阿里算法专家。'}
]
)
print(response.choices[0].message.content)7. 总结与反思
遇到的坑 (Troubleshooting)
CLI 版本兼容性:SWIFT v3 更新后,--model_type 参数被废弃,需改为 --model <model_id>,导致初期启动失败。通过查阅官方文档解决。
推理路径问题:swift infer 在加载本地适配器时需要使用绝对路径,否则会报错 Invalid repo_id。
心得体会
这次实战让我完成了从**“数据标注员”到“AI 开发者”**的身份跨越。
SFT 不神秘:它本质上就是用高质量的数据去“对齐”模型的概率分布。
数据为王:20 条高质量的数据配合激进的参数,就能在特定任务上吊打通用大模型。
闭环思维:训练只是中间环节,数据构建(Data Engineering)和模型部署(MLOps)才是落地的关键。
Next Step:计划将该模型量化(Quantization)为 GGUF 格式,并在本地 Ollama 环境中运行,探索端侧部署的可能性。