目录

1.低秩适配(LoRA)概述

2.低秩适配(LoRA)原理

3.低秩适配(LoRA)的工作原理


      在大语言模型(LLM)、多模态大模型(MLLM)等预训练模型的应用落地中,监督微调(Supervised Fine-Tuning, SFT)是连接通用能力与特定任务的关键桥梁。传统的全参数微调虽然能带来显著的效果提升,但对GPU资源的高需求、训练成本的高昂以及过拟合风险的增加,使其在资源受限场景下难以普及。

1.低秩适配(LoRA)概述

      一个矩阵具有低秩性,意味着它包含较多的冗余信息,将其分解为多个低维矩阵后,不会损失过多有用信息。例如一个大小为1024×1024的矩阵可以被近似分解为一个大小为1024×2的矩阵与一个大小为2×1024的矩阵的乘积,那么这个1024×1024矩阵参数量减少至原来的0.4%左右。这种低秩特性应用广泛,例如,可以利用SVD分解实现数据压缩、特征降维、图像处理,以及模型计算加速等任务。

       LoRA(Low-Rank Adaptation)是轻量化微调的里程碑式技术,其核心在于:预训练模型与微调后模型的参数差异ΔW具有低秩性。这意味着可以用两个低秩矩阵A和B的乘积AB近似表示ΔW,从而将参数量压缩至原矩阵的0.1%~1%。

      从原理上看,LoRA通过在Transformer的注意力层或全连接层旁增加一条并行的低秩计算路径,将原始输出与低秩模块输出相加,实现对模型行为的微调。在训练阶段,仅更新A和B矩阵,预训练参数保持冻结;推理阶段可将AB与原权重合并,不增加额外计算开销。相较于全参数微调,LoRA在参数量减少99%的情况下,仍能保持接近的效果,同时支持多任务 LoRA模块的实时切换,成为当前大模型微调的主流选择。

随着LoRA的广泛应用,一系列衍生技术不断突破其性能边界:

AdaLoRA:自适应为不同权重矩阵分配不同秩,将更多参数分配给关键层,实现 “好钢用在刀刃上”。

QLoRA:将预训练模型量化为4位,大幅降低显存占用,使单卡即可微调70B级大模型。

PiSSA:通过奇异值分解初始化低秩矩阵,冻结权重次要成分,微调主要成分,加速训练收敛。

DoRA:将预训练权重分解为幅度和方向两部分,用LoRA进行方向更新,进一步提升微调效果。

2.低秩适配(LoRA)原理

      假设参数矩阵Wq​为预训练模型的参数,在微调过程中被冻结,保持不变。LoRA参数,在微调过程中自由更新。预训练参数矩阵Wq​​分别并联了LoRA模块,并对这些模块进行微调。实践中可以选择性地仅对部分参数矩阵并联LoRA模块。

利用LoRA 模块对预训练参数矩阵Wq​进行微调的原理,LoRA模块通过增加一条并行的轻量级计算路径,与原模型无缝衔接。然后通过如下的公式将LoRA模块的计算结果Q2​与原始模型的结果Q1​相加,得到最终结果Q。随后,将Q输入后续环节进行常规计算,上下游模块对LoRA的引入与否完全无感知。

LoRA模块由两个低维矩阵组成,其核心思想是通过降维和升维操作,利用低秩特性实现参数微调。在矩阵的两个维度中,较小的维度被称为LoRA的秩(rank, r)。

其中:

A:LoRA模块的降维参数矩阵,形状为[模型隐层大小,r]

B:LoRA模块的升维参数矩阵,形状为[r,模型隐层大小]

A和 B的初始化方式:矩阵A使用随机数初始化,如kaiming初始化;而矩阵B使用全0初始化,或使用极小的随机数初始化,目的是确保在训练初期,插入的LoRA模块不会对模型整体输出造成过大的扰动。

3.低秩适配(LoRA)的工作原理

LoRA工作原理可以通过如下的流程图来表示:

如上图所示:

1. 原始路径:输入→W→Wx

保持预训练知识不变,权重W被冻结,不参与训练。

2. LoRA路径:输入→A→B→BAx

A矩阵:负责降维,从维度d降到秩r;

B矩阵:负责升维,从秩r升回维度d;

只有A和B参与训练,参数量极少。

3. 融合输出:Wx+BAx

结合原始能力和新学知识;

实现高效的参数更新;

其中:                      

W: 原始大模型权重 (d × k) ;

A: LoRA降维矩阵 (d × r), r << d;

B: LoRA升维矩阵 (r × k) ;

ΔW = BA: 权重更新量

       在推理阶段,可以预先将LoRA权重与原始模型权重合并,PEFT库中已经提供了成熟的权重合并接口。推理时只需加载合并后的模型权重 ,从而使推理的计算量与原始模型保持一致。如果要同时处理多种任务,那么可以将每个任务对应的LoRA模块权重、预训练权重全部加载到GPU,根据请求的任务类型,在GPU上实时切换并启用相应的LoRA模块,执行对应任务的推理。

LoRA的实现程程序如下:

import math
import torch
import torch.nn as nn
import torch.nn.functional as F


class LoRA(nn.Linear):
    def __init__(
        self,
        in_features: int,
        out_features: int,
        lora_rank: int,
        lora_alpha: int = 1,
        lora_dropout: float = 0.0,
        **kwargs
    ):
        super().__init__(in_features, out_features, **kwargs)
        
        self.lora_rank = lora_rank
        self.lora_alpha = lora_alpha
        self.lora_dropout = nn.Dropout(lora_dropout)
        self.scaling = float(lora_alpha) / float(lora_rank)
        
        # 创建可训练的 LoRA A、B 矩阵
        self.lora_A = nn.Parameter(torch.zeros((in_features, lora_rank)))
        self.lora_B = nn.Parameter(torch.zeros((lora_rank, out_features)))
        
        # 初始化:A 使用 kaiming 均匀分布,B 使用 0 初始化
        nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
        nn.init.zeros_(self.lora_B)
        
        # 冻结预训练的权重矩阵
        self.weight.requires_grad = False
        self.if_weights_merged = False  # 忽略权重合并相关代码

    def forward(self, x: torch.Tensor):
        if not self.if_weights_merged:
            # 原始线性层输出
            result = F.linear(x, self.weight, bias=self.bias)
            # LoRA 分支输出
            lora_output = self.lora_dropout(x) @ self.lora_A
            lora_output = lora_output @ self.lora_B
            # 缩放并合并
            result += lora_output * self.scaling
            return result
        else:
            # 权重已合并,直接使用合并后的权重
            return F.linear(x, self.weight, bias=self.bias)


# ------------------------------
# 使用示例
# ------------------------------
if __name__ == "__main__":
    # 假设输入维度为 1024,输出维度为 1024,LoRA 秩为 8
    lora_layer = LoRA(
        in_features=1024,
        out_features=1024,
        lora_rank=8,
        lora_alpha=16,
        lora_dropout=0.1
    )
    
    # 随机输入
    x = torch.randn(2, 1024)  # batch_size=2, hidden_dim=1024
    out = lora_layer(x)
    print("输出形状:", out.shape)  # torch.Size([2, 1024])

通过上述程序,假设原始权重矩阵W是一个10000x10000的庞然大物,有一亿个参数。

全参数微调: 需要训练这一亿个参数。

LoRA微调: 设定一个很小的秩r=8。

矩阵A的大小是10000x8 (80000参数)。

矩阵B的大小是8x1000 (80000参数)。

总共只需要训练160000个参数,仅仅是全参数微调的1.6%。

通过训练这极少量的参数,LoRA就能捕捉到模型适应新任务所需的核心变化,实现高效微调。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐