大模型微调完全指南
面向对象:具备 Linux 运维和 Python 基础,希望系统掌握大模型微调的工程师。
配套环境:阿里云 PAI-DSW(DSW-AMD)单卡 AMD Instinct MI300X / 192GB HBM3 / ROCm。
整理时间:2026-10-09
目录
| 章节 | 内容 | 适合什么时候读 |
|---|---|---|
| 第 1 章 | 微调是什么、为什么有用、成本结构 | 第一次接触 |
| 第 2 章 | 决策树:Prompt / RAG / 微调 怎么选 | 做技术选型时 |
| 第 3 章 | SFT、DPO、LoRA、继续预训练的区别 | 理解原理时 |
| 第 4 章 | LLaMA-Factory / ms-swift / Unsloth 怎么选 | 动手前 |
| 第 5 章 | 显存怎么算、你的 MI300X 能做什么 | 动手前 |
| 第 6 章 | 数据格式、设计原则、清洗方法 | 最重要 |
| 第 7 章 | 从零到跑起来的完整命令 | 动手时 |
| 第 8 章 | 评估方法、防止自欺欺人 | 训完之后 |
| 第 9 章 | 故障现象 → 根因 → 解决 | 出问题时 |
| 第 10 章 | 分阶段学习计划、进阶技术 | 跑通之后 |
| 附录 A | ROCm 环境的具体坑 | 你这台机器 |
路径说明
本指南写作时用的 /models/...、~/LLaMA-Factory 是通用示例。在 AMD 192G 实例上实际对应的路径:模型放 /mnt/workspace/models/,LLaMA-Factory 源码在 /mnt/workspace/finetune/LLaMA-Factory/,数据集与训练产物在 /mnt/workspace/finetune/,venv 环境在 /mnt/workspace/envs/finetune/。执行下文命令时把示例路径替换为上述实际路径即可。
第 1 章 微调的含义与意义
1.1 微调是什么
大模型的分阶段流程是这样的:
通用语料(数万亿 token) → 预训练(Pre-training) → 基座模型(Base)
↓
大量人类偏好数据
↓
对齐(Alignment) → Chat 模型
↓
你的业务数据
↓
微调(Fine-tuning) → 你的专用模型微调就是第四步:用一个已经训好的模型继续训练,让它的输出更贴合你的具体需求。
这里有个关键区分:微调改的是模型的权重参数。这和"改提示词"是完全不同的两件事——提示词是在模型外面加规则,微调是把规则"烧进"模型里。
1.2 微调改变的是什么
微调最有价值的地方在于它能把三类东西固化下来:
① 输出格式
通用模型输出 JSON 时经常犯这些毛病:字段名不统一、字段顺序随机、偶尔带上一句"好的,以下是 JSON:"、甚至直接用 Markdown 代码块包起来。这些对人类无所谓,但对下游程序是灾难。
微调可以把输出锁定成严格格式,一字不差。
② 判定标准
这是最被低估的价值。举例:判断一张工单是"紧急"还是"不紧急",需要一套公司内部约定的主观标准。这套标准:
- 无法用 prompt 描述清楚(写出来要几百字,模型还是会飘)
- 每次回答都不一样(同一个人两次判断可能不同)
- 但只要写成几百条标注样本,模型就能稳定学会
微调的本质是把"每次都要重复解释的业务规则"变成"模型内部的本能"。
③ 语言风格和话术
让模型用特定的语气、术语体系、句式结构回答。客服话术、法律文书风格、行业术语习惯,都属于这一类。
1.3 微调不能做什么(必须认清的边界)
微调不是往模型里"灌知识"的好工具。
这是最常见的误解。你微调了 1000 条公司内部文档,模型确实记住了这些内容,但你换个问法问它,它不一定能准确回忆出来。更糟的是它可能编造一个看起来合理但完全错误的答案。
模型不是数据库。RAG 是"你问什么我去查",微调是"我把东西背进 weights,然后凭感觉回忆"。后者的可靠性远不如前者。
同理,微调也不适合提升通用智力。 让模型掌握新数学能力、新的编程语言这类事,需要的是继续预训练(数据量以 GB 计),不是几百条 SFT 数据能做到的。
1.4 微调 vs Prompt vs RAG 成本对比
| 维度 | Prompt 工程 | RAG | 微调 |
|---|---|---|---|
| 补充事实知识 | ❌ 不行 | ✅ 核心能力 | ⚠️ 不可靠 |
| 统一输出格式 | ⚠️ 能,但不稳定 | ⚠️ 能 | ✅ 核心能力 |
| 固化主观判定标准 | ❌ 不稳定 | ❌ 需在 prompt 里硬写 | ✅ 核心能力 |
| 注入领域表达风格 | ⚠️ 能 | ❌ 部分 | ✅ 核心能力 |
| 单 token 推理成本 | 低 | 高(多了检索+拼接) | 低(输出短、无检索) |
| 需要 GPU | ❌ | ❌ | ✅ |
| 知识更新速度 | 改 prompt,秒级 | 加文档,分钟级 | 重训,小时级 |
| 维护复杂度 | 极低 | 中 | 高 |
看最后三行——微调真正的经济价值常常在推理侧:输出 token 更短(不用解释推理过程)、不需要检索链路,单次调用成本可能比 RAG 方案更低。这一点在调用量大的生产环境里很重要。
1.5 一句话总结
微调是把"每次都要在提示词里反复试、反复解释的那部分固定下来"的过程。
第 2 章 什么场景该用微调
2.1 决策树
在动手之前,先诚实回答这个问题:这个需求,一个足够好的 prompt 加 RAG 能不能解决八成?
如果能,微调带来的运维成本和复杂度大概率不划算。
按下面三个问题依次判断:
问题 1:模型缺的是"事实"还是"行为"?
│
├─ 缺事实(不知道某文档写了什么)
│ → 用 RAG。微调解决不了。
│
└─ 缺行为(知道该怎么做,但每次输出不一致 / 格式不对 / 判不准)
↓
问题 2:这种不一致能用 prompt 稳定解决吗?
│
├─ 能(规则清晰、情况不多)
│ → 用 Prompt 工程。成本低十倍。
│
└─ 不能(规则模糊、需要大量示例才说得清、要保证 100% 一致)
↓
问题 3:推理成本/延迟是瓶颈吗?
│
├─ 是(高并发、大流量)
│ → 微调。缩短输出、省掉检索链路,长期省钱。
│
└─ 不是
→ 微调的收益主要是"体验一致性",值不值得看你对
一致性的要求有多苛刻。2.2 适合微调的场景
| 场景 | 说明 | 典型数据量 |
|---|---|---|
| 结构化抽取 | 从非结构化文本抽字段,输出严格 JSON | 500 ~ 5000 |
| 分类打标 | 意图识别、情感判断、风险分级 | 1000 ~ 20000 |
| 客服话术 | 固定语气、流程、应答模板 | 500 ~ 5000 |
| 代码生成规范 | 公司特定的代码风格、API 用法 | 1000 ~ 10000 |
| 判定标准固化 | 主观但有内部共识的判断(如紧急程度) | 200 ~ 2000 |
| 格式严格输出 | 拒绝解释性文字,强制纯结构化 | 200 ~ 1000 |
表里的数字是怎么来的(以及该怎么用它)
先说清楚:这张表里的数字不是从某个公式推导出来的。 它们来自三个来源——社区实践的常见量级(教程、框架示例里反复出现的范围)、机器学习的一般直觉(覆盖输出空间、覆盖输入分布)、以及对任务复杂度的经验外推。换句话说,"500~5000"里的 500 和 5000 并不是什么有理论意义的上下界,只是同一个数量级里偏保守和偏激进的两端。所以你拿 800 条去做,或者拿 1200 条去做,都不代表你做错了或做对了。
而且这张表混了两个不同的目标:
- "够学会":几百条就能让模型开始稳定输出正确格式
- "够生产用":要覆盖足够多的边界情况,输入分布要贴近真实流量
500~5000 这个量级更接近后者。所以如果它看起来比你直觉高,是正常的。
真正决定数据量的,是下面四个因素,不是模型参数量:
| 因素 | 说明 |
|---|---|
| 任务增量(最关键) | 模型预训练时已经会多少。输出 JSON 很多模型本来就会,几十到几百条够;教一套全新的判别规则(如"紧急程度"的内部标准),需要的数据就翻倍 |
| 输出空间规模 | 经验规则:每个需要区分的输出取值,至少准备 30~50 条样本。二分类每类 50~100 条;5~10 类分类每类 100~300 条;上百个枚举值的槽位填充则需要更多 |
| 输入空间覆盖 | 按真实输入的"说法多样性"估。长尾往往比头部长得多——20 种问法可能用 200 条覆盖,自由输入的爬虫数据可能要 2000 条 |
| 一致性严格度 | 90% 对就行 → 数据可少;99.9% 对(结构化输出、下游要解析)→ 必须把所有边界和异常输入都覆盖到,数据量翻倍 |
**参数量对数据量的影响比直觉小,而且方向相反:**更大的模型样本效率更高。70B 用 200 条通常能打过 7B 用 2000 条,因为大模型从少量示例里归纳规则的能力更强。所以从 7B 到 27B/32B,同一任务所需的数据量大致是 0.5~0.7 倍,不是线性放大。真正区分数据量级的是任务难度,不是模型规模。
但参数量确实影响三件别的事:收敛 epoch 数(32B 通常 1~2 轮收敛,7B 要 3 轮)、LoRA rank 是否够(模型越大要注入的改变越多,r=16 可能不够)、灾难性遗忘风险(大模型相对更脆弱)。这些详见 第 5 章 和 第 10 章。
所以这张表应该被你自己项目的实测值替换掉。 正确的做法是跑数据量曲线:用 50 / 150 / 500 / 1500 条训练同一配置,记录 eval loss 和测试集上的任务指标,画两条曲线找拐点。拐点就是你的答案。 拐点靠前说明模型本来就会(任务增量小),拐点靠后说明任务增量大(可能要换基座模型而不是加数据)。这个实验的具体做法见 第 8 章 实验一。
注
本表数据量区间和第 6 章"200~1000 多数任务的甜点区间"并不矛盾——后者说的是"够学会"的量级,前者说的是"够生产用"的量级。两个数据分别对应不同的目标。
2.3 不适合微调的场景
| 场景 | 应该用 |
|---|---|
| 知识经常更新 | RAG(重训成本太高) |
| 需要引用原文出处 | RAG(微调无法给引用) |
| 数据量不足 100 条 | Prompt + few-shot 示例 |
| 需要模型学会新语言/新学科 | 继续预训练 |
| 需要推理能力的提升 | 更好的基座模型 + 强化学习 |
| 一次性/临时需求 | 就写个 prompt |
2.4 混合方案(生产环境最常见)
真实项目很少二选一。一个典型的企业知识问答系统是:
用户提问
↓
微调模型:判断意图、提取关键实体、控制输出格式
↓
RAG:检索内部文档
↓
微调模型:基于检索结果,用公司话术生成回答
↓
输出微调负责"怎么做",RAG 负责"内容是什么"。 两者互补,不是竞争关系。
第 3 章 微调的技术形态
3.1 继续预训练(Continual Pre-training)
在通用语料上再跑一段训练,把模型往某个领域推(法律文本、医疗记录、特定代码库)。
- 数据量:几 GB 到几十 GB 文本
- 显存:需要全参训练(下面 LoRA 的省显存技巧在这里不适用)
- 目的:改变模型的"知识底座"和"语言能力"
绝大多数业务场景不需要这个。 只有当你要处理的语料和模型预训练分布差距极大(比如小语种、专业领域)时才考虑。
3.2 监督微调(SFT)—— 99% 的场景
给「指令 → 理想回答」的样本对,训练模型遵循指令。这是本文后续重点讨论的对象。
3.3 偏好对齐(DPO / RLHF)
给「同一个提示的多个回答 → 哪个更好」,让模型学会人类偏好。
执行顺序:SFT 之后做 DPO。直接用偏好数据做 SFT 效果不好,因为模型学不到"为什么 A 比 B 好",只会学到"要输出 A"。
什么时候需要:
- SFT 后模型已经输出正确内容,但表达方式不符合你的偏好
- 需要在多个"都正确"的答案里选择特定风格
- 模型的语气、详略程度需要调整
什么时候不需要:输出格式和判定标准用 SFT 就能解决,不必上 DPO。
3.4 LoRA / QLoRA —— 让微调变得便宜
3.4.1 核心思想
原模型权重全部冻结,在若干层旁边并联一个很小的可训练分支:
原始计算: y = W · x (W 冻结,不动)
微调计算: y = W · x + (α/r) · B·(A · x)
└──┬──┘
这部分可训练,通常只有几十 MB其中 A 是降维矩阵(rank × hidden),B 是升维矩阵(hidden × rank),r 是秩。
由于 BA 是低秩矩阵,只能表达权重更新的一个低维子空间——这既是它的限制,也是它省显存的原因。
3.4.2 r(秩)的含义
r 决定了 LoRA 的表达能力:
| r | 可训练参数(27B 模型) | 适用场景 |
|---|---|---|
| 8 | ~20M | 格式调整、简单风格 |
| 16 | ~40M | 通用起点 |
| 32 | ~80M | 需要较强风格适应 |
| 64 | ~160M | 接近全参效果 |
| 128 | ~320M | LoRA+ / rsLoRA 的研究区间 |
一般规律:r 越大越接近全参,但收益递减明显。从 8 调到 16 提升可能很明显,从 64 调到 128 基本没区别却慢一倍。先用 16,遇到"学不动"再往上加。
3.4.3 LoRA+ 技巧
标准的 LoRA 让 A 和 B 用同一个学习率。LoRA+ 建议给 B 一个更大的学习率(通常是 A 的 16 倍),实测能加快收敛、提升效果,不增加任何显存成本。很多框架已内置,找 loraplus_lr_ratio 参数。
3.4.4 QLoRA
把基座模型 4-bit 量化后再挂 LoRA,可训练分支仍用 bf16。
优势:显存再降一个数量级,7B 模型单张 24GB 卡能训。 代价:速度慢 20~30%,且部分算子支持不完整。
你的 MI300X 有 192GB,不需要 QLoRA。 bf16 直接跑更稳、更快。
3.5 各方案对比
| 方案 | 可训参数 | 显存(27B) | 数据要求 | 适用 |
|---|---|---|---|---|
| 全参 SFT | 27B | ~430GB | 数千条起 | 极致质量追求 |
| LoRA SFT | ~40M | 70100GB | 数百条 | 绝大多数场景 |
| QLoRA SFT | ~40M | 2545GB | 数百条 | 显存受限 |
| LoRA + DPO | ~40M | 70100GB | 偏好对数千 | 风格/偏好对齐 |
| 继续预训练 | 27B | ~430GB+ | GB 级语料 | 领域迁移 |
第 4 章 框架选择
4.1 主流框架对比
| 框架 | 优势 | 劣势 | 适合 |
|---|---|---|---|
| LLaMA-Factory | 图形界面 + 命令行;功能最全(SFT/LoRA/DPO/预训练/量化);社区最活跃 | ROCm 需自行适配 | ⭐ 首次上手首选 |
| ms-swift | ModelScope 阿里自家,ROCm 支持最顺;中文文档好;模型/数据集生态丰富 | UI 简陋 | ROCm 环境首选 |
| Unsloth | 显存优化激进,速度快 2 倍以上;官方有 AMD 分支 | 快速迭代中,偶有兼容问题 | 追求速度 |
| Axolotl | 配置文件驱动,可复现性好;GitHub Actions 集成 | 无 UI,学习曲线陡 | 工程化 / CI |
| xtuner | 上海 AI Lab 出品,文档详尽 | 社区较小 | 企业内部 |
| peft + trl | HuggingFace 官方库,最底层 | 需要自己写训练循环 | 需要完全控制 |
4.2 底层依赖关系
上面的框架基本都是这几个库的上层封装:
HuggingFace 生态
├── transformers 模型定义、加载
├── datasets 数据加载与处理
├── peft LoRA / QLoRA 实现
├── trl 训练器(SFT / DPO 训练循环)
├── accelerate 分布式与混合精度
├── deepspeed ZeRO / FSDP 优化器
├── liger-kernel 融合算子,省显存、提速
└── bitsandbytes 量化(ROCm 上支持不完整,慎用)建议路线:先用 LLaMA-Factory 的 WebUI 跑通 → 需要精细控制时再直接用 peft + trl。
4.3 LLaMA-Factory 的关键概念
数据集注册 dataset_info.json
LLaMA-Factory 靠这个文件知道"你的 JSON 长什么样、哪个键是什么角色"。字段写错会静默失败——不报错、loss 也降,就是效果奇差。
{
"my_dataset": {
"file_name": "train.json",
"formatting": "sharegpt",
"columns": { "messages": "conversations" },
"tags": {
"role_tag": "from", "content_tag": "value",
"user_tag": "human", "assistant_tag": "gpt",
"system_tag": "system"
}
}
}三种数据格式
ShareGPT(推荐,功能最全)
{"conversations": [
{"from": "system", "value": "你是..."},
{"from": "human", "value": "问题"},
{"from": "gpt", "value": "答案"}
]}Alpaca(最简单,适合单轮指令)
{"instruction": "问题", "input": "可选的上下文", "output": "答案"}input 为空时写 ""。这种格式不支持 system 角色。
对话历史 + 复杂角色(需要多轮、工具调用时)
{"messages": [
{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}
]}这种格式用 "formatting": "conversations" 注册。
Template(chat template)—— 最隐蔽的坑
template 参数决定对话怎么被拼成模型认识的 token 序列。不同模型家族的模板不同:Qwen、Llama、Baichuan、ChatGLM 各自一套标记方式。
模板错位的后果极其隐蔽:不报错、训练能收敛、loss 曲线看起来正常,但模型效果明显下降。你会觉得"怎么训完变笨了",而真实原因只是对话标记和基座模型预训练时不一致。
正确做法:用模型自带的模板。LLaMA-Factory 里 template: qwen 即可,也可以写 template: qwen + default_system 兜底。
验证方法:训练前先用 llamafactory-cli chat 或直接加载 tokenizer 检查实际拼出的字符串长什么样。
第 5 章 硬件配置与显存估算
5.1 你的 MI300X 定位
| 项目 | 参数 |
|---|---|
| 架构 | CDNA3(gfx942) |
| 显存 | 192GB HBM3 |
| 带宽 | 1.3 TB/s |
| bf16 稠密算力 | ~1300 TFLOPS |
| FP8 支持 | 原生硬件加速 |
| 生态 | ROCm(软件栈与 CUDA 有差异) |
对标关系:单卡 MI300X 在 bf16 算力上大致相当于甚至略强于单张 H100 SXM/H200 级别,但生态成熟度明显不如 CUDA。
一个反直觉但重要的结论:在"训 27B LoRA"这个具体任务上,MI300X 192GB 比 H100 80GB 更合适。因为 27B bf16 权重就要 54GB,H100-80G 加载完就吃掉三分之二显存,激活一上来就 OOM;而 192GB 有 2.4 倍余量。
5.2 显存构成拆解
训练时的显存 = 下面几项之和:
① 模型权重 N × 2 bytes (bf16)
② 梯度 只算可训练参数 (LoRA 下极小)
③ 优化器状态 可训练参数 × 8~16 (AdamW 的 fp32 状态)
④ 激活 主要开销,随 seq_len 平方增长
⑤ logits + CE 容易被低估的大头!见下
⑥ 碎片 CUDA caching allocator,预留 10%关键:logits 显存常被低估
Qwen 系列 vocab 约 15 万。序列长度 4096 时,单个样本的 logits 就是:
4096 × 151936 × 2 bytes ≈ 1.24 GB如果为了算交叉熵上采样到 fp32,直接变 2.5GB;如果实现不好算全 batch,就会瞬间爆掉。
这是长上下文场景下最常见的 OOM 元凶,比模型权重本身更容易背锅。
缓解手段:
- Liger Kernel:分块计算交叉熵,logits 显存降一个数量级,强烈推荐
cutoff_len控制序列长度- 减小
per_device_train_batch_size
激活显存
用梯度检查点(gradient_checkpointing: true)后,激活只保存每层的输入,显存约与 seq_len × batch × hidden 成正比而不是平方增长。这是单卡微调的必开选项。
5.3 27B 模型的显存账
| 方案 | 权重 | 优化器 | 激活+logits | 合计 | 你的 192GB |
|---|---|---|---|---|---|
| LoRA + bf16 | 54GB | ~1GB | 15~40GB | 70~100GB | ✅ 宽裕 |
| LoRA + FP8 冻结基座 | 27GB | ~1GB | 15~35GB | 45~70GB | ✅ 很宽松 |
| LoRA + Liger + seq 8192 | 54GB | ~1GB | 30~50GB | 85~110GB | ✅ 可行 |
| 全参微调 | 54GB | 216GB | 20GB | ~430GB | ❌ 差 240GB |
| 全参 + ZeRO-3 CPU offload | 54GB | 108GB | 20GB | ~180GB + 大量内存 | ⚠️ 可行但慢 |
结论:你的机器上现实的选择是 LoRA / DoRA / LoRA+。全参需要 2~4 台这样的机器用 FSDP 拼起来。
5.4 训练速度预期
LoRA 训练 MFU(模型算力利用率)在 MI300X 上实测通常能到 40~50%,即有效 500~600 TFLOPS。
按 6·N·T 估算训练时长:
FLOPs = 6 × 参数量 × token 数
总 token 数 = 样本数 × 平均长度 × epoch 数
时间 = FLOPs ÷ 有效算力举例:27B 模型、1000 条样本 × 1000 token × 3 epoch:
6 × 27e9 × 3e6 ≈ 4.9e17 FLOPs ÷ 5e14 ≈ 970 秒 ≈ 16 分钟实际加 LoRA 前向/反向的多分支开销,会比这个理论值慢 30~50%。
| 场景 | 预计耗时(27B LoRA) |
|---|---|
| 23 条数据 × 3 epoch | 1~2 分钟 |
| 500 条 × 3 epoch | 20~30 分钟 |
| 2000 条 × 3 epoch | 1~2 小时 |
| 2000 条 × 3 epoch @ seq 8192 | 3~4 小时 |
这个速度对你意义很大:意味着你可以做真正的迭代实验(换数据、改超参、跑对比),而不是"训一次等一晚上看结果"。
小模型(4B/7B)快 4~6 倍,适合做配置调试。
5.5 各规模模型的实际门槛
| 模型规模 | LoRA 最低显存 | 全参最低显存 | 你的机器 |
|---|---|---|---|
| 0.5B | 3GB | 12GB | ✅ 随便跑 |
| 1.7B | 6GB | 24GB | ✅ 随便跑 |
| 7B | 16GB | 56GB | ✅ 宽松 |
| 14B | 28GB | 112GB | ✅ 宽松 |
| 32B | 70GB | 256GB | ✅ 可行(LoRA) |
| 72B | 150GB | 576GB | ⚠️ 勉强 LoRA,seq 要压小 |
| 235B-A22B | 需量化 | - | ❌ 原生不行 |
第 6 章 数据集准备
全文最重要的部分
大量实践表明:微调效果的 80% 由数据决定,框架和超参只占 20%。很多人在超参上反复调优,收益远不如把数据做扎实。
6.1 数据质量五原则
① 同质性 > 数量
所有样本的格式、风格、字段命名必须高度一致。
❌ 反例:样本 A 用 {"product": "登录", ...}
样本 B 用 {"module": "login", ...}
样本 C 输出 {"产品": "登录", ...}
结果:模型输出变得混乱,有时用中文键名,有时用英文,有时字段顺序随机。判断标准:把所有样本的输出拼在一起看,如果风格跳变,说明同质性有问题。
② 数量:收益递减很快
| 数据量 | 效果 |
|---|---|
| < 50 条 | 基本学不到什么,容易过拟合 |
| 50 ~ 200 条 | 格式类任务够用,风格类勉强 |
| 200 ~ 1000 条 | 多数任务的甜点区间 |
| 1000 ~ 5000 条 | 稳定收益 |
| > 10000 条 | 需要更大 rank,否则学不满 |
先做到 200 条质量极高,再考虑扩量。 1000 条中等质量不如 300 条精心设计的。
③ 去重与防泄漏
训练集和测试集必须严格不重叠。 这是最容易被忽略、后果最严重的错误——泄漏会给你虚高的评估分数,让你以为微调成功了,上线后表现崩塌。
检查方法:
# 精确去重
seen = set()
for s in data:
key = s["conversations"][1]["value"] # user 输入
if key in seen: continue
seen.add(key)
# 近似去重(语义相似)
# 用 embedding 模型算相似度,相似度 > 0.9 的视为重复还要检查:测试集的输入是否在训练集中以变体形式出现过?
④ 分布对齐
训练数据的问法要覆盖真实使用时的输入方式,包括:
- 不同长度:短的输入、长的输入
- 不同表达:正式/口语、完整/省略、有错别字
- 边界情况:空输入、超长输入、信息不全、矛盾信息
- 拒答场景:信息不足时应该怎么处理
只训"清晰典型"的样本,模型在真实场景的第一条模糊输入上就会崩。
⑤ 自洽性:规则和标注必须一致
如果你的 system 里写"high = 已造成资金损失",那么所有涉及资金损失的样本都必须标 high。
❌ system 说 high 包含资金损失,
但"扣款未到账"标成了 mid —— 模型学到的是噪声。标注的内部一致性,比标注的绝对准确率更重要——只要规则一致,模型就能学会;规则自相矛盾,标得再准也没用。
6.2 Chat Template 校验
训练前务必检查实际拼出的 token 序列:
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("你的模型路径")
msgs = [
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "问题"},
{"role": "assistant", "content": "答案"},
]
print(repr(tok.apply_chat_template(msgs, tokenize=False)))对比模型卡上给的官方示例,确保 <|im_start|> / <|im_end|> 这类特殊标记的位置和名字一致。
这一步花 2 分钟,能省掉几小时的 debugging。
6.3 数据清洗流水线
# 1. 去重
# 2. 长度过滤(过短的样本信息量不足,过长的要截断或丢弃)
# ↓
# 3. 格式校验(JSON 输出必须能被 json.loads 解析)
# 4. 一致性检查(键名、字段顺序、枚举值)
# 5. 敏感性检查(是否包含真实用户隐私数据)
# 6. 划分 train / valid / test(严格不重叠)第 3 步特别重要:如果训练目标是输出 JSON,就在数据清洗时用 json.loads() 验证每一条。有一条坏数据,模型就会学会"偶尔输出不合法的 JSON"。
6.4 数据来源的优先级
从好到差:
- 真实业务数据(效果最好,但需要脱敏和标注)
- 强模型生成的合成数据(用大模型批量生成,注意质量过滤)
- 公开高质量数据集(HuggingFace / ModelScope 上有大量中文指令数据)
- 人工编造(只适合验证流程,绝对不适合真实任务)
合成数据的陷阱:用强模型批量生成时,会出现大量"格式完全一致但内容重复"的样本,模型学到的是表面模式而非真实能力。务必做去重和多样性检查。
6.5 关于训练数据的版权与合规
- 公开数据集要看 License(Apache-2.0 / MIT / 非商用)
- 用户数据脱敏(手机号、身份证、地址、姓名)
- 不要用竞争对手的产品输出做训练
- 商业用途前确认基座模型的 License 允许商用
第 7 章 安装与训练步骤
以下步骤以 LLaMA-Factory + 你的 MI300X (ROCm) 为例。
7.0 铁律:隔离环境
这一步不做,后面全白干
你的 MI300X 上已经部署了验证可用的 vLLM 0.28.0 + Qwen FP8 模型。LLaMA-Factory 会拖进来特定版本的 transformers / peft / trl,它们很可能强制升级 torch,直接把 vLLM 搞坏。
务必开独立 conda 环境:
conda create -n finetune python=3.11 -y
conda activate finetune绝对不要在 base 环境里 pip install 训练框架。
7.1 第一步:环境体检
# 1.1 确认 ROCm PyTorch 可用
python -c "import torch; print('hip:', torch.version.hip); print('device:', torch.cuda.get_device_name(0)); print('avail:', torch.cuda.is_available())"期望输出:
hip: 6.2.4xxxx
device: AMD Instinct MI300X
avail: True如果 hip 是 None 或 device 显示 NVIDIA,说明装的是 CUDA 版 torch,必须换:
pip uninstall -y torch torchvision torchaudio
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.2ROCm 版本要求:MI300X 建议 ROCm 6.2 以上。
# 1.2 确认显卡状态和显存占用
rocm-smi关键:确认 vLLM 没有在跑。它会占掉 27GB+ 显存。训练前要么停掉它,要么明确知道两者相加的显存需求。
# 查看是谁在占显存
rocm-smi --showpids7.2 第二步:安装训练框架
cd ~
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"ROCm 环境常见问题:
| 问题 | 说明 | 解决 |
|---|---|---|
bitsandbytes 报错 | ROCm 上支持不完整 | 不要装。显存够,用 bf16 |
| flash-attn 编译失败 | gfx942 需专门编译 | 用 attn_implementation="sdpa" 兜底 |
| 依赖冲突 | torch 版本被改 | pip install -e . --no-deps,手动补装缺的包 |
flash-attn 的处理:MI300X 上装不上 FA2 是常态,不是你的问题。PyTorch 的 SDPA 在 ROCm 6.2+ 有可用的 flash 后端,性能损失通常可接受。
7.3 第三步:准备基座模型
不能用 FP8 量化模型训练
FP8 是给 vLLM 推理用的量化产物,没有反向传播所需的反量化路径,无法用于训练。微调必须用原始 bf16 权重。
# ModelScope(国内速度快)
pip install modelscope
modelscope download --model Qwen/Qwen3-32B --local_dir /models/Qwen3-32B
# 或 HuggingFace
huggingface-cli download Qwen/Qwen3-32B --local_dir /models/Qwen3-32B磁盘空间:27B bf16 约 54GB,留 100GB 以上。
关于模型名称:请核实 Qwen3.8-27B 是否真实存在。Qwen3 系列通常是 30B-A3B(MoE)、32B(dense)、235B-A22B。如果是 MoE(config.json 里 num_experts > 0),激活显存和 LoRA 挂载点都要按 MoE 配。
# 检查是不是 MoE
python -c "import json; c=json.load(open('/models/Qwen3-32B/config.json')); print('experts:', c.get('num_experts', 0))"7.4 第四步:准备数据
建议先用一份小规模示例数据跑通全流程(例如 23 条工单结构化抽取数据),目录结构如下:
practice/
├── train_data_sample.json # ShareGPT 格式训练数据
├── llamafactory_config.yaml # 训练配置
└── data/
└── dataset_info.json # 数据集注册文件放到 LLaMA-Factory 目录下:
cp -r /path/to/实践数据/* ~/LLaMA-Factory/建议的学习顺序:先用 4B 或 1.7B 小模型跑通全流程(几分钟一轮),配置和数据都摸熟了再上 32B。
7.5 第五步:配置训练参数
llamafactory_config.yaml:
### model
model_name_or_path: /models/Qwen3-32B # ← 改成你的实际路径
trust_remote_code: true
### method
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 16
lora_alpha: 32 # 建议 = 2 × rank
lora_dropout: 0.05
lora_target: all
### dataset
dataset: demo_json
dataset_dir: data
template: qwen # ← 必须和基座匹配
cutoff_len: 4096
max_samples: 2000
overwrite_cache: true
preprocessing_num_workers: 16
### output
output_dir: saves/qwen3-32b/lora/demo
logging_steps: 5
save_steps: 200
plot_loss: true
overwrite_output_dir: true
### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 16 # 显存不够就靠它
learning_rate: 1.0e-4 # LoRA 专用,比全参高一个量级
num_train_epochs: 3.0 # 小数据别超 3 轮
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
gradient_checkpointing: true # 单卡必开
ddp_timeout: 180000000
### eval
val_size: 0.1 # 一定要留验证集
per_device_eval_batch_size: 1
eval_strategy: steps
eval_steps: 200超参起点速查:
| 参数 | 起始值 | 说明 |
|---|---|---|
lora_rank | 16 | 学不动再升到 32/64 |
lora_alpha | 2×rank | |
learning_rate | 1e-4 | 全参微调用 1e-5 ~ 2e-5 |
num_train_epochs | 3 | 再多基本是过拟合 |
warmup_ratio | 0.1 | |
cutoff_len | 4096 | 显存够可升 8192 |
gradient_accumulation_steps | 16 | 保住有效 batch |
7.6 第六步:开始训练
cd ~/LLaMA-Factory
# 命令行训练
llamafactory-cli train llamafactory_config.yaml
# 或 WebUI(推荐初次使用,参数组合太多)
llamafactory-cli webui # 浏览器打开 http://localhost:7860WebUI 里记得在"高级设置"中确认:
- 微调方式:LoRA
- 模型路径
- 数据集名称
- 模板(template):qwen
- 显存可视:观察显存占用
7.7 第七步:观察训练过程
正常现象:
1/1200 [ 45%▋......] 12:34/elapsed 00:11/it, 1.85it/s, loss: 0.3421, grad_norm: 0.234重点观察三个指标:
| 指标 | 健康范围 | 异常含义 |
|---|---|---|
loss | 平稳下降,1.5 → 0.3 左右 | 一直卡在 1.5+ 说明没学到 |
grad_norm | 0.1 ~ 1.0 波动 | 持续为 0 → 学习率过小或梯度断了 |
epoch | — | loss 平稳但 eval loss 上升 = 过拟合 |
loss 的经验判断:
- 起始 1.52.0(正常,随机初始化)
- 0.5 左右:学到了基本格式
- 0.3 以下:已经很贴合
- 接近 0:过拟合信号,模型在背答案
7.8 产物说明
训练完成后 output_dir 下会有:
saves/qwen3-32b/lora/demo/
├── adapter_model.safetensors # LoRA 权重(几十 MB)★
├── adapter_config.json # LoRA 配置
├── trainer_state.json # 训练状态,可断点续训
├── training_args.bin
└── all_results.json # 训练/验证 loss只需要 adapter_model.safetensors 和 adapter_config.json 两个文件。 这就是 LoRA 的优势——多个任务可以热切换。
7.9 第八步:合并与部署
方式一:vLLM 动态加载 LoRA(推荐,测试阶段用这个)
不用重新训练模型,加载 adapter 即可:
vllm serve /models/Qwen3-32B \
--enable-lora \
--max-lora-rank 16 \
--lora-modules mytask=/path/to/adapter_model.safetensors \
--port 8000请求时指定 adapter 名:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mytask",
"messages": [{"role": "user", "content": "安卓上打开就闪退"}],
"temperature": 0
}'方式二:合并权重成独立模型(生产部署用这个)
llamafactory-cli export \
--model_name_or_path /models/Qwen3-32B \
--adapter_name_or_path saves/qwen3-32b/lora/demo \
--template qwen \
--finetuning_type lora \
--export_dir /models/Qwen3-32B-mytool \
--export_size 5合并后是一个完整的独立模型,推理时不需要额外管理 LoRA。
保护现有部署
合并和测试都可以在新环境做,不要动那个已经跑通的 vLLM 服务。如果必须用同一个端口,先备份好原有启动命令。
第 8 章 微调效果验证
最容易被跳过、也最容易翻车的一章
评估做得好不好,直接决定你对微调效果的判断是真实还是幻觉。
8.1 铁律:必须有独立测试集
训练/测试数据重叠 = 你测的是记忆,不是泛化。
# 划分时确保严格不重叠
train, test = dataset[:-200], dataset[-200:]
assert set(x["input"] for x in train) & set(x["input"] for x in test) == set()泄漏会给你虚高的分数,让你以为微调成功,上线后表现崩塌。
8.2 评估的三个层次
层次一:任务指标(自动判分)
分类、抽取类任务可以直接算:
| 任务类型 | 指标 |
|---|---|
| 分类 | Accuracy、Macro-F1(类别不均衡时看这个) |
| 结构化抽取 | 字段级 Precision / Recall / F1 |
| JSON 输出 | 合法率(能被 json.loads 解析的比例) |
| 代码生成 | Pass@1 |
合法率是最容易算也最容易被忽略的指标:微调前可能 60% 输出带解释文字,微调后应该接近 100%。
层次二:人工抽检
抽 30~50 条,看输出是否符合你的业务预期。
这是唯一能发现"指标好看但实际不好用"的手段。比如分类准确率 95%,但模型把 5% 的高风险样本误判成低风险——这在业务上可能是不可接受的。
层次三:通用能力回归检测(最容易被忽略)
微调可能"按下葫芦浮起瓢"
微调后模型可能在指定任务上变好,同时把别的东西弄坏。这件事必须主动检查。
准备一组与训练任务无关的问题,测微调前后:
general_probes = [
"介绍一下量子计算的基本原理",
"帮我写一个 Python 快速排序",
"把这句话翻译成英文:今天天气不错",
"计算 1234 × 5678 等于多少",
"什么是 TCP 三次握手?",
]对比两个模型的回答,看有没有变短、变机械、答错、或者开始胡编。
退化原因:数据量太小、epoch 太多、学习率太高、训练数据分布过窄。
缓解手段:
- 混入 10~20% 的通用指令数据
- 控制 epoch(3 轮封顶)
- 降低学习率
- 用 rsLoRA 或更小的 rank
8.3 正确的工作流
① 训之前的基线 → 记录原模型在测试集上的表现
② 微调训练
③ 训之后的评估 → 同一份测试集,同一批指标
④ 对比分析
├─ 任务指标提升 + 通用能力无退化 → ✅ 可以上线
├─ 任务指标提升 + 通用能力明显退化 → ⚠️ 混合数据或减少 epoch
└─ 任务指标没提升 → ❌ 回到数据,问题在数据不在超参第①步经常被跳过,导致训完之后无法判断到底有没有变好。 请务必先测基线。
8.4 客观指标之外:必须人工看
微调的效果有一部分是"看起来对不对",脚本算不出来。
建议做一个对比表,人工评估:
| 测试输入 | 基线输出 | 微调后输出 | 是否更好 | 备注 |
|---|---|---|---|---|
| 安卓上闪退 | ... | ... | ✓ | |
| 支付失败 | ... | ... | ✓ | |
| 信息不全的输入 | ... | ... | ✗ | 仍需追问 |
最后一行最重要——边界情况的处理能力是真实业务和玩具测试的主要差距。
8.5 三个值得做的对比实验
跑通一次只是入门,下面三个实验才能让你真正理解微调:
实验一:数据量曲线
用 23 / 50 / 100 / 500 / 1000 条训练同样的配置,画 eval loss 曲线。
你会亲自看到收益递减发生在哪——这决定了项目实际投入多少数据是合理的。
实验二:消融 system 提示
把数据里的 system 段删掉重训,对比效果。
亲手感受"把规则固化在数据里"值多少。你会发现输出格式稳定性显著下降。
实验三:过拟合演示
只用 5 条数据训 20 轮,然后用训练集里没见过的输入去测。
你会看到模型把训练样本背得一字不差,面对新输入却答得一塌糊涂。
这就是为什么必须留验证集——这个体感比读十篇文章都值。
第 9 章 常见问题排查
9.1 显存问题
| 现象 | 根因 | 解决 |
|---|---|---|
| 启动即 OOM | 权重就装不下 | 换小模型,或用 FP8/量化基座 |
| 训练几步后 OOM | 激活或 logits 累积 | gradient_checkpointing: true + cutoff_len 调小 + batch_size: 1 + Liger Kernel |
| eval 时 OOM | 评估 batch 太大 | per_device_eval_batch_size: 1 |
| 显存持续增长但没报错 | 变量泄漏 / 评估没加 torch.no_grad() | 检查 eval 循环 |
排查顺序:batch_size → cutoff_len → 开梯度检查点 → 上 Liger → 量化。
9.2 训练不收敛
| 现象 | 检查项 |
|---|---|
| loss 一直卡在 1.5~2.0 | ① 学习率是否太小(试试 2e-4)② 数据格式是否被正确解析 ③ template 是否匹配 |
loss 变成 nan | ① 学习率太大(降到 5e-5)② 序列长度截断导致标签全被 mask ③ 数据里有空样本 |
grad_norm 一直是 0 | 学习率过小,或 LoRA 没挂上(检查 lora_target) |
| loss 降得很好但效果差 | 数据和测试分布不匹配,或有泄漏 |
9.3 效果问题
| 现象 | 最可能的原因 |
|---|---|
| 输出格式仍然不稳定 | ① template 不匹配 ② 数据量太少(<100 条)③ 数据格式本身不一致 |
| 模型"变笨了",通用问题答不好 | 灾难性遗忘:混入通用数据、减少 epoch、降低 lr |
| 输出带有解释文字 | system 里没约束,数据里也没有纯结构化输出样本 |
| 学会格式但内容全错 | 标注本身有噪声,或规则自相矛盾 |
| 分类准确率高但业务不能用 | 类别不均衡,看 Macro-F1 而非 Accuracy |
9.4 ROCm / AMD 专属问题
| 现象 | 原因 | 解决 |
|---|---|---|
torch.cuda.is_available() == False | 装的 CUDA 版 torch | 装 ROCm 版 |
torch.version.hip is None | 同上 | 同上 |
| bitsandbytes 导入报错 | ROCm 支持不完整 | 卸载,用 bf16 |
| flash-attn 编译失败 | gfx942 需专门编译 | attn_implementation="sdpa" |
| 训练极慢 / GPU 利用率低 | 未装 Liger Kernel | 装 Liger Kernel |
Pytorch ROCL error | 驱动版本过低 | ROCm 6.2+,更新驱动 |
| 推理正常但训练崩 | vLLM 占着显存 | rocm-smi --showpids 查并停掉 |
| 之前能用的 vLLM 挂了 | 训练框架升级了 torch | conda 环境隔离,避免污染 base |
9.5 数据问题排查清单
训完效果不好时,先怀疑数据,再怀疑超参:
第 10 章 学习路径与进阶
10.1 分阶段学习计划
阶段一:跑通流程(1~2 天)
目标:建立"完整链路"的心智模型
- 用 4B/1.7B 小模型 + 公开数据集,用 LLaMA-Factory WebUI 跑一次 LoRA SFT
- 理解每个参数的作用(改一个参数,观察 loss 变化)
- 用
llamafactory-cli chat看看微调前后的差异
验收标准:能独立完成从数据到 adapter 的全流程。
阶段二:数据能力(1~2 周)
目标:理解"数据决定效果"
- 用小规模示例数据做实验一(数据量曲线)
- 做实验二(system 消融)
- 做实验三(过拟合演示)
- 自己设计一个任务,造 200 条数据
验收标准:能解释为什么某次微调效果好、另一次不好。
阶段三:工程化(2~4 周)
目标:能支撑生产环境
- 多任务:训练 2~3 个 adapter,理解热切换
- DPO:SFT 之后做偏好对齐
- 评估体系:自动化评估脚本 + 回归检测
- 部署:vLLM + LoRA / 合并部署
阶段四:进阶(持续)
| 方向 | 内容 |
|---|---|
| 高效微调 | rsLoRA、DoRA、PiSSA、LoRA+ |
| 多模态 | LLaMA-Factory 支持视觉微调 |
| MoE 微调 | Qwen3-30B-A3B 这类,专家路由的处理 |
| 分布式 | FSDP / DeepSpeed ZeRO-3 |
| 量化微调 | QLoRA、NF4、FP8 LoRA |
| 领域继续预训练 | 数据构造与训练调度 |
10.2 持续学习的资源方向
- 官方文档:LLaMA-Factory、ms-swift、PEFT、TRL 的 README 和示例
- 论文:LoRA、QLoRA、DPO、FlashAttention(有官方论文的技巧最可靠)
- 模型卡:基座模型卡里的 chat template 和推荐参数
学习建议:每学一个技巧,都在自己的数据上验证一遍。别人的结论不一定适用于你的任务和硬件。
10.3 最后的忠告
微调不是银弹。
开始之前,诚实地问自己一句:这个需求,一个足够好的 prompt 加 RAG 能不能解决八成?
如果能,微调带来的运维复杂度(数据维护、定期重训、监控、版本管理)大概率不划算。
微调真正不可替代的场景是:规则复杂到无法用 prompt 描述清楚,且要求输出 100% 一致,且调用量足够大到能抵消训练和运维成本。
满足这三条,微调就是明确划算的。
附录 A:你的 MI300X 环境专项
A.1 硬件参数
| 项目 | 参数 |
|---|---|
| 型号 | AMD Instinct MI300X |
| 架构 | CDNA3(gfx942) |
| 显存 | 192GB HBM3 |
| 带宽 | ~1.3 TB/s |
| bf16 稠密算力 | ~1300 TFLOPS |
| FP8 | 原生硬件加速 |
| 对标 | 单卡 ≈ H100 SXM 级别(算力),生态弱于 CUDA |
A.2 能力总结
| 任务 | 可行性 | 说明 |
|---|---|---|
| 27B/32B LoRA SFT | ✅ 推荐 | 显存 70~110GB,宽裕 |
| 14B 及以下全参微调 | ✅ | 112GB 内可行 |
| 32B 全参微调 | ❌ | 需 ~256GB,差 64GB |
| 72B QLoRA | ⚠️ | 勉强,需压小 seq |
| 继续预训练(27B) | ⚠️ | 需全参,同上受限 |
| 多任务 LoRA 热切换 | ✅ | 显存充裕,天然适合 |
A.3 速度预期
实测 LoRA MFU 通常 40~50%,有效算力约 500~600 TFLOPS。
| 场景 | 预计耗时 |
|---|---|
| 23 条 × 3 epoch | 1~2 分钟 |
| 500 条 × 3 epoch | 20~30 分钟 |
| 2000 条 × 3 epoch | 1~2 小时 |
| 2000 条 × 3 epoch @ seq 8192 | 3~4 小时 |
小模型(4B/7B)快 4~6 倍。
A.4 ROCm 环境清单
必需:
ROCm 6.2+ 驱动
PyTorch ROCm 版(torch.version.hip 应为 6.x)
transformers / datasets / peft / trl / accelerate
LLaMA-Factory推荐:
Liger Kernel 融合算子,省显存提速明显
flash-attn(可选) 装不上就用 SDPA禁止:
bitsandbytes ROCm 支持不完整,显存够不需要A.5 环境体检清单
训练前逐项确认:
A.6 保护现有 FP8 部署
你的 vLLM + Qwen FP8 是已验证可用的资产。保护要点:
- 训练环境完全隔离(conda env / venv)
- 训练前记录当前可用的启动命令和参数
- 不要在训练环境里测 vLLM
- 训练完的 adapter 用独立端口测试,确认后再切回
附录 B:速查表
微调 vs 其他方案
| 需求 | 方案 |
|---|---|
| 补事实知识 | RAG |
| 统一输出格式 | 微调 SFT |
| 固化主观判定标准 | 微调 SFT |
| 注入表达风格 | 微调 SFT |
| 输出偏好选择 | 微调 DPO |
| 领域语言能力 | 继续预训练 |
超参速查
| 参数 | LoRA SFT 起点 |
|---|---|
| lora_rank | 16 |
| lora_alpha | 32 |
| lora_dropout | 0.05 |
| learning_rate | 1e-4 |
| num_train_epochs | 3 |
| warmup_ratio | 0.1 |
| cutoff_len | 4096 |
| per_device_train_batch_size | 1 |
| gradient_accumulation_steps | 16 |
| gradient_checkpointing | true |
数据量参考
| 数据量 | 效果 |
|---|---|
| <50 | 基本学不到 |
| 50~200 | 格式类够用 |
| 200~1000 | 甜点区间 |
| 1000~5000 | 稳定收益 |
| >10000 | 需更大 rank |
显存速查(bf16 LoRA,含激活)
| 模型 | 显存 |
|---|---|
| 1.7B | ~6GB |
| 7B | ~16GB |
| 14B | ~28GB |
| 32B | ~70GB |
| 72B | ~150GB |
排查优先级
效果不好 → 数据 → 模板 → 学习率 → rank → 其他永远先怀疑数据。
附录 C:角色扮演微调专项
角色扮演(RP)是微调社区最活跃的应用方向之一,但它和结构化/知识类任务的技术选型差异很大。本附录解释"为什么 RP 微调大多用 7B 以内的模型",以及数据、配置、评估怎么做。
C.1 为什么是 7B:三条硬理由 + 一条路径依赖
① 延迟和成本是产品约束(最硬的理由)
RP 是典型的 C 端高并发场景,核心体验指标是首 token 延迟——超过 500ms 沉浸感就断了,超过 1 秒用户流失。规模是超线性涨的:
| 模型 | 首 token 延迟(单请求) | 单卡并发量级 | 1000 并发硬件成本 |
|---|---|---|---|
| 1.5B | ~20ms | 数万 | 1 张卡 |
| 7B | ~50-100ms | 数千 | 1 张卡 |
| 14B | ~150-250ms | 数百~千 | 1~4 张卡 |
| 32B | ~300-500ms | 数十~百 | 8 张卡起 |
7B 到 32B 之间,角色"像不像"的提升有限,延迟和账单却是质变。
② 小模型更"听话"(最被忽略的理由)
现代大模型经过重度 RLHF 对齐,有很强的 AI 身份认同和拒答倾向,RP 时经常跳出角色("作为一个 AI,我没有真实的情感……")。一次跳出角色,沉浸感全无。
小模型对齐程度浅,"我是谁"的自我认同弱——你给它角色设定,它就真的相信自己是那个角色。RP 训练的本质是"覆盖先验",基座先验越弱,角色越容易压过去。反直觉但真实:模型越"笨",越能老老实实当那个角色。
③ 数据量和容量的匹配
RP 数据的特点是量大(社区有百万级数据)但信息密度低——教的是语气、措辞、情绪节奏,不是知识和推理。这造成错配:
- 7B 配几百万条:刚好把人格"吃透"
- 32B 配同样数据:只能学到"输出风格像 RP",学不到"稳定的人格一致性"
而且大模型要真正改变人格需要更大的 rank 和更多数据(7B 用 r=16 就能强覆盖,32B 可能要 r=64+)——容量越大,要"压住"它越难。
④ 生态惯性
早期 RP 社区参与者大多用消费级显卡,7B 是当时唯一能在家跑 LoRA 的量级(16GB 显存即可)。数据 → 教程 → 工具 → 更多数据,形成路径依赖。这不是最优解的证明,是历史形成的现实。
一句话总结:RP 是典型的"行为覆盖"任务而非"知识注入"任务,它不需要大模型的智力,需要的是低成本、低延迟、以及"不会被自己的对齐训练拽回 AI 身份"。
C.2 格局在变
这不是铁律:
- 14B 是新甜点:Qwen2.5-14B / Qwen3-14B 在 RP 社区采用率很高,效果明显好于 7B,又没有 32B 的成本
- 32B 有真实用例:付费深度对话、复杂剧情 RPG、需要角色有推理能力(RP+解谜/策略)的场景
- 推理成本快速下降,延迟约束会逐步松动
但 ①延迟账、②大模型拒答倾向、③数据错配 三条例外不随硬件变便宜而消失。
C.3 一个产品层面的观察
RP 中过度聪明会破坏沉浸感。用户要的是"活在设定里的人",不是"每次都给出最优解的助手"。当模型开始长篇分析角色动机、解释背景设定时,它就从"一个人"变成了"一个在描述那个人的系统"——出戏了。社区说"越笨越像",就是这个产品直觉。
C.4 数据特点与选择
RP 数据集的关键属性是角色绑定:一份高质量 RP 数据几乎总是围绕单一角色构建(统一的 system prompt、统一的人设),而不是多角色通用语料。使用前必须想清楚三件事:
- 你是要这个角色,还是借这个格式? 只要格式 → 用它学风格,换自己的 system 和人设数据;要这个角色 → 直接用
self_cognition类文件会覆盖模型身份。改角色时必须剔除或重写,否则模型会自认是别人的角色- 真实提问 vs 占位符触发词。占位符(如
<生成推文: 情感建议>)是快捷标记,模型要靠训练学会"标记 → 对应输出";评估泛化能力时占位符和训练同款输入都不能进测试集
一个典型例子是开源社区围绕单一角色构建的数据集(如沐雪的 Muice-Dataset,CC-BY-NC-4.0 非商用):单一角色、统一 system、自带 self_cognition 数据和占位符触发词。社区也有现成转换脚本,可把这类数据转成 LLaMA-Factory 可用的 ShareGPT 格式,并自动完成无泄漏的测试集划分。
C.5 RP 任务配置起点(相对默认配置的差异)
| 参数 | 通用默认 | RP 建议 | 原因 |
|---|---|---|---|
lora_rank | 16 | 32 | 人设是行为级强覆盖,r=16 可能压不住 |
lora_alpha | 32 | 64 | 跟随 rank |
learning_rate | 1e-4 | 2e-4 | 数据量适中,加快收敛 |
cutoff_len | 4096 | 2048 | RP 对话轮次多但单轮短,2048 够用且省显存 |
gradient_accumulation_steps | 16 | 8 | RP 数据量大,有效 batch 8 足够 |
num_train_epochs | 3 | 2~3 | 人格覆盖 2 轮通常已收敛,3 轮以上容易把口头禅背死 |
val_size | 0.1 | 0.05 | 数据量大时验证集绝对条数已够 |
C.6 RP 的评估怎么做
RP 效果很难自动判分,但有三层可操作的办法:
- 功能触发词检查:用占位符输入,验证模型能按训练学会的格式响应(用作者 test.json 或自建触发词表)
- 人格一致性人工评估:抽 30~50 条对话,从三个维度打分——语气像不像、设定记不记得住(多轮之后还知道"自己是谁")、会不会跳出角色
- 通用能力回归:照第 8 章 8.2 的探针问题测一遍。RP 数据对事实/代码/推理的覆盖几乎为零,退化是必然的,问题是退化多少、你能否接受——数据集 README 里明说"事实性知识容易出错、代码推理能力可能下降"就是这个原因
如果退化幅度不可接受 → 混入 10~20% 通用对话数据(dataset 字段可以写多个数据集混训)再训一版对比。
相关文章
- vLLM 大模型部署指南:原理、显存与选型 —— 微调产物的推理部署引擎怎么选、怎么调参
- Qwen3.8-27B-FP8 部署实录:AMD MI300X 上跑通 vLLM —— 本文配套环境的推理服务部署实测,训练前请先按其步骤停掉 vLLM 释放显存
- NebulaGraph 介绍与服务器 Docker 部署 —— 另一篇服务器部署实测记录