一文带你了解当前主流PEFT技术

随着LLaMA3的发布,大模型开源社区的战力又提升了一分,国内目前应该已经有不少大佬已经开始着手对LLaMA3进行研究或微调,对于微调技术,目前比较常见的就是Peft系列的技术,那么什么是PEFT,有哪些分类,为什么这么受大家欢迎呢?今天我们就好好聊聊这个话题。

什么是PEFT? 有哪些技术?

PEFT的全称叫做 Parameter-Efficient Fine-Tuning ,中文叫做参数高效微调,相对于全量微调,最重要的区别当然就是只针对部分参数进行微调,并且能提升微调的效率。我们都知道现在大模型的体量动不动就上百亿上千亿的,普通人甚至很多企业都无法承受微调的成本,但一些垂直领域的能力大模型可能比较缺乏,必须要微调后才能很好的处理这方面的任务,因此绝大部分人都会采取PEFT的手段来进行大模型微调。

对于PEFT技术有一篇很好的综述性的文章,叫做 Scaling Down to Scale Up: A Guide to Parameter-Efficient Fine-Tuning ,将大部分PEFT技术都做了一个很好的分类,对于这篇论文中没提到的但目前也很热门的技术,本文也会选择一些说明。

所谓的参数效率微调 (PEFT),旨在通过仅训练现有模型参数的一个子集或额外添加的一组新参数,来解决模型训练的效率问题。这类方法在参数效率、内存效率、训练速度、模型最终质量及可能产生的额外推理成本等方面表现各异。不同的PEFT方法有着不同的表现效果,微调的重点也可能不一样,文中做了一个很直观的分类总览图如下:

在这张图上有着很多大家都很熟悉的身影,比如Adapters、LoRA等,作者主要是分成了三个大类,并且在Additive的分类中,增加了 Adpaters 和 Soft prompts 两个子分类

  • Addition-based
    • Adapters
    • Soft prompts
    • Others
  • Selective-based
  • Reparametrization-based

本文主要介绍Add下的两个子类和重参数化,要特殊说明的是,PEFT 的技术虽然有很多,而且很多技术说其适用于各种架构的神经网络,但有很多只适合于Transformer架构,因为它们是针对此架构上的某些层进行的优化操作。

在 Transformer 架构中,里面基础的Block如上所示,这个架构最核心的部件其实就是**多头注意力层 (MHA) ** 和 全连接前馈网络(FFN) 。图中的 Add & Norm 层指的是残差和归一化,主要是避免网络过深导致的梯度消失,以及稳定训练的作用。注意力的计算公式相信大家都很熟悉了,我就不额外说明了。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> A t t ( Q , K , V ) = s o f t m a x ( Q K T d K ) ∗ V Att(Q, K, V) = softmax(\frac{QK^T}{\sqrt{d_K}}) * V </math>Att(Q,K,V)=softmax(dK QKT)∗V

为什么要提一嘴Transformer架构呢,因为这些PEFT的方法,可能就是针对这个架构中的某一部分进行的调整,对照这个架构看,会更好的理解PEFT具体做了什么工作。

Additive

先讲讲第一个大分类,Additive,这一类的特点在于它们都是基于在模型层之间添加全连接网络的思想构建的,首先来讲讲Adapters。

Adapters

Adapters 最早出现于 2017和2018的两篇论文,Learning multiple visual domains with residual adaptersEfficient parametrization of multi-domain deep neural networks ,主要是用于多领域图像分类的。但在2019年,有研究者将其应用到了NLP领域,Parameter-Efficient Transfer Learning for NLP,他们在注意力层和FFN层后加上了个全连接网络,并且这个网络的维度要比输入的维度要小,它可以通过调整不到总模型参数的 4% 就可以达到与完全微调相竞争的性能。

上图我们可以看到增加了Adapter之后的Transformer架构的样子,以及Adapter内部的结构。适配器由一个相对于原模型中注意力层和前馈层参数较少的瓶颈部分构成,并包含一个跳跃连接。在调整适配器时,所谓的绿色层会针对下游数据进行训练,包括适配器本身、层归一化的参数,以及最终的分类层。

在此论文中,他们使用了bottleneck 架构。适配器首先将原始 d 维特征降维至 m 维,经过一个非线性处理后再升维回 d 维。包括偏置在内,每层新增的参数总数为 2md + d + m。通过将 m 设定得远小于 d,从而减少了每个任务所增加的参数数量;通常情况下,这些参数约占原始模型参数的 0.5% 至 8%。瓶颈维度 m 使得在性能和参数效率之间进行权衡变得简单。适配器模块内置一个跳跃连接,如果投影层的参数初始化接近零,模块会初始化为一个近乎恒等的功能。

原文关于Adapter的实验结果我就不贴出来了,虽然跟全量微调比是有所下降,但只需要训练总参数的4%的参数量,这个损失显然是值得接受的。

除了Adapters这篇文章,文中还介绍了另一种使用适配器的方式 AdaMix, 它通过以混合专家 (MoE) 的方式使用多个适配器来提高适配器的性能。这意味着每个适配器层是一组层(专家),并且在每个前向传递中只激活一小组专家。MoE的思想也是目前很火热的研究方向,大家感兴趣的可以自行阅读一下原论文 AdaMix: Mixture-of-Adaptations for Parameter-efficient Model Tuning

Soft Prompts

对于Soft Prompts,这个名字一听就知道是在Prompt这一块做文章,但它为什么叫做Soft Prompts呢?有没有Hard Prompts?

答案是肯定的,所谓Hard Prompts我们可以理解成就是我们平时为了让模型得到更好的输出,使用各种prompt技术尝试不同的prompt,比如 few-shot 就是我们经常用的策略,通过这些策略,找到最佳的一个prompt,这就是 Hard Prompts 的技术,它是固定的、不可变的文本提示。我个人的理解是,它实际上就是平时常说的提示工程技术。

那么 Soft Prompts,就不是这种通过各种语言提示来去寻找最优解,而是将寻找 Hard 提示这种离散优化问题,转成连续问题。

我们这里介绍最常见的三个,Prompt Tuning、Prefix Tuning 以及 P-Tuning。其中P-Tuning在这篇综述论文中没有介绍,但它的本质上也是属于这个分类,因此我这里就一块说明了。

Prompt Tuning

Prompt Tuning 提出在模型输入嵌入前加入一个可训练的张量,通过梯度下降法进行直接优化。论文中使用的模型是T5,并创新性地提出将所有的文本任务,如摘要、分类、问答等,都视为文本生成问题。

如上图,Prompt Tuning 只需为每个任务存储一个任务特定的小型提示,并能够在原始预训练模型的基础上进行混合任务推理。例如,在使用 T5 "XXL" 模型时,每个调整后的模型副本需要 11 亿个参数。相比之下,我们的调整提示每个任务仅需要 20,480 个参数,假设每个提示长度为 5 个 Token,这相当于减少了超过五个数量级。

从实验结果上看,T5的标准模型微调虽然性能出色,但它需要为每一个最终任务存储独立的模型副本。相比之下,Prompt Tuning 在模型规模增加时同样能够达到优质的效果,并且只需使用一个单个冻结的模型就可以处理所有任务。此外,此方法甚至对比 GPT-3 进行的少样本提示设计上也表现出明显的优势。下图展示了三次运行中调整方法的平均值和标准差。

文中还对Prompt Tuning 进行了超参数的消融实验,在 Prompt 的长度、Prompt 初始化、预训练方法以及微调步数上进行了实验对比。

实验结果如下:

  • 提示长度:增加到20个以上的 Token 通常会大幅提升性能,但 XXL 即使在单 Token 提示下也表现良好。
  • 提示初始化:随机均匀初始化在使用采样词汇或类标签嵌入的更"先进"的初始化方式面前稍显逊色,但在 XXL 规模下这种差异消失。
  • 预训练目标:LM 适应性优于跨度损坏,即使在下游任务目标中添加了哨兵,但 XXL 与任何方法都配合得很好。
  • LM 适应性:更长的适应期通常会带来更大的收益,但 XXL 对短适应也很稳健。

Prefix Tuning

Prefix Tuning 于2021年由斯坦福大学提出,核心思想是在模型处理实际输入之前先输入一系列可训练的参数(称为"前缀"),这些参数不是传统意义上的文本字符串,而是嵌入到模型的内部状态中的。这个方法特别适用于序列生成任务。

与Prompt Tuning相似,Prefix Tuning的目的是调整模型以更好地适应下游任务,但它不需要对整个模型进行更新,而是只更新一小部分参数。它通过添加一个固定长度的前缀,这个前缀由一系列可学习的向量组成,来指导模型的生成过程。

Prefix Tuning 冻结了语言模型参数并且仅优化前缀(红色前缀块)。因此,我们只需要为每个任务存储前缀,使得前缀微调模块化且节省空间。并且,前缀向量的更新可以对模型生成的内容进行精细控制,提供任务特定的引导。请注意,每个垂直块表示一个时间步的 Transformer 激活。

论文中提出了两个文本生成的任务,一个是 table-to-text 任务,一个是 summarization 任务。并在自回归模型和编码器-解码器架构模型上使用Prefix Tuning进行微调。

在自回归模型中,比如GPT2,生成的文本是逐个 token 依次产生的,每个新产生的 token 都依赖于前面所有已生成的 token。那么使用Prefix Tuning 的流程大致如下:

  1. 通过在输入序列前添加一系列可训练的向量,即prefix,来进行调整。这些prefix被视为"虚拟的token",模型在生成每个token时都会参考这些prefix。
  2. 在自回归模型的输入部分,传统的输入x和输出y之间(x;y),通过prefix-tuning引入了prefix(P)。因此,整个输入序列变为 [PREFIX; x; y]。这里的PREFIX是一组经过训练的连续向量,作为模型生成文本的上下文。
  3. 对于每个位置i,在PREFIX范围内的激活向量 hi 直接从训练中得到的矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> P θ P_\theta </math>Pθ 中获取(由参数 θ 确定)。对于其他位置的激活向量,则由语言模型根据前一个时间步的激活向量和当前的输入计算得出。这样,prefix中的向量直接影响了随后生成的文本。
  4. 训练过程中,不再更新整个语言模型的参数,而是固定这些参数,只对prefix中的参数进行优化。这意味着语言模型的核心部分保持不变,而生成文本的特定风格或内容则通过优化prefix来调整。

通过这种方法,prefix-tuning允许在不牺牲模型原有能力的情况下,灵活地调整生成的文本内容,使其适应特定的任务需求。这种技术在资源有限或者希望保持模型泛化能力的场合尤其有用。此外,它也支持快速调整和部署,因为只需要存储和更新相对较小的prefix参数。

对于编码器-解码器结构的模型,prefix-tuning不仅在编码器的输入部分添加了prefix,还在解码器的输入部分添加了一个可能不同的prefix,称为Prefix′。因此,整个输入-输出序列被调整为 [PREFIX; x; PREFIX′; y] 的形式。这里x是原始输入,y是期望输出。

  • 在编码器中,PREFIX前缀会影响编码器处理输入x的方式,提供额外的上下文信息。在解码器中,PREFIX′前缀则影响解码器根据编码器的输出以及前面的解码输出生成最终文本的过程。
  • 激活向量的计算:
    • 对于编码器,所有位于PREFIX区域的激活向量hi直接从训练得到的前缀矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> P θ P_\theta </math>Pθ中取得
    • 对于解码器,在PREFIX′区域的激活向量也类似地从另一个训练得到的矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> P ′ θ P^\prime \theta </math>P′θ中取得。
    • 对于x和y中的元素,激活向量的计算遵循标准的编码器-解码器运算规则,但会考虑到PREFIX和PREFIX′的影响。
  • 在prefix-tuning的训练过程中,固定编码器和解码器的原始参数,仅对PREFIX和PREFIX′中的参数进行优化。这样,可以在不改变模型主体结构的情况下,通过调整前缀来精细控制模型的行为。

文中在E2E、WebNLG和DART数据集上进行了测试,Prefix Tuning仅仅新增0.1%的参数,但效果却超过了其他轻量基准,并且与完整的微调方法性能相当。在训练参数量相同甚至更少时,性能超过了Adapter微调方法。

总的来说,prefix-tuning在高效性、空间节省、不同数据规模的表现上、灵活性上都有着独特的优势,是一种很好的微调手段。

P-Tuning

P-Tuning 也是一种Soft Prompt 的技术,通过调整特定的参数来优化模型在特定任务上的表现。它包含了两个版本v1和v2。

P-Tuning v1的核心思想是在预训练语言模型的输入阶段引入可训练的prompt 。这些prompt是连续的向量(称为"prompt嵌入"),它们在模型训练过程中与模型的其他参数一起优化。这种方法允许模型在不同任务中使用不同的prompt,从而使得模型能够更好地适应特定的任务。它和Prefix Tuning有点相似,但Prefix Tuning 是在模型开头加上额外的嵌入,但P-Tuning 的嵌入位置更为灵活。此外,Prefix Tuning 在每个注意力层 增加嵌入来引入额外参数,并使用多层感知机(MLP)进行初始化,而P-Tuning 仅在输入时增加嵌入,并通过 LSTM 加 MLP 进行初始化。

P-Tuning v2是v1的改进版本,它通过在模型的每一层都引入可训练的prompt嵌入来进一步提升微调的效果。这种方法被称为深度提示优化(Deep Prompt Tuning),它使得P-Tuning v2能够更有效地适应不同规模的模型和各种自然语言理解(NLU)任务。

它引入了几项优化措施,以提高模型的效率和性能,主要包括:

  1. 重参数化技术:重参数化通常涉及使用像多层感知机(MLP)这样的编码器来转换可训练的嵌入。然而,在不同的 NLU 任务和数据集中,重参数化的效用是不一致的。例如,在 RTE 和 CoNLL04 数据集上,MLP 带来了一致的性能提升;而在 BoolQ 和 CoNLL12 数据集上,MLP 对结果的影响微乎其微或甚至负面。
  2. 提示长度和优化策略:P-Tuning v2 通过实验找到了不同任务最适合的提示长度,并且根据任务的复杂性调整提示长度。此外,它还对不同任务采取不同的训练优化策略,以达到最佳效果。简单的分类任务通常更适合较短的提示(少于20个词),而复杂的序列标记任务则可能需要较长的提示(约100个词)。
  3. 多任务学习:P-Tuning v2 支持多任务学习,可以在多个任务共享连续提示的基础上进行优化。这种策略提高了模型的泛化能力,同时还可以提升单一任务的性能。
  4. 分类头:在之前的提示调整方法中,使用语言模型头来预测标签是常见的,但在 P-Tuning v2 中,发现在完整数据设置下不再需要这种方法。相反,P-tuning v2 采用随机初始化的分类头直接应用于标记,类似于 BERT 中的做法。

P-Tuning v2 在SuperGLUE数据集上以及复杂任务上进行了实验测试,展现了和全面微调接近的性能,并且在复杂任务上也起到了很好的表现。具体内容大家可以看看原论文

Reparametrization

接下来讲讲第三个分类,基于重参数化的训练方法。在介绍它之前,我们可以想想上面说的Adapter还有Soft Prompt那些方法有什么不足之处。

  • 对于Adapter Tuning,它 通过在Transformer的每个模块中插入额外的适配器层来调整模型。这种方法虽然提供了灵活性,但在某些情况下会引入额外的推理延迟,尤其是在参数较多的情况下
  • 对于Soft Prompt, 它通过优化模型输入的一小部分来实现调整,这通常涉及到对部分输入向量的修改。这种方法在某些应用中可能较难优化,并且由于其修改了输入的长度,可能会对处理长序列的能力造成影响。

LoRA

LoRA 的全名叫做 Low-Rank Adaptation,即低秩适配,为什么会提出这个想法呢?这是因为,尽管大模型参数规模巨大,但关键作用通常是由其中的低秩本质维度发挥的。它指的是,模型虽然包含大量参数,但其有效的信息或功能可以通过一个远小于模型参数总数的维度来捕捉。这表明模型的许多参数可能是冗余的,而真正影响模型决策和性能的只是其中一小部分。

在数学和矩阵理论中,"秩"(Rank)描述的是矩阵行或列向量中线性独立的数量。一个低秩矩阵意味着它的行或列之间存在较强的线性关系,即多个行或列可以通过其他行或列的线性组合来表示。在模型参数的上下文中,低秩表示模型的行为可以通过较少的、线性相关的因素来充分描述。

"本质维度"则是指能够捕获数据集或模型行为关键特征的维数。在大型模型中,尽管参数众多,它们操作的有效维度(即本质维度)可能远小于参数的总数,这意味着模型的复杂性可以通过更简化的方式来理解和操作。

利用模型的低秩本质维度可以帮助我们更有效地进行模型训练和微调。例如,在参数非常多的大型模型中,通过识别和优化那些最能影响模型输出的关键参数(即那些"本质维度"),可以大幅度减少需要调整的参数数量,从而节省计算资源和训练时间。这也是为什么像LoRA这样的技术可以通过仅修改一小部分参数来有效地调整大型预训练模型的原因。

在LoRA中,不直接修改原始的预训练权重矩阵 W,而是通过引入低秩矩阵 A 和 B 来间接调整权重。原始权重矩阵 W 保持不变,而 A 和 B 作为增量矩阵被添加到模型中,其秩 r 通常远小于 W 的维度。

更新后的权重可以表达为:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> W ′ = W + B A W^\prime=W+BA </math>W′=W+BA

这里,B 和 A 分别是维度 d*r 和 r*k 的矩阵,其中 r 是远小于 d 和 k 的秩。这里的A矩阵会将输入的 d 维数据降维到 r 维,B 矩阵将 r 维数据映射会 d 维。

在训练阶段,仅 A 和 B 是可训练的,而 W 保持冻结。这样,模型在适应特定任务时只需要调整 A 和 B,从而显著减少了需要训练的参数数量。在推理阶段,可以预先计算出 W′ 并使用它替代原始的 W,从而不会增加额外的推理延时。

相对于 Soft Prompts, LoRA 有着下面几个优势:

  • 在训练时仅需优化少量参数,推理延迟低。
  • 对内存和存储的需求极低,且无须牺牲输入空间,这使得LoRA在大型模型如GPT-3的应用中尤为有效,可以大幅减少GPU内存的使用和模型的存储空间。
  • 直接作用于模型结构,相对于Soft prompts 这种操作输入数据来影响模型输出的方式, LoRA 是直接影响到模型结构的。
  • LoRA提供了较好的泛化能力,因为它通过对权重的低秩更新保持了模型的原始结构,这有助于保留预训练时学到的知识。
  • 更接近全参数微调的效果。

文中同样做了一些实验来验证 LoRA 的效果,表明LoRA在减少训练参数数量的同时,能够保持与完整微调相当或更好的性能。LoRA即使参数数量显著减少,也能达到与全参数微调相匹敌的效果。此外,它在不同语言理解任务中的通用性和有效性也是表现优秀。

对于 LoRA 的使用,有两个关键点的选择可能会很大的影响到最终的效果,一个是权重矩阵的种类选择,一个是矩阵的秩的选择。

论文中对这两个选项也进行了一些对比实验,可以看出,哪怕在极低的秩值下,LoRA 的表现还是很好,在权重矩阵的选择上,同时使用 q 和 v 的时候,表现最好,在只选择 q 或 k 的情况下,效果是最差的。比起单一类型的权重使用更大的秩进行调整,更推荐调整多个权重矩阵。

AdaLoRA

接下来,我们来看看LoRA的一种改进办法:AdaLoRA

上文介绍了 LoRA 是如何进行微调的,其中矩阵的秩以及权重矩阵的选择对微调效果是存在影响的。原版 LoRA中,秩 r 一旦决定了就无法修改,此外它其实忽略了在微调预训练模型时不同权重矩阵的重要性可能会有很大的不同,再者就是只微调了 Attention 模块, FFN 模块忽略了。为了解决这些问题,AdaLoRA 提出了动态地在权重矩阵间分配参数预算的方法。具体来说,AdaLoRA使用 SVD 提升矩阵低秩分解性能,动态调整∆的秩。通过这种方法,AdaLoRA能够根据每个矩阵的重要性,调整其对应的秩,使得更重要的矩阵能获得更多的参数预算,而不那么重要的矩阵则进行削减(模型剪枝),从而在保持或甚至提升模型性能的同时,进一步提高参数效率。根据论文的第三章,我们可以将它分成3个步骤:

  1. 使用 SVD 提升矩阵低秩分解性能
  2. 对模块参数(特征)重要性建模
  3. 根据重要性评分剪枝和自适应调整本征秩 r

使用 SVD 提升矩阵低秩分解性能

AdaLoRA的核心思想之一是使用奇异值分解(SVD)来近似参数化增量更新∆。AdaLoRA不直接计算完整的SVD,而是通过参数化形式近似,从而避免了高计算成本。具体地:

将增量更新∆表示为两个小矩阵P和Q的乘积以及一个对角矩阵Λ,即∆ = PΛQ。这里,P和Q分别代表左右奇异向量,Λ代表奇异值。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> W = W ( 0 ) + Δ = W ( 0 ) + P Λ Q (论文公式3) W=W^{(0)}+Δ=W^{(0)}+PΛQ \tag{论文公式3} </math>W=W(0)+Δ=W(0)+PΛQ(论文公式3)

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> W ( 0 ) W^{(0)} </math>W(0)是预训练模型的初始权重矩阵。
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> Δ \Delta </math>Δ是增量更新,通过低秩矩阵 PΛQ 进行参数化,其中 P 和 Q 分别是左右奇异向量矩阵,而 Λ 是对角矩阵,包含奇异值。

此外,为了维护和强化矩阵 P 和 Q 的正交性(即 <math xmlns="http://www.w3.org/1998/Math/MathML"> P T P = Q Q T = I P^TP=QQ^T=I </math>PTP=QQT=I),AdaLoRA引入了正则化项:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> R ( P , Q ) = ∥ P T P − I ∥ F 2 + ∥ Q Q T − I ∥ F 2 . (论文公式4) R(P, Q) = \| P^T P - I \|_F^2 + \| Q Q^T - I \|_F^2. \tag{论文公式4} </math>R(P,Q)=∥PTP−I∥F2+∥QQT−I∥F2.(论文公式4)

这里

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> ∥ ∥ F \|\|_F </math>∥∥F 表示Frobenius范数,用于计量矩阵偏离单位矩阵的程度。
  • 正则化项确保 P 和 Q 尽可能接近正交,有助于稳定训练过程并减少因参数化带来的信息损失。

对模块参数(特征)重要性建模

如何评价某个参数的重要性呢?文中给出了一个重要性评分的一个公式:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> s ( t ) ( w i j ) = I ( t ) ( w i j ) ⋅ U ( t ) ( w i j ) s^{(t)}(w_{ij}) = I^{(t)}(w_{ij}) \cdot U^{(t)}(w_{ij}) </math>s(t)(wij)=I(t)(wij)⋅U(t)(wij)

其中 s 就是重要性, I 表示敏感性,U 表示不确定性。 那么敏感性和不确定性又是个啥?

敏感性其实就是权重 <math xmlns="http://www.w3.org/1998/Math/MathML"> w i j w_{ij} </math>wij跟梯度乘积 <math xmlns="http://www.w3.org/1998/Math/MathML"> ∇ w i j L \nabla_{w_{ij}}\mathcal{L} </math>∇wijL的绝对值:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> I ( ω i j ) = ∣ ω i j ∇ w i j L ∣ I(\omega_{ij}) = |\omega_{ij}\nabla_{w_{ij}}\mathcal{L}| </math>I(ωij)=∣ωij∇wijL∣

简单地说,就是一个参数越重要,那么它的梯度就越大。

则敏感性建模如下,根据前一个步骤的敏感性以及一个超参数来得到:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> I ( t ) ( w i j ) = β 1 I ( t − 1 ) ( w i j ) + ( 1 − β 1 ) I ( t ) ( w i j ) I^{(t)}(w_{ij}) = \beta_1 I^{(t-1)}(w_{ij}) + (1 - \beta_1)I^{(t)}(w_{ij}) </math>I(t)(wij)=β1I(t−1)(wij)+(1−β1)I(t)(wij)

不确定性的建模类似,但跟敏感性也挂钩,这里的 <math xmlns="http://www.w3.org/1998/Math/MathML"> β 2 \beta_2 </math>β2表示滑动平均超参数:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> U ( t ) ( w i j ) = β 2 U ( t − 1 ) ( w i j ) + ( 1 − β 2 ) ∣ I ( t ) ( w i j ) − I ( t ) ( w i j ) ∣ U^{(t)}(w_{ij}) = \beta_2 U^{(t-1)}(w_{ij}) + (1 - \beta_2)\left| I^{(t)}(w_{ij}) - I^{(t)}(w_{ij}) \right| </math>U(t)(wij)=β2U(t−1)(wij)+(1−β2) I(t)(wij)−I(t)(wij)

将上面的重要性评分落实到具体的参数中就如下所示:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> S k , i = s ( λ k , i ) + 1 d 1 ∑ j = 1 d 1 s ( P k , j i ) + 1 d 2 ∑ j = 1 d 2 s ( Q k , i j ) S_{k,i} = s(\lambda_{k,i}) + \frac{1}{d_1}\sum_{j=1}^{d_1} s(P_{k,ji}) + \frac{1}{d_2}\sum_{j=1}^{d_2} s(Q_{k,ij}) </math>Sk,i=s(λk,i)+d11j=1∑d1s(Pk,ji)+d21j=1∑d2s(Qk,ij)

右边三个部分分别表示奇异值(对角矩阵)、左奇异向量、右奇异向量。从而,AdaLoRA 使用 SVD 的三元组代替了 LoRA 的二元组:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> G k , i = P k , ∗ i , λ k , i , Q k , i ∗ \mathcal{G}{k,i} = {\mathcal{P}{k,*i}, \lambda_{k,i}, \mathcal{Q}_{k,i*}} </math>Gk,i=Pk,∗i,λk,i,Qk,i∗

根据重要性评分剪枝和自适应调整本征秩 r

重要性得到后,需要根据重要性来进行参数更新:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> Λ ^ k ( t ) = Λ k ( t ) − η ∇ Λ k L ( P ( t ) , E ( t ) , Q ( t ) ) \hat{\Lambda}k^{(t)} = \Lambda_k^{(t)} - \eta \nabla{\Lambda_k} \mathcal{L}(P^{(t)}, \mathcal{E}^{(t)}, \mathcal{Q}^{(t)}) </math>Λ^k(t)=Λk(t)−η∇ΛkL(P(t),E(t),Q(t))

其中:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> Λ ( t ) \Lambda^{(t)} </math>Λ(t) 是当前的奇异值矩阵。
  • η 是学习率。
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> ∇ Λ L ∇_ΛL </math>∇ΛL 是损失函数 L 关于 Λ 的梯度。

接下来是奇异值修剪的过程,使用一个阈值函数T来修剪奇异值,基于它们的重要性评分:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> Λ k ( t + 1 ) = T ( Λ ~ k ( t ) , S k ( t ) ) , with T ( Λ ~ k ( t ) , S k ( t ) ) i i = { Λ ~ k , i i ( t ) if S k , i ( t ) is in the top- b ( t ) of S ( t ) , 0 otherwise , \Lambda_k^{(t+1)} = T(\tilde{\Lambda}k^{(t)}, S_k^{(t)}), \text{ with } T(\tilde{\Lambda}k^{(t)}, S_k^{(t)}){ii} = \begin{cases} \tilde{\Lambda}{k,ii}^{(t)} & \text{if } S_{k,i}^{(t)} \text{ is in the top-}b(t) \text{ of } S^{(t)}, \\ 0 & \text{otherwise}, \end{cases} </math>Λk(t+1)=T(Λ~k(t),Sk(t)), with T(Λ~k(t),Sk(t))ii={Λ~k,ii(t)0if Sk,i(t) is in the top-b(t) of S(t),otherwise,

S表示重要性评分,b(t)表示在训练步骤t时保留的奇异值的预算:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> b ( t ) = { b ( 0 ) if 0 ≤ t < t i b ( T ) + ( b ( 0 ) − b ( T ) ) ( 1 − t − t i T − t i − t f ) 3 if t i ≤ t < T − t f b ( T ) otherwise b(t) = \begin{cases} b(0) & \text{if } 0 \leq t < t_i \\ b(T) + (b(0) - b(T))\left(1 - \frac{t - t_i}{T - t_i - t_f}\right)^3 & \text{if } t_i \leq t < T - t_f \\ b(T) & \text{otherwise} \end{cases} </math>b(t)=⎩ ⎨ ⎧b(0)b(T)+(b(0)−b(T))(1−T−ti−tft−ti)3b(T)if 0≤t<tiif ti≤t<T−tfotherwise

在原论文中给出了算法介绍如下:

输入: 数据集 <math xmlns="http://www.w3.org/1998/Math/MathML"> D D </math>D; 总迭代次数 <math xmlns="http://www.w3.org/1998/Math/MathML"> T T </math>T; 预算调度 <math xmlns="http://www.w3.org/1998/Math/MathML"> { b ( t ) } t = 0 T \{b^{(t)}\}_{t=0}^T </math>{b(t)}t=0T; 超参数 <math xmlns="http://www.w3.org/1998/Math/MathML"> η \eta </math>η, <math xmlns="http://www.w3.org/1998/Math/MathML"> γ \gamma </math>γ, <math xmlns="http://www.w3.org/1998/Math/MathML"> β 1 \beta_1 </math>β1, <math xmlns="http://www.w3.org/1998/Math/MathML"> β 2 \beta_2 </math>β2. 对于 <math xmlns="http://www.w3.org/1998/Math/MathML"> t = 1 t = 1 </math>t=1 到 <math xmlns="http://www.w3.org/1998/Math/MathML"> T T </math>T,执行以下步骤:

  • 从数据集 <math xmlns="http://www.w3.org/1998/Math/MathML"> D D </math>D 中采样一个小批量,并计算梯度 <math xmlns="http://www.w3.org/1998/Math/MathML"> ∇ L ( P , E , Q ) \nabla L(P, E ,Q) </math>∇L(P,E,Q)。
  • 计算每个参数的灵敏度 <math xmlns="http://www.w3.org/1998/Math/MathML"> I ( t ) I^{(t)} </math>I(t),见论文公式(8)。
  • 更新灵敏度 <math xmlns="http://www.w3.org/1998/Math/MathML"> I ( t ) I^{(t)} </math>I(t) 和不确定性 <math xmlns="http://www.w3.org/1998/Math/MathML"> U ( t ) U^{(t)} </math>U(t),见公论文式(9)和(10)。
  • 根据公式(7),计算重要性评分 <math xmlns="http://www.w3.org/1998/Math/MathML"> S k , i ( t ) S_{k,i}^{(t)} </math>Sk,i(t)。
  • 更新 <math xmlns="http://www.w3.org/1998/Math/MathML"> P k ( t + 1 ) = P k ( t ) − η ∇ P k L P_k^{(t+1)} = P_k^{(t)} - \eta \nabla_{P_k} L </math>Pk(t+1)=Pk(t)−η∇PkL 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> Q k ( t + 1 ) = Q k ( t ) − η ∇ Q k L Q_k^{(t+1)} = Q_k^{(t)} - \eta \nabla_{Q_k} L </math>Qk(t+1)=Qk(t)−η∇QkL。
  • 根据重要性评分和预算 <math xmlns="http://www.w3.org/1998/Math/MathML"> b ( t ) b^{(t)} </math>b(t),更新 <math xmlns="http://www.w3.org/1998/Math/MathML"> Λ k ( t + 1 ) \Lambda_k^{(t+1)} </math>Λk(t+1),见论文公式(6)。 输出: 经过微调的参数 <math xmlns="http://www.w3.org/1998/Math/MathML"> { P ( T ) , E ( T ) , Q ( T ) } \{P^{(T)}, E^{(T)}, Q^{(T)}\} </math>{P(T),E(T),Q(T)}。

在论文中的实验中,对于GLUE基准测试,AdaLoRA在几乎所有数据集上,尤其是在参数预算较低的情况下,都取得了最佳的性能。对于问答任务,SQuAD v1.1 和 SQuADv2.0数据集上,AdaLoRA在精确匹配(EM)和F1得分方面都超过了其他方法。特别是在预算最低的0.08%情况下,在SQuADv2.0数据集上取得了1.2%的F1分数提升。自然语言生成任务上,AdaLoRA在自然语言生成任务中同样表现出了显著的性能提升。它在两个数据集上的ROUGE得分表现都优于或至少与LoRA相当,尤其是在参数预算较低的情况下。

QLoRA

最后再介绍一种 LoRA 的方法,叫做 QLoRA。 它多出的 Q 指的就是量化技术,通过冻结的4位量化的预训练语言模型将梯度反向传播到低秩适配器(LoRA)。这允许在单个48GB GPU上微调高达65B参数的模型,同时保持与16位完全微调任务相同的性能。

上图就展示了不同的微调方式(FFT,LoRA、QLoRA)的内存使用对比。

我们首先看看 QLoRA 产生的背景。量化是将输入从一个包含更多信息的表示转换为信息量较少的表示的过程。这通常意味着将数据类型从高位转换为低位。例如,将32位浮点数转换为8位整数。为了确保低位数据类型的整个范围被使用,输入数据类型通常通过绝对最大值归一化到目标数据类型范围。例如,将32位浮点张量量化到具有范围[-127, 127]的Int8张量的过程可以用以下公式表示:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> X I n t 8 = round ( 127 absmax ( X F P 32 ) ⋅ X F P 32 ) = round ( c F P 32 ⋅ X F P 32 ) X_{Int8} = \text{round}\left(\frac{127}{\text{absmax}(X_{FP32})} \cdot X_{FP32}\right) = \text{round}(c^{FP32} \cdot X_{FP32}) </math>XInt8=round(absmax(XFP32)127⋅XFP32)=round(cFP32⋅XFP32)

其中c是量化常数或量化比例。反量化是逆过程:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> dequant ( c F P 32 , X I n t 8 ) = X I n t 8 c F P 32 = X F P 32 \text{dequant}\left(c^{FP32}, X_{Int8}\right) = \frac{X_{Int8}}{c^{FP32}} = X_{FP32} </math>dequant(cFP32,XInt8)=cFP32XInt8=XFP32

如果输入张量中出现大幅度值(即异常值),则某些量化桶------特定的位组合------使用得不好,少数或没有数字被量化到这些桶中。为了防止异常值问题,常用的方法是将输入张量分块,每个块独立量化,每个块都有自己的量化常数 c。

4-bit NormalFloat

QLoRA 提出了新的数据类型 4-bit NormalFloat(NF4). 这种数据类型是为了量化语言模型中通常呈现正态分布的权重而设计的,它在信息论上对于正态分布的数据是最优的。NormalFloat数据类型通过以下方式实现更有效的量化:

对于正态分布的输入张量,如果我们知道其量化常数,则可以假设输入张量的分位数是固定的,这使得精确的分位数估计在计算上是可行的。

由于预训练神经网络权重通常具有以零为中心的正态分布,我们可以通过缩放标准差 σ,将所有权重转换为一个固定的分布范围内。对于我们的数据类型,将此范围设定为[-1, 1]。因此,数据类型的分位数和神经网络权重需要被规范化到这个范围内。

对于零均值正态分布,其任意标准差 σ 在范围 [-1, 1] 内的信息理论最优数据类型的计算方法如下:

  1. 估计理论 N(0,1) 分布的 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 k + 1 2^k+1 </math>2k+1 个分位数,以获得 k位的分位数量化数据类型。
  2. 将此数据类型的值规范化到 [-1, 1] 范围。
  3. 通过将输入权重张量规范化到 [-1, 1] 范围(通过绝对最大值重新缩放)来量化输入权重张量。

完成权重范围和数据类型范围的匹配后,可以按常规方法进行量化。这一步等价于重新缩放权重张量的标准差,使其与 k位数据类型的标准差相匹配。我们估计数据类型的 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 k 2^k </math>2k值 <math xmlns="http://www.w3.org/1998/Math/MathML"> q i q_i </math>qi如下:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> q i = 1 2 ( Q x ( i 2 k + 1 ) + Q x ( i + 1 2 k + 1 ) ) , q_i = \frac{1}{2} \left( Q_x \left( \frac{i}{2k+1} \right) + Q_x \left( \frac{i+1}{2k+1} \right) \right), </math>qi=21(Qx(2k+1i)+Qx(2k+1i+1)),

这里的 <math xmlns="http://www.w3.org/1998/Math/MathML"> Q x Q_x </math>Qx 是标准正态分布 <math xmlns="http://www.w3.org/1998/Math/MathML"> N ( 0 , 1 ) N(0,1) </math>N(0,1) 的分位数函数, <math xmlns="http://www.w3.org/1998/Math/MathML"> q i q_i </math>qi 是计算得到的第 <math xmlns="http://www.w3.org/1998/Math/MathML"> i i </math>i 个量化级别,而 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k 是量化的位数,这里是4位,所以 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 k 2k </math>2k 就是8个不同的量化级别。

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> Q x ( p ) Q_x(p) </math>Qx(p) 是分位数函数,返回随机变量 <math xmlns="http://www.w3.org/1998/Math/MathML"> X X </math>X 在分布中小于等于 <math xmlns="http://www.w3.org/1998/Math/MathML"> p p </math>p 的值。
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> i 2 k + 1 \frac{i}{2k+1} </math>2k+1i 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> i + 1 2 k + 1 \frac{i+1}{2k+1} </math>2k+1i+1 分别代表了相邻量化级别的下界和上界对应的概率。

论文将具有每个量化区间中预期相等数量值的结果数据类型称为 k-bit NormalFloat(NFk)。

双量化技术

论文还提出了一种名为双量化(Double Quantization)的技术。这种方法进一步优化了模型的内存使用,通过对量化常数本身进行二次量化来实现。

在传统的量化方法中,权重被量化为低位数(如4-bit)格式,但量化过程中使用的量化常数(scale factors)通常仍以更高位数(如32-bit)存储,这会增加额外的内存占用。双重量化技术通过量化这些量化常数来减少所需的内存空间。

具体操作如下:

  1. 首次量化:模型权重被量化到低位数格式,例如将32位浮点数(FP32)量化到4位整数(Int4)。
  2. 量化常数的量化:首次量化使用的量化常数本身再次被量化。例如,可以将32位的量化常数进一步量化为8位浮点数(FP8)。

这种方法允许模型在不显著影响性能的情况下,显著减少内存占用。例如,如果原始的量化常数使用32位存储,并且每个权重块的大小为64,则这些量化常数平均会为每个参数增加0.5位的内存占用。通过双重量化,可以将这个数字减少至更少的位数,例如0.127位,从而为整个模型节省大量内存空间。

页优化器(Paged Optimizers)

除了上面两个技术,论文还提出了页优化器技术,页优化器是一种使用NVIDIA统一内存(Unified Memory)来处理内存需求峰值的方法。这种技术允许系统在GPU内存不足时,自动将数据页转移到CPU内存中,从而避免因内存不足而导致的程序崩溃。这是通过在GPU和CPU之间进行动态的内存页迁移来实现的,类似于操作系统在硬盘和RAM之间进行数据交换的方式。

它极大地提高了单GPU上微调大型语言模型的可行性和效率,同时减轻了内存成本的压力。这些技术的组合使QLORA能够在保持高性能的同时,显著降低资源消耗和运行成本。

汇总

使用上述组件,我们在量化基模型的单层线性层中定义了一个 LoRA 适配器的 QLORA:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> Y B F 16 = X B F 16 doubleDequant ( c 2 , k − b i t F P 32 , W N F 4 ) + X B F 16 L 1 B F 16 L 2 B F 16 , Y_{BF16} = X_{BF16}\text{doubleDequant}\left(c^{FP32}{2, k-bit}, W{NF4}\right) + X_{BF16}L^{BF16}{1}L^{BF16}{2}, </math>YBF16=XBF16doubleDequant(c2,k−bitFP32,WNF4)+XBF16L1BF16L2BF16,

这里:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> Y B F 16 Y_{BF16} </math>YBF16 表示输出张量。
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> X B F 16 X_{BF16} </math>XBF16 表示输入张量。
  • doubleDequant() 表示双重反量化操作,用于将量化后的权重 <math xmlns="http://www.w3.org/1998/Math/MathML"> W N F 4 W_{NF4} </math>WNF4 和相对的量化常数 <math xmlns="http://www.w3.org/1998/Math/MathML"> c 2 , k − b i t F P 32 c^{FP32}_{2, k-bit} </math>c2,k−bitFP32 反量化回浮点精度的张量。
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> L 1 B F 16 L^{BF16}_1 </math>L1BF16 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> L 2 B F 16 L^{BF16}_2 </math>L2BF16 是传统连续型的矩阵因数,用于微调过程。

doubleDequant 定义如下:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> doubleDequant ( c 2 , k − b i t F P 32 , W k − b i t ) = dequant ( dequant ( c 2 , k − b i t F P 32 ) , W 4 − b i t ) = W B F 16 , \text{doubleDequant}(c^{FP32}{2, k-bit}, W{k-bit}) = \text{dequant}(\text{dequant}(c^{FP32}{2, k-bit}), W{4-bit}) = W_{BF16}, </math>doubleDequant(c2,k−bitFP32,Wk−bit)=dequant(dequant(c2,k−bitFP32),W4−bit)=WBF16,

这里:

  • doubleDequant() 是双重反量化函数,首先使用 <math xmlns="http://www.w3.org/1998/Math/MathML"> c 2 , k − b i t F P 32 c^{FP32}{2, k-bit} </math>c2,k−bitFP32 反量化 <math xmlns="http://www.w3.org/1998/Math/MathML"> W k − b i t W{k-bit} </math>Wk−bit,然后使用 <math xmlns="http://www.w3.org/1998/Math/MathML"> c F P 32 c^{FP32} </math>cFP32 进一步反量化操作。
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> W 4 − b i t W_{4-bit} </math>W4−bit 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> W B F 16 W_{BF16} </math>WBF16 分别表示量化和反量化后的权重。

总结一下,QLORA 有一个存储数据类型(通常是 4-bit 的 NormalFloat)和一个计算数据类型(16-bit 的 BrainFloat)。我们将存储数据类型反量化到计算数据类型,以执行前向和后向传播,但只针对使用 16-bit BrainFloat 的 LoRA 参数计算权重梯度。

最后文中的实验我这里就不细说了,从实验结果上看:

  1. 内存和性能:QLORA成功将65B参数模型的微调内存需求从780GB减少到小于48GB,同时保持了与16-bit全微调相当的性能。
  2. 基准测试表现:在多个基准测试中,QLORA微调的模型与传统16-bit微调模型相比,展现了竞争力甚至更好的性能。特别是在GLUE和MMLU基准测试中,QLORA表现优异。
  3. 数据质量 vs. 数据量:发现数据质量比数据量对于模型性能的影响更大,高质量的小数据集在指导微调方面更为有效。
  4. 适配器效果:通过在每个网络层使用适配器,QLORA几乎避免了所有以往工作中观察到的准确性折中。
  5. 模型竞争性:在与其他公开模型的对比中,使用QLORA的Guanaco模型家族在Vicuna基准上达到了与ChatGPT相媲美的表现。

QLORA的实验结果表明,该技术不仅可以显著降低大型语言模型微调的内存需求,而且还能在多个NLP任务上保持或提高性能。这些实验验证了QLORA在实际应用中的有效性,展示了它在推动大型模型微调技术发展方面的潜力。

UniPELT 框架

文章的最后,我再简单提一下 UniPELT 框架。

前面我们介绍了三类主流PEFT技术:

  • Adapters
  • Soft Prompts
  • Representation-based 研究者将这些有效的微调技术,整合成了一个统一的微调框架,针对不同的下游任务,可以学习和配置不同的微调模块,这个框架就是 UniPELT.

上面是 UNIPELT 的示意图,该系统纳入现有的 PELT 方法作为子模块,并通过门控机制 G 控制。根据不同样本,可以激活不同的(或组合的)子模块。可训练的参数以蓝色显示。

UNIPELT 中, 每个 PELT 子模块在 UNIPELT 中通过一个可训练的门控函数进行控制,这个门控函数根据任务的需要调整子模块的激活程度。例如,对于Adapter子模块,门控函数基于前馈网络的直接输入来估算其重要性,并调整其输出的比重。这个框架不是简单地将所有子模块的输出求和,而是在序列化的顺序中动态整合,其中每个子模块的输出可以影响后续子模块的行为。这种设计考虑到了不同子模块间的依赖性和多层次的复合效应。

在实验结果上,UNIPELT的表现通常优于所有单独子模块的最佳组合性能,这表明结合多种PELT方法可能比单一方法更为有效。并且在低资源条件下,UNIPELT尤其表现出色,能够有效地在不同的子模块间动态选择和组合,以适应不同的任务需求。

结语

以上就是关于 PEFT 的一些主流技术的介绍,相信随着技术的发展,还会涌现出更加优秀的微调技术,可以进一步减少参数量和资源需求,同时对于灵活性、多任务学习等方向也可能会有能力的提升。随着边缘设备和移动设备对 AI 模型部署的需求增加,PEFT 技术还可能会关注模型压缩和加速推理方面的技术。大家一起努力,可不要被这些技术落下了。

相关推荐
我算是程序猿25 分钟前
用AI做电子萌宠,快速涨粉变现
人工智能·stable diffusion·aigc
萱仔学习自我记录28 分钟前
微调大语言模型——超详细步骤
人工智能·深度学习·机器学习
湘大小菜鸡38 分钟前
NLP进阶(一)
人工智能·自然语言处理
XiaoLiuLB1 小时前
最佳语音识别 Whisper-large-v3-turbo 上线,速度更快(本地安装 )
人工智能·whisper·语音识别
哪 吒1 小时前
吊打ChatGPT4o!大学生如何用上原版O1辅助论文写作(附论文教程)
人工智能·ai·自然语言处理·chatgpt·aigc
Eric.Lee20211 小时前
音频文件重采样 - python 实现
人工智能·python·深度学习·算法·audio·音频重采样
大神薯条老师1 小时前
Python从入门到高手5.1节-Python简单数据类型
爬虫·python·深度学习·机器学习·数据分析
爱喝白开水a1 小时前
关于大模型在企业生产环境中的独立部署问题
人工智能·深度学习·llm·大语言模型·ai大模型·计算机技术·本地部署大模型
代码骑士2 小时前
【一起学NLP】Chapter3-使用神经网络解决问题
python·神经网络·自然语言处理
可惜已不在2 小时前
AI 搜索引擎工具集合
人工智能·ai