模型微调(SFT)
大模型监督微调(Supervised Fine-Tuning, SFT)是将预训练模型适配到特定任务的关键技术。本文详解SFT的应用场景、PEFT技术、LoRA系列方法。
SFT的六大应用场景
风格化交互输出:针对特定语料风格进行适配训练,典型应用如将模型输出调整为《甄嬛传》古风对话体,满足特定场景的文体一致性需求。
限定域问答优化:适用于查询空间高度受限的业务场景(如5-6千条标准问题库),通过精准问答对训练提升查询匹配准确率,需注意当业务复杂度超出阈值时需进行全参数后训练。
轻量级领域知识注入:适用于专业不太复杂的垂直领域知识迁移,对于医学、法律等高复杂度专业领域,建议采用领域预训练(Post pre-train)而非单纯微调方案。
数学代码等专项能力强化训练:主流技术路径包括代码生成优化与数理逻辑提升,比如如DeepSeek、Kimi等发新模型之前,都会基于特定数据集的定向微调刷个榜。
Function calling支持优化:通过结构化输出训练增强API调用、工具使用等函数执行能力,构建模型与外部系统的标准化交互接口。
Agent协作能力强化:针对多Agent协同、任务拆解与状态维护等复杂场景,通过特定训练模式提升模型的流程控制与策略规划能力。
业界共识
Prompt的质量和多样性远比数量重要,全量微调一个30B量级的base model只需要10w条数据即可,LoRA微调一个30B的大约需要2w条。(经验值)
合成数据很重要! 数据需要尽可能覆盖所有类别的业务问题,要通过不同方式进行多路合成,减少合成数据的bias。
可以加点预训练的数据进去,减少灾难性遗忘(Catastrophic Forgetting)。
一般训练1~3个epoch。
可以全量微调,就不要PEFT。(比如上述六大应用场景中,只有1、2、6适合做PEFT)
SFT阶段不能做太多知识注入,过多的知识注入,或者超出模型能力本身的回答过多会导致对齐税(Alignment Tax)。
PEFT技术概述
PEFT(Parameter-Efficient Fine-Tuning) 技术旨在通过最小化微调参数的数量和计算复杂度,来提高预训练模型在新任务上的性能,从而缓解大型预训练模型的训练成本。通过特定领域数据对预训练模型进行针对性优化,以提升其在特定任务上的性能。
LoRA
通过在模型的关键层(多头注意力和Feed-Forward)中添加低秩矩阵,并将其加到原始权重矩阵上。该方法不用改变整个模型的结构,在推理时不需要额外的计算量,并且能保持原有的性能。与之相比,Adapter引入了额外的层,该层必须按顺序处理,影响并行训练;Prefix-tuning在用户输入前加可训练的前缀,减少了可用于处理下游任务的sequence长度,性能较差。
LoRA原理
LoRA固定原始参数
其中
LoRA可以看作是全量微调的一个泛化形式,随着模型可训练参数的增加(也就是增大
),LoRA就收敛到了原始模型,而基于Adapter的方法收敛到MLP,基于Prefix Tuning的方法收敛到不能接受长输入的模型。
LoRA超参数
在原论文中,还有一个超参数
LoRA应用位置
给定参数上限时,应该尽可能的在更多参数矩阵应用LoRA,即使
LoRA的最佳r值
实验证明LoRA在
和 之间的关系
AdaLoRA
目的:根据需求调整
正交矩阵
SVD分解
采用SVD分解,把
其中
top_b策略
也就是逐渐加秩,让模型尽可能多探索。到后期再慢慢把top_b降下来,直到最后以稳定的top_b进行训练,达到AdaLoRA的总目的:把训练资源留给最重要的参数。这个过程就和warm-up非常相似。
QLoRA
背景知识
Scaling Law
计算量FLOP(假设算
量化基础
- 非对称量化:需要记录缩放因子
和零点值 。 - 对称量化:记录缩放因子
,使用更普遍,精度较低(当数据非对称分布时,会导致部分区间浪费)。 - 非均匀量化:针对原始数值分布不均匀的情况,需要记录每一个桶的位置,参数更多。
对比以上三种量化,最常用的是对称量化。
量化的粒度:粒度大时损失的值会更多,且异常值影响到的参数更多。粒度小需要存储更多的scale factor。
训练时计算资源消耗分布
- 模型参数和梯度:完整的模型参数和需要更新的参数的梯度。
- 优化器:Adam需要维护一阶和二阶动量
- 激活函数:在反向传播时,需要前向传播的activation的值。
LoRA能大量减少梯度和激活值需要的计算资源,但参数仍然占据很大空间。
QLoRA原理
QLoRA通过减少每个参数对应的bit数,实现计算量的减少。在训练过程中,QLoRA首先将模型用4-bit存储,然后在训练时把数值反量化到bf16后进行训练。这样的设计使得训练所需的显存大大减少。
QLoRA使用4-bit NormalFloat (NF4),也就是Quantile Quantization,一种在信息理论上最优的数据类型,确保输入向量落入到每个量化区间的值的数量相同。QLoRA论文证明预训练神经网络权重通常遵循以0为中心的正态分布,因此可以通过缩放
NF4量化与反量化
针对输入向量
注意尽管0.0142距离0.0更近,但是在神经网络中通常用0进行padding,为了区分padding和较小的数,不能将较小的数舍入为0。舍入后保存在NF4中对应的下标,例如0.1609对应9。
反量化则是乘以
总结一下,分为3个步骤:
- 计算出量化常数
(在非对称量化中 ),将输入向量 映射到目标量化区间 (例如 )中。 - 对于
中的每个元素,找到分位函数 中计算到的最相近的值 - 将
对应的索引 存到输出向量 中。
分位数量化
最理想的情况是,分位函数中的每一个数都以相同概率被用到。例如采用NF4做为分位函数,输入
为了实现这一特性,可以将概率分布函数
NF4实现
delta = 1/2*(1/32+1/30)
print(torch.linspace(delta, 0.5, 8))
print(norm.ppf(torch.linspace(delta, 0.5, 8)))
# 输出:
# tensor([0.0323, 0.0991, 0.1659, 0.2327, 0.2996, 0.3664, 0.4332, 0.5000])
# [-1.84813142 -1.28665578 -0.97040379 -0.72985929 -0.52568489 -0.34148556
# -0.16827238 0. ]
print(torch.linspace(0.5, 1-delta, 9))
print(norm.ppf(torch.linspace(0.5, 1-delta, 9)))
# 结果:
# tensor([0.5000, 0.5585, 0.6169, 0.6754, 0.7339, 0.7923, 0.8508, 0.9092, 0.9677])
# [0. 0.14707497 0.29742005 0.45484772 0.6245116 0.81448966
# 1.03979003 1.33611882 1.84813166]将两部分
二次量化
二次量化的目的是为了节省量化常数占用的空间。为了降低异常值对量化的影响,量化的粒度一般比较小,这需要存储大量的量化常数。例如当粒度为64,且采用32位的量化常数时,对于每个参数相当于增加
Paged optimizers
使用NVIDIA统一内存特性,在CPU和GPU之间进行页传输。当GPU内存不足时,将部分状态转移到CPU RAM中,并在优化器更新步骤需要内存时分页回到GPU内存中。
QLoRA代码实现
qlora_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)LoRA其他变种
- X-LoRA:采取MOE的思路,对每个token经过多个expert,额外训练一个scaling network,通过输出的scaling对各个expert的输出进行加权。
- LoHa:对于秩
的LoRA,将 和 分别拆分为两个矩阵,分别做矩阵相乘再做点积,秩变成了 ,表达能力更强。
Adapter
在模型层之间插入小的可训练的Adapter模块,原始模型参数训练时保持不变。Adapter一般采用bottleneck结构。
Adapter Fusion
针对多任务训练,每个任务训练出一个Adapter,对所有Adapter进行fuse,采用cross attention实现知识融合。
Prefix Tuning
为LLM添加可训练的、任务特定的前缀,这种Prefix实际就是连续可微的Virtual Token(Soft Prompt/Continuous Prompt),相比离散的Token,更好优化,效果更好。在输入序列前边加上任务相关的前缀,前缀可以是固定的(即手动设计的静态提示)或可训练的(即模型在训练过程中学习的动态提示)。不对prefix做位置编码。
Prompt Tuning
同样在输入中添加可学习的向量。不同的是,prompt模仿自然语言中的提示,引导模型生成特定的输出。prefix提供输入数据的上下文信息,作为模型内部表示的一部分,可以影响整个模型的行为。
P-Tuning
类似Prompt tuning。
Prompt Tuning:使用静态的、可训练的虚拟标记嵌入。这些嵌入在初始化后保持固定,除非在训练过程中被更新,相对简单,因为它只涉及调整一组固定的嵌入参数。在处理多种任务时表现良好,但可能在处理特别复杂或需要细粒度控制的任务时受限。只在输入前边插入。
P-Tuning:使用一个可训练的LSTM模型(称为prompt_encoder)来动态生成虚拟标记嵌入,允许根据输入数据的不同生成不同的嵌入,提供了更高的灵活性和适应性,适合需要精细控制和理解复杂上下文的任务,相对复杂,因为它涉及一个额外的LSTM模型来生成虚拟标记嵌入。可以在任意位置插入prompt。
P-Tuning v2
P-Tuning只在输入的embedding中添加提示,其他层的prompt embedding都来自上一层。这样由于模型层数增多,prompt对后面的影响难以预估,影响模型稳定性。并且prompt不能过长(跟sequence长度匹配)。
P-Tuning v2在许多层都插入prompt,prompt之间相互独立,增加了可训练的参数。