LoRA微调:解锁大模型优化的低秩矩阵魔法,高效、快速且性能卓越的模型优化新思路

什么是LoRA微调

LoRA(Low-Rank Adaptation)是一种高效的微调技术,主要用于对大规模语言模型进行优化,使其更好地适应特定任务,直译为低秩矩阵微调。它最初应用于NLP领域,LoRA通过仅训练低秩矩阵,然后将这些参数注入到原始模型中,从而实现对模型的微调。这种方法不仅减少了计算需求,而且使得训练资源比直接训练原始模型要小得多,因此非常适合在资源有限的环境中使用。

总的来说,LoRA是一种高效的微调技术,与全量微调相比,它消耗的计算资源更少,并且训练速度更快,但效果很好,因此适合在资源有限的环境中使用。

动机

在传统的全参数微调中,预训练模型的所有参数都会被更新,这会导致以下问题:
计算成本高:更新大量参数需要大量的计算资源和时间。
内存占用大:需要存储大量的梯度和优化器状态。
容易过拟合:在小数据集上容易导致模型过拟合。
LoRA通过只更新少量参数来解决这些问题。

1.什么是矩阵的秩

矩阵的秩(Rank of a Matrix)线性代数中的一个基本概念,它描述了矩阵中线性独立的行向量或列向量的最大数目。
矩阵的秩是为了量化矩阵中的线性独立性。我们可以将一个矩阵分解为一些线性独立的向量;这种矩阵的形式被称为“行阶梯形式”。

因此,矩阵可以包含一定程度的 “重复信息” ,即线性相关性。如果你有一个大矩阵,具有显著的线性相关性(因此秩较低),可以将该矩阵表示为两个相对较小的矩阵的乘积。这种分解的思想使得LoRA占用了如此小的内存空间。

1.1 低秩矩阵

如果一个矩阵的秩较低,说明其行向量或列向量之间存在较强的线性相关性。这意味着矩阵中包含的独立信息较少,可以通过更少的参数来表示。例如,LoRA(Low-Rank Adaptation)技术正是利用低秩矩阵来减少模型参数,从而降低计算成本和内存占用

简单来说,一个矩阵秩代表者矩阵的信息量,如果一个大矩阵的秩较低,说明其信息量较少,可以通过更少的参数来表示,,即用小矩阵来表示大矩阵,从而降低计算成本和内存占用

1.2低秩分解

通过将高秩矩阵分解为低秩矩阵的乘积,可以显著减少模型的参数量。例如,矩阵分解技术(如SVD)可以用于压缩神经网络的权重矩阵,从而降低模型的存储和计算成本

在这里插入图片描述

在这里插入图片描述

2.LoRA微调的步骤

2.1 低秩矩阵分解

对于模型中的某些关键层(如注意力层的q_projk_projv_projo_proj,以及MLP层的gate_projup_projdown_proj),LoRA将原始权重矩阵W分解为两个低秩矩阵AB的乘积:

  • 原始权重矩阵:W
  • 低秩分解:W ≈ A × B
  • 其中,AB的秩远小于W的秩,从而减少参数量。

2.2 对原矩阵进行更新

W ′ = W + A × B W' = W + A \times B W=W+A×B

  • W W W是原始模型的权重矩阵。
  • A A A B B B 是低秩矩阵,其秩 r r r远小于 W W W的秩。
  • W ′ W' W 是微调后的权重矩阵

2.3 配置参数

低秩矩阵的秩r :决定了低秩矩阵的维度。通常取值范围为4到16,具体值需要根据任务和模型大小调整。

缩放因子 α:用于控制低秩矩阵更新的强度。通常设置为 r 的倍数。
目标模块:指定哪些模块需要应用LoRA。通常包括注意力模块和前馈网络模块。
训练模式:选择是否在训练时启用LoRA,并设置是否使用混合精度训练(如FP16)以节省显存。

在LoRA微调中,缩放因子 alpha 通常用于以下公式中:

W ′ = W + α r ( A × B ) W' = W + \frac{\alpha}{r}(A \times B) W=W+rα(A×B)

其中:

  • ( W ) 是原始模型的权重矩阵。
  • ( A ) 和 ( B ) 是低秩矩阵,其秩 ( r ) 远小于 ( W ) 的秩。
  • ( W’ ) 是微调后的权重矩阵。
  • ( \frac{\alpha}{r} ) 是缩放因子,用于调整低秩矩阵更新的强度。

2.4 冻结原始模型的参数

冻结原始权重:将原始模型的 权重 W 冻结,只训练低秩矩阵 A 和 B

2.5 训练LoRA微调模型

在训练过程中,仅训练低秩矩阵 A 和 B ,而不训练原始权重 W 。
通过梯度下降来更新低秩矩阵 A 和 B 。

2.6 保存LoRA权重

保存LoRA权重: 将LoRA适配层的参数保存到磁盘,方便后续加载。
合并权重(可选):将LoRA权重合并到原始模型中,生成一个完整的微调模型。这一步在部署时非常有用,因为合并后的模型可以直接加载使用。
模型部署:将微调后的模型部署到生产环境,如Web服务、移动应用等。

3 LoRA微调的优点

3.1 参数高效

LoRA通过低秩矩阵分解,显著减少了需要训练的参数数量。例如,对于一个 1024 × 1024 1024 \times 1024 1024×1024 的权重矩阵,直接更新需要 1024^2 个参数,而LoRA只需要训练 2 × 1024 × r 2 \times 1024 \times r 2×1024×r 个参数(假设 (r = 16))

3.2 训练快速

由于需要更新的参数较少,LoRA微调的训练速度比全参数微调快得多,同时计算成本也大幅降低。

3.3 性能优异

尽管参数量减少,LoRA微调后的模型在特定任务上的性能通常与全参数微调相近,甚至更好。这是因为LoRA通过低秩矩阵捕捉了关键信息,同时避免了对原始模型的大幅改动。

3.4 易于部署

微调后的模型可以通过简单的权重合并操作直接部署,无需额外的适配层。这使得LoRA微调后的模型在实际应用中更加灵活。

4 总结

LoRA微调是一种高效且灵活的模型微调方法,特别适合在资源受限或数据量较少的场景下使用。通过低秩矩阵分解和稀疏更新LoRA在减少参数数量的同时,保持了模型的性能。其原理简单,流程清晰,易于实现和部署,是当前大语言模型微调中的一种重要技术。

5 代码演示

使用Python和Hugging Face的transformers库进行LoRA微调的示例代码:

# 首先,确保安装了transformers和datasets库。
pip install transformers datasets

# 步骤2:加载预训练模型和分词器
from transformers import AutoModelForSequenceClassification, AutoTokenizer

model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=8)  # 假设是8分类任务

# 步骤3:设置LoRA配置

from peft import LoraConfig, PeftModel, TaskType

lora_config = LoraConfig(
    r=16,  # 低秩矩阵的秩
    lora_alpha=32,  # 缩放因子
    target_modules=[torch.nn.Linear],  # 应用LoRA的模块
    lora_dropout=0.1,  # Dropout率
    bias="none"  # 是否更新偏置
)

model = PeftModel(model, lora_config=lora_config)

# 步骤4 准备数据
# 这里我们使用datasets库加载一个示例数据集。
from datasets import load_dataset

dataset = load_dataset("glue", "sst2")
train_dataset = dataset["train"].shuffle(seed=42).select(range(1000))  # 仅使用1000个样本进行演示
eval_dataset = dataset["validation"].shuffle(seed=42).select(range(100))  # 仅使用100个样本进行演示

# 步骤5:训练模型

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir="./results",#定义模型和日志文件的保存目录
    num_train_epochs=3,#定义训练的总轮数(epochs)。每个epoch意味着模型将遍历整个训练数据集一次。
    per_device_train_batch_size=16,#定义每个设备(如GPU或CPU)上的训练批次大小。即每个设备上每次训练迭代中处理的样本数量。
    per_device_eval_batch_size=64,#定义每个设备上的评估批次大小。即在评估阶段,每个设备上每次迭代中处理的样本数量。
    warmup_steps=500,#定义学习率预热的步数。在预热阶段,学习率会从0逐渐增加到预设的最大值,以稳定训练初期的优化过程。
    weight_decay=0.01,#定义权重衰减系数,用于L2正则化。权重衰减有助于防止模型过拟合。
    logging_dir='./logs',#定义日志文件的保存目录
    logging_steps=10,#定义记录日志的频率,即每训练多少步记录一次日志。
    evaluation_strategy="epoch",#定义评估策略,可以是"steps"(每训练一定步数进行一次评估)或"epoch"(每个epoch结束时进行评估)。
    save_strategy="epoch",#定义模型保存策略,可以是"steps"(每训练一定步数保存一次模型)或"epoch"(每个epoch结束时保存模型)。
    load_best_model_at_end=True,#定义是否在训练结束时加载表现最好的模型。如果设置为True,则会在训练结束时根据指定的评估指标加载最佳模型。
    metric_for_best_model="accuracy",#定义用于选择最佳模型的评估指标
    greater_is_better=True,#定义评估指标是越大越好还是越小越好。例如,对于准确率,这个值应设置为True;对于损失值,这个值应设置为False
    save_on_each_node=True,#定义在分布式训练中是否在每个节点上保存模型。如果设置为True,则每个节点都会保存模型的副本。
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset
)

trainer.train()

# 步骤6:评估和保存模型

results = trainer.evaluate()
print(results)

model.save_pretrained("./lora_model")
tokenizer.save_pretrained("./lora_model")

示例展示了如何使用LoRA进行微调的基本流程。请注意,实际应用中可能需要根据具体任务调整模型架构、数据集和训练参数.

作者码字不易,觉得有用的话不妨点个赞吧,关注我,持续为您更新AI的优质内容。

### LoRA 中的矩阵解释 在深度学习领域,尤其是针对大规模预训练模型微调过程中,LoRA (Low-Rank Adaptation) 提供了一种高效的方法来调整这些大型模型而不需要重新训练整个网络。这种方法的核心在于利用矩阵分解技术。 #### 什么是矩阵? 当提到矩阵时,指的是可以通过两个较小规模矩阵相乘得到的一个较大矩阵。假设有一个原始权重矩阵 \(W\) ,通过引入一对新的矩阵 \( A \in R^{m\times r} \),\( B \in R^{r\times n}\),使得 \( AB \approx W \)[^2]。这里的关键点是 \(A\) 和 \(B\) 的尺寸都比原来的 \(W\) 小得多,从而减少了存储需求以及计算复杂度。 #### 如何应用于神经网络中? 对于给定的预训练模型中的某个特定层的权重矩阵 \( W_0 \),采用 LoRA 技术意味着不再直接修改这个大矩阵本身,而是创建一个新的结构——即把该层的部分或全部连接替换成由上述定义的小型矩阵组合而成的新形式: \[ W_{\text{new}} = W_0 + BA \] 这里的 \(BA\) 表达了一个相对简单的增量变化,它能够捕捉到目标任务所需的学习方向而不破坏原有知识[^3]。由于 \(A\) 和 \(B\) 维度较,这不仅降了参数数量,而有助于缓解过拟合现象的发生。 #### 实现细节:MergedLinear 类案例分析 为了更好地理解这一过程的实际操作方式,考虑 `MergedLinear` 这样的实现类。此类负责处理线性和 LoRA 层的同时初始化工作,在其构造函数 (`__init__`) 中会完成如下几项重要任务: - **初始化**:设置好标准线性层(`nn.Linear`)的基础配置; - **启用 LoRA**:如果指定了使用 LoRA,则进一步构建对应的矩阵 \(lora_A\) 及 \(lora_B\) 并冻结原有的全量权值; - **维度转换**:必要情况下调整输入/输出数据流的方向以适应内部运算逻辑; 此外还有其他辅助功能如 `reset_parameters()` 来随机化初始状态、`zero_pad()` 处理不一致大小的数据块等问题解决措施[^4]。 ```python import torch.nn as nn class MergedLinear(nn.Module): def __init__(self, in_features, out_features, rank=4, enable_lora=True): super().__init__() self.linear_layer = nn.Linear(in_features, out_features) self.enable_lora = enable_lora if enable_lora: # Initialize low-rank matrices based on the specified rank. self.lora_A = nn.Parameter(torch.randn(rank, in_features)) self.lora_B = nn.Parameter(torch.randn(out_features, rank)) # Freeze pre-trained weights when enabling LoRA adaptation. for param in self.linear_layer.parameters(): param.requires_grad_(False) def forward(self, x): base_output = self.linear_layer(x) if not self.enable_lora or getattr(self, 'merged', False): return base_output lora_contribution = self.merge_AB() final_output = base_output + lora_contribution return final_output def merge_AB(self): ab_product = torch.matmul(self.lora_B, self.lora_A) padded_result = F.pad(ab_product, pad=(0, 0), mode='constant') return padded_result ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值