1. 核心思想
LoRA 是一種參數高效的微調方法,旨在減少微調大型語言模型 (LLMs) 所需的計算資源和存儲空間。其核心思想是:
- 凍結預訓練模型權重: 在微調過程中,保持預訓練 LLM 的原始權重不變。
- 引入低秩矩陣: 對于 LLM 中的某些層(通常是 Transformer 層的 Query/Key/Value 投影矩陣),引入一對小型的低秩矩陣 A 和 B。
- 僅訓練新增參數: 只訓練這些新增的低秩矩陣 A 和 B,而原始預訓練權重保持不變。
即只訓練新增低秩矩陣的參數,該低秩矩陣帶來的參數量遠小于整體的參數量,為何采用秩這個概念:
矩陣的秩(rank)本質上是反映了矩陣的“信息量”或“獨立性”的概念,在 LoRA 中,秩控制了分解矩陣的“復雜度”或“容量”。如果秩太低,分解后的矩陣可能無法捕捉到足夠的特征,從而影響模型的性能;如果秩太高,參數量可能無法大幅度減少。因此選擇一個合適的秩同樣重要
來源于 LoRA論文
2. 原理詳解
-
線性層的秩分解: LLM 中的線性層可以表示為矩陣乘法:
y = Wx
,其中 W 是預訓練的權重矩陣。 -
LoRA 的修改:
LoRA 在此基礎上增加了一個并行路徑:
y = Wx + BAx
A
是一個形狀為 (r, in_features
) 的矩陣,其中 r 是秩。B
是一個形狀為 (out_features, r
) 的矩陣。r
通常遠小于in_features
和out_features
,因此BA
是一個低秩矩陣。
-
訓練過程: 在微調過程中,W 被凍結,只有 A 和 B 被訓練。
-
推理過程: 可以選擇將 BA 和 W 合并,也可以分開計算。
注意:與全參數微調相比,LoRA 的性能可能略有下降,尤其是在需要模型進行較大改變的任務上
import torch
import torch.nn as nnclass LoRALinear(nn.Module):def __init__(self, linear_layer, r: int):super().__init__()self.linear = linear_layerself.r = rself.lora_A = nn.Parameter(torch.randn(r, linear_layer.in_features))self.lora_B = nn.Parameter(torch.zeros(linear_layer.out_features, r))self.scaling = linear_layer.out_features / rnn.init.kaiming_uniform_(self.lora_A, a=5**0.5)self.weight = None # For merged weightsdef forward(self, x: torch.Tensor):if self.weight is not None:return F.linear(x, self.weight, self.linear.bias)else:return self.linear(x) + (x @ self.lora_A.T @ self.lora_B.T) * self.scalingdef merge(self):if self.weight is None:self.weight = self.linear.weight + self.lora_B @ self.lora_A
在具體實現中存在一個縮放系數scaling
,縮放系數用于調節低秩分解后的參數在微調過程中的影響力。在 LoRA 中,模型的權重 W
被低秩分解為兩個矩陣 A
和 B
,并在模型的訓練過程中對這些矩陣進行微調。縮放系數用于控制這些低秩矩陣對最終模型輸出的影響力。
在使用的經驗中,縮放系數通常設置為秩的 2 倍表現性能最優