背景
在自然语言处理领域,预训练 - 微调(pretrain-finetune)范式非常流行。
通过指令微调,大语言模型能够更好地学习遵循和执行人类指令。但是,由于大语言模型的参数量巨大,进行全参数微调时,预训练模型通常需要大量的计算资源和时间,而在具体任务上微调模型时,仍然需要调整大量的参数,这使得微调过程非常耗费资源。
LoRA 的目标就是通过低秩适应来减少微调过程中的参数量,从而降低计算资源的需求。
什么是 LoRA?
LoRA,全称 Low-Rank Adaptation,是一种高效的模型适应技术,主要用于对大型预训练模型进行微调。
核心思想
LoRA 的核心思想是利用低秩矩阵分解技术,将大型预训练模型的参数矩阵分解为两个低秩矩阵的乘积,从而在微调时只需要调整这两个低秩矩阵。
具体来说,LoRA 假设原始模型的权重矩阵可以表示为两个低秩矩阵的乘积,即:
W = W0 + ΔW
其中,W0 是预训练模型的原始权重矩阵,ΔW 是通过低秩分解得到的两个矩阵的乘积。
低秩矩阵分解
在 LoRA 技术中,秩表示用于分解大矩阵的两个低秩矩阵的维度。
具体来说,假设我们有一个权重矩阵 W,通过低秩分解,我们将其表示为两个矩阵 A 和 B 的乘积,即:
ΔW = A × B
其中,A 的维度是 (m, r),B 的维度是 (r, n),这里 r 就是秩(Rank)。
选择较小的 r 可以显著减少参数量,从而降低计算和存储成本。其中,A 和 B 的秩要比 W0 小得多,这样可以显著减少需要调整的参数数量。具体步骤如下:
- 预训练模型权重初始化:使用预训练模型的权重矩阵 W0 初始化。
- 低秩矩阵初始化:初始化低秩矩阵 A 和 B。
- 微调过程:在微调过程中,只调整低秩矩阵 A 和 B 的参数,而不改变预训练模型的原始权重矩阵 W0。
优势
- 降低计算资源需求:通过调整低秩矩阵来适应模型,只需微调较少的参数,大大降低了计算成本和内存需求。
- 提高适应效率:低秩矩阵分解可以在不显著影响模型性能的情况下,提高微调的效率和速度。
- 适应不同任务:LoRA 可以轻松适应不同的下游任务,只需调整少量的参数即可实现高效的迁移学习。
示例代码
以下是一个简单的示例代码,展示了如何使用 LoRA 技术对一个预训练的 Transformer 模型进行微调:
import torch
import torch.nn as nn
from transformers import GPT2Model, GPT2Tokenizer
class LoRAAdapter(nn.Module):
def __init__(self, original_weight, rank=4):
super(LoRAAdapter, self).__init__()
self.rank = rank
self.A = nn.Parameter(torch.randn(original_weight.size(0), rank))
self.B = nn.Parameter(torch.randn(rank, original_weight.size()))
():
W0 + torch.matmul(.A, .B)
model_name_or_path =
tokenizer = GPT2Tokenizer.from_pretrained(model_name_or_path)
model = GPT2Model.from_pretrained(model_name_or_path)
name, param model.named_parameters():
()
original_weight =
name, param model.named_parameters():
name:
original_weight = param
original_weight :
ValueError()
lora_adapter = LoRAAdapter(original_weight)
optimizer = torch.optim.Adam(lora_adapter.parameters(), lr=)
tokenizer.pad_token = tokenizer.eos_token
():
texts = [, , ]
encodings = tokenizer(texts, return_tensors=, padding=, truncation=)
dataset = torch.utils.data.TensorDataset(encodings[])
torch.utils.data.DataLoader(dataset, batch_size=)
dataloader = get_dataloader()
():
model.train()
epoch (epochs):
batch dataloader:
inputs = batch[]
outputs = model(input_ids=inputs).last_hidden_state
loss = outputs.mean()
optimizer.zero_grad()
loss.backward()
optimizer.step()
torch.no_grad():
updated_weight = lora_adapter(original_weight)
layer model.h:
layer.attn.c_attn.weight.copy_(updated_weight)
()
train(model, lora_adapter, dataloader, optimizer)


