Fine-tuning

大模型微调的方法

模型微调(Fine tuning)是深度学习中的一项关键技术,它允许我们在现有的预训练模型(Pre trained Model)或者在一些已经进行过后训练的模型的基础上,利用特定领域的小规模数据集进行二次训练。 简单来说,如果预训练…

TL;DR

模型微调(Fine tuning)是深度学习中的一项关键技术,它允许我们在现有的预训练模型(Pre trained Model)或者在一些已经进行过后训练的模型的基础上,利用特定领域的小规模数据集进行二次训练。 简单来说,如果预训练…

模型微调(Fine-tuning)是深度学习中的一项关键技术,它允许我们在现有的预训练模型(Pre-trained Model)或者在一些已经进行过后训练的模型的基础上,利用特定领域的小规模数据集进行二次训练。 简单来说,如果预训练是让模型读完“万卷书”具备通用常识,那么微调就是让它进入某些特定领域,如“医学院”或“法学院”进行专业深造。 但还有另一个问题:训练时,应该如何修改模型参数?因为现在的大模型参数量非常庞大: - 7B = 70亿参数 - 14B = 140亿参数 - 72B = 720亿参数 如果直接修改全部参数(全参微调),对显存和算力要求会非常高。因此,业界出现了很多“参数高效微调(PEFT,Parameter-Efficient Fine-Tuning)”方法,也就是: 只修改少量参数,就让模型学到新能力。 这些方法最常用于 SFT 阶段,但它们本质上属于“参数微调技术”,而不是新的训练阶段。

微调的相关方法

- BitFit(只调 Bias) - 把大模型绝大部分参数冻结,只训练各层线性层/注意力里的 bias(偏置)。 - 优点:参数量极小、训练快、显存省。 - 缺点:可塑性有限,任务差异大时效果上限偏低。 - 适合:轻量适配、数据少、想快速试水。 - P-Tuning(学"提示词向量") - 不改模型内部权重,在输入前面加一段可训练的"软提示"(一串可学习的向量)。 - 优点:改动最小、参数少、切换方便。 - 缺点:对复杂指令跟随/长程推理/强风格约束不如 LoRA。 - 适合:分类/抽取/固定格式任务。 - Prefix Tuning / P-Tuning v2(学"每层的前缀记忆") - 给 Transformer 各层注意力的 K/V 加一段可训练的"前缀"。 - 优点:比纯 Prompt Tuning 更强,参数仍然相对少。 - 缺点:实现/部署比 prompt 更复杂;效果常被 LoRA 超过。 - 适合:需要比 prompt 更强,但又想保持参数高效的场景。 - Adapter(插"瓶颈小网络") - 在每层 Transformer 里插入一个很小的两层 MLP(先降维再升维),只训练这些 Adapter。 - 优点:稳定、可控、任务切换方便。 - 缺点:推理会多一段前向计算,延迟更明显。 - 适合:多任务/多领域挂载,且能接受一定推理开销。 - LoRA(最常用:给大矩阵加"低秩增量") - Low-Rank Adaptation,一种轻量级的 AI 模型微调技术 - 只学"低秩增量"来改大矩阵 - 优点: - 效果强:SFT 场景非常常见,性价比最好。 - 训练省:只训练少量 LoRA 参数,显存/算力开销小。 - 部署方便:可以保存为 adapter 按需加载,也可以合并回原模型。 - 生态成熟:PEFT/ms-swift/transformers 等都支持。 - 缺点: - rank/目标模块选不好会影响上限或稳定性; - 多个 LoRA 叠加管理需要规范。 - 适合:绝大多数指令微调(SFT)、领域适配、风格对齐。 - MAM Adapter - Adapter 思路的变体,更强调在注意力/特定模块里做更细粒度的适配。 - 优点:在某些结构/任务上能比普通 adapter 更针对性。 - 缺点:不如 LoRA 通用;工程实现成本更高。 - 适合:对特定架构/任务做研究或深度定制时。 - UniPELT(组合多种方法) - 把 Prefix / Adapter / LoRA 组合在同一个框架里,有门控/选择机制。 - 优点:潜在上限更高,适应性更强。 - 缺点:复杂、超参更多。 - 适合:追求极致效果、并且能承担工程复杂度的项目。 在同等算力/数据下,LoRA 往往是最稳的高性价比选择。

LoRA/QLoRA微调

LoRA微调

- 背景:大模型里有很多全连接层(线性层),本质是做 y=Wx+b 的矩阵乘法。原始权重矩阵W通常是“满秩/高秩”的。 - 关键直觉:当我们把模型拿去适配某个具体下游任务时,真正需要改变的方向并不多。也就是说,权重更新 ΔW 的“有效自由度”往往很低,可以理解为它的本征秩(intrinsic rank)很小。 - LoRA 的做法:不直接训练整块 ΔW,而是把它限制在一个低维子空间里: 你可以把它理解成:只允许模型沿着少数 r个方向去“拧”权重,轻微的扭曲矩阵空间,但这对特定任务通常已经足够。 - 为什么有效:因为如果任务所需的更新确实是低秩的(或近似低秩的),那用小 r 的 AB 也能很好逼近需要的 ΔW,从而在少量可训练参数下达到接近全量微调的效果。 下面我结合一个数学实例给大家解释一下: 普通线性层(Transformer 里面大量使用):

y = W x

假设:输入维度 = 4,输出维度 = 3,所以:

W 是 3x4 矩阵

原始权重矩阵 W

W =

[ 1   2   3   4  ]
[ 5   6   7   8  ]
[ 9  10  11  12 ]

输入向量 x

x =

[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]

普通前向计算

y = W x

第一行

1×1 + 2×2 + 3×3 + 4×4
= 1 + 4 + 9 + 16
= 30

第二行

5×1 + 6×2 + 7×3 + 8×4
= 5 + 12 + 21 + 32
= 70

第三行

9×1 + 10×2 + 11×3 + 12×4
= 9 + 20 + 33 + 48
= 110

最终输出

y =

[ 30  ]
[ 70  ]
[ 110 ]

LoRA 怎么插进去的呢?LoRA 不修改原始 W;而是:

y = (W + ΔW) x

其中:

ΔW = B A

LoRA 实际训练的是:

A 和 B

而不是整个 W;LoRA 实际例子 原矩阵:

W 是 3x4

现在:

rank = 2

所以:

A 是 2x4
B 是 3x2

设置 A 矩阵

A =

[ 0.1  0.2  0.3  0.4 ]
[ 0.5  0.6  0.7  0.8 ]

设置 B 矩阵

B =

[ 1  2 ]
[ 3  4 ]
[ 5  6 ]

计算 ΔW = B A 第一行

[1  2] × A

第一列:

1×0.1 + 2×0.5
= 0.1 + 1.0
= 1.1

第二列:

1×0.2 + 2×0.6
= 0.2 + 1.2
= 1.4

第三列:

1×0.3 + 2×0.7
= 0.3 + 1.4
= 1.7

第四列:

1×0.4 + 2×0.8
= 0.4 + 1.6
= 2.0

得到:

[ 1.1  1.4  1.7  2.0 ]

第二行

[3  4] × A

得到:

[ 2.3  3.0  3.7  4.4 ]

第三行

[5  6] × A

得到:

[ 3.5  4.6  5.7  6.8 ]

得到 LoRA 增量矩阵 ΔW

ΔW =

[ 1.1  1.4  1.7  2.0 ]
[ 2.3  3.0  3.7  4.4 ]
[ 3.5  4.6  5.7  6.8 ]

新权重矩阵

W' = W + ΔW

得到:

W' =

[  2.1   3.4   4.7   6.0  ]
[  7.3   9.0  10.7  12.4  ]
[ 12.5  14.6  16.7  18.8  ]

再做一次前向计算 输入还是:

x =

[1]
[2]
[3]
[4]

第一行

2.1×1 + 3.4×2 + 4.7×3 + 6.0×4
= 2.1 + 6.8 + 14.1 + 24
= 47

第二行

7.3×1 + 9×2 + 10.7×3 + 12.4×4
= 7.3 + 18 + 32.1 + 49.6
= 107

第三行

12.5×1 + 14.6×2 + 16.7×3 + 18.8×4
= 12.5 + 29.2 + 50.1 + 75.2
= 167

最终输出

y' =

[  47  ]
[ 107  ]
[ 167  ]

大模型的能力,本质上是存储在大量参数矩阵中的。训练的过程,就是不断调整这些矩阵参数,让模型逐渐学会输入与输出之间的映射关系,让输出越来越接近正确答案: - 什么词和什么词更相关 - 什么上下文下应该输出什么内容 - 什么模式更符合训练目标 因此,当 LoRA 训练出新的 ΔW 后,本质上就是在改变模型内部的参数空间。虽然只增加了少量参数,但这些参数会影响模型对输入信息的在某些方面或者领域的理解方式和输出倾向。所以: LoRA 并不是给模型新增知识库,而是在原有能力(矩阵)基础上,重新调整模型的行为偏向与特征映射关系。 假设原始矩阵:

W 是 4096 × 4096

参数量:

≈ 1600 万

如果是全量训练,那么就意味着这1600万参数需要全量更新。 但是lora微调,假如我们设置:

rank = 8

那么矩阵 A:

8 × 4096

矩阵 B:

4096 × 8

参数量≈ 6.5 万,一下从:1600 万下降至 6 万,带了的好处是什么?那就是显存大幅降低!用极少的算力,也可以训练大模型。这也是为什么lora: 即使只训练很少量参数,也依然能够明显改变模型表现。

Qlora

大模型参数本质上都是数字,不同的数据格式会影响显存占用与训练稳定性。 - FP32(32位浮点数)最传统的浮点格式,每个参数占 4 字节,精度高但显存占用非常大。 - FP16(16位浮点数)每个参数占 2 字节,相比 FP32 显存减半、训练更快,目前大模型训练广泛使用。但数值范围较小,训练时可能出现数值不稳定。 - BF16(Brain Float16)同样是 16 位,但相比 FP16 拥有更大的数值范围,因此训练更稳定。现在很多大模型训练更偏向使用 BF16。 - 4-bit 量化属于参数量化技术,每个参数仅占 4 bit(0.5 字节)。相比 FP16/BF16 可以进一步大幅降低显存占用,但会带来一定精度损失。 QLoRA = LoRA 的训练方式 + 把底座模型权重 量化到 4-bit 来省显存。 | 项目 | LoRA | QLoRA | | --- | --- | --- | | 底座权重 | 通常 FP16/BF16(不训练,但占显存) | 4-bit 量化存放(显存大幅下降) | | 训练的参数 | 只训练 LoRA 的 A,BA,B(以及可选 bias/LN) | 同样只训练 LoRA 的 A,BA,B(底座仍冻结) | | 显存占用 | 低(但底座权重仍较大) | 更低(尤其适合单卡显存紧张) | | 训练速度 | 较快 | 可能略慢(量化/反量化与算子实现有关) | | 效果 | 强(常用) | 通常接近 LoRA(有时略有差异) | | 典型用途 | 显存够、追求稳定/简单 | 显存不够想上更大模型(比如 7B/14B 在单卡上) |

很多人可能会有一个误区: “LoRA 不是只训练 A/B 两个小矩阵吗?为什么还需要加载整个大模型?” 原因是:LoRA 并不是替代原模型,而是在原模型基础上增加一个增量: y = (W + BA)x 其中: - W 是原始大模型参数 - BA 是 LoRA 学到的低秩增量 虽然训练时只更新 A/B,我们在上面也讲了,真正推理计算时,仍然需要原始 W 参与计算。 因此,底座模型参数依然会大量占用显存。QLoRA 的核心优化点就在这里: 既然 W 不参与训练,只负责前向计算,那么就可以把 W 从 FP16/BF16 压缩成 4-bit 量化存储。 当显存成为瓶颈时,通过 4-bit 量化冻结底座权重,可以在几乎不牺牲效果的前提下把训练显存打下来,让单卡也能训更大的模型。 | 微调方法 | 全称 | 核心原理 | 优点 | 缺点 | 适用场景 | | --- | --- | --- | --- | --- | --- | | Full FT | Full Fine-Tuning | 更新模型内所有参数。 | 模型表现上限高,任务适配最彻底。 | 算力成本极高,容易产生“灾难性遗忘”。 | 拥有海量数据和算力,追求极致性能。 | | LoRA | Low-Rank Adaptation | 在原矩阵旁引入低秩矩阵(A、B),只训练低秩矩阵。 | 性价比最高,显存占用低,支持权重合并。 | 参数量极小时,复杂任务表现略逊于全量微调。 | 当前最主流,个人开发者及企业首选。 | | Adapter | Adapter Tuning | 在 Transformer 层中插入小型网络模块。 | 参数效率高,原模型冻结。 | 增加了推理延迟(由于串行结构)。 | 需要在一个模型上部署多个下游任务。 | | Prefix Tuning | - | 在输入层或隐藏层前添加可训练的连续向量。 | 仅需存储少量前缀参数。 | 占用一部分上下文长度(Input Token)。 | 自然语言生成任务(NLG)。 | | Prompt Tuning | - | 在输入端添加可学习的 Soft Prompt 向量。 | 极其节省参数(万分之一左右)。 | 对模型规模有要求,小模型效果差,收敛慢。 | 超大规模模型(千亿级)的多任务适配。 | | QLoRA | Quantized LoRA | 将模型量化为 4-bit 后再进行 LoRA 微调。 | 显存需求极低,甚至能在消费级显卡微调大模型。 | 训练速度比标准 LoRA 稍慢。 | 硬件资源受限的情况。 |

版本提示

模型、框架与接口会持续变化。涉及版本号、参数与生产配置时,请在实践前对照对应官方文档。

LLMentor系统化学习大模型应用工程

内容来自个人课程知识库备份,并经过结构化整理。技术版本持续演进,生产使用前请结合官方文档验证。