LoRA微调参数详解:target_modules、r、alpha、dropout等

266

在大型语言模型(LLM)的微调中,LoRA(Low-Rank Adaptation)参数设置是提高训练效率和模型性能的关键。LoRA通过减少需要更新的参数量,从而在保持模型性能的同时,显著降低计算成本。本文将深入探讨LoRA的参数设置,以及其他关键的微调参数,如学习率、批次大小和优化器,并通过多轮对话数据集的微调实验,帮助你理解微调的核心原理,并提供一套完整的操作指南。

LoRA参数的设置

LoRA的核心思想是通过引入低秩矩阵来近似原始模型的全秩矩阵,从而减少参数数量和计算复杂度。具体来说,对于一个全秩矩阵W,LoRA将其分解为两个低秩矩阵A和B的乘积,即W ≈ A * B。其中,A和B的秩远小于W的秩,从而显著减少了参数数量。

LoRA 原理

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

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

公式表示为:

𝑊′=𝑊+Δ𝑊=𝑊′+𝐴⋅𝐵

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

𝑊′=𝑊+𝛼𝑟𝐴⋅𝐵

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

LoraConfig参数详解

peft库是一个用于高效微调大规模预训练模型的工具,旨在减少训练时的计算和存储成本,同时保持模型性能。它通过引入LoRA、Adapter等技术,使得只需调整部分参数即可实现有效的微调。LoraConfigpeft库中的一个配置类,用于设置LoRA相关的超参数,如低秩矩阵的秩、缩放因子等,它帮助用户定制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  # 训练模式
)

target_modules

target_modules是LoRA中的关键参数,用于指定模型中需要插入低秩矩阵调整的模块。LoRA的核心思想是通过对预训练模型中的特定层进行低秩矩阵插入,实现参数高效微调而无需修改原始权重。对于语言模型,通常选择影响权重更新较大的模块,例如q_projk_proj(负责查询和键的变换),v_proj(值的变换),以及o_proj(输出投影)等。这些模块主要集中在自注意力和前馈网络中,通过插入的低秩矩阵调整这些模块的权重,使模型在保持原始能力的同时适应新任务,极大减少微调的计算和存储开销。

为了更清晰地了解模型结构,我们可以使用以下代码来查看模型每一层具体是什么:

from transformers import AutoModelForCausalLM

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

LlamaForCausalLM为例,其模型结构如下:

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)
)

接下来,我们详细介绍哪些层会参与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_projup_projdown_proj
  • 可能在某些场景下微调embed_tokenslm_head

通过这种方式,LoRA能够有效减少参数量和计算成本,同时保持微调的效果。

r、alpha、dropout

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

  • r: 通常用于LoRA方法中,表示低秩矩阵的秩值。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。

task_type

LoraConfig中的task_type是一个指定模型任务类型的参数,它帮助LoRA配置不同的微调策略,以适应特定的任务需求。task_type可以有多个选项,通常包括以下几种类型:

  1. CAUSAL_LM

自回归语言建模任务,模型基于输入的部分文本(上下文)来预测下一个词,适用于生成任务,如文本生成和语言建模。

  1. SEQ_CLS

文本分类任务,模型将整个输入文本分类到某个类别。常见的应用包括情感分析、垃圾邮件检测、新闻分类等。

  1. SEQ_2_SEQ_LM

序列到序列的语言建模任务。该任务类型处理输入序列并生成一个输出序列。通常用于机器翻译、文本摘要等任务。

  1. TOKEN_CLS

标记分类任务,模型为输入文本的每个标记(通常是词或子词)分配一个类别标签。常见应用包括命名实体识别(NER)、词性标注(POS)、依存句法分析等。

  1. QUESTION_ANS

问答任务,模型根据输入的问题和上下文,提取答案。常见应用包括阅读理解、基于文档的问答等。

  1. FEATURE_EXTRACTION

特征提取任务,模型提取输入数据的隐藏状态(通常是编码器的输出),这些隐藏状态可以用于下游任务,如聚类、分类或作为其他任务的输入特征。比如给定一段文本,模型输出该文本的向量表示,这些向量可以用于情感分析、推荐系统或相似度计算等任务。

bias

LoraConfig配置中,bias参数用于指定LoRA微调时如何处理偏置(bias)项。具体来说,这个参数控制了在低秩适应中,是否保留或者修改偏置项。LoRA微调一般会将权重矩阵拆分成低秩矩阵来减少训练时的计算开销,但偏置项通常会保留或处理得不同。

bias参数的常见选项:

  1. “none”: 不对偏置项进行微调,也就是说,偏置项保持原样,不参与LoRA的低秩适应过程。这是默认选项,表示不修改偏置项,保持原有权重。
  2. “all”: 对所有的偏置项进行微调,这意味着LoRA不仅会对权重矩阵进行低秩适应,还会对偏置项进行相应的调整。
  3. “lora_only”: 仅对LoRA引入的低秩矩阵中的偏置项进行微调。即在LoRA的低秩变换部分,偏置项会被包含在内,并进行优化。

为什么选择 “none” 作为 bias 的值?

在许多LoRA微调的实现中,偏置项通常被认为是模型的一个稳定部分,尤其是在进行低秩微调时,可能并不需要对它们进行调整。使用 “none” 的选择意味着微调过程只会集中在权重矩阵的低秩部分,而不涉及偏置项的变动,这有助于减少额外的计算和参数调节,保持模型的原始结构。