参数高效微调(Parameter Efficient Fine Tuning, PEFT)是在微调大模型时仅更新少量参数的技术,通过冻结大部分原始权重,仅训练添加的适配器层、低秩矩阵或特定参数子集,大幅降低计算和存储成本,避免灾难性遗忘,实现高效适配新任务,典型方法包括LoRA、Adapter、Prefix Tuning等。

一、模型参数是什么?

模型参数(Model Parameters)是机器学习/深度学习模型中通过训练数据自动学习得到的内部变量,它们决定了模型如何将输入数据转换为输出预测。参数是模型"知识"的载体,包含了从数据中学到的模式和规律。

1. 不同模型中的参数体现

1.1. 线性模型
在线性模型中,参数通常表现为权重(Weights)偏置(Biases)

y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b
  • 参数:权重 w₁, w₂, ..., wₙ 和偏置 b
  • 数量:特征数 + 1

1.2. 神经网络
在神经网络中,参数就是神经元之间连接的强度(权重)。

  • 权重矩阵
    • 输入层到隐藏层:W₁ ∈ ℝ^(n×h)
    • 隐藏层到输出层:W₂ ∈ ℝ^(h×m)
  • 偏置向量:每层的偏置项 b₁, b₂
  • 激活函数参数:某些激活函数的可学习参数。
  • 示例:一个简单的全连接网络,参数数量如下:
    输入层(784) → 隐藏层(256) → 输出层(10)
    参数数量 = (784×256 + 256) + (256×10 + 10) = 203,530
    

1.3. Transformer模型
在Transformer模型中,参数是指模型中所有通过训练学习得到的可调整权重和偏置。主要包含以下类型参数:

1.3.1. Embedding层参数

Token Embeddings

  • 作用:将输入token(词或子词)映射为稠密向量
  • 参数数量vocab_size × hidden_size
  • 示例:BERT-base中,词汇表大小30,522,隐藏层大小768,参数数量 = 30,522 × 768 ≈ 23.4M

Position Embeddings

  • 作用:编码token在序列中的位置信息
  • 参数数量max_position_embeddings × hidden_size
  • 示例:最大序列长度512,隐藏层大小768,参数数量 = 512 × 768 = 393,216

1.3.2. Self-Attention机制参数

Q, K, V投影矩阵

  • 作用:将输入投影到Query、Key、Value空间
  • 数学表示
    Q = XW_q, K = XW_k, V = XW_v
    
  • 参数数量3 × hidden_size × hidden_size
  • 示例:隐藏层大小768,参数数量 = 3 × 768 × 768 ≈ 1.77M

Attention输出投影矩阵

  • 作用:将多头注意力输出投影回隐藏层维度
  • 参数数量hidden_size × hidden_size
  • 示例:768 × 768 = 589,824

1.3.3. 前馈神经网络(FFN)参数

第一层(扩展层)

  • 作用:将隐藏层维度扩展到更高维度(通常4倍)
  • 参数数量hidden_size × intermediate_size
  • 示例:768 × 3072 = 2,359,296

第二层(投影层)

  • 作用:将扩展后的维度投影回原始隐藏层维度
  • 参数数量intermediate_size × hidden_size
  • 示例:3072 × 768 = 2,359,296

1.3.4. Layer Normalization参数

每个LayerNorm包含:

  • Gamma (γ):缩放参数
  • Beta (β):偏移参数
  • 参数数量2 × hidden_size(每层)
  • 示例:每层2 × 768 = 1,536个参数

1.3.5. 每个Transformer层的参数分布

以标准Transformer层为例(隐藏层大小=768,中间层大小=3072):

组件 参数数量 占比
Self-Attention (Q,K,V) 1,769,472 ~20%
Attention输出投影 589,824 ~7%
FFN第一层 2,359,296 ~27%
FFN第二层 2,359,296 ~27%
2个LayerNorm 3,072 <1%
每层总计 7,100,960 100%

2. 参数的核心特征

  • 内部变量:它们存在于模型内部,用户通常看不到具体的数值,只能看到它们的集合(模型文件)。
  • 通过训练获得:参数不是人工设定的,而是通过算法(如梯度下降、反向传播)从数据中“优化”出来的。
  • 决定模型性能:参数调整得好,模型就聪明;调整得不好,模型就笨(预测不准)。
  • 数量代表能力:通常来说,参数越多,模型越复杂,能处理的任务越难(但也更耗电脑资源,更容易“死记硬背”即过拟合)。

3. 参数和超参数

参数(Parameters)和超参数(Hyper parameters)有本质上的区别,下面是从不同维度来比对他们之间的区别:

维度 参数(Parameters) 超参数(Hyperparameters)
定义 模型在训练过程中通过优化算法自动学习的内部变量,直接决定预测能力并承载模型知识 在训练前人工设定的配置变量,用于控制模型结构、训练过程和学习行为,需通过手动调整或搜索算法优化
学习方式 通过训练数据自动学习 人工预先设定
调整时机 训练过程中动态更新 训练前设定,训练中通常固定
数量规模 通常数量巨大(百万到千亿级) 数量相对较少(几个到几十个)
存储位置 模型文件的核心内容 配置文件或代码中的设定
优化方法 梯度下降等优化算法 网格搜索、随机搜索、贝叶斯优化
可迁移性 通常绑定特定任务 可在不同任务间复用经验

常见的超参数

# 训练超参数
learning_rate = 1e-5        # 学习率
batch_size = 32             # 批大小
num_epochs = 10             # 训练轮数

# 模型结构超参数
num_layers = 12             # Transformer层数
hidden_size = 768           # 隐藏层维度
num_heads = 12              # 注意力头数
dropout_rate = 0.1          # Dropout比率

# 优化器超参数
weight_decay = 0.01         # 权重衰减
beta1 = 0.9, beta2 = 0.999  # Adam优化器参数

二、为什么需要进行参数微调?

大语言模型从海量的预训练数据中掌握了丰富的世界知识,但是预训练数据涉及较少的垂直领域,大语言模型需要对这些领域及相应的下游任务进行适配。上下文学习和指令微调是进行下游任务适配的有效途径,但它们在效果或效率上存在缺陷。为弥补这些不足,参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)技术应运而生。

通俗的说,大模型预训练的知识储备大,看似什么都会,但在一些专业领域的回答,给人的感觉是什么都不专业,这时候为了节约计算成本开销,让大模型能够精通某领域,就需要使用参数微调技术,让大模型表现出行业专精。

参数高效微调有什么优势?

参数高效微调(Parameter-Effcient Fine-Tuning,PEFT)旨在避免微调全部参数,减少在微调过程中需要更新的参数数量和计算开销,从而提高微调大语言模型的效率。参数高效微调有以下优势:

  • 计算效率高:PEFT技术减少了需要更新的参数数量,从而降低了训练时的计算资源消耗。
  • 存储效率高:通过减少需要微调的参数数量,PEFT显著降低了微调模型的存储空间,特别适用于内存受限的设备。
  • 适应性强:PEFT能够快速适应不同任务,而无需重新训练整个模型,使得模型在面对变化环境时具有更高的灵活性。

三、传统下游任务适配的方法

通常,大语言模型通过在大规模数据集上进行预训练,能够积累丰富的世界知识,并获得处理多任务的能力。但由于开源大语言模型训练数据有限,因此仍存在知识边界,导致其在垂直领域(如医学、金融、法学等)上的知识不足,进而影响在垂直领域的性能表现。因此,需要进行下游任务适配才能进一步提高其在垂直和细分领域上的性能。主流的下游任务适配方法有两种:上下文学习(In-contextlearning)、指令微调(InstructionTuning)

1. 上下文学习(In-contextlearning)

在之前的提示工程中,我们介绍过上下文提示的核心思想是将不同类型的任务都转化为生成任务,通过设计Prompt来驱动大语言模型完成这些下游任务。小样本上下文学习将数据集中的样本-标签对转化为自然语言指令和样例,并拼接上需要测试的样本一同输入给大语言模型,将模型输出作为最终预测结果。

上下文学习完全不需要对模型进行参数更新,因此能快速将单个模型应用到多种不同的任务上。尽管在实际应用中,上下文学习能有效利用大语言模型的能力,但它缺点也很明显:

  • 上下文学习的性能和微调依旧存在差距,并且Prompt设计需要花费大量的人力成本,不同Prompt的最终任务性能有较大差异;
  • 上下文学习虽然完全不需要训练,但在推理阶段的代价会随Prompt中样例的增多快速增加。

因此,微调大语言模型在许多场景和任务中依旧是必要的,尤其是在垂直领域。

2. 指令微调(InstructionTuning)

指令微调(InstructionTuning)是另一种进行下游任务适配的方法。指令微调旨在对模型进行任务指令的学习,使其能更好地理解和执行各种自然语言处理任务的指令。指令微调需首先构建指令数据集,然后在该数据集上进行监督微调。

  • 指令数据构建: 指令数据通常包含指令(任务描述)、示例(可选)、问题和回答。构造这种指令数据一般有两种方式:

    • 数据集成。通过使用模板将带标签的自然语言数据集,转换为指令格式的<输入,输出>对。如Flan和P3数据集基于数据集成策略构建。
    • 大语言模型生成。通过人工收集或者手写少量指令数据,再使用GPT-3.5-Turbo或GPT4 等闭源大语言模型进行指令扩展。如InstructWild和Self-Instruct数据集采用这种方法生成。

    在这里插入图片描述

  • 监督微调:通过上述方法构建完数据集后,可以用完全监督的方式对预训练模型进行微调,在给定指令和输入的情况下,通过顺序预测输出中的每个token来训练模型。经过微调的大语言模型能够显著提升指令遵循(Instruction-following)能力,这有助于增强其推理水平,泛化到新任务和新领域。

尽管指令微调能有效帮助大语言模型理解新领域的数据知识,提高大语言模型在下游任务上的性能。然而,监督微调需要较大的计算资源,以LLaMA2-7B模型为例,直接进行全量微调需要近60GB内存,普通的消费级GPU(如RTX4090 24GB)无法完成微调。因此,为了在资源受限的环境中有效微调大语言模型,研究参数高效的微调技术显得尤为重要。


四、参数高效微调有哪些方法?

参数高效微调主流的PEFT方法可以分为三类:

1. 参数附加方法(Additional Parameters Methods)

2. 参数选择方法(Parameter Selection Methods)

3. 低秩适配方法(Low-Rank Adaptation Methods)

在这里插入图片描述


五、参数附加方法(Additional Parameters Methods)

参数附加方法(Additional Parameter Methods)通过增加并训练新的附加参数或模块对大语言模型进行微调。参数附加方法按照附加位置可以分为三类:加在输入、加在模型、加在输出

1. 加在输入

加在输入的方法将额外参数附加到模型的输入嵌入(Embedding)中,其中最经典的方法是Prompt-tuning。

Prompt-tuning 在模型的输入中引入可微分的连续张量,通常也被称为软提示(Soft prompt)。软提示作为输入的一部分,与实际的文本数据一起被送入模型。在微调过程中,仅软提示的参数会被更新,其他参数保持不变,因此能达到参数高效微调的目的。

1.1. Prompt-tuning 实现原理?
给定一个包含 n n n 个 token 的输入文本序列 { w 1 , w 2 , … , w n } \{w_1, w_2, \ldots, w_n\} {w1,w2,,wn},首先通过嵌入层将其转化为输入嵌入矩阵 X ∈ R n × d X \in \mathbb{R}^{n \times d} XRn×d,其中 d d d 是嵌入空间的维度。新加入的软提示参数被表示为软提示嵌入矩阵 P ∈ R m × d P \in \mathbb{R}^{m \times d} PRm×d,其中 m m m 是软提示长度。然后,将软提示嵌入拼接上输入嵌入矩阵,形成一个新矩阵 [ P ; X ] ∈ R ( m + n ) × d [P;X] \in \mathbb{R}^{(m+n) \times d} [P;X]R(m+n)×d,最后输入 Transformer 模型。通过反向传播最大化输出概率似然进行模型训练。训练过程仅软提示参数 P P P 被更新。

举个例子:你想让模型做“情感分析”。

  • 传统微调:重写模型的脑回路,让它学会什么是正面/负面。
  • Prompt-tuning:在输入前加一串软提示 [V1, V2, V3, V4] + “这部电影很好看”。模型看到这串软提示,就知道后面要做情感分析,于是输出“正面”。这串软提示就是训练出来的 Prompt。

在这里插入图片描述

在实际使用中,软提示的长度范围是1到200,并且通常在20以上就能有一定的性能保证。此外,软提示的初始化对最终的性能也会有影响,使用词表中的token或者在分类任务中使用类名进行初始化会优于随机初始化。值得一提的是,Prompt-tuning原本被提出的动机不是为了实现参数高效微调,而是自动学习提示词。

1.2. Prompt-tuning vs 提示工程 (Prompt Engineering) ?

  • Prompt Engineering(提示工程)人工写提示词。比如你自己尝试写“请总结这段话,要求…”,不行就改词,直到模型输出满意。这是靠人脑试错
  • Prompt-tuning(提示调优)机器自动写提示词。你把一堆数据喂给模型,算法自动计算出那串最优的“软提示向量”。这是靠梯度下降算法训练。因为它是连续的向量,而不是人类语言,所以也叫“Soft Prompt”,区别于人类能读懂的“Hard Prompt”。

1.3. Prompt-tuning 核心优势

  • 内存效率高:Prompt-tuning显著降低了内存需求。例如,T5-XXL模型对于特定任务的模型需要11B参数,但经过Prompt-tuning的模型只需要20480个参数(假设软提示长度为5)。
  • 多任务能力:因为模型主体没变,你可以给同一个基础模型挂载100个不同的 Prompt-tuning 模块(比如一个模块写代码,一个模块写诗,一个模块做翻译)。切换任务时,只需要切换前面的“小纸条”,不需要切换整个大模型。
  • 缩放特性:随着模型参数量的增加,Prompt-tuning的性能会逐渐增强,并且在10B参数量下的性能接近(多任务)全参数微调的性能。
  • 避免灾难性遗忘:因为大模型的原始知识没被破坏,它不会因为学了医学知识就忘了怎么写小说。

2. 加在模型

加在模型的方法将额外的参数或模型添加到预训练模型的隐藏层中,其中经典的方法有:Prefx-tuning、Adapter-tuning 和 AdapterFusion

2.1. Prefx-tuning

Prefx-tuning和上一节介绍的Prompt-tuning十分类似,区别在于Prompt-tuning 仅将软提示添加到输入嵌入中,而Prefx-tuning将一系列连续的可训练前缀(Pre-fxes,即Soft-prompt)插入到输入嵌入以及Transformer注意力模块中。

如下图所示, P k {P_k} Pk P v {P_v} Pv是插入到Transformerblock中的前缀,相比Prompt-tuning,Prefx-tuning 大幅增加了可学习的参数量。
在这里插入图片描述
Prefx-tuning引入了一组可学习的向量 P k {P_k} Pk P v {P_v} Pv,这些向量被添加到所有Transformer注意力模块中的键 K {K} K 和值 V {V} V 之前。类似于Prompt-tuning,Prefx-tuning也会面临前缀参数更新不稳定的问题,从而导致优化过程难以收敛。因此,在实际应用中,通常需要在输入Transformer模型前,先通过一个多层感知机(MLP)进行重参数化。这意味着需要训练的参数包括MLP和前缀矩阵两部分。训练完成后,MLP的参数会被丢弃,仅保留前缀参数。

Prefx-tuning 优势

  • 参数效率:只有前缀参数在微调过程中被更新,这显著减少了训练参数量。
  • 任务适应性:前缀参数可以针对不同的下游任务进行定制,微调方式灵活。
  • 保持预训练知识:由于预训练模型的原始参数保持不变,Prefx-tuning 能够保留预训练过程中学到的知识。

2.2. Adapter-tuning

Adapter-tuning 向预训练语言模型中插入新的可学习的神经网络模块,称为适配器(Adapter)。适配器模块通常采用瓶颈(Bottomneck)结构,即一个上投影层、一个非线性映射和一个下投影层组成的全连接模块。其中,下投影层将信息压缩到一个低维的表示,经过非线性映射后再通过上投影层扩展回原始维度。

与Transformer的全连接层不同,由于采用了瓶颈结构,适配器的隐藏维度通常比输入维度小。和其他PEFT方法类似,在训练时,通过固定原始模型参数,仅对适配器、层正则化(图中绿色框)以及最后的分类层参数(图中未标注)进行微调,可以大幅缩减微调参数量和计算量,从而实现参数高效微调。

在这里插入图片描述
如下图所示,Adapter-tuning在 Transformer 的每一个多头注意力层(Multi-head AttentionLayer,图中红色块)和全连接层(Feed-forwardNetworkLayer,图中蓝色块)之后添加适配器。适配器模块通常由一个下投影矩阵 W d ∈ R d × r W_d \in \mathbb{R}^{d \times r} WdRd×r 和一个上投影矩阵 W u ∈ R r × d W_u \in \mathbb{R}^{r \times d} WuRr×d 以及残差连接组成,其中 r ≪ d r \ll d rd
A ( l ) = σ ( W d ∗ H ( l − 1 ) ) W u + H ( l − 1 ) A^{(l)} = \sigma(W_d * H^{(l-1)}) W_u + H^{(l-1)} A(l)=σ(WdH(l1))Wu+H(l1)
其中, σ ( ⋅ ) \sigma(\cdot) σ() 是激活函数,如 ReLU 或 Sigmoid。 A ( l ) A^{(l)} A(l) 是适配器的输出, H ( l − 1 ) H^{(l-1)} H(l1) 是第 l − 1 l-1 l1 层的隐藏状态。

在适配器中,下投影矩阵将输入的 d d d 维特征压缩到低维 r r r,再用上投影矩阵投影回 d d d 维。因此,每一层中的总参数量为 2 d r + d + r 2dr + d + r 2dr+d+r,其中包括投影矩阵及其偏置项参数。通过设置 r ≪ d r \ll d rd,可以大幅限制每个任务所需的参数量。进一步地,适配器模块的结构还可以被设计得更为复杂,例如使用多个投影层,或使用不同的激活函数和参数初始化策略。此外,Adapter-tuning还有许多变体,例如通过调整适配器模块的位置、剪枝等策略来减少可训练参数的数量。

2.3. AdapterFusion

由于Adapter-tuning无需更新预训练模型,而是通过适配器参数来学习单个任务,每个适配器参数都保存了解决该任务所需的知识。因此,如果想要结合多个任务的知识,可以考虑将多个任务的适配器参数结合在一起。基于该思路,AdapterFusion 提出一种两阶段学习的方法,先学习多个任务,对每个任务进行知识提取;再“融合”(Fusion)来自多个任务的知识。具体的两阶段步骤如下:

  • 第一阶段:知识提取。给定N个任务,首先对每个任务分别训练适配器模块,用于学习特定任务的知识。该阶段有两种训练方式,分别如下:
    • Single-TaskAdapters(ST-A):对于N个任务,模型都分别独立进行优化,各个任务之间互不干扰,互不影响。
    • Multi-TaskAdapters(MT-A):通过多任务学习对N个任务进行联合优化。
  • 第二阶段:知识组合。单个任务的适配器模块训练完成后,AdapterFusion将不同适配器模块进行融合(Fusion),以实现知识组合。
    • 该阶段引入一个新的融合模块,该模块旨在搜索多个任务适配器模块的最优组合,实现任务泛化。在该阶段,语言模型的参数以及N个适配器的参数被固定,仅微调AdapterFusion模块的参数,并优化以下函数的损失:
      在这里插入图片描述
      其中, D n {D_n} Dn是第 n 个任务的训练数据, L n {L_n} Ln是第 n 个任务的损失函数,Θ表示预训练模型参数, Φ i {Φ_i} Φi表示第 i 个任务的适配器模块参数,Ψ是融合模块参数。

在这里插入图片描述
如上图所示,每层的AdapterFusion模块包括可学习的Key(键)、Value (值)和Query(查询)等对应的投影矩阵。全连接层的输出当作Query,适配器的输出当作Key和Value,计算注意力得到联合多个适配器的输出结果。此外,在不同的适配器模块间参数共享融合模块,可以进一步减少参数数量。

3. 加在输出

在微调大语言模型时,通常会面临以下问题:首先,大语言模型的参数数量可能会非常庞大,例如LLaMA系列最大的模型拥有70B参数,即使采用了PEFT技术,也难以在普通消费级GPU上完成下游任务适应;其次,用户可能无法直接访问大语言模型的权重(黑盒模型),这为微调设置了障碍。

为了应对这些问题,代理微调(Proxy-tuning)提供了一种轻量级的解码时(Decoding-time)算法,允许我们在不直接修改大语言模型权重的前提下,通过仅访问模型输出词汇表预测分布,来实现对大语言模型的进一步定制化调整。
在这里插入图片描述
如上图所示,给定待微调的代理模型 M \mathcal{M} M 以及较小的反专家模型(Anti-expert model) M − \mathcal{M}^- M,这两个模型需要相同的词表。我们对 M − \mathcal{M}^- M 进行微调,得到微调后的专家模型(Expert model) M + \mathcal{M}^+ M+。在每一个自回归生成的时间步中,代理微调首先计算专家模型 M + \mathcal{M}^+ M+ 和反专家模型 M − \mathcal{M}^- M 之间的 logits 分布差异,然后将其加到代理模型 M \mathcal{M} M 下一个词预测的 logits 分布中。

具体来说,在代理微调的计算阶段,针对每一时间步 t t t 的输入序列 x < t x_{<t} x<t,从代理模型 M \mathcal{M} M、专家模型 M + \mathcal{M}^+ M+ 和反专家模型 M − \mathcal{M}^- M中获取相应的输出分数 s M , s M + , s M − s_{\mathcal{M}}, s_{\mathcal{M}^+}, s_{\mathcal{M}^-} sM,sM+,sM。通过下式调整目标模型的输出分数 s ~ \tilde{s} s~
s ~ = s M + s M + − s M − \tilde{s} = s_{\mathcal{M}} + s_{\mathcal{M}^+} - s_{\mathcal{M}^-} s~=sM+sM+sM
然后,使用 s o f t m a x ( ⋅ ) \mathrm{softmax}(\cdot) softmax() 对其进行归一化,得到输出概率分布,
p M ~ ( X t ∣ x < t ) = s o f t m a x ( s ~ ) p_{\tilde{\mathcal{M}}}(X_t \mid x_{<t}) = \mathrm{softmax}(\tilde{s}) pM~(Xtx<t)=softmax(s~)
最后,在该分布中采样得到下一个词的预测结果。

在实际使用中,通常专家模型是较小的模型(例如,LLaMA-7B),而代理模型则是更大的模型(例如,LLaMA-13B 或 LLaMA-70B)。通过代理微调,我们将较小模型中学到的知识,以一种解码时约束的方式迁移到比其大得多的模型中,大幅节省了计算成本。同时,由于仅需要获取模型的输出分布,而不需要原始的模型权重,因此该方法对于黑盒模型同样适用。


六、参数选择方法(Parameter Selection Methods)

参数选择方法是参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)的核心策略之一。其核心思想是:在微调过程中,仅更新预训练模型中的一小部分关键参数子集,而将绝大部分参数冻结

这种方法的主要优势在于:

  • 无需引入新参数:与参数附加方法(如LoRA、Adapter)不同,它不增加模型结构,因此在推理阶段不会产生额外的计算开销,保持了推理速度。
  • 显著降低资源消耗:减少了需要更新的参数数量,从而降低了训练所需的显存、计算量和存储空间。
  • 适用于资源受限场景:使得在消费级硬件上微调大模型成为可能。

根据实现方式的不同,参数选择方法通常可分为两大类:基于规则的方法基于学习的方法

1. 基于规则的方法 (Rule-based Methods)

基于规则的方法依赖于人工经验、启发式规则或简单的统计特性来预先决定哪些参数需要被更新。这种方法简单、直接且计算效率极高。

1.1. 核心思想
在训练开始前,通过固定的规则冻结大部分模型参数,只解冻一小部分参数进行微调。

1.2. 代表性方法:

  • BitFit (Bias-Term Fine-Tuning)

    • 方法:仅优化神经网络各层中的偏置项(Biases)和任务相关的分类头(Classifier Head)
    • 原理:研究发现,尽管偏置项在模型总参数中占比极小(约 0.08%–0.09%),但它们捕捉了任务的关键偏差信息。
    • 效果:在GLUE Benchmark等任务上,其性能可以与全量微调相媲美,甚至在某些任务上表现更优。同时,由于更新的参数少,可以允许使用更大的学习率,使训练过程更稳定。
    • 局限:目前主要在BERT、RoBERTa等中小规模模型上得到验证,在更大规模的模型(如LLaMA-70B)上的效果仍有待研究。
  • 层选择方法 (Layer-wise Tuning)

    • 方法:仅微调模型的最后几层。例如,对于BERT或RoBERTa模型,通常只微调最后四分之一的Transformer层。
    • 效果:这种简单的策略可以达到全量微调约90%的性能。其背后的直觉是,模型的底层捕捉通用语言特征,而顶层更偏向于特定任务的抽象表示。
    • 实现:通过代码可以轻松实现,例如冻结所有层的参数,然后只解冻最后几层的参数。
    # 示例:使用Hugging Face Transformers冻结除最后1/4层外的所有参数
    from transformers import BertForSequenceClassification
    
    model = BertForSequenceClassification.from_pretrained("bert-base-uncased")
    num_layers = len(model.bert.encoder.layer)
    # 解冻最后1/4层
    layers_to_tune = range(num_layers - num_layers // 4, num_layers)
    
    # 1. 冻结所有参数
    for param in model.parameters():
        param.requires_grad = False
    
    # 2. 只解冻最后1/4层和分类头的参数
    for name, param in model.named_parameters():
        if any(f"encoder.layer.{i}." in name for i in layers_to_tune) or "classifier" in name:
            param.requires_grad = True
    
    # 3. 优化器将只更新 requires_grad=True 的参数
    optimizer = AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=5e-4)
    
  • PaFi (Parameter Selection by Absolute Value)

    • 方法:选择模型权重中绝对值最小的参数作为可训练部分。
    • 原理:假设绝对值较小的权重对模型当前的知识贡献较小,因此在微调时更容易被调整以适应新任务。
    • 效果:进一步减少了需要训练的参数数量,降低了计算开销。

1.3. 优缺点

  • 优点
    • 简单高效:实现简单,无需额外计算,训练速度快。
    • 参数效率极高:更新的参数量极少(如BitFit)。
  • 缺点
    • 依赖经验:规则的制定依赖专家经验,不一定是最优选择。
    • 泛化性有限:为特定模型或任务设计的规则可能不适用于其他场景,在大模型上的效果尚不明确。

2. 基于学习的方法 (Learning-based Methods)

基于学习的方法在训练过程中自动、动态地选择需要更新的参数子集,而不是依赖预先设定的固定规则。这种方法通常能找到更优的参数组合,但计算成本也更高。

2.1. 核心思想

引入一个可学习的 掩码矩阵(Mask Matrix) 或通过某种重要性度量标准,在训练的每一步或某个阶段决定哪些参数参与梯度更新。

2.2. 代表性方法:Child-Tuning

Child-Tuning是此类方法中最具代表性的一种。它通过引入一个与模型权重维度相同的0-1掩码矩阵 M t M_t Mt,仅对被选中的子网络进行梯度更新,而屏蔽其他参数的梯度。

梯度更新公式
W t + 1 = W t − η ⋅ ( ∂ L ( W t ) ∂ W t ⊙ M t ) W_{t+1} = W_t - \eta \cdot \left( \frac{\partial L(W_t)}{\partial W_t} \odot M_t \right) Wt+1=Wtη(WtL(Wt)Mt)
公式说明

  • W t + 1 W_{t+1} Wt+1:更新后的权重。
  • W t W_t Wt:当前权重。
  • η \eta η:学习率。
  • ∂ L ( W t ) ∂ W t \frac{\partial L(W_t)}{\partial W_t} WtL(Wt):损失函数对权重的梯度。
  • ⊙ \odot :哈达玛积(逐元素相乘)。
  • M t M_t Mt:掩码矩阵(0或1),为1的元素对应的参数会被更新,为0的则保持冻结。

Child-Tuning主要有两种变体:

  1. Child-Tuning_F (任务无关变体)

    • 方法:不依赖任何下游任务数据,在每次迭代时,从一个 伯努利分布(Bernoulli Distribution) 中随机抽样生成0-1掩码 M t M_t Mt。掩码中1的比例 p F p_F pF 是一个超参数。
    • 原理:这种随机掩码引入了噪声,可以看作是对全梯度的一种正则化,有助于防止在小数据集上过拟合,提高模型的泛化能力。
    • 优点:无需额外计算,实现简单。
  2. Child-Tuning_D (任务驱动变体)

    • 方法:利用下游任务的数据来评估每个参数的重要性,并选择最重要的参数组成子网络。
    • 原理:使用 费舍尔信息矩阵(Fisher Information Matrix, FIM) 来衡量参数对特定任务的重要性。费舍尔信息值越高的参数,被认为对任务越重要。
    • 计算步骤
      1. 计算 FIM:用上面的公式算出模型所有参数的 F ( W ) F(W) F(W)
      2. 排序:把所有参数按照 F ( W ) F(W) F(W) 的值从大到小排序。
      3. 选择 Top-K:设定一个比例 p D p_D pD(比如前 20%),只保留 F ( W ) F(W) F(W) 最大的那 20% 参数。
      4. 生成掩码 M M M
        • 被选中的参数位置, M = 1 M=1 M=1
        • 没被选中的参数位置, M = 0 M=0 M=0
      5. 微调:在后续训练中,只更新 M = 1 M=1 M=1 的参数(使用最开始的掩码更新公式)。
    • 费舍尔信息计算公式
      F ( i ) ( W ) = 1 ∣ D ∣ ∑ j = 1 ∣ D ∣ [ ( ∂ log ⁡ p ( Y j ∣ X j ; W ) ∂ W ( i ) ) 2 ] F^{(i)}(W) = \frac{1}{|D|} \sum_{j=1}^{|D|} \left[ \left( \frac{\partial \log p(Y_j|X_j; W)}{\partial W^{(i)}} \right)^2 \right] F(i)(W)=D1j=1D[(W(i)logp(YjXj;W))2]
      公式说明:
      • F ( i ) ( W ) F^{(i)}(W) F(i)(W):第 i i i 个参数(或参数块)的费舍尔信息值(即重要性分数)。该值越大,代表该参数对模型预测结果的影响越大,越重要。
      • W W W:模型当前的权重参数矩阵。在计算费舍尔信息时,通常使用预训练好的原始权重,尚未进行微调。
      • W ( i ) W^{(i)} W(i):模型中的第 i i i 个具体参数矩阵(例如某一层的权重或偏置)。
      • ∣ D ∣ |D| D训练数据集 D D D 的样本总数。用于对所有样本的计算结果取平均值,消除数据量对数值的影响。
      • j j j:样本索引,表示正在遍历数据集中的第 j j j 个样本(从 1 1 1 ∣ D ∣ |D| D)。
      • X j , Y j X_j, Y_j Xj,Yj:第 j j j 个样本的输入数据(如文本Token)和真实标签(Ground Truth)。
      • p ( Y j ∣ X j ; W ) p(Y_j|X_j; W) p(YjXj;W):在当前参数 W W W 下,模型输入 X j X_j Xj 后预测出真实标签 Y j Y_j Yj概率(通常指Softmax层输出的概率值)。
      • log ⁡ \log log自然对数函数(Log-Likelihood)。用于将概率的连乘运算转化为求和运算,使计算更稳定、更高效。
      • ∂ … ∂ W ( i ) \frac{\partial \dots}{\partial W^{(i)}} W(i)梯度运算符。计算对数概率 log ⁡ p \log p logp 对参数 W ( i ) W^{(i)} W(i) 的偏导数,衡量参数微小变化对预测结果的敏感度。
      • ( …   ) 2 (\dots)^2 ()2平方操作。作用有二:1. 消除梯度的正负号(负梯度也代表影响大);2. 放大显著信号,抑制微小噪声。
      • ∑ \sum 求和符号。将数据集中所有样本计算出的平方梯度值进行累加。
      • 1 ∣ D ∣ … \frac{1}{|D|} \dots D1平均操作。将累加和除以样本总数,得到期望意义上的平均信息量。
    • 优点
      • 精准:比随机掩码(Child-Tuning_F)更懂模型,能找到对当前任务真正关键的参数。
      • 性能好:通常能达到比随机选择更高的精度。
    • 缺点(致命伤)
      • 计算昂贵:虽然比算海森矩阵便宜,但仍需要把数据集跑一遍(前向+反向)来计算梯度。对于超大模型(如 LLaMA-70B),即使只跑一遍也是很耗时的。
      • 存储压力:需要存储和模型一样大的 FIM 矩阵(如果是逐参数的)。

2.3. 其他基于学习的方法

  • FishMask:与Child-Tuning_D类似,也基于费舍尔信息,但会在每个训练周期动态重新计算掩码。
  • LT-SFT (Lottery Ticket Subnetwork Fine-Tuning):受“彩票假设”启发,在微调初期通过观察参数变化,找出最重要的参数子集(即“中奖彩票”),然后只对这个子网络进行微调。

2.4. 优缺点

  • 优点
    • 自动化:无需人工设计规则,能自动发现对特定任务最重要的参数。
    • 性能更优:通常比基于规则的方法能取得更好的性能,尤其是在数据有限的情况下。
    • 降低过拟合风险:通过限制模型的假设空间(即只更新部分参数),可以有效降低过拟合风险。
  • 缺点
    • 计算成本高:特别是任务驱动的变体,计算参数重要性(如费舍尔信息)需要额外的计算资源和时间。
    • 实现复杂:比基于规则的方法更复杂。

七、低秩适配方法(Low-Rank Adaptation Methods)

低维固有维度假设表明:过参数化模型的固有维度是很低的;换言之,存在可以与全参数更新媲美的低维的参数更新。基于这一假设,低秩适配方法(Low-rankAdaptationMethods)通过低秩矩阵来近似原始权重权更新矩阵,并仅微调低秩矩阵,以大幅降低模型参数量。其中最经典的低秩适配方法为LoRA。

1. 低秩适配(Low-Rank Adaptation, LoRA)

LoRA 是目前大语言模型(LLM)微调领域最流行、最成功的参数高效微调(PEFT)技术之一。它由微软研究院在 2021 年提出(论文:《LoRA: Low-Rank Adaptation of Large Language Models》)。

与我们之前讨论的“参数选择方法”(如 BitFit、Child-Tuning)不同,LoRA 属于参数附加方法(Parameter-Additive Methods)。它的核心思想不是“冻结一部分参数,只更新另一部分”,而是冻结原始模型的所有参数,额外增加一小部分可训练的低秩矩阵,并将它们的输出叠加到原始模型上。
在这里插入图片描述

1.1. 为什么 LoRA 高效?

在全量微调中,我们更新所有权重矩阵 W W W。LoRA 的作者提出了一个关键假设:

虽然大模型需要很多参数来学习通用语言表示,但在针对特定下游任务(如医疗问答、代码生成)进行微调时,模型只需要在原有知识基础上进行很小的“增量调整”。

这个“增量调整”所需的信息量其实很小,不需要改变整个高维矩阵。因此,我们可以用一个极低秩(Low-Rank) 的矩阵来近似这个调整量 Δ W \Delta W ΔW

1.2. 核心公式
W ′ = W + Δ W = W + B A W' = W + \Delta W = W + B A W=W+ΔW=W+BA

其中:

  • W W W:原始预训练模型的权重矩阵(例如 d × d d \times d d×d 维),在微调过程中冻结(不更新)。
  • W ′ W' W:微调后实际使用的权重。
  • Δ W \Delta W ΔW:微调带来的权重变化量。
  • A A A B B B:LoRA 引入的两个可训练的低秩矩阵。
    • A A A 的形状是 r × d r \times d r×d(从高维映射到低维)。
    • B B B 的形状是 d × r d \times r d×r(从低维映射回高维)。
    • r r r秩(Rank),是一个远小于 d d d 的超参数(例如 d = 4096 , r = 8 d=4096, r=8 d=4096,r=8)。

参数量对比(以 d = 4096 , r = 8 d=4096, r=8 d=4096,r=8 为例)

方法 参数量 计算方式
全量微调 (Full Fine-tuning) 4096 × 4096 ≈ 16.7 M 4096 \times 4096 \approx 16.7M 4096×409616.7M 更新整个 W W W
LoRA ( 4096 × 8 ) + ( 8 × 4096 ) ≈ 65 K (4096 \times 8) + (8 \times 4096) \approx 65K (4096×8)+(8×4096)65K 只训练 A A A B B B
压缩比 约 0.4% 参数量减少了 256 倍

推导过程
原本需要学习的 Δ W \Delta W ΔW 是一个 d × d d \times d d×d 的大矩阵,有 d 2 d^2 d2 个参数。
通过分解为 B B B ( d × r d \times r d×r) 和 A A A ( r × d r \times d r×d),参数量变成了 d × r + r × d = 2 d r d \times r + r \times d = 2dr d×r+r×d=2dr
因为 r ≪ d r \ll d rd,所以 2 d r ≪ d 2 2dr \ll d^2 2drd2

1.3. LoRA 的计算流程

在训练和推理时,LoRA 的计算流程如下:

  1. 输入 x x x 同时进入两条路径:
    • 路径 1(主路):经过冻结的原始权重 W W W,输出 h 1 = W x h_1 = Wx h1=Wx
    • 路径 2(旁路/Adapter):经过可训练的低秩矩阵 A A A B B B,输出 h 2 = B ( A x ) h_2 = B(Ax) h2=B(Ax)
      • 注意:通常会有一个缩放系数 α \alpha α(alpha)来调节旁路的影响强度,即 h 2 = α r B ( A x ) h_2 = \frac{\alpha}{r} B(Ax) h2=rαB(Ax)
  2. 合并输出:将两条路径的输出相加。
    h = h 1 + h 2 = W x + B ( A x ) h = h_1 + h_2 = Wx + B(Ax) h=h1+h2=Wx+B(Ax)
  3. 后续处理 h h h 继续进入激活函数等后续层。

关键点:在推理阶段,为了不增加计算延迟,我们可以将 B A B A BA 预先计算并加到 W W W 上,即 W n e w = W + B A W_{new} = W + BA Wnew=W+BA。这样推理时就和原始模型结构完全一样,没有任何额外的计算开销。

1.4. LoRA 的实现细节

A. 应用位置
LoRA 通常应用在 Transformer 模型的 自注意力机制(Self-Attention) 的线性层中:

  • W q W_q Wq (Query)
  • W k W_k Wk (Key)
  • W v W_v Wv (Value)
  • W o W_o Wo (Output)

有些变体(如 LoRA+)也会将其应用于 MLP 层的上投影和下投影层( W u p , W d o w n W_{up}, W_{down} Wup,Wdown)。

B. 超参数

  1. Rank ( r r r):最重要的超参数。
    • r r r 越大,表达能力越强,但参数量越大。
    • 常见取值:4, 8, 16, 32, 64。对于 LLaMA-7B,通常 r = 8 r=8 r=8 16 16 16 就能达到很好的效果。
  2. Alpha ( α \alpha α):缩放因子。
    • 通常设置为 2 r 2r 2r r r r。作用是平衡主路和旁路的梯度。
    • 公式: h = W x + α r ( B A ) x h = Wx + \frac{\alpha}{r} (BA)x h=Wx+rα(BA)x
  3. Dropout:在 A A A B B B 之间通常会加一个 Dropout 层防止过拟合。
  4. Target Modules:指定对哪些层应用 LoRA(通常是所有的 Attention 层)。

1.5. LoRA优缺点

优点

  • 极高的参数效率:只需训练原始模型 0.1% - 1% 的参数。
  • 推理无延迟:训练完成后, B A BA BA 可以合并到 W W W 中,推理速度与原模型完全一致(这是它比 Adapter 方法强的地方)。
  • 存储方便:不同任务只需存储 tiny 的 LoRA 权重(几 MB),而不是整个模型(几十 GB)。可以在同一个基座模型上快速切换不同任务的 LoRA。
  • 训练稳定:由于不破坏原始权重,训练过程比全量微调更稳定,不容易出现“灾难性遗忘”。
  • 效果接近全量微调:在大多数 NLP 任务上,LoRA 的性能可以达到甚至超过全量微调的 90%-95%。
  • 任务泛化性能:在训练后可以将 LoRA 参数与模型参数分离,使其能够封装为被多个用户共享和重复使用的插件,当我们有多个任务的 LoRA 插件时,可以将这些插件组合在一起,以获得良好的跨任务泛化性能。

缺点

  • 超参数敏感 r r r α \alpha α 的选择对结果有影响,需要简单的调参。
  • 不适合从零学习:LoRA 依赖于强大的预训练基座模型。如果基座模型本身能力很弱,LoRA 很难救回来。
  • 显存占用:虽然训练参数少,但在训练时仍需加载整个模型的权重和优化器状态(如果使用 AdamW),显存占用主要来自激活值(Activations)和梯度(虽然梯度只针对 A/B,但优化器状态如果针对全量则依然大,通常配合 8-bit 优化器使用)。

2. LoRA 相关变体

虽然LoRA在一些下游任务上能够实现较好的性能,但在许多复杂的下游任务(如数学推理)上,LoRA与全量微调之间仍存在性能差距。为弥补这一差距,许多LoRA变体方法被提出,以进一步提升LoRA在下游任务中的适配性能。目前LoRA变体方法主要从以下几个角度进行改进:打破低秩瓶颈、动态秩分配、训练过程优化。

2.1. 打破低秩瓶颈

LoRA的低秩更新特性使其在参数效率上具有优势。然而,这也限制了大规模语言模型记忆新知识和适应下游任务的能力,即存在低秩瓶颈。Biderman等人的实验研究表明,全量微调的秩显著高于LoRA的秩(10-100 倍),并且增加LoRA的秩可以缩小LoRA与全量微调之间的性能差距。因此,一些方法被提出,旨在打破低秩瓶颈。

例如,ReLoRA 提出了一种合并和重置(merge-and-reinit)的方法,该方法在微调过程中周期性地将 LoRA 模块合并到大语言模型中,并在合并后重新初始化 LoRA 模块和优化器状态。具体地,合并的过程如下:
W i ← W i + α B i A i W^i \leftarrow W^i + \alpha B^i A^i WiWi+αBiAi
其中, W i W^i Wi 是原始的权重矩阵, B i B^i Bi A i A^i Ai 是低秩分解得到的矩阵, α \alpha α 是缩放因子。合并后,将重置 B i B^i Bi A i A^i Ai 的值重置,通常 B i B^i Bi 会使用特定的初始化方法(如 Kaiming 初始化)重新初始化,而 A i A^i Ai 则被设置为零。

为了防止在重置后模型性能发散,ReLoRA 还会通过幅度剪枝对优化器状态进行部分重置。合并和重置的过程允许模型在保持总参数量不变的情况下,通过多次低秩 LoRA 更新累积成高秩状态,从而使得 ReLoRA 能够训练出性能接近全秩训练的模型。

2.2. 动态秩分配

然而,LoRA的秩并不总是越高越好,冗余的LoRA秩可能会导致性能和效率的退化。并且,微调时权重的重要性可能会因Transformer模型中不同层而存在差异,因此需要为每个层分配不同的秩。

例如,AdaLoRA 通过将参数更新矩阵参数化为奇异值分解(SVD)的形式,再通过奇异值剪枝动态调整不同层中 LoRA 模块的秩。具体地,AdaLoRA 使用奇异值分解重新表示 Δ W \Delta W ΔW,即
W = W 0 + Δ W = W 0 + P Λ Q W = W_0 + \Delta W = W_0 + P \Lambda Q W=W0+ΔW=W0+PΛQ
其中, P ∈ R d × r P \in \mathbb{R}^{d \times r} PRd×r Q ∈ R r × k Q \in \mathbb{R}^{r \times k} QRr×k 是正交的, Λ \Lambda Λ 是一个对角矩阵,其中包含 { λ i } 1 ≤ i ≤ r \{\lambda_i\}_{1 \le i \le r} {λi}1ir 的奇异值。在训练过程中, W 0 W_0 W0 的参数被固定,仅更新 P P P Λ \Lambda Λ Q Q Q 的参数。根据梯度权重乘积大小的移动平均值构造奇异值的重要性得分,对不重要的奇异值进行迭代剪枝。此外,为了增强稳定训练性,AdaLoRA 引入一个额外的惩罚项确保 P P P Q Q Q 之间的正交性:
R ( P , Q ) = ∥ P T P − I ∥ F 2 + ∥ Q Q T − I ∥ F 2 R(P, Q) = \|P^T P - I\|_F^2 + \|Q Q^T - I\|_F^2 R(P,Q)=PTPIF2+QQTIF2
其中, I I I 是单位矩阵, ∥ ⋅ ∥ F \|\cdot\|_F F 代表 Frobenius 范数。

2.3. 训练过程优化

在实际微调过程中,LoRA 的收敛速度比全量微调要慢。此外,它还对超参数敏感,并且容易过拟合。这些问题影响了 LoRA 的效率并阻碍了其下游适配性能。

为了解决这些问题,一些工作尝试对 LoRA 的训练过程进行优化。代表性方法 DoRA(权重分解低秩适应)提出约束梯度更新,侧重于更新参数的方向变化。它将预训练权重 W 0 ∈ R d × k W_0 \in \mathbb{R}^{d \times k} W0Rd×k 分解为方向和大小两个组件,并仅将 LoRA 应用于方向组件以增强训练稳定性。具体地,DoRA 将 W 0 ∈ R d × k W_0 \in \mathbb{R}^{d \times k} W0Rd×k 重新表示为:
W 0 = m V ∥ V ∥ c = ∥ W 0 ∥ c W 0 ∥ W 0 ∥ c W_0 = m \frac{V}{\|V\|_c} = \|W_0\|_c \frac{W_0}{\|W_0\|_c} W0=mVcV=W0cW0cW0
其中, m ∈ R 1 × k m \in \mathbb{R}^{1 \times k} mR1×k 是大小向量, V ∈ R d × k V \in \mathbb{R}^{d \times k} VRd×k 是方向矩阵, ∥ ⋅ ∥ c \|\cdot\|_c c 是矩阵在每一列上的向量范数。随后,DoRA 仅对方向矩阵 V V V 施加 LoRA 进行参数化,定义为:
W ′ = m V + Δ V ∥ V + Δ V ∥ c = m W 0 + B A ∥ W 0 + B A ∥ c W' = m \frac{V + \Delta V}{\|V + \Delta V\|_c} = m \frac{W_0 + BA}{\|W_0 + BA\|_c} W=mV+ΔVcV+ΔV=mW0+BAcW0+BA
其中, Δ V \Delta V ΔV 是由 LoRA 学习的增量方向更新,下划线参数表示可训练参数。

3. 基于 LoRA 插件的任务泛化

在LoRA微调结束后,我们可以将参数更新模块B和A从模型上分离出来,并封装成参数插件。这些插件具有即插即用、不破坏原始模型参数和结构的优良性质。我们可以在不同任务上训练的各种LoRA模块,将这些模块插件化地方式保存、共享与使用。

此外,我们还可以将多个任务的LoRA插件组合,然后将不同任务的能力迁移到新任务上。LoRAHub 提供了一个可用的多LoRA组合的方法框架。其可将已有任务上得到的LoRA插件进行组合,从而获得解决新任务的能力。
在这里插入图片描述

如上图所示,LoRAHub 包括两个阶段:组合阶段和适应阶段。在组合阶段,LoRAHub 将已学习的 LoRA 模块通过逐元素线性加权组合为单一模块:
m ^ = ( w 1 A 1 + w 2 A 2 + ⋯ + w N A N ) ( w 1 B 1 + w 2 B 2 + ⋯ + w N B N ) \hat{m} = (w_1 A_1 + w_2 A_2 + \cdots + w_N A_N)(w_1 B_1 + w_2 B_2 + \cdots + w_N B_N) m^=(w1A1+w2A2++wNAN)(w1B1+w2B2++wNBN)
其中, w i w_i wi 是第 i i i 个 LoRA 模块的权重, m ^ \hat{m} m^ 是组合后的模块, A i = 1 N A_{i=1}^N Ai=1N B i = 1 N B_{i=1}^N Bi=1N 分别是 N N N 个 LoRA 分解矩阵。在适应阶段,给定一些新任务的示例,通过无梯度方法 Shiwa \cite{shiwa} 自适应地学习权重组合。适应和组合经过 k k k 次迭代,直至找到最优的权重组合,以完成对新任务的适应。


八、PEFT 流行的框架

目前,大模型参数高效微调(PEFT)领域已经形成了几个主流的框架和库。这些框架极大地降低了微调门槛,使得普通开发者也能在消费级硬件上训练大模型。以下是目前业界最主流、最核心的 PEFT 框架详细介绍,按生态地位和功能侧重分类。

1. Hugging Face PEFT (Transformers-PEFT)

Hugging Face 推出的 peft 库是目前使用最广泛的 PEFT 工具库。它与 transformers 库无缝集成,几乎支持所有主流的 PEFT 方法。

地位**:行业绝对标准**,生态最完善,兼容性最强。
GitHubhuggingface/peft
核心功能

  • 统一接口:无论是 LoRA、Prefix Tuning、P-Tuning 还是 Adapter,都通过统一的 get_peft_model() 接口加载。
  • 多方法支持:支持 LoRA, LoRA+, AdaLoRA, IA3, BOFT, ReLoRA 等几十种方法。
  • 量化兼容:完美支持 bitsandbytes 的 4-bit/8-bit 量化加载(QLoRA 的基础)。
  • 模型合并:提供 merge_and_unload 功能,训练完后可将 LoRA 权重合并到基座模型,推理无延迟。
  • Hub 集成:可以直接将训练好的 Adapter 推送到 Hugging Face Hub,也可以直接加载 Hub 上的 Adapter。

适用场景:几乎所有科研和工业落地场景,特别是需要快速验证不同 PEFT 方法效果时。
核心代码示例

    from peft import LoraConfig, get_peft_model, TaskType

    # 配置 LoRA
    config = LoraConfig(
        r=8, 
        lora_alpha=16, 
        target_modules=["q_proj", "v_proj"], # 指定作用于哪些层
        lora_dropout=0.05, 
        bias="none",
        task_type="CAUSAL_LM"
    )

    # 包裹模型
    model = get_peft_model(base_model, config)
    model.print_trainable_parameters() # 查看可训练参数量

2. Microsoft LoRA (Original Implementation)

这是 LoRA 论文作者原班人马开源的实现。虽然现在 Hugging Face PEFT 已经集成了 LoRA,但微软原版在某些底层优化上仍然有独特优势。

地位LoRA 的鼻祖,底层优化最强。
GitHubmicrosoft/LoRA
核心特点

  • 底层 CUDA 核函数:针对特定硬件(如 NVIDIA GPU)编写了高度优化的 CUDA kernel,训练速度在某些场景下比 Hugging Face 版本更快。
  • 多任务支持:原生支持多任务联合训练(Multitask Learning),可以在一个模型上同时挂载多个 LoRA 模块。
  • 长上下文优化:对长文本(Long Context)场景有专门的优化策略。
  • 适用场景:对训练速度有极致要求,或者需要复现论文原始实验结果的场景。

3. DeepSpeed-Chat / DeepSpeed

DeepSpeed 是微软开源的深度学习优化库,其 DeepSpeed-Chat 模块包含了针对 LLM 训练的 PEFT 优化。

地位**:大规模分布式训练的王者**。
GitHubmicrosoft/DeepSpeed
核心特点

  • ZeRO-Offload:可以将优化器状态、梯度甚至部分模型参数卸载到 CPU 内存,使得单卡显存不足时也能训练超大模型。
  • LoRA 专用优化:DeepSpeed 针对 LoRA 的计算图进行了重构,减少了显存碎片和通信开销。
  • Activation Checkpointing:通过梯度检查点技术,极大节省显存(以时间换空间)。
  • 混合精度训练:支持 BF16/FP16 混合精度,加速训练。
  • 适用场景:企业级训练、多卡/多机分布式训练、显存极度受限(如单卡 24G 想训 70B 模型)的场景。

4. BitsAndBytes

虽然它主要是一个量化库,但它是 QLoRA(Quantized LoRA) 能够实现的核心。

地位量化与显存优化的基石
GitHubTimDettmers/bitsandbytes
核心功能

  • 4-bit/8-bit 量化加载load_in_4bit=True 可以将模型以 4-bit 精度加载,显存占用减少 75% 以上。
  • NF4 数据类型:引入了 NormalFloat4 数据类型,比传统 INT4 精度更高,几乎不损失模型性能。
  • 双重量化:对量化常数也进行量化,进一步节省显存。
  • 与 PEFT 联动:Hugging Face PEFT 依赖 BitsAndBytes 来实现 QLoRA。
  • 适用场景:显存非常小(如 12G-24G)的消费级显卡用户,必须配合 PEFT 使用。

5. LLaMA-Factory

这是一个集成了上述所有底层库(Transformers, PEFT, BitsAndBytes, DeepSpeed)的上层应用框架。它提供了一个 Web 界面和命令行工具,让不懂代码的人也能微调大模型。

地位最强的“开箱即用”工具/WebUI
GitHubhiyouga/LLaMA-Factory
核心特点

  • WebUI 界面:通过浏览器点击按钮即可选择模型、数据集、微调方法(LoRA/Full/GaLore 等)并开始训练。
  • 一键启动:集成了数据预处理、模型加载、训练、导出(导出为 GGUF/AWQ/GPTQ 格式)的全流程。
  • 支持广泛:支持 Llama, Mistral, Qwen, Yi, ChatGLM 等几乎所有主流开源模型。
  • 多种微调模式:支持 SFT(监督微调)、RM(奖励模型)、PPO(强化学习)、DPO(直接偏好优化)。
  • 适用场景:个人开发者、缺乏深度学习背景的用户、需要快速产出垂直领域模型的企业。

6. OpenDelta / AdapterHub

地位模块化与插件化研究
OpenDelta:清华大学等机构推出,专注于研究不同 PEFT 方法之间的组合与插拔。它的特点是可以动态地在模型上“插拔”不同的 Adapter 模块,而不需要重新加载整个模型。
AdapterHub:一个类似 Hugging Face Hub 的仓库,专门收集和分享各种预训练好的 Adapter 模块(不仅仅是 LoRA,还包括各种早期的 Adapter)。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐