当前位置: 首页 > news >正文

LoRA:大模型的低阶自适用(论文复现)

LoRA:大模型的低阶自适用(论文复现)

本文所涉及所有资源均在传知代码平台可获取

文章目录

    • LoRA:大模型的低阶自适用(论文复现)
        • 简介
        • LoRA文章主要贡献
        • LoRA技术模型图
        • 技术细节
        • 论文实验结果
        • LoRA在bert的运用
        • LoRA核心代码
        • 实战分析

简介

本文将先介绍论文中的LoRA技术,然后以BERT为例在IMDB数据集上代码实现运用这项微调技术

LoRA文章主要贡献

文章的主要贡献是提出了一种名为LoRA(Low-Rank Adaptation)的方法,用于在不牺牲模型质量的前提下,高效地对大型预训练语言模型进行微调。LoRA的核心思想是在Transformer架构的每一层注入可训练的低秩分解矩阵,同时冻结预训练模型权重,从而大幅减少下游任务中的可训练参数数量。

具体来说,LoRA的主要贡献包括:

高效的参数更新:LoRA通过低秩矩阵更新模型权重,而不是对整个模型进行微调。这种方法大幅减少了所需的训练参数数量和GPU内存需求。例如,与GPT-3 175B模型的全参数微调相比,LoRA可以将可训练参数减少10,000倍,GPU内存需求减少3倍。

保持模型质量:尽管LoRA使用的可训练参数远少于全参数微调,但它在多个模型(如RoBERTa、DeBERTa、GPT-2和GPT-3)上的表现与全参数微调相当或更好。

提高训练效率:LoRA降低了硬件门槛,因为它不需要计算大多数参数的梯度或维护优化器状态。此外,LoRA的设计允许在部署时将训练的矩阵与冻结的权重合并,从而不会引入额外的推理延迟。

实证研究:文章提供了关于语言模型适应性中秩不足性的实证研究,这有助于解释LoRA的有效性。

总的来说,LoRA提出了一种创新的方法来解决大型语言模型在特定任务上的适应问题,同时保持了模型的性能,降低了资源消耗,并提高了操作效率。这对于需要在资源受限的环境中部署和使用大型模型的应用场景尤为重要

LoRA技术模型图

在这里插入图片描述

正所谓大智若愚,LoRA这项技术的模型图就是这么简洁明了,x表示数据输入,左边表示预训练大模型的参数(冻结),右边表示两个低秩矩阵(训练),当大模型微调的时候,不再是全参数微调,而是仅仅微调右边的低秩矩阵。

这样一来,就能大大减少我们微调时候的工作量和需要的资源,并且使用这种方法微调模型的性能和全参数微调差不多,从而实现四两拨千斤的效果

技术细节

在这里插入图片描述

在这里插入图片描述

使用LoRA后,这262144个参数就冻结了

此时增加两个低秩矩阵 例如5122和2512

那么此时需要调整的参数大小就为5122+2512 = 2048个参数

2048 / 262144 = 0.0078125

此时要训练的参数就减少了许多

而且,当我们面对不同的下游任务时,因为原本的预训练模型是冻结的,所以预训练模型用一个就行,只需要保存的参数就是加入的低秩矩阵,这样的话,也能节省大量的存储空间

class LowRankMatrix(nn.Module):def __init__(self, weight_matrix, rank, alpha=1.0):super(LowRankMatrix, self).__init__()self.weight_matrix = weight_matrixself.rank = rankself.alpha = alpha / rank  # 将缩放因子与秩相关联# 初始化低秩矩阵A和Bself.A = nn.Parameter(torch.randn(weight_matrix.size(0), rank), requires_grad=True)self.B = nn.Parameter(torch.randn(rank, weight_matrix.size(1)), requires_grad=True)def forward(self, x):# 计算低秩矩阵的乘积并添加到原始权重上# 应用缩放因子updated_weight = self.weight_matrix + self.alpha * torch.mm(self.B.t(), self.A)return updated_weight

α和r用于缩放矩阵,帮助更好的训练

A矩阵使用随机高斯初始化

B矩阵初始化为0

论文实验结果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

LoRA在bert的运用

这里主要以bert-base-uncased为例来实现LoRA微调技术的运用。
bert-base-uncased的参数量为110M也就是1.1亿个参数

LoRA核心代码

主要使用文章提出的开源loralib来对bert的注意力机制线性层进行LoRA层的增加

def get_lora_bert_model(model, r=8, lora_layer=["q", 'k', 'v', 'o']):encoder_layers = list(model.encoder.layer)for layer_index, encoder_layer in enumerate(encoder_layers):# 访问多头自注意力层attention = encoder_layer.attention# 获取Q、K、V线性层q_linear = attention.self.queryk_linear = attention.self.keyv_linear = attention.self.value# 获取O线性层(实际上,O是V经过加权求和后的结果,通常不单独存储)o_linear = attention.output.densefor l in lora_layer:if l == 'q':new_q_proj = lora.Linear(q_linear.in_features, q_linear.out_features, r=r)model.encoder.layer[layer_index].attention.self.query = new_q_projelif l == 'k':new_k_proj = lora.Linear(k_linear.in_features, k_linear.out_features, r=r)model.encoder.layer[layer_index].attention.self.key = new_k_projelif l == 'v':new_v_proj = lora.Linear(v_linear.in_features, v_linear.out_features, r=r)model.encoder.layer[layer_index].attention.self.value = new_v_projelif l == 'o':new_o_proj = lora.Linear(o_linear.in_features, o_linear.out_features, r=r)model.encoder.layer[layer_index].attention.output.dense = new_o_projreturn model

可以看到对每层注意注意力机制层的q k v o的线性层都添加了LoRA层

def mark_only_LLM_lora_as_trainable(model: nn.Module, bias: str = 'none', LLM_name: str = 'default_value') -> None:if LLM_name == 'default_value':for n, p in model.named_parameters():if 'lora_' not in n:p.requires_grad = Falseif bias == 'none':returnelif bias == 'all':for n, p in model.named_parameters():if 'bias' in n:p.requires_grad = Trueelif bias == 'lora_only':for m in model.modules():if isinstance(m, LoRALayer) and \hasattr(m, 'bias') and \m.bias is not None:m.bias.requires_grad = Trueelse:raise NotImplementedErrorelse:for n, p in model.named_parameters():if 'lora_' not in n and LLM_name in n:# and "bert.pooler" not in p.requires_grad = Falseif bias == 'none':returnelif bias == 'all':for n, p in model.named_parameters():if 'bias' in n:p.requires_grad = Trueelif bias == 'lora_only':for m in model.modules():if isinstance(m, LoRALayer) and \hasattr(m, 'bias') and \m.bias is not None:m.bias.requires_grad = Trueelse:raise NotImplementedError

添加LoRA层后,每次训练模型的时候,就只需要训练bert加入的LoRA层,此时我们就需要用到mark_only_LLM_lora_as_trainable()来帮助我们实现,考虑到可能我们基于bert的分类模型可能还会涉及到我们自己加入的某些结构,这些部分是需要进行训练的,所以对于这种情况就这么来使用:

mark_only_LLM_lora_as_trainable(model, LLM_name='bert')
实战分析

本文采用IMDB影评情感分析数据集测试训练集各25000条来进行实验。

因为bert才1.1B,可能在bert上使用这个东西有点小题大做了,但是一屋不扫何以扫天下,现在的大模型架构基本都是基于transformer架构的(bert可以说是第一个),其实本质上都是差不多的,只不过我感觉可能更大一些的模型LoRA的效果会更加显著,模型越大,这个方法的优越性就会越强。

之前对bert全参数微调的准确率是93%,而使用LoRA微调技术得出的结果大约是86%左右,确实有一定的差距,我个人感觉可能是因为模型不够大,只有1.1B,因为低秩势必导致信息的损失,只有当你的模型够大的时候,这些损失才能够忽略不计。

但是使用LoRA技术,对于训练速度、显存占用有了巨大的提升。

首先来看显存占用量(同样是batch_size=64):

在这里插入图片描述

这是使用LoRA之后的,可以看到除了第一个epoch可能涉及数据加载、GPU预热等情况稍微慢点,其余epoch都是2.5分钟不到就完成了,节约了大概43%的训练时间。

不过准确率也下降了,从93%掉到了86%,准确率大约下降了7.5%。

如果对更大的模型使用LoRA技术,训练时间和显存占用的节省会更多,而性能的下降则会更少,确实是一项很不错的技术。

由此可见,LoRA这项技术确实十分有意义,能够大大降低模型微调的成本,同时不会增加推理的时间延迟,我们可以看到模型评估的时间都是一模一样的。

所以,这项技术其实一定程度上让大模型的门槛降低了一些,让大模型的使用成本大大降低,虽然性能上可能有些损失,但是,至少落地的可能性变大了

文章代码资源点击附件获取


http://www.mrgr.cn/news/45102.html

相关文章:

  • 解决Docker环境下Next.js和FastAPI的跨容器通信问题
  • #String StringBuilder StringBuffer
  • vulnhub-THE PLANETS-EARTH靶机
  • 【JVM系列】深入理解Java虚拟机(JVM)的核心技术:从加载到初始化的全过程解析(一、Java类加载器)
  • 2的幂次方表示
  • 算法复杂度与图算法 - 离散数学系列(十)
  • wsl下vim中文字复制到window环境中方法
  • HDLBits中文版,标准参考答案 | 3.2.4 More Circuits | 更多电路
  • Allegro如何合并同名网络铜皮操作指导
  • BUCK降压电路
  • 2024年十大前沿目标检测模型汇总
  • 用Python实现运筹学——Day 15: 线性规划的项目实战
  • 【动态规划】斐波那契模型 dp
  • 基于Springboot vue的流浪狗领养管理系统设计与实现
  • Spring Boot 进阶-详解Spring Boot整合数据库
  • ASR的King:我又回来了,更小,且更快——openai/whisper-large-v3-turbo
  • 【C++堆(优先队列)】2233. K 次增加后的最大乘积|1685
  • 深度优先搜索与并查集
  • Windows VSCode 配置 Java 环境 (Maven)
  • Steam Deck掌机可装“黑苹果” 开发者成功安装macOS 15 Sequoia