白话大模型微调
序言
在大模型(LLM
)出现之前就有迁移学习(Transfer Learning
)的概念,旨在提升训练效率,复用训练成果,用更少的训练数据微调,让模型能迁移到不同的任务上。很自然地想到,LLM
也能学习下游任务。
LLM
难训练、成本高,首先没有足够的算力把模型加载进来,就算加载进来后,因为LLM
的参数太多了,不知道该微调哪些。预训练就像一个乱炖的过程,把足够多的数据清洗好后,给LLM
去学习,学到能达到及格分后再去精调。
起初大家都不知道怎么微调LLM
,GPT-3
提出In-Context Learning
,那时提出的高效微调概念,都是考虑LLM
的训练成本太高了,所以才做模型高效微调(不是微调全量参数)来适应下游任务。这也是GPT-3
提出In-Context Learning
影响力很大的原因,它主张先不调整模型,只是单纯让LLM
自己响应不同的Prompt
(Zero-shot
)。最初Prompt
比较原始(由人手工产生),为了让生成的Prompt
质量更高,GPT-3
也提出了Data Driven Prompts
的想法。
BERT
则是采用预训练+
下游任务微调的思路,把BERT
整体加载到GPU
里,构建下游数据去微调,当时验证是可行的,但成本相当高,每一个新的下游任务都得重新微调(Fine Tuning
),每次微调需要完全加载模型,还需要构建下游任务的数据集,所以这种范式很难大规模地推广落地。
为了让LLM
适应各种下游任务,并大规模推广落地,LLM
高效微调技术——PEFT
(Parameter-Efficient Fine Tuning
)应运而生。
PEFT (Parameter-Efficient Fine Tuning)
Adapter Tuning: 开启大模型PEFT (2019)
2019
年,Google
提出了NLP
领域高效的迁移学习方法——Adapter Tuning
,使得在LLM
上迁移学习的成本大大降低,论文在具体的下游任务上和完全Full Fine Tuning
的效果做了比对,如果Full Fine Tuning
的效果要追上Adapter Tuning
,需要加载更多的模型参数。这篇论文在LLM
微调上具有划时代的意义。
Adapter Tuning
的思路是,在高维空间上学习,导致模型参数太大,那么就主动降维学习,可以大大减少参数,学习完后再升维,过程中使用残差网络保证效果不会退化。
Adapter
嵌入Transformer
模型,如下图,
- 在两个
FFN
层中间增加Adapter
层; Adapter
内部学习降维后特征,减少参数;- 使用
Skip-Connection
,最差退化为identity
; - 提升微调效率和稳定性,可复用性增强。
实验结果显示,Adapter
只用了1%+
的参数量,就调出了非常好的效果。
Adapter Tuning 和 Full Fine Tuning 效果比对(图片来自论文)
Soft Prompts
Prefix Tuning: 自动化构造Prompts (2021)
论文:Prefix-Tuning: Optimizing Continuous Prompts for Generation
代码开源:PrefixTuning GitHub
Prefix Tuning
研究的问题还是,怎么把LLM微调的成本降下来。和Adapter Tuning
不同,轻量级微调LLM
,特定下游任务有一个特定的Prefix
(特定的特征向量),加在Transformer
模型的前面,实验里用的LLM
和任务也不一样。虽然同样是冻结Transformer
的全部参数,但它比Adapter Tuning
还夸张,在Transformer
网络层的前面增加一部分前缀,让它生成一些新的Token
,这些Token
去适配特定的任务。
手工人造的Hard Prompt
,都是人在干Prefix Tuning
的工作,一开始使用Prompt
提示词工程时,人也是不知道怎么写Prompt
,不断地换Prompt
和LLM
交流。现在希望用自动化的手段,让Prefix Tuning
生成面向特定任务的指令集,在一些特定任务上,让模型生成人想要的结果。
Prefix Tuning
嵌入Transformer
模型,如下图,
- 在预训练
Transformer
前增加Prefix
模块; - 仅训练
Prefix
,冻结Transformer
全部参数; - 降低
GPU
算力和训练时间成本; - 特别适合于那些参数数量庞大的模型,如
GPT-3
,使微调这些模型成为可能; Prefix
:任务特定的指令集,引导模型生成特定任务的输出。
Adapter Tuning
需要加载整个LLM
,只不过微调的参数没那么多,但Prefix Tuning
不需要加载整个LLM
,只需要加载Prefix
模块,大大降低了GPU
的计算开销,适合调整千亿规模的LLM
,比如GPT-3
。但需要较高的训练技巧,可以被训练出来,但很难被训练出来,全量微调的难度太高了。
Prefix Tuning
的局限:Prefix
是在本来想干的Prompt
的基础上,加了前缀,前缀占Token
,会变成Prompt
的一部分。如果占比较多,会影响主要生成任务。另外,Prefix
只改Embedding
层,不如后面全改(Infix
)效果好。
Prompt Tuning: Soft Prompts 开创者(2021)
论文:The Power of Scale for Parameter-Efficient Prompt Tuning
代码开源:PromptTuning GitHub
Prompt Tuning
对复杂的Prefix Tuning
做了简化,无论LLM
多复杂,都需要在对应的Transformer
层加上Prefix Block
(MLP
),再去做训练。初始化值(手工设计的部分在)对它也有影响,对特定任务找到特定任务Prefix
的值,响应会更好。
Prompt Tuning
提出Soft Prompts
,跟人硬编码Prompt
对比,Prompt Tuning
只是在输入层加入Prompt Token
,不需要像Prefix
加那么多MLP
,就可以更好地造出能让LLM
响应特定任务Prompt
的模型。虽然没有全面超越Fine Tuning
,但逼近了Fine Tuning
。
Prompt Tuning
训练方法,
- 设计提示:根据任务选择硬提示(固定文本)或软提示(可训练向量)作为输入;
- 融入输入:硬提示直接加入文本,软提示作为向量加入序列;
- 训练过程:硬提示下全面微调模型;软提示下只调整提示向量,其他参数不变;
- 执行任务:训练后模型用于
NLP
任务(如问答、摘要),输出由提示引导。
和前面完整的Fine Tuning
做个对比,左边是全量的训练预训练模型,有多少任务就得训练多少个这样的模型,比如BERT
、Adapter
、Prefix
等都是这样,Prefix
在训练的时候不用全部加载,但在用的时候还是得全部加载。
Prompt Tuning
搞了一个轻量外部模型,在预训练模型外部加了一个新的模型,和预训练模型是隔开的。训练过程可以把不同任务放到一起。A
、B
、C
还是需要手工设计,之后在新场景用的时候,不需要设计了。学习任务A
应该用哪个Prompt
,但所有任务都是由一个人学,最终学出这么一个模型(约2w Parameters
),已经往指令微调的方向走了。
Prompt Tuning
主要贡献,
- 直观性:
Prompt Tuning
使用直观的语言提示来引导模型,使其更易于理解和操作;- 适用性:这种方法特别适用于那些预训练模型已经掌握了大量通用知识的情况,通过简单的提示就能激发特定的响应;
- 微调成本低:
Prompt Tuning
可以在微调时减少所需的计算资源,同时保持良好的性能。
P-Tuning v1: 解决人工设计Prompt 的问题(2021)
P-Tuning
的创新之处在于,将提示词(Prompt
)转化为可学习的嵌入层(Embedding Layer
),但直接对嵌入层参数进行优化时面临两大挑战,
- 离散性(
Discreteness
):已经通过预训练优化过的正常语料嵌入层与直接对输入提示嵌入进行随机初始化训练相比,可能会导致后者陷入局部最优解; - 关联性(
Association
):这种方法难以有效捕捉提示嵌入之间的相互关系。
解决人工设计Prompt
的问题,把提示词本身变成一个可学习的Layer
,Prompt
给了10
个词,只要有一个词没搞好,对模型性能是灾难性的;前面都是固定大模型,只调小模型,有过拟合的风险。比如打榜的时候,把测试集加到里边训练,自然结果很好。看看怎么做的?
一个关于
The capital of Britain is [MASK]
示例,
传统方式与P-Tuning
对比,
- 在
a
中,提示生成器只接收离散奖励; - 在
b
中,连续的提示嵌入(Prompt Embedding
)和提示编码器(Prompt Encoder
)以可微的方式进行优化。
就是说,在原来模型Input Embedding
这一层,加入了P-Tuning
参数,一起去训练。
Embedding
本身就有语义提取能力,Prompt
不做在自然语言层面,而是做在Embedding
层面,就相当于绕过了一定问题,提取了更本质的语义信息。
P-Tuning
和Prefix-Tuning
的主要区别在于,
Prefix Tuning
类似于模仿指令,通过在模型开头加入额外的嵌入(embedding
),而P-Tuning
的嵌入位置更为灵活;Prefix Tuning
在每个注意力层增加前缀嵌入来引入额外参数,并用多层感知机(MLP
)进行初始化;相比之下,P-Tuning
仅在输入时加入嵌入,并通过长短期记忆网络(LSTM
)加MLP
进行初始化。
P-Tuning v2: 提升小模型和多任务微调质量(2022)
论文:P-Tuning v2: Prompt Tuning Can be Comparable to Fine-tuning Universally Across Scales and Tasks
代码开源:P-tuning-v2 GitHub
P-Tuning
在小模型上性能不佳,P-Tuning v2
旨在使提示调整(Prompt Tuning
)在不同规模的预训练模型上,针对各种下游任务都能达到类似全面微调(Fine-tuning
)的效果。
之前的方法在以下两方面有所限制,
- 模型规模差异:在大型预训练模型中,
Prompt Tuning
和P-Tuning
能取得与全面微调相似的效果,但在参数较少的模型上则表现不佳; - 任务类型差异:无论是
Prompt Tuning
还是P-Tuning
,在序列标注任务上的表现都较差。
P-Tuning v2
解决了P-Tuning v1
的一些局限。Scale
很重要,只要规模足够大,在各种任务上响应都很好,那如果模型没那么大,Prompt Tuning
就没那么好使了。Prompt
长度、初始化词选取、模型微调的Step
都会影响Prompt Tuning
的效果,P-Tuning v1
也是这样。P-Tuning v2
在小模型的效果,可以和Fine Tuning
效果相当。
不同任务的数据集不一样,同样的词在任务里边做的事不一样,意义不同,这种场景如果能增加重参数化,把单任务搞到最优,不要把一些可能会相斥冲突的事情放在Multi-Task Learning
里边去做。
不断地把人为的设计挪走,让数据驱动这件事回归本质,让足够多的数据和好的网络结构学出来,在一些能看得到的明显有冲突的任务和数据里边,人为地减少冲突。
可学习的参数变多了;生成结构的设计,每一层Layer
都有Prompts
,吸收了Prompt Tuning
的优点。
总结
P-Tuning v2
的核心思路
- 重参数化(
Reparameterization
)
- 在
Prefix Tuning
和P-tuning
中,多层感知机(MLP
)被用来构造可训练的嵌入(Embedding
)。P-Tuning v2
的研究发现,针对不同的任务和数据集,这种方法可能产生相反的效果,特别是在自然语言理解领域。- 提示长度(
Prompt Length
)
- 不同任务对应的最优提示长度(
Prompt Length
)是不一样的。- 例如,在简单的分类任务中,长度为
20
的提示可能是最佳选择;而对于更复杂的任务,则需要更长的提示长度。- 多任务学习(
Multi-task Learning
)
- 对于
P-Tuning v2
而言,多任务学习是可选的,但它可以提供更好的参数初始化,从而进一步提升模型性能。- 分类头(
Classification Head
)
- 在
Prompt Tuning
中,使用语言模型(LM
)头来预测动词是核心思路。- 然而,
P-Tuning v2
的研究发现,在完整数据集上这种做法并非必要,且与序列标记任务不兼容。- 因此,
P-Tuning v2
采用类似BERT的方式,在第一个token
处应用随机初始化的分类头。
之前的
PEFT
方法,局限与挑战总结如下,
Adapter
方法,通过增加模型深度而额外增加了模型推理延时。Prompt Tuning
、Prefix Tuning
、P-Tuning
等方法中的提示较难训练,同时缩短了模型可用的序列长度。- 往往难以同时实现高效率和高质量,效果通常不及完全微调(
full-finetuning
)。- 简而言之,尽管大模型参数规模巨大,但关键作用通常是由其中的低秩本质维度(
low intrinsic dimension
)发挥的。
受此启发,微软提出了低秩适配(LoRA)方法,设计了特定结构,在涉及矩阵乘法的模块中引入两个低秩矩阵A和B,以模拟完全微调过程。这相当于只对语言模型中起关键作用的低秩本质维度进行更新。
LoRA(大模型低秩适配)
LoRA
论文:Low-Rank Adaptation of Large Language Models
代码开源:微软开源了LoRA的实现
目前难以兼顾高效率、高质量,通常也没法达到全量微调的效果。LoRA
方法论比较通用,不仅用于大模型,也可用于文生图等模型,用更小的矩阵解决LLM难易训练的问题。
为了使微调更加高效,LoRA
通过低秩分解将权重更新表示为两个较小的矩阵(称为更新矩阵)。这些新矩阵可以在适应新数据的同时保持整体变化数量较少进行训练。原始权重矩阵保持冻结状态,并且不再接受任何进一步的调整。最终结果是通过将原始权重和适应后的权重进行组合得到。
最下边x
是局部的输入,有两条路可以走,左边是预训练模型的模型权重(d*d
高维空间),现在希望用一个小的低维的模型空间,就能够表达出原来的高维空间。A
和B
小矩阵都是梯形的,r
远远小于d
(两到三个数量级),LoRA3
甚至到4
个数量级(千分之一,万分之一)。
LoRA
提出一个新思路,不管左边这条路,右边新训练一个模型,模型很小,这里叫更新矩阵(Update Matrix
),就是A
和B
,初始化的方式也不一样,A
是一个正态分布,B
全是0
。原来有一个神经网络,连接是已经定义好的,且预训练成功了,现在在旁边增加一个附加的网络通路,可以说是外挂通路,就是用小矩阵模拟大矩阵。
在LoRA
方法中,实际上是在原始预训练语言模型(PLM
)旁增加一个附加的网络通路,这可以视作一种“外挂”结构。这个外挂结构的目的是通过两个矩阵A
和B
的相乘来模拟本征秩(intrinsic rank
)。
右边的图,就等于左边的公式,h
就是在局部环境里输出的一个值。大模型里充满了矩阵相乘,每一个小的模块都可以抽象出上边右边LoRA
的结构图。
原来
LLM
微调是什么样的数学形式化的定义呢?
h
要适应下游的任务,通常要做微调,微调的是什么?
原来的微调,W0
是原来预训练的权重,△W
通过下游任务的训练集,对原来的大的高维矩阵d*d
空间里调整了一定的模型权重;LoRA
把公式做了变换,把△W
从d*d
高维空间,变成了B*A*x
,x
怎么就能跟B*A
相乘达到△W*x
?这就是后边要做的事情。
- 整体设计:
B
、A
输入、输出的维度都是d
,跟原来x
要求的维度一样; - 低秩分解:
A
是r*d
的矩阵,B
是d*r
的矩阵,A*B
和△W
的维度保持一致; - 回映射:
B
矩阵把这些r
维数据再映射回d
维(可以自己调整),和预训练模型的其他部分保持兼容。
低秩分解,A
矩阵把输入的d
维矩阵降到r
维,A
、B
都叫做更新矩阵,或者增量矩阵,原来的模型里没有,新增加的矩阵,△W
原来要被替代掉的一部分,r
就是矩阵的秩,一个高维空间,表达如果是有冗余的,一定可以通过降维的方式降低维度,降到不能再降为止,这个维度就是它的秩(rank
),既然有rank
,就会有本身的向量,假设r
是10
维,就会有10
维的方向向量(单位向量)。r
的出现,使得计算量降低了,模型的参数量也降低了,从图的几何上面来看,A
和B
的维度要比W
小得多。
用BA
来模拟W
,模拟整个大语言模型,LoRA
方法是用两个小矩阵模拟更大的大语言模型,底层是有一定的数学依据可以参考的。
高维空间都是有一定冗余的,大语言模型也不例外,大语言模型把参数做这么大也是有一定道理的,它要学习的知识类别太多了,如果一开始就把模型参数的维度搞的非常低的话,就会有很经典的问题——欠拟合。如果把模型搞的非常大,超过要拟合数据分布的需求时,肯定会有冗余,这时模型有可能需要降维了,降维并不会影响模型的性能。这就是LoRA
的理念。
像PEFT
、LoRA
,只需要关注下游的特定任务,下游特定任务原本就不需要大语言模型中所有的预训练知识,降维把不需要的知识给它删掉移除,影响也不大。
通过这样的低秩适配,LoRA
能够有效地在保持预训练模型的复杂性和表达能力的同时,减少所需的计算资源,并提高微调的效率。这种结构使得只有一小部分参数(A
和B
矩阵)需要在特定任务上进行训练,而不是整个模型,从而提高了微调的效率和实用性。
LoRA
相比Adapter
方法的优势
- 推理性能高效
- 与
Adapter
方法相比,LoRA
在推理阶段直接利用训练好的A
、B
低秩矩阵替换原预训练模型的对应参数。这种替换避免了增加网络深度所带来的额外计算量和推理延时。LoRA
方法使得推理过程与全参数微调(Full-FineTuning
)相似,但并不增加额外的计算负担。保持了高效的推理性能,同时实现了对模型的有效调整。- 模拟全参数微调的效果
LoRA
通过对模型关键部分的低秩调整,实际上模拟了全参数微调的过程。- 这种方法几乎不会导致训练效果的损失,后续实验结果也证明了这一点。 综上所述,
LoRA
提供了一种在保持推理效率的同时,有效微调大型预训练模型的方法,特别适用于对推理速度和模型性能都有高要求的应用场景。
LoRA
相比Soft Prompts
方法的优势
- 更深层次的模型修改
LoRA
通过修改模型的权重矩阵,直接影响模型的内部表示和处理机制,而不仅仅是输入层级。- 这意味着
LoRA
能够在模型的更深层次上产生影响,可能导致更有效的学习和适应性。- 无需牺牲输入空间
Soft prompts
通常需要占用模型的输入空间,这在有限的序列长度下可能限制了其他实际输入内容的长度。LoRA
不依赖于Prompt
调整方法,避免了相关的限制,因此不会影响模型能处理的输入长度。- 直接作用于模型结构
LoRA
通过在模型的特定层(如Transformer
层)内引入低秩矩阵来调整模型的行为,这种修改是直接作用于模型结构的。- 相比之下,
Soft prompts
更多是通过操纵输入数据来影响模型的输出。- 更高的灵活性和适应性
LoRA
提供了更大的灵活性,在不同的层和模型部件中引入低秩矩阵,可以根据具体任务进行调整。- 这种灵活性使得
LoRA
可以更精细地调整模型以适应特定的任务。- 模拟全参数微调的效果
LoRA
的设计思路是模拟全参数微调的过程,这种方法通常能够带来更接近全面微调的效果,尤其是在复杂任务中。 总的来说,LoRA
的优势在于其能够更深入地、不占用额外输入空间地修改模型,从而提供更高的灵活性和适应性,尤其适合于需要深层次模型调整的场景。
权重矩阵种类和秩r的选择对训练效果的影响
LoRA 权重矩阵种类和秩r的选择对训练效果的影响(图片来自论文)
GPT-3
的大语言模型用LoRA
训练的参数总量有10
倍的提升,但是在特定的测试集上,表现的更差了,这个是为什么?
LoRA
会有哪些超参数需要调整?下面表5
和表6
是LoRA
论文里边两个训练结果,结合Transformer
的Multi-Head Attention
结构,看看问题到底出在哪里?
实验结果的表5
,每一列是不同的训练超参数,对应得到的结果。最左边4
列,是4
个孤立的权重矩阵W q
、W k
、W v
、W o
,对它们分别单独做LoRA
微调,得出的实验结果。r
本身也是一个超参数,可以选择降到d*8
,或者d*2
,后边是不同组合微调,从实验结果来看,孤立微调的效果并不是很好。
实验结果的表6
,在W
矩阵不变的情况下,单调r
,效果并不明显,正好印证了GPT-3的大语言模型用LoRA训练的参数总量有10倍的提升,但是在特定的测试集上,表现的更差了。反而W
影响更大些。
当然,r
有最佳的值(全局最优)。在不同的基准测试上,最佳的r
也不一样。在相同的基准测试上,不同W
组合,最佳的r
也不一样。
回顾下,
Transformer
中的Q
、K
、V
,不是X
,而是来自X
。
Scaled Dot-Product Attention
的公式中,Q * K T
,为了得到Q
和K
之间的关联度(相似度)。
Seq2Seq
用Attention
机制捕捉的是,输入的隐藏层变量h
和输出的隐藏层变量s
之间的相关性,这个相关性有Attention Weights
来表达。
Self-Attention
没有Encoder
、Decoder
两个神经网络的隐藏层变量,它就是自己,就是输入的X
,这时Q
和K
都是有X
变化来的,只不过Q
和K
捕捉的是,这段自然语言的Query
里边什么Key
(Keywords
也好,KeyToken
也好)是最关键的。整个Query
之间有个自注意力,哪些Key
关键,权重值就大点。
最后,乘上V
,V
还是来源于X
,目的是为了保留X
自己,V
约等于X
。
虽然LoRA
不需要像P-Tuning
考虑手工生成Prompt
去适应下游任务,但LoRA
需要考虑Weight Type
和r
怎么选择,这些超参数要怎么做。
AdaLoRA: 自适应权重矩阵的高效微调(2023)
论文:Adaptive Budget Allocation for Parameter-Efficient Fine-Tuning
代码开源:AdaLoRA GitHub
LoRA
核心思想:对下游任务增量训练小模型(W=W0+△W
)。 在LoRA
里边,r
是在一开始预训练时,固定设置的一个值。AdaLoRA
提出了针对性解决方案,解决原来LoRA
没有考虑好的问题。
LoRA
问题
- 预先指定超参数增量矩阵的本征秩
r
,无法自适应调整;- 低估了权重矩阵的种类和不同层对的微调效果影响;
- 只微调了
Attention
,忽略了FFN
模块。
AdaLoRA
解决思路
- 使用
SVD
提升矩阵低秩分解性能;- 模型剪枝:对模块参数(特征)的重要性建模;
- 根据重要性评分,动态调整不同权重矩阵的本征秩
r
。
降维,用BA
替代W
矩阵,怎么做降维更好,本质是这个问题。机器学习时代,就有SVD
(奇异值分解),AdaLoRA
就是把SVD
用到了极致。
模型剪枝,LLM
不是所有参数都有用,怎么找出最有用的参数,这个过程对一个巨大的千亿级的模型去建模,相当于把模型参数(每一个单独的参数)都当成建模的对象,每个参数都有自己的重要性,对这个重要性进行评分。AdaLoRA
做了很多重要扩展。
r
不能自适应地调整,也不能拍脑袋决定在这个任务和特定的训练集上哪个r
好,自适应地动态调整r
,也是AdaLoRA
的重要研究成果。
AdaLoRA
选择不同W
和r
微调后得到的结果不一样,W
不止调整q
、k
、v
、o
,还有f1
、f2
(FFN
)。
AdaLoRA的具体实现
AdaLoRA
使用SVD
提升矩阵低秩分解性能
- 定义
- 奇异值分解(
Singular Value Decomposition
,SVD
)是一种在数学和信号处理中常用的矩阵分解技术;- 它将任意一个矩阵分解为三个特定的矩阵的乘积:一个左奇异向量矩阵、一个奇异值矩阵和一个右奇异向量矩阵。
- 数学表示
- 对于任意一个矩阵(
A
),SVD
表示为(A = UΣV^T
);- 其中,(
U
) 和(V
)是正交矩阵,表示左右奇异向量;Σ
是对角矩阵,对角线上的元素是奇异值。- 应用
- 在数据科学和机器学习中,
SVD
用于降维、数据压缩、噪声过滤等;- 在自然语言处理中,
SVD
用于提取文本数据的潜在语义结构。
SVD
是最核心的,很早以前在数学和信号处理里边用的矩阵分解,早期在NLP
里用来处理语义结构的,本身W
就存在语义信息,用SVD
就是一个很自然的结果。
给任意一个m*n
的矩阵,可以有一个SVD
的分解,分解会把一个大矩阵变成3
个矩阵相乘的结果,U
、V
是正交矩阵,中间是个对角矩阵,对角线上的值是奇异值。
怎么玩AdaLoRA
? P
、Q
是左右奇异向量,中间是对角矩阵,AdaLoRA
用SVD
替代BA
来替代W
。
AdaLoRA
在原来LoRA
的目标函数里新增了惩罚项,确保P
、Q
正交。P
、Q
正交,SVD
分解过程计算相对稳定,不会带来一些不必要的分解。如果在训练反向传播的过程中更新参数,P
、Q
不正交,有些结果就没有用。
怎么选这些W
?哪些W
用来分解? 下边是AdaLoRA
对模块参数(特征)重要性建模,
s(wij)
,用来评估任意一个权重的重要性评分的方法。
- 参数的敏感性
- 参数的不确定性
简述下训练过程,把模型所有权重加载到TPU
里,把训练数据一批批整合到一起,推理得到输出值,输出值和真值求损失,反向传播去更新参数。每一批都是有顺序的,t
就是训练步数,训练时不一定都非常平整,引入滑动平均超参,用于训练这一步和下一步之间的异常值和采样带来的一些问题。
每一批数据加载进来,都可以对每一个wij
,计算出它的敏感性和不确定性,也就得到了它的重要性。
具体展开,就回到刚才的SVD
分解了。每一个不同的W
,都对应这样一个三元组,AdaLoRA
的核心就是用SVD
的三元组替代了LoRA
的二元组。
怎么理解敏感性?怎么理解不确定性?
不确定性,是引用的自己原来的一篇论文,详细可去了解;
敏感性,因为有不同的下游任务,下游任务都有特定训练集,训练集是需要特定的大模型的模型权重做特定更改去适应任务,既然要做更改,就要选最有用的权重值去做调整。敏感性就是针对于这一批训练数据敏感,看公式就能看出来,不同的数据丢过来,如果算出来的敏感性的值比较大, 自然权重就会被放大。如果本身W无论怎么变,前向推理当中都不会怎么变,自然也就不会被迭代到。
AdaLoRA
根据重要性评分剪枝和自适应调整本征秩r
AdaLoRA根据重要性评分剪枝和自适应调整本征秩r(图片来自网络)
重要性高的或低的,后续的处理有什么区别?
重要性评分的核心,关注的是SVD
的三元组,SVD
在新的目标函数里增加了惩罚项,目标函数也做了更新,在损失函数之上增加了新的正则项,我们在训练什么,我们在训练的就是P
、Q
、对角矩阵,最终才能让模型在特定任务上表现的好。
训练迭代,第t步怎么更新参数,loss
值乘上一个学习率,在做反向传播时,学习率乘上类似的loss
,求一个梯度,更新整个模型。
之前我们建模重要性,是为了把重要参数找出来,不相关的参数就不要了,这是做重要性评分的初衷。怎么去做?就到这篇paper
的名字,Adaptive Budget Allocation For Parameter-Efficient Fine-Tuning
,自适应的分配预算,预算指的是什么?
这里的b
就是budget
,budget
取了一个分段函数,不同训练阶段有不同取值,
- 训练刚开始的时候,
budget
有一个初始值b0
, - 训练过程当中,中间做综合调整,
- 最后剩下,稳定在一个状态里边,取一个局部最优解, 这个过程怎么理解,挺关键的。
在第t
步,更新参数,传统更新参数,算出很多△W
,这些△W
都要减到原来对应的W
上面,模型剪枝怎么处理?算出很多△W
,通过预算的调整,取到底哪些模型参数需要被更新,哪些不需要被更新。到底要不要更新,AdaLoRA
通过Skit
(重要性评分)来做确定调整。
整个重要性里边画一道线,超过一些值后就更新,不超过就不更新,重要性会不断迭代,ski
是在不断迭代,对每一个Wij
每一个不同W
都在不断算出评分。在整个训练过程中,Budget
也会在调整,最开始的时候,b0
会设置的较大些,让模型能快速达到60
分(相对好的效果),接下来有一个衰减过程,使用3
次方的过程逐渐减小,可以理解为对整个大模型里边需要更新的模型参数在逐步的剪枝,最后锁定在一个值里边。
最开始,b0
取得很大,重要性>0
,就要去更新,一开始所有被SVD
分解的W
都要更新,到了一定步数后,只更新重要度高的W
,最后稳定在一个更新频率上。
Budget
,约等于可训练参数量,可训练参数量约等于rank
。
整个
LoRA
在调整模型参数,这些模型参数是怎么被调整的?
其实就是r
,r
就是我们要调的东西。r
越大,训练的参数越多。AdaLoRA
这里就是SVD
里边的拉姆达,奇异值的数量,对角矩阵拉的越大,模型也就越大,要找到一个特定的r
,r
需要在不同模型不同任务不同训练集上自适应。
总预算,所有小的
W
的rank
,有什么关系?
所有小的可调整量加在一起,就是参数量,在训练过程中的表述。
预算,约等于可训练参数量。
参数重要性,为什么和不确定性成正相关?
不确定性是拿敏感性算出来的,上一批的数据和上上一批敏感性的差值,乘上1-β2
。
QLoRA: 高效微调量化大模型(2023)
论文:QLoRA:Efficient Finetuning of Quantized LLMs
代码开源:artidoro/qlora、bitsandbytes-foundation/bitsandbytes
QLoRA
通过冻结的int4
量化预训练语言模型反向传播梯度到低秩适配器LoRA
来实现微调。新的训练微调的方法,训练量化的LLM
,怎么理解量化?
通过这种方式,可以用一个48G
的GPU
显卡,去微调一个650
亿的大模型,训练效果甚至可以和16
字节(标准float
)微调任务差不多。
QLoRA
通过三个技术叠加,使得量化后的LLM
比16
字节微调的LLM
还好。
4-bit NormalFloat
(NF4
)数据类型Double Quantization
(DF
)量化策略Paged Optimizers
内存显存管理
上图,从左到右看,
- 单精度字节的
Transformer
,Transformer
的Full FineTuning
; LoRA
和Full FineTuning
相比,首先加了小的Adapter
,做低成本的更新;QLoRA
,把16bit
的Transformer
压缩成4bit
,以前用16bit
存的模型参数变成了4bit
就够了,需要的显存更少;Paged Optimizers
在GPU
显存不够用的情况下,暂时可以用CPU
。
QLoRA 提出新数据类型4-bit NormalFloat (NF4)
NormalFloat
(NF
) 数据类型建立在分位数量化之上,Quantile quantization
是一种信息论最优的数据类型,可确保每个量化bin
具有从输入张量分配的相同数量的值。
分位数量化的工作原理是,通过经验累积分布函数估计输入张量的分位数,主要限制是分位数估计的过程是昂贵的。因此,快速分位数近似算法,如SRAM
分位数,常被用来估计它们。
由于预训练的神经网络权重通常具有标准差为σ
的零中心正态分布,可以通过缩放σ
将所有权重转换为单个固定分布,以便分布恰好符合我们数据类型的范围。
理解新数据类型是怎么运作的?为什么有用?
QLoRA
提出了自己的想法,用来表达数字,只用4
个bit
表达出要存的这些数。
深度学习阶段就有很多量化技术,比如剪枝,量化,蒸馏,包括8-bit
、4-bit
,都是那时候提出来的。
8-bit
量化,8
个bit
来表达整数,来表达原来用32位浮点数才能表达的内容。
8-bit
量化出来的数字是整数,没有小数点,有效精度是小数点前的1
。round
就是四舍五入,这个数要怎么除呢,怎么得来的?用原来量化前精度最高的那个数,它是一个32
位的浮点数,做一个伸缩变换,把数取出来后,把小数点后面砍掉,最后存到了用新的int8
表达的X
。如果做的足够好,就是32
位浮点数砍掉了后面的小数点。
通过这个变换,理解整型量化是怎么回事。
这个公式变换后,有个新的值,cFP32
,c
叫量化常数。
NormalFloat
,想用足够小的显存,存下表达能力足够强的权重。Quantile quantization
是香农提出来的一种信息论最优的数据类型,可以这样理解,设计出这些数据表达,就像一个一个的桶,有很多坑位,int8
变量有8
个桶,8
个桶可以排列组合,可以表达2^7
,负数也有2^7
。如果浮点数的话,通过一个数学公式,可以把小数点后的很多位和比较大的数据范围都表达出来。
把数字一开始就想象成离散的,一个萝卜一个坑,浮点数的麻烦在于,0
和1
之间有无数多个数,无数多个数要怎么表达呢?浮点数里边是通过有效位数(精度)来表达的,0
和1
中间假设有30
多位,30
多位后的那些数就表达不了了。但是用这种方式表达正常的计算机编码里是合适的,因为正常计算机的应用是很广泛的,完全不知道它会出现一个什么样的数字,需要被编码后存下来,所以要尽可能地覆盖当前这个区域范围,比如int8
(-128-127
),相当于框下来一大片数,因为不知道未来是怎么分布的。但是我们都清楚,机器学习和深度学习里边,我们需要存的模型参数的特点是什么,这些模型参数本身不是随机的,它会聚集在一块区域,数字的选择会聚集在一块区域,这个区域按照这个思想,就叫分位数量化的思想。
这堆数字都摆在这里了,就拿一个个桶去装它们,无非就是通过装的过程,理解它的有效精度到底需要多少,精度决定了桶之间的密度。还有可能因为分布有特点,桶跟桶之间的密度还不一样。正态整体的分布密集表达,两侧长尾的部分稀疏表达就够了。这是很多量化技术的核心。
香农的信息论,最优的数据类型,假设你知道数据分布,或者可以拟合数据分布,通过经验的积累,通过历史数据的拟合,可以知道要存的这些数,大概是什么样的分布函数,可以去估计输入张量的分位数。分位数的估计过程比较昂贵,过去有这么多数需要存,算出这些分布函数比较昂贵,但是会有一些近似算法,不能全局最优解,但是能求出八九十分的解,比如SRAM
分位数。
就这么一个核心思想。所有这些预训练模型的权重,通常可以转换为一个标准差为得σ
的零中心正态分布,可以做变换,把权重映射到这样一个正态分布上,把缩放指数因子记下来,就能存下一个正态分布,在特定有效精度下边的一个表达方式,去存这些权重,就不用存那么大了。
训练过程中,预训练的权重具有零中心的正态分布,缩放标准差,使得分布正好适应NF
的范围。NF
最终是要把这些数字框进来,脑洞打开,把预训练的这些权重转换为一个特定的正态分布,这个正态分布需要有一个NF
的数据类型把它框住,密度也要合适,不会把它漏掉,就可以搞定了。这个范围比如设置到[-1, 1]
这样的范围,神经网络的Normalization
有各种归一化跟这个操作很像。归一化后,核心要解决的就是,要怎么把它存下来,因为上下限确定了。
和int8
量化还不一样,int8
量化很简单,一个量化常数,乘上我们原来的数字后,所有的值都是整数,整数就一个萝卜一个坑,固定密度存下来就好了。但NormalFloat
能存小数,只不过小数放在-1
到1
之间,理论上零中心正态分布,标准差是1
,模拟一个正态分布分位数(2^k + 1
个分位数),分位数就是孤立的,可量化的一个个数字,得到正态分布的k
位分位数量化数据类型就是k-bit NormalFloat
。
具体要做的时候,就是用数据类型将它的值归一化到[-1, 1]
分布的范围内,通过一个最大缩放把它缩放回去,得到缩放后的值(输入权重张量)。
NFk
的范围和要表达的数字范围能够匹配上,就能够量化了。
量化不是把这两个数值之间的桶,桶和桶之间的距离和密度摆的刚好,量化就是有2^k + 1
个坑,有2^k + 1
个数,顺序记下来了,基本就能还原回去,只不过是成本高低而已。范围不是太离谱,可复用的张量就好搞一些。
NF4
就能表达2^4 + 1
的分位数,分位数函数的逻辑。
QLoRA 提出双量化技术:量化(量化常数)
QLoRA
设计了一个存储数据类型(4-bit NormalFloat
)和一个计算数据类型(16-bit BrainFloat
)。
QLoRA
将权重从存储数据类型反量化为计算数据类型,以执行前向和后向传播,但仅计算16-bit
BrainFloat
的LoRA
参数的权重梯度。权重仅在需要时才解压缩,因此在训练和推理期间内存使用率都能保持较低水平。
双量化(Double Dequant
)和单量化的区别,前面我们说把NF4
存下来了,真正计算的时候肯定不能那么存,还得表达的更复杂一些。跟原来的16-bit
要连起来,因为除了QLoRA
,其他还是用16-bit
在做计算。
解释公式,4-bit
是存了一个一个坑位(分位数),BF16
才是真正的值,分位数变成真正可以用的值,中间是需要解压缩的,什么需要解压缩,需要计算的时候,才需要解压缩,需要计算的时候是BF16
,存在那的时候用的4-bit
。
双量化,就是把原来的量化常数c
和c k-bit
再进行了一次量化,又省出来了百分之几十的空间。乘法过程中,会有量化常数的产生,这些量化常数要做解压缩,就要乘上这些数字,也可以被量化的存下来。
Paged Optimizers
为了防止系统不崩,数据如果没选择好变大了,可能会崩,Nvidia
统一内存,可以在CPU
和GPU
之间页面到页面之间进行传输。
- 存算一体,
Float32
,这么存,拿起来就算。 - 存算分离,
NF4
,极致地压缩了存储空间,计算和存储分离,用足够多的计算时间换空间,让之前没法做这个研究的(显存不够的),可以做这个研究了。
大模型高效微调(PEFT)未来发展趋势
Adapters
Adapters
(适配器),Google Adapter Tuning
-Parallel Adapter
,多头Adapter
AdaMix
,对Adapter
做了各种优化
Soft Prompts
(软提示)- 不用去调整预训练模型的权重,直接对
LLM
的响应机制做训练,能改进LLM
的生成结果。 Prefix Tuning
Prompt Tuning
P-Tuning
MAM
(Mix and Match
)Adapter
(把Adapter
和Soft Prompts
混合在一起)
- 不用去调整预训练模型的权重,直接对
UniPELT
(把前人的成果混合在一起,弄到一个框架下)- 增量模型的核心是,尽可能针对下游任务时,去增量训练一个小模型,增量的参数不一定是一个孤立的小模型,通过增量模型或增量参数的方式,来高效微调大模型,来适应下游任务。
Selective
(选择性方法)- 慧眼能挑出一些原来大模型应该去调整的参数或者模块,能挑的准,能挑的好。
reparametrization-based
(基于重参数化方法)- 原来有一个模型,这个模型把它的参数(甚至网络结构)重新做调整(或者部分调整),把
LLM
的能力复刻出来。 LoRA
UniPELT
- 原来有一个模型,这个模型把它的参数(甚至网络结构)重新做调整(或者部分调整),把
UniPELT 探索PEFT 大模型的统一框架(2022)
论文:UniPELT: A Unified Framework for Parameter-Efficient Language Model Tuning
代码开源:UniPELT GitHub
UIUC
和Meta AI
研究人员发表的UniPELT
提出将不同的PEFT
方法模块化。
通过门控机制学习激活最适合当前数据或任务的方法,尤其是最常见的3
大类PEFT
技术,
Adapters
Soft Prompts
Reparametrization-based
作者试图将已经被广泛证明有效的技术,整合为一个统一的微调框架。针对不同的下游任务,可以学习和配置不同的微调模块。
门控机制的核心是,集百家之长,什么都能学,可以直连,也可以跳过。
关于组合3
类主流PEFT
技术的探讨,
Adapter
- 接入位置(如
FFN
) - 接入方式(串行
or
并行) MLP
设计(△h
)
- 接入位置(如
Soft Prompts
- 嵌入方式(
Prompt-tuning
,Prefix-Tuning
,P-Tuning
) Prompt
微调方法(手工生成or
连续可微优化)
- 嵌入方式(
Reparametrization-based
- 缩放因子(
Scale: Rank r
) - 模型参数/模块类型(如
WQ
,WV
)
- 缩放因子(
PEFT
发展到今天有3-4
年时间了,期间一直都想把LLM
微调做好,但目前技术好像没有一个抓手,大家都在瞎猫碰死耗子,改改结构,改改嵌入方法等等,这些技术本身都有自己的应用场景和价值,不系统,很难规模化和工程化,所以这一类研究PEFT
的统一框架也是一个很重要的研究方向,只有框架统一了,写Network
的框架才会统一,就像等神经网络的backbone
统一了,PyTorch
、TensorFlow
这些就不用再大改。
把多种不同的PEFT
模块放到一个框架里边去训练,还有很长的路要走,但这条路是一条对的路。
(IA)3 探索新的增量训练方法(2022)
论文:Few-Shot Parameter-Efficient Fine-Tuning is Better and Cheaper than In-Context Learning
代码开源:r-three/t-few
少样本PEFT
,多快好省
为了使微调更加高效,北卡罗来纳教堂山分校的研究人员提出新的增量训练方法(IA)3
(通过学习向量来对激活层加权进行缩放,Infused Adapter by Inhibiting and Amplifying Inner Activations
)。
本文基于作者团队之前的工作T0
大模型,修改了损失函数以适应小样本学习,无需针对特定任务进行调整或修改即可应用于新任务,命名为T-Few
,并在RAFT
基准测试上取得了全新的SOTA
结果,超过了人类基准水平。
(IA)3 探索新的增量训练方法
与LoRA
相似,IA3
具有许多相同的优势,
IA3
通过大幅减少可训练参数的数量使微调更加高效。(对于T0
,一个使用IA3
模型仅有大约0.01%
的可训练参数,而即使是LoRA
也有大于0.1%
的可训练参数)。- 原始的预训练权重保持冻结状态,这意味着您可以构建多个轻量且便携的
IA3
模型,用于各种基于它们构建的下游任务。 - 使用
IA3
进行微调的模型的性能与完全微调模型的性能相媲美。 IA3
不会增加推理延迟,因为适配器权重可以与基础模型合并。- 原则上,
IA3
可以应用于神经网络中的任何权重矩阵子集,以减少可训练参数的数量。根据作者的实现,IA3
权重被添加到Transformer
模型的关键、值和前馈层中。具体来说,对于Transformer
模型,IA3
权重被添加到关键和值层的输出,以及每个Transformer
块中第二个前馈层的输入。
鉴于注入IA3
参数的目标层,可根据权重矩阵的大小确定可训练参数的数量。
原则上,IA3
可以应用于神经网络中的任何权重矩阵子集,以减少可训练参数的数量。根据作者的实现,IA3
权重被添加到Transformer
模型的K
、V
和FFN
中。具体来说,对于Transformer
模型,IA3
权重被添加到关键和值层的输出,以及每个Transformer
块中第二个前馈层的输入。
根据注入IA3
参数的目标层,可以根据权重矩阵的大小确定可训练参数的数量。
大模型高效微调技术未来发展趋势
- 更高效的参数优化:研究将继续寻找更高效的方法来微调大型模型,减少所需的参数量和计算资源。这可能包括更先进的参数共享策略和更高效的
LoRA
等技术。 - 适应性和灵活性的提升:微调方法将更加灵活和适应性强,能够针对不同类型的任务和数据集进行优化。
- 跨模态和多任务学习:
PEFT
可能会扩展到跨模态(如结合文本、图像和声音的模型)和多任务学习领域,以增强模型处理不同类型数据和执行多种任务的能力。 - 模型压缩和加速:随着对边缘设备和移动设备部署AI模型的需求增加,
PEFT
技术可能会重点关注模型压缩和推理速度的提升。 - 低资源语言和任务的支持:将
PEFT
技术应用于低资源语言和特定领域任务,提供更广泛的语言和任务覆盖。
注:
LLM
微调,重点在于处理开源数据集和开源模型,学习数据处理和模型训练的经验,模型不会大改,更多的是怎么把数据用的更好。
预训练这个方式成本大,耗费的资源大,如果只是想学习这个技术,可以用带量化的预训练(QLoRA
)掌握预训练技术。