【大模型】LoRA微调时如何选择参数

本文主要内容参考:一文带你熟悉lora微调各类参数,轻松上手deepseek模型微调(全过程代码+结果对比)

1. LoRA原理

LoRA(Low-Rank Adaptation)是一种针对大型语言模型的微调技术,旨在降低微调过程中的计算和内存需求。其核心思想是通过引入低秩矩阵来近似原始模型的全秩矩阵,从而减少参数数量和计算复杂度。

在LoRA中,原始模型的全秩矩阵被分解为低秩矩阵的乘积。具体来说,对于一个全秩矩阵W,LoRA将其分解为两个低秩矩阵A和B的乘积,即W ≈ A * B。其中,A和B的秩远小于W的秩,从而显著减少了参数数量。

在这里插入图片描述

上图为 LoRA 的实现原理,其实现流程为:

  1. 在原始预训练语言模型旁边增加一个旁路,做降维再升维的操作来模拟内在秩;
  2. 用随机高斯分布初始化 A,用零矩阵初始化B,训练时固定预训练模型的参数,只训练矩阵 A 与矩阵 B ;
  3. 训练完成后,将 B 矩阵与 A 矩阵相乘后合并预训练模型参数作为微调后的模型参数。

公式表示为:
在这里插入图片描述

其中,W是原始的权重矩阵,A是一个尺寸为dr的矩阵,B是一个尺寸为rd’的矩阵,r是低秩矩阵的秩。通过这种分解,原始矩阵W的更新仅由A和B的乘积决定。进一步地,LoRA引入了一个缩放因子α,使得更新公式为:

在这里插入图片描述

代码表示如下:

scaling = alpha / r
weight = weight + scaling * (lora_B @ lora_A) 

2. 参数设置

那么在实际使用的时候,我们如何确定lora参数?这些参数的变化对实验结果产生什么影响?模型具体哪些部分参数需要使用lora?等等这些问题,我们应该如何应对?

下面我们先以 基于 PEFT 的 LORA 微调为例,介绍下 LORA 究竟包含哪些主要的参数:

PEFT(Parameter-Efficient Fine-Tuning) 库是一个用于高效微调大规模预训练模型的工具,旨在减少训练时的计算和存储成本,同时保持模型性能。它通过引入LoRA、Adapter等技术,使得只需调整部分参数即可实现有效的微调。

LoraConfig 是peft库中的一个配置类,用于设置LoRA相关的超参数,如低秩矩阵的秩、缩放因子等,它帮助用户定制LoRA微调的细节,优化训练过程的效率和效果。

基于 LoraConfig 类设置 lora 微调参数的示例代码如下:

from peft import LoraConfig, TaskType

lora_config = LoraConfig(
        r=16,
        lora_alpha=32,
        lora_dropout=0.05,
        bias="none",
        target_modules=['up_proj', 'gate_proj', 'q_proj', 'o_proj', 'down_proj', 'v_proj', 'k_proj'],
        task_type=TaskType.CAUSAL_LM,
        inference_mode=False  # 训练模式
    )

从上面的代码可以看出,LORA微调的核心参数包括:

  • target_modules
  • r
  • lora_alpha
  • lora_dropout

下面我们来一一进行讲解。

2.1 target_modules

target_modules 是 LoRA(Low-Rank Adaptation)中的关键参数,用于指定模型中需要插入低秩矩阵调整的模块。 LoRA 的核心思想是通过对预训练模型中的特定层进行低秩矩阵插入,实现参数高效微调而无需修改原始权重。

对于语言模型,通常选择影响权重更新较大的模块,例如q_proj和k_proj(负责查询和键的变换),v_proj(值的变换),以及o_proj(输出投影)等。这些模块主要集中在自注意力和前馈网络中,通过插入的低秩矩阵调整这些模块的权重,使模型在保持原始能力的同时适应新任务,极大减少微调的计算和存储开销。

比如,LLaMa的默认模块名为[q_proj, v_proj],我们也可以自行指定为:[q_proj,k_proj,v_proj,o_proj]。

这里,为了搞清楚具体哪些层会参与lora微调,我们使用 deepseek 观察模型每一层具体都是什么:

# 查看模型层的代码如下
# 本文使用的是大模型的通用对话功能,因此导入AutoModelForCausalLM查看

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained(model_name)
print(model)

具体模型结构如下:

LlamaForCausalLM(
  (model): LlamaModel(
    (embed_tokens): Embedding(102400, 4096)
    (layers): ModuleList(
      (0-29): 30 x LlamaDecoderLayer(
        (self_attn): LlamaSdpaAttention(
          (q_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (k_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (v_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (o_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (rotary_emb): LlamaRotaryEmbedding()
        )
        (mlp): LlamaMLP(
          (gate_proj): Linear(in_features=4096, out_features=11008, bias=False)
          (up_proj): Linear(in_features=4096, out_features=11008, bias=False)
          (down_proj): Linear(in_features=11008, out_features=4096, bias=False)
          (act_fn): SiLU()
        )
        (input_layernorm): LlamaRMSNorm((4096,), eps=1e-06)
        (post_attention_layernorm): LlamaRMSNorm((4096,), eps=1e-06)
      )
    )
    (norm): LlamaRMSNorm((4096,), eps=1e-06)
    (rotary_emb): LlamaRotaryEmbedding()
  )
  (lm_head): Linear(in_features=4096, out_features=102400, bias=False)
)

可以看到deepseek模型也是采取的Llama模型结构,那么具体哪些层会参与lora微调呢?下面将详细介绍:

(1)Attention层

  • Self-attention层: 这些层通常对模型性能影响较大。LoRA会被应用于自注意力的查询(q_proj)、键(k_proj)、值(v_proj)和输出(o_proj)投影矩阵。这些矩阵包含了大量的可训练参数,因此是LoRA微调的理想目标。
  • LlamaSdpaAttention中的矩阵
    • q_proj: 查询投影
    • k_proj: 键投影
    • v_proj: 值投影
    • o_proj: 输出投影
  • Rotary Embedding:虽然在一些实现中会对嵌入进行微调,但通常LoRA不会直接用于rotary_emb,因为它通常是固定的。

(2)MLP层 - MLP层中的Gate、Up和Down投影

  • gate_proj:控制门投影
  • up_proj:上升投影
  • down_proj:下降投影
  • MLP层的Gate、Up和Down投影通常涉及大量的可训练参数,因此对这些投影进行LoRA微调,可以在不显著增加计算负担的情况下优化模型表现。通过低秩适应,LoRA能够在减少参数量的同时,增强模型对复杂模式的适应能力。这些曾在处理非线性变换时起到重要作用,通常也是LoRA微调的目标。

(3)LayerNorm层

  • RMSNorm: 在Llama中使用的是LlamaRMSNorm(Root Mean Square Layer Normalization),它与标准的LayerNorm不同,但也可以通过LoRA微调。虽然这部分常常不会进行微调,但如果需要微调,通常会集中在注意力层和MLP层上。

(4)Embedding层

  • embed_tokens:如果对词嵌入有需要进行微调,LoRA也可以应用于嵌入矩阵。尤其在词汇量较大的情况下,嵌入矩阵的参数量非常庞大,这样进行LoRA微调也可以获得一定的性能提升。

(5)线性层(lm_head)

  • lm_head:在模型输出时,lm_head是从隐藏层到词汇表的最后一层线性转换。通常,LoRA不会直接应用于输出层,但在某些微调场景下,可以将LoRA应用于该层以调整模型输出。

【总结】: 一般来说,LoRA微调会集中在以下层:

  • Attention层的查询、键、值和输出投影(q_proj, k_proj, v_proj, o_proj)
  • MLP层的gate_proj、up_proj 和 down_proj
  • 可能在某些场景下微调embed_tokens和lm_head 通过这种方式,LoRA能够有效减少参数量和计算成本,同时保持微调的效果。

2.2 r、alpha、dropout

在模型微调的过程中,r、alpha和dropout是常见的超参数,用于优化模型训练和提升其泛化能力。

  • r:通常用于LoRA(Low-Rank Adaptation)方法中,表示 低秩矩阵的秩值 。r决定了微调时使用的低秩矩阵的维度,较小的r可以减少参数数量,从而提高训练效率,但可能牺牲一定的模型表现。较小的r(例如 8-32)适用于较小模型或需要较低资源的情况,而较大的r(例如 64-128)适用于更大规模的模型。
  • alpha:是LoRA中的一个超参数,用来 控制低秩矩阵的 缩放因子 。通过调整alpha,可以平衡低秩矩阵的影响,使模型能够在微调过程中保持足够的表达能力。16-32 是比较常见的选择,较大的alpha值通常会增加模型的表达能力,但也可能增加训练难度。
  • Dropout:是一种正则化技术,通过 在训练过程中随机丢弃神经网络中的部分神经元来防止过拟合。dropout率控制丢弃的概率,较高的dropout率有助于减少模型的复杂度,从而提升其在新数据上的泛化能力。对于大多数任务,0.2-0.3 是比较常见的取值,较低的dropout值(如 0.1)适合于较小的模型,而较高的dropout值(如 0.4-0.5)适合于较大的网络,尤其是在防止过拟合时。

【总结】:

  • r:通常选择 8-128,根据任务和模型规模调整。
  • alpha:常见值在 16-64,推荐 16-32。
  • Dropout:常见值在 0.1-0.5,推荐 0.2-0.3。

2.3 Lora参数的设置经验

Lora参数的设置经验参考博客:

(1)如何选择合适的 rank 值?
没有任何好的启发式方法来选择一个好的r,这是一个需要为每个LLM和每个数据集探索的超参数。

  • 太大的r可能会导致更多的过拟合
  • 小的r可能无法在数据集中捕获各种任务

总体来说,数据集中的任务越多样化,r 就应该越大。例如,如果我只想要一个执行基本2位算术的模型,那么一个很小的r可能已经足够了。

(2)rank 和 alpha 的设置经验

Choosing alpha as two times r is a common rule of thumb when using LoRA for LLMs, but I was curious if this still holds for larger r values.

将alpha设置为r的两倍是一种常见的经验法则,即 alpha = 2*rank。

然而,这条规则对于较大的r值也不一定适用。在模型和数据集的这种特定组合中,其中 r=256 且 alpha=128(0.5 倍缩放)性能甚至更好。如下图所示:
在这里插入图片描述

(3)dropout 的设置经验
一般设置为 0.05 或者 0.1 都可以。

(4)如何避免过拟合?
通常,较大的 rank 会导致更多的过拟合,因为它决定了可训练参数的数量。我们如果使用小模型微调时,使用的 rank 值可以选择 {8,16,32}之类的。
如果一个模型存在过拟合问题,可以减小 r 或增加数据集大小来避免过拟合。此外,也可以尝试在AdamW或SGD优化器中增加权重衰减率,也可以考虑增加LoRA层的dropout值。

参考资料

一文带你熟悉lora微调各类参数,轻松上手deepseek模型微调(全过程代码+结果对比)

### LoRA微调方法概述 对于大规模语言模型(LLMs),传统的全参数微调方式需要大量计算资源和训练数据,这使得其应用受到限制。为了应对这一挑战,低秩适应(LoRA, Low-Rank Adaptation)被提出作为一种高效的微调技术[^2]。 #### LoRA的核心原理 LoRA通过冻结原始模型中的大部分权重,在此基础上引入少量可训练的新参数来实现特定任务的学习目标。具体来说,它利用矩阵分解的思想,将原本高维的权重矩阵替换为两个较小维度矩阵的乘积形式。这种方法不仅显著减少了所需的训练参数数量,还保持了较高的性能表现水平。 以下是基于Keras框架下使用LoRA对Gemma模型进行微调的一个简单实例: ```python import tensorflow as tf from keras.layers import Dense from loralib.layers.dense import LoraDense # 假设这是支持LoRA功能的库 def create_lora_model(input_dim, output_dim, rank=4): inputs = tf.keras.Input(shape=(input_dim,)) # 使用LoRA层替代标准密集连接层 lora_layer = LoraDense(output_dim, r=rank)(inputs) predictions = Dense(output_dim, activation='softmax')(lora_layer) model = tf.keras.Model(inputs=inputs, outputs=predictions) return model model = create_lora_model(input_dim=768, output_dim=10, rank=8) model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"]) ``` 上述代码片段展示了如何构建一个带有LoRA机制的小型神经网络结构,并指定`r=8`作为低秩近似因子大小。实际操作过程中可以根据硬件条件调整此超参值以平衡效率与效果之间的关系。 #### 关键优势总结 - **减少内存占用**: 只需存储新增加的部分小型张量即可完成整个系统的部署工作; - **加速收敛速度**: 更少待优化变量意味着每轮迭代所需间更短; - **维持良好泛化能力**: 即使只修改一小部分原有架构也能达到接近甚至超越传统FT的结果质量; 尽管如此,值得注意的是并非所有场景都适合采用此类简化策略——某些复杂度较高或者高度定制化的应用场景可能仍然依赖于完整的再训练流程才能获得最佳解决方案。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值