Llama-Factory 微调 Qwen 实战:让 0.5B 小模型学会信息抽取

**副标题:手把手带你走完 LoRA 微调全流程——下模型、造数据、训练、合并、Ollama 部署,一篇文章跑通"

你有没有过这样的崩溃瞬间——好不容易把一个开源大模型跑起来了,问它「帮我抽出这句话里的实体关系」,它却给你编了一通漂亮但没用的废话?模型明明能跟你谈天说地,可一到结构化信息抽取这种"正经活儿"就集体"装傻"。

这不是模型笨,是它没学过这门手艺。预训练让它读完了"互联网这所大学",指令微调让它学会了"听人话、说人话",但它从来没见过你公司那种「从一句话里抠出主语-谓词-宾语」的业务范例。想让它会,得给它补一节岗前培训课——这就是微调干的事。

而全量微调动辄几十上百 GB 显存,普通开发者根本玩不起。好在有 LoRA:不用回炉重造整个模型,只在旁边贴一叠"岗位专用便利贴"就行。这篇就用 LLaMA-Factory 框架,带你对 Qwen2.5-0.5B-Instruct 做一次完整的 LoRA 微调,让它学会信息抽取,最后还能用 Ollama 在本地跑起来。

我会用一个贯穿全文的比喻把每一步串起来:把 Qwen2.5-0.5B-Instruct 想成一个刚通识毕业的实习生——基础扎实、懂礼貌,但还没学过你公司的业务;我们要做的不是让他回炉重造,而是给他一份岗前培训手册 + 一叠便利贴小抄,让他快速上岗。


一、先认识工具:LLaMA-Factory 是个什么"培训学校"

自己从零写微调脚本,要处理数据加载、对话模板、梯度累积、显存优化、断点续训……每一项都能让你 debug 到天亮。LLaMA-Factory 就是把这些脏活累活全打包好的"培训学校",它整合了主流的高效微调技术,适配 Qwen、LLaMA、Baichuan 等一堆开源模型,还附带一个可视化 WebUI,点点鼠标就能训练。

它有几个让人心动的特点:

  • 多阶段训练:支持预训练(PT)、监督微调(SFT)、人类反馈对齐(RLHF),覆盖大模型成长的每个阶段。
  • 高层次接口:训练、推理、评测、API 服务一条龙,不用自己拼。
  • 高效微调:内置 LoRA 等技术,把显存门槛从"几十 G"压到"一张 T4 就能跑"。
  • WebUI 可视化:英文不好的同学也能上手,参数都有中文说明。

1. 三种"培训方式"怎么选

LLaMA-Factory 给了三种微调方法,对应三种"带实习生"的策略:

微调方法比喻训练什么显存需求适用场景
full(全量微调)让实习生回炉重造,所有知识重学模型全部权重极高(几十 G+)算力充足、任务差异大
freeze(冻结微调)只让实习生改某几科目的学习方法,其余冻结部分层权重中等折中方案
lora(LoRA 微调)不动实习生脑子,给他配一叠"岗位便利贴"旁路小矩阵(adapter)低(T4 16G 够用)普通开发者首选

💡 为什么我们选 LoRA? 它只训练一叠"便利贴"(低秩适配矩阵),不动原始模型一根毫毛,显存占用低、训练快、还能随时插拔。对 0.5B 这种小模型,LoRA 几乎是性价比之王。

2. 这次要走的完整流水线

先把整条路画出来,心里有个全景,后面每一步都不慌:

下载 Qwen2.5-0.5B

原始模型面试
发现不会抽取

编写培训手册
构造 NER 数据集

LoRA 微调训练
贴上便利贴

合并模型
便利贴誊抄进脑子

验证效果
信息抽取测试

导出 GGUF
Ollama 部署上岗

我们就按这条线,一步步走完。


二、开学准备:装环境、下模型、先"面试"一下

1. 装好 LLaMA-Factory 这所"培训学校"

硬件要求:建议 T4、RTX 3090 或 4090(16GB 显存)就能跑 0.5B~1.5B 的 LoRA 训练。先确认你的"车间"开没开:

nvidia-smi   # 查看显卡状态和 CUDA 版本,能看到卡型号就说明驱动 OK

安装步骤

# 1. 拉取框架源码
git clone https://github.com/hiyouga/LLaMA-Factory.git

# 2. (可选)建个干净的虚拟环境,避免污染全局
# conda create -n llama_factory python=3.10
# conda activate llama_factory

# 3. 进入目录,安装框架(含 torch 和 metrics 依赖)
cd LLaMA-Factory
pip install -e '.[torch,metrics]'

⚠️ 注意pip install -e '.[torch,metrics]' 里的 -e 是"可编辑安装",意思是后续改框架源码会即时生效。如果只是用,不打算改源码,去掉 -e 也行。安装时间较长(要拉 PyTorch),耐心等。

2. 把"实习生"请进来:下载 Qwen2.5-0.5B-Instruct

国内访问 HuggingFace 不稳定,推荐用 ModelScope(魔搭) 下载:

# 第一步:装 modelscope 工具
pip install modelscope

# 第二步:下载 Qwen2.5-0.5B-Instruct
modelscope download --model Qwen/Qwen2.5-0.5B-Instruct

# 第三步:把模型从缓存目录挪到工作区(方便管理)
mv /root/.cache/modelscope/hub/models/Qwen/Qwen2.5-0.5B-Instruct .

💡 为什么选 0.5B? 不是因为它最强,而是因为它——下载快、显存友好、训练快,特别适合教学和练手。跑通流程后,把模型名换成更大的(比如 Qwen2.5-7B-Instruct),整套步骤一模一样。

3. 启动 WebUI 并"面试"原始模型

启动 LLaMA-Factory 的可视化界面:

cd LLaMA-Factory
llamafactory-cli webui

看到这行就说明启动成功了:

* Running on local URL:  http://0.0.0.0:7860

浏览器打开 http://0.0.0.0:7860(如果你在云服务器上,换成服务器公网 IP),就能看到 WebUI 界面。语言选 zh(中文),英语不好的同学不用慌。

面试环节:训练前先加载模型进 Chat 模式,看看这个"实习生"原本啥水平。在 WebUI 里切到 Chat 标签,选好模型名称(Qwen2.5)和模型路径(刚才下载的绝对路径),加载后问它:

你现在是一个很厉害的阅读理解器,严格按照人类指令进行回答。
帮我抽出这句话里的实体关系,用 JSON 回答:
白胸三刺角蝉是属于同翅目的一种动物。

原始模型的回答大概率是"车轱辘话"——它能理解这句话,但不会按你要求的 JSON 格式做信息抽取。这正是我们要通过微调解决的问题:让它学会"读一句话→吐出结构化 SPO(主-谓-宾)"这门手艺。

🕳️ 陷阱:很多人一上来就训练,结果模型没学会还以为是参数不对。其实先"面试"原始模型很重要——你得先知道它不会什么,才能判断微调到底有没有效果。训练后再用同样的问题问一遍,对比才直观。


三、编写"培训手册":数据集准备

微调的核心是数据。再好的模型,喂垃圾数据也练废。LLaMA-Factory 支持 alpaca 数据格式,我们以 SFT(监督微调)阶段为例。

1. alpaca 格式:一份"培训手册"长什么样

alpaca 格式是一个 JSON 对象的列表,每条数据就是一道"培训题":

[
  {
    "instruction": "用户指令(必填,师傅出的题)",
    "input": "用户输入(选填,题目材料)",
    "output": "模型回答(必填,标准答案)",
    "system": "系统提示词(选填,岗位守则)",
    "history": [
      ["第一轮指令(选填)", "第一轮回答(选填)"],
      ["第二轮指令(选填)", "第二轮回答(选填)"]
    ]
  }
]

用"师傅带徒弟"的比喻理解每个字段:

字段比喻作用是否必填
instruction师傅出的题(“帮我抽取实体”)告诉模型这题要干啥必填
input题目材料(那句话本身)这题要处理的具体内容选填
output标准答案(抽出的 JSON)模型该学会的回答方式必填
system岗位守则(“你是阅读理解器”)给模型设定角色和规则选填
history之前几轮的问答记录多轮对话的上下文选填

单轮对话示例(一问一答,最常见):

{
  "instruction": "写一个有效的比较语句",
  "input": "篮球和足球",
  "output": "篮球和足球都是受欢迎的运动。"
}

多轮对话示例(带上下文,模型要"接着聊"):

{
  "instruction": "谢谢",
  "input": "",
  "output": "不用谢! 很高兴我提供的信息能够帮助到你!",
  "history": [
    [
      "请你给我写一个面试准备计划,我想要去面试微软的程序员岗位",
      "首先,你可以去微软官网寻找招聘信息并申请面试……"
    ]
  ]
}

2. 我们的信息抽取数据集长啥样

本次任务是教模型做信息抽取——给定一句话,抽取出其中的 SPO(主语-谓词-宾语)三元组,输出 JSON。一条典型的训练数据:

{
  "instruction": "你现在是一个很厉害的阅读理解器,严格按照人类指令进行回答。",
  "input": "句子中包含了哪些信息,输出json:\n\n白胸三刺角蝉是属于同翅目的一种动物。",
  "output": "[{\"predicate\": \"目\", \"object_type\": \"目\", \"subject_type\": \"生物\", \"object\": \"同翅目\", \"subject\": \"白胸三刺角蝉\"}]"
}

拆开看这道"培训题":

  • instruction:岗位守则——“你是个阅读理解器,要听指令”。
  • input:题目——“从这句话里抽信息,输出 JSON:白胸三刺角蝉属于同翅目……”
  • output:标准答案——一个 JSON 数组,里面是 主语-谓词-宾语 的结构化三元组。

💡 直觉补充:信息抽取本质上就是高级版的"语文阅读理解里划主谓宾"。subject(白胸三刺角蝉)是主语,predicate(目)是谓词关系,object(同翅目)是宾语。模型要学会的就是把自然语言句子"翻译"成这种结构化 JSON。

3. 注册数据集:把手册录入"学校系统"

光有数据文件还不够,得在 LLaMA-Factory 的"系统"里登记,框架才知道去哪找它。两步操作:

第一步:把你的数据集文件(比如 ner_dataset.json)复制到 LLaMA-Factory 的 data/ 目录下。

第二步:打开 data/dataset_info.json,在里面注册一条:

"ner_dataset": {
  "file_name": "ner_dataset.json"
}

就这样,ner_dataset 这个名字就会出现在 WebUI 的数据集下拉框里。

⚠️ 注意dataset_info.json 是个 JSON 文件,编辑时别忘了逗号。"ner_dataset" 前面那行如果有内容,记得加逗号分隔,否则 JSON 解析报错,WebUI 会加载不出数据集列表。


四、正式上岗前培训:LoRA 微调训练

数据准备好了,现在让"实习生"开始上课。整个训练过程都在 WebUI 里操作,不用手敲命令(但可以看生成的命令学习)。

训练目标:让 Qwen2.5-0.5B-Instruct 学会 ner_dataset 数据集里的信息抽取能力。

1. 在 WebUI 里选数据集

切到 Train 标签:

  1. 模型名称选 Qwen2.5(这会自动匹配它的对话模板,不用自己设)。
  2. 模型路径填下载好的绝对路径(比如 /workspace/Qwen2.5-0.5B-Instruct)。
  3. 微调方法选 lora
  4. 数据集勾选 ner_dataset,可以点"预览数据集"看看数据对不对。

2. 配置训练参数

WebUI 里能可视化调参,调完点"预览命令"会生成完整的训练命令。因为本次数据量较少,为了让模型学扎实,训练轮数(epoch)建议设大一点,比如 15 轮

生成的训练命令长这样(关键参数我都注释了):

llamafactory-cli train \
    --stage sft \                              # 训练阶段:监督微调
    --do_train True \                          # 启用训练模式
    --model_name_or_path /workspace/Qwen2.5-0.5B-Instruct \  # 基座模型路径
    --preprocessing_num_workers 16 \           # 数据预处理线程数
    --finetuning_type lora \                   # 微调方法:LoRA(贴便利贴)
    --template qwen \                          # 对话模板:Qwen 专用
    --flash_attn auto \                        # Flash Attention 自动启用(加速)
    --dataset_dir data \                       # 数据集目录
    --dataset ner_dataset \                    # 数据集名称
    --cutoff_len 2048 \                        # 单条数据最大长度(超了截断)
    --learning_rate 5e-05 \                    # 学习率
    --num_train_epochs 3.0 \                   # 训练轮数(数据少时建议调大到 15)
    --max_samples 100000 \                     # 每个数据集最大采样数
    --per_device_train_batch_size 4 \          # 每卡批次大小
    --gradient_accumulation_steps 2 \          # 梯度累积步数(显存不够时调大)
    --lr_scheduler_type cosine \               # 学习率调度:余弦退火
    --max_grad_norm 1.0 \                      # 梯度裁剪阈值
    --logging_steps 5 \                        # 每 5 步打印一次日志
    --save_steps 100 \                         # 每 100 步存一次检查点
    --warmup_steps 0 \                         # 预热步数
    --packing False \                          # 是否打包短序列
    --report_to none \                         # 不上报到 wandb 等
    --output_dir saves/Qwen2.5-0.5B-Instruct/lora/train_2025-05-03-08-10-32 \  # 输出目录
    --bf16 True \                              # 用 bf16 半精度训练(省显存)
    --plot_loss True \                         # 绘制 Loss 曲线
    --trust_remote_code True \                 # 信任远程代码
    --optim adamw_torch \                      # 优化器
    --lora_rank 8 \                            # LoRA 秩(便利贴的"宽度")
    --lora_alpha 16 \                          # LoRA 缩放系数
    --lora_dropout 0 \                         # LoRA dropout
    --lora_target all                          # LoRA 作用到所有线性层

3. 关键参数速查表

参数太多记不住?挑最关键的几个理解透就行:

参数比喻说明调参建议
stage培训阶段sft = 监督微调信息抽取任务用 sft
finetuning_type培训方式lora = 贴便利贴普通开发者首选 lora
template对话模板必须匹配模型Qwen 模型就选 qwen
num_train_epochs培训几轮整个数据集过几遍数据少就调大(如 15)
learning_rate学习步子多大每次更新幅度5e-5 是 LoRA 常用值
per_device_train_batch_size一批学几道题每卡 batch size显存够就调大
gradient_accumulation_steps凑几批再更新梯度累积显存不够时调大补偿
cutoff_len一道题最多多长序列截断长度2048 够大多数任务
lora_rank便利贴多宽LoRA 秩 r8/16 常用,越大越能学但越费
lora_alpha便利贴权重多大缩放系数通常设为 rank 的 2 倍
bf16用半精度省显存混合精度训练T4 之后都支持

⚠️ 注意:老显卡(如 V100)不支持 bf16,得改成 --fp16 True。如果训练直接报错崩掉,先怀疑这个。

💡 关于 LoRA 的 lora_ranklora_rank(r)就是那叠"便利贴"的宽度。r 越大,模型能学的东西越复杂,但参数也越多、越费显存。r=8 是个很稳的起点;任务复杂、数据多时可以调到 16、32。lora_alpha 一般设成 r 的 2 倍(r=8 → alpha=16),这是社区经验值。

4. LoRA 到底在干什么(一张图看懂)

输入 x

冻结的原始权重 W
实习生脑子里的通识知识

LoRA 旁路
低秩矩阵 A·B
一叠岗位便利贴

原始输出 W·x

增量输出 Δx

相加

最终输出

原始权重 W 是冻结的(实习生的通识知识不动),LoRA 在旁边加一条小路:用一个低秩矩阵 A·B(便利贴)算出一个增量,加到原始输出上。训练时只更新 A 和 B,所以参数量极小、显存友好。这就像不动实习生的脑子,只让他多带一叠随时可换的便利贴。

5. 开始训练 & 观察 Loss

在 WebUI 里点 “开始” 按钮,服务器终端会打印训练过程。训练完成后,output_dir 下会保存这些内容:

  • adapter*LoRA 模型权重(就是那叠训练好的"便利贴",体积很小)。
  • training_losstrainer_log:训练损失和日志。
  • 其他:训练参数备份。

Loss 趋势是判断训练效果的关键指标:

训练 Loss 理想走势(示意):
步数    5      50     100    200    300    400    500
Loss  3.20  → 2.10  → 1.50  → 1.05  → 0.72  → 0.55  → 0.48
       ↘     ↘      ↘      ↘      ↘      ↘      ↘
       高开,逐渐下降,趋于平稳 ← 理想:降到 1 以下

💡 Loss 怎么看? Loss 应该逐渐下降并趋于平稳。理想情况下能降到 1 以下。如果 Loss 一直不降,可能是学习率太小或数据有问题;如果 Loss 降得太狠甚至接近 0,要警惕过拟合——模型把训练集"背"下来了,但换个问题就不会。

⚠️ 注意:WebUI 训练完成后界面会展示完成状态。如果你勾选了 --plot_loss True,还能看到 Loss 曲线图,直观判断训练是否收敛。


五、考核转正:合并模型 + 验证效果

训练完得到的只是"便利贴"(LoRA adapter),推理时可以动态加载它,也可以把便利贴"誊抄"进模型脑子里——这就是合并(merge)。合并后得到一个完整的独立模型,部署更方便。

1. 合并模型:把便利贴正式誊抄进脑子

在 WebUI 切到 Export 标签:

  1. 检查点路径:选之前训练保存的 LoRA 模型路径(output_dir 下的那个)。
  2. 导出目录:自定义合并后完整模型的保存路径(比如 /workspace/qwen-ner-merged)。
  3. “导出”,等它把 LoRA 权重合并进基座模型,存成一个完整的模型目录。

基座模型
Qwen2.5-0.5B
冻结的通识知识

合并

LoRA 权重
adapter
训练好的便利贴

完整合并模型
qwen-ner-merged
一个独立可部署的模型

💡 为什么要合并? 动态加载 LoRA 推理时需要同时带上基座模型 + adapter 两个东西;合并后只有一个完整模型,部署、转换格式(如 GGUF)都更省事。

2. 验证效果:让"转正"的模型做题

合并完,切回 Chat 标签测试。关键一步:模型路径改成合并后的完整模型绝对路径(比如 /workspace/qwen-merged),而不是原来的基座路径。

加载模型后,用之前"面试"时问过的同样问题再问一遍,对比效果:

你现在是一个很厉害的阅读理解器,严格按照人类指令进行回答。
帮我抽出SPO,用JSON回答。
《武汉这些天一直在下雨》是钟立风的音乐作品,收录在《她为我编织毛衣》专辑中。

微调后,模型应该能稳定输出结构化 JSON,比如:

[
  {"predicate": "所属专辑", "subject": "武汉这些天一直在下雨", "object": "她为我编织毛衣"},
  {"predicate": "创作者", "subject": "武汉这些天一直在下雨", "object": "钟立风"}
]

🎮 对比验证小技巧:把微调前后对同一问题的回答截图并排放,是写技术博客和汇报时最有说服力的素材。微调前是一堆废话,微调后是干净的结构化 JSON——这种"前后对比"比任何文字描述都直观。

🕳️ 陷阱:如果合并后效果还是不行,别急着调参数。先检查三件事:① 数据集质量(标注对不对);② 训练轮数够不够(数据少就多训几轮);③ 模型路径有没有指对(指向了基座而非合并模型)。90% 的"没学会"都是这三个原因。


六、外派上岗:导出 GGUF + Ollama 部署(扩展)

合并后的模型是 HuggingFace 格式(.safetensors),要在本地轻量化部署,还得转成 GGUF 格式,再用 Ollama 跑起来——相当于把这个"转正员工"打包成"可外派的版本",派到任何机器都能用。

1. 为什么要 GGUF + Ollama?

方案格式部署复杂度资源占用适用场景
HuggingFace 原版.safetensors高(需 Python + transformers)研究、二次开发
GGUF + Ollama.gguf低(一行命令)小(支持量化)本地部署、生产使用

GGUF 是 llama.cpp 生态的模型格式,支持量化(把模型体积压到原来的 1/4 甚至更小),Ollama 则提供了最简单的本地运行方式。

2. 转换为 GGUF 格式

先装好 llama.cpp 的 GGUF 工具库:

# 1. 拉取 llama.cpp 源码
git clone https://github.com/ggerganov/llama.cpp.git

# 2. 安装 gguf Python 库
cd llama.cpp/gguf-py
pip install --editable .

# 3. 回到 llama.cpp 目录
cd ..

执行格式转换:

# 把合并后的 HF 模型转成 GGUF
python convert-hf-to-gguf.py /workspace/qwen-merged

执行完会在模型目录下生成一个 .gguf 文件,这就是打包好的"外派版"模型。

⚠️ 注意:如果转换时报缺少某个权重或配置的错,多半是合并模型不完整。回去检查 Export 步骤有没有把所有文件(config.jsontokenizer 等)都导全了。

3. 用 Ollama 跑起来

安装 Ollama

curl -fsSL https://ollama.com/install.sh | sh

注册模型:写一个 Modelfile(类似 Dockerfile),告诉 Ollama 用哪个 GGUF 文件:

ollama create chat-merged -f /workspace/qwen-merged/gguf-model.gguf

启动对话

ollama run chat-merged

进入交互式问答,直接输入信息抽取的指令就能用了。输入 /bye 退出。

合并模型
HF 格式

convert-hf-to-gguf.py

GGUF 文件
可量化、体积小

ollama create

ollama run
本地一行命令上岗

到这一步,从"下载一个啥也不会的实习生",到"训练、转正、打包外派上岗"的完整闭环就跑通了。


核心要点小结

  1. LLaMA-Factory 是大模型微调的"培训学校":把数据加载、对话模板、训练循环等脏活全打包,WebUI 可视化操作,普通开发者也能上手。
  2. LoRA = 不回炉重造,只贴便利贴:只训练旁路的低秩矩阵,不动原始权重,显存友好、训练快,是普通开发者的首选微调方法。
  3. 数据是微调的灵魂:alpaca 格式 = instruction(出题)+ input(材料)+ output(标准答案),数据集要在 data/dataset_info.json 里注册才能被框架识别。
  4. 关键参数记三个finetuning_type=lora(贴便利贴)、num_train_epochs(数据少就调大)、learning_rate(5e-5 是 LoRA 常用值);老显卡记得把 bf16 换成 fp16
  5. 训练完要"转正"再"外派":先用 Export 合并 LoRA 得到完整模型,再转 GGUF 用 Ollama 部署,一条龙跑通从训练到本地上线。
  6. 效果验证靠前后对比:用同一问题问微调前后的模型,结构化输出 vs 废话,对比最直观。

动手思考题

  1. 如果把 lora_rank 从 8 调到 32,训练参数量会增加多少?模型效果一定会变好吗?什么情况下反而会过拟合?
  2. 你的训练 Loss 降到 0.05 了,但换一批没见过的测试数据,模型却胡言乱语——这是出了什么问题?该怎么解决?
  3. 如果想让模型同时学会「信息抽取」和「身份认知」(知道自己是某某公司开发的助手),数据集和训练参数要怎么调整?
  4. 0.5B 模型跑通后,你想换 7B 模型微调,除了改模型路径,还需要调整哪些参数?显存不够时第一步该调什么?


📚 关于本系列

本文是 「大模型微调实战」 系列中的一篇。所有文章在我的个人博客上都有 可交互动画 + 完整学习路线 版本,建议配合食用 👇

🗺️ 查看完整 AI 学习路线
👉 bestsdz.xyz

觉得有帮助的话,欢迎去博客点个收藏 ⭐,你的支持是我更新的最大动力!

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐