摘要:本文记录了我从零开始,使用 ModelScope SWIFT 框架对 Qwen2.5-1.5B 模型进行 LoRA 微调的全过程。通过构建垂直领域的简历抽取数据集,解决了通用大模型在结构化输出(JSON)时指令遵循能力弱的问题,最终实现了 Loss < 0.001 的高精度微调,并完成了模型的合并导出与 API 服务化部署。


1. 项目背景:为什么要做微调?

在日常的“专家标注”工作中,我发现通用大模型(如 GPT-3.5/4 或原始 Qwen)在处理特定格式信息提取时存在两个痛点:

  1. 废话多:喜欢输出“好的,这是为您提取的信息...”等自然语言,难以直接用于代码解析。

  2. 格式漂移:在零样本(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_v2

4.2 关键参数调优逻辑

  1. Epoch (训练轮数) 5 -> 20

    • 针对小样本特定任务,为了让模型形成肌肉记忆,我采用了“过拟合”策略,强制模型收敛。

  2. LoRA Rank 8 -> 16

    • 增加了 LoRA 适配器的参数量,提升模型捕捉复杂格式映射的能力。

  3. Learning Rate 1e-4 -> 2e-4

    • 略微调大学习率,加速在特定域的收敛。

4.3 训练结果

  • Loss (损失值):从初始的 1.22 一路下降至 0.0008

  • Token Acc (准确率):最终达到 1.0 (100%)

![alt text](https://via.placeholder.com/800x200?text=Loss+Curve:+1.2+->+0.0008)

(示意图)


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_model

6.2 部署 API (Deploy)

使用 SWIFT 内置的部署工具,启动一个兼容 OpenAI 协议的 API 服务。

codeBash

swift deploy --model /mnt/workspace/my_final_model --port 8000

6.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)

  1. CLI 版本兼容性:SWIFT v3 更新后,--model_type 参数被废弃,需改为 --model <model_id>,导致初期启动失败。通过查阅官方文档解决。

  2. 推理路径问题swift infer 在加载本地适配器时需要使用绝对路径,否则会报错 Invalid repo_id

心得体会

这次实战让我完成了从**“数据标注员”“AI 开发者”**的身份跨越。

  • SFT 不神秘:它本质上就是用高质量的数据去“对齐”模型的概率分布。

  • 数据为王:20 条高质量的数据配合激进的参数,就能在特定任务上吊打通用大模型。

  • 闭环思维:训练只是中间环节,数据构建(Data Engineering)和模型部署(MLOps)才是落地的关键。

Next Step:计划将该模型量化(Quantization)为 GGUF 格式,并在本地 Ollama 环境中运行,探索端侧部署的可能性。