从原理到调参,小白也能读懂的大模型微调算法Lora

身为一名AI工程师,我过去的工作主要集中在应用层开发,对算法的理解并不深入。然而,近期我开始对算法产生了浓厚的兴趣,并转向研究模型微调。在众多微调算法中,Lora以其普遍应用引起了我的关注,我计划在本文中对它进行详细介绍。将Lora仅仅视为一种算法可能并不准确,它更像是一种精妙的技巧或策略。下文将围绕几个核心问题,全面探讨和解析Lora技术,希望这些内容能为对模型微调感兴趣的你提供有用的参考和帮助。

Lora是什么

假设大模型的原始的权重矩阵w是:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> W = [ 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 ] W = \begin{bmatrix} 1.0 & 2.0 & 3.0 & 4.0 \\ 5.0 & 6.0 & 7.0 & 8.0 \\ 9.0 & 10.0 & 11.0 & 12.0 \\ 13.0 & 14.0 & 15.0 & 16.0 \\ 17.0 & 18.0 & 19.0 & 20.0 \end{bmatrix} </math>W= 1.05.09.013.017.02.06.010.014.018.03.07.011.015.019.04.08.012.016.020.0

全量微调需要更新 5 * 4 = 20个参数,假设微调后的参数是:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> W ′ = [ 1.41 2.44 3.47 4.50 5.93 7.00 8.07 9.14 10.45 11.56 12.67 13.78 14.97 16.12 17.27 18.42 19.49 20.68 21.87 23.06 ] W' = \begin{bmatrix} 1.41 & 2.44 & 3.47 & 4.50 \\ 5.93 & 7.00 & 8.07 & 9.14 \\ 10.45 & 11.56 & 12.67 & 13.78 \\ 14.97 & 16.12 & 17.27 & 18.42 \\ 19.49 & 20.68 & 21.87 & 23.06 \end{bmatrix} </math>W′= 1.415.9310.4514.9719.492.447.0011.5616.1220.683.478.0712.6717.2721.874.509.1413.7818.4223.06

这个可以转化为:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> W ′ = W + Δ W = [ 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 ] + [ 0.41 0.44 0.47 0.50 0.93 1.00 1.07 1.14 1.45 1.56 1.67 1.78 1.97 2.12 2.27 2.42 2.49 2.68 2.87 3.06 ] W' = W + \Delta W = \begin{bmatrix} 1.0 & 2.0 & 3.0 & 4.0 \\ 5.0 & 6.0 & 7.0 & 8.0 \\ 9.0 & 10.0 & 11.0 & 12.0 \\ 13.0 & 14.0 & 15.0 & 16.0 \\ 17.0 & 18.0 & 19.0 & 20.0 \end{bmatrix} + \begin{bmatrix} 0.41 & 0.44 & 0.47 & 0.50 \\ 0.93 & 1.00 & 1.07 & 1.14 \\ 1.45 & 1.56 & 1.67 & 1.78 \\ 1.97 & 2.12 & 2.27 & 2.42 \\ 2.49 & 2.68 & 2.87 & 3.06 \end{bmatrix} </math>W′=W+ΔW= 1.05.09.013.017.02.06.010.014.018.03.07.011.015.019.04.08.012.016.020.0 + 0.410.931.451.972.490.441.001.562.122.680.471.071.672.272.870.501.141.782.423.06
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> W ′ = W + Δ W = [ 1.41 2.44 3.47 4.50 5.93 7.00 8.07 9.14 10.45 11.56 12.67 13.78 14.97 16.12 17.27 18.42 19.49 20.68 21.87 23.06 ] W' = W + \Delta W = \begin{bmatrix} 1.41 & 2.44 & 3.47 & 4.50 \\ 5.93 & 7.00 & 8.07 & 9.14 \\ 10.45 & 11.56 & 12.67 & 13.78 \\ 14.97 & 16.12 & 17.27 & 18.42 \\ 19.49 & 20.68 & 21.87 & 23.06 \end{bmatrix} </math>W′=W+ΔW= 1.415.9310.4514.9719.492.447.0011.5616.1220.683.478.0712.6717.2721.874.509.1413.7818.4223.06

其中ΔW 可以分解为
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> Δ W = A ⋅ B = [ 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 ] ⋅ [ 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 ] \Delta W = A \cdot B = \begin{bmatrix} 0.1 & 0.2 \\ 0.3 & 0.4 \\ 0.5 & 0.6 \\ 0.7 & 0.8 \\ 0.9 & 1.0 \end{bmatrix} \cdot \begin{bmatrix} 1.1 & 1.2 & 1.3 & 1.4 \\ 1.5 & 1.6 & 1.7 & 1.8 \end{bmatrix} </math>ΔW=A⋅B= 0.10.30.50.70.90.20.40.60.81.0 ⋅[1.11.51.21.61.31.71.41.8]

  • 矩阵 ( A ):尺寸 ( 5 * 2 ),共10个参数
  • 矩阵 ( B ):尺寸 ( 2 * 4 ),共8个参数
  • LoRA总参数:( 10 + 8 = 18 ) 个

也就是说通过LoRA微调,调参对象从 W 变为 A、B,使得参数量从20个减少为18个,这是简化的例子。在实际案例中,参数量可以减少为0.01%~3%左右。

为什么需要LoRA

LoRA最早出现在2021年由微软研究院提出的一篇论文中(《LoRA: Low-Rank Adaptation of Large Language Models》),LoRA的核心思路是:与其每次都复制整个模型,不如只调整一小部分参数,把成本降下来。它的目标是解决大模型微调中的两大痛点:

  1. 资源消耗太大:大型语言模型动辄几亿甚至几千亿参数,全参数微调需要为每个新任务保存一份完整的模型副本。比如,一个10亿参数的模型,假设每个参数用4字节(float32),光存储就得4GB。多个任务下来,硬盘和显存都吃不消。

  2. 训练效率低下 :全参数微调不仅占空间,还需要大量计算资源和时间。每次训练都得更新所有参数。

LoRA的核心亮点

  1. 参数少

    • 它只微调原始参数的1%甚至更少。
      • 在GPT-3上,r = 8的LoRA参数量占全微调的0.01%-0.1%,性能却达到全微调的95%-99%。
      • 在GLUE任务(BERT),r = 16的LoRA用0.1%参数,平均得分仅比全微调低0.5-1分。
  2. 速度快

    • 训练和部署都比全参数微调省时省力。
  3. 模块化

    • 训练好的LoRA"插件"可以随时加载或卸载,不影响原始模型,特别适合多任务场景。

模块化设计的优点

  • 避免灾难性遗忘
    直接修改 W 可能导致模型在新任务上表现良好,但在原始任务上性能下降(即"灾难性遗忘")。LoRA通过冻结核心 W,保留了原始模型的能力。
  • 存储高效
    一个大模型可以搭配多个LoRA模块,每个模块只占用MB级空间,相比全模型微调动辄几GB,节省显著。
  • 快速切换任务
    任务切换只需加载不同LoRA文件,几秒钟搞定,不用重新训练。
  • 兼容性强
    原始模型完全不动,多个团队可以共享同一个基础模型,只开发自己的LoRA模块。

为什么可以对增量权重 ΔW 低秩分解?

低秩分解的核心思想是:矩阵里的信息往往不是均匀分布的,很多维度是冗余的,只需要抓住"主要方向"就够了。

1. 什么是矩阵的秩(Rank)?

在线性代数中,一个矩阵的秩(rank)是它的线性独立行或列的数量。如果一个矩阵是"低秩"的,意味着它的信息可以用少量独立方向表达,而不是需要完整的维度。

比如下述矩阵,第5行 [1, 2, 0, 3, 0] 是第1行 [1, 0, 0, 2, 0] 和第2行 [0, 2, 0, 1, 0] 的线性组合(第5行=第1行+第2行),第5行没有提供更多的信息,理论上这个矩阵有前4行就能提供所有信息了,因此矩阵的行秩为4(列秩也为4,第5列全为0,没有信息增量)。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> S = [ 1 0 0 2 0 0 2 0 1 0 0 0 3 0 0 2 1 0 5 0 1 2 0 3 0 ] S = \begin{bmatrix} 1 & 0 & 0 & 2 & 0 \\ 0 & 2 & 0 & 1 & 0 \\ 0 & 0 & 3 & 0 & 0 \\ 2 & 1 & 0 & 5 & 0 \\ 1 & 2 & 0 & 3 & 0 \end{bmatrix} </math>S= 1002102012003002105300000

2. 低秩分解的原理

奇异值分解(SVD)可以把任意矩阵分解成三个矩阵的乘积。对于一个形状 ( d * k ) 的矩阵 ( W ),SVD可以写成:

  • ( U ) 是 ( d * d ) 的正交矩阵
  • (Σ ) 是 ( d * k ) 的对角矩阵(奇异值按降序排列)
  • ( V^T ) 是 ( k * k ) 的正交矩阵(( V ) 的转置)

其中 ( r ) 是矩阵的秩(非零奇异值的数量)。通过保留前 ( r ) 个最大的奇异值(低秩近似),可以用更少的参数近似原矩阵 ( W )。

任意矩阵(无论是实数还是复数、方阵还是非方阵、满秩还是不满秩)都可以通过**奇异值分解(SVD)**精确拆分为三个特定矩阵的乘积

举个例子,针对上述矩阵 ( S ) 的SVD分解(计算过程略):
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> U ≈ [ 0.3 0 0.34 − 0.68 − 0.58 − 0.22 0 − 0.76 0.2 − 0.58 − 0.7 0 0.36 0.32 0 − 0.22 0 − 0.42 − 0.46 − 0.56 ] , Σ = [ 7.03 0 0 0 0 0 3 0 0 0 0 0 2.15 0 0 0 0 0 0.11 0 0 0 0 0 0 ] , V ≈ [ 0.34 − 0.32 0 − 0.89 0 0 0 − 1 0 0 0.3 − 0.93 0 0.22 0 − 0.89 − 0.19 0 0.41 0 0 0 0 0 1 ] \begin{aligned} U &\approx \begin{bmatrix} 0.3 & 0 & 0.34 & -0.68 & -0.58 \\ -0.22 & 0 & -0.76 & 0.2 & -0.58 \\ -0.7 & 0 & 0.36 & 0.32 & 0 \\ -0.22 & 0 & -0.42 & -0.46 & -0.56 \end{bmatrix}, \\ \Sigma &= \begin{bmatrix} 7.03 & 0 & 0 & 0 & 0 \\ 0 & 3 & 0 & 0 & 0 \\ 0 & 0 & 2.15 & 0 & 0 \\ 0 & 0 & 0 & 0.11 & 0 \\ 0 & 0 & 0 & 0 & 0 \end{bmatrix}, \\ V &\approx \begin{bmatrix} 0.34 & -0.32 & 0 & -0.89 & 0 \\ 0 & 0 & -1 & 0 & 0 \\ 0.3 & -0.93 & 0 & 0.22 & 0 \\ -0.89 & -0.19 & 0 & 0.41 & 0 \\ 0 & 0 & 0 & 0 & 1 \end{bmatrix} \end{aligned} </math>UΣV≈ 0.3−0.22−0.7−0.2200000.34−0.760.36−0.42−0.680.20.32−0.46−0.58−0.580−0.56 ,= 7.03000003000002.15000000.11000000 ,≈ 0.3400.3−0.890−0.320−0.93−0.1900−1000−0.8900.220.41000001

如果只保留前三个奇异值(),重构后的矩阵 ( S' ) 与原矩阵 ( S ) 几乎一致(三个矩阵分别取前三列,前三行&前三列,前三行):
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> S ′ = [ 0.93 − 0.01 0 2.03 0 0.02 2.00 0 0.99 0 0.00 0.00 3.00 0.00 0 2.05 1.01 0 4.98 0 0.95 1.99 0 3.02 0 ] S' = \begin{bmatrix} 0.93 & -0.01 & 0 & 2.03 & 0 \\ 0.02 & 2.00 & 0 & 0.99 & 0 \\ 0.00 & 0.00 & 3.00 & 0.00 & 0 \\ 2.05 & 1.01 & 0 & 4.98 & 0 \\ 0.95 & 1.99 & 0 & 3.02 & 0 \end{bmatrix} </math>S′= 0.930.020.002.050.95−0.012.000.001.011.99003.00002.030.990.004.983.0200000

结果对比原始矩阵和重构矩阵,直观上来看,基本保持一致,这就是说:如果只保留最大的几个奇异值,就能用更少的参数近似表示w

3. 为什么可以对增量权重 ΔW 低秩分解?

研究发现:

  1. 信息集中性:微调后的权重变化 ( ΔW) 的奇异值分布中,前10-20个奇异值占据了90%以上的信息(LoRA论文在GPT-3上的实验结论)。
  2. 结构化特性:(ΔW ) 的变化不是随机的,而是集中在少数"任务相关方向"上(例如让模型学习法律术语只需调整少量语义方向)。
  3. 高效近似:直接用低秩矩阵 ( A * B ) 构造 (ΔW ),无需完整SVD计算,参数量从 ( d * k ) 降至 ( (d + k) * r )。

直观理解: 微调类似于让一个已学会"说话"的模型掌握某种"口音"。这种调整只需修改少数关键维度(如词汇选择),而非全部语言规则,因此低秩足够。

举个例子

对一个 ( 512 * 512 ) 的权重矩阵(262,144参数):

  • 全微调:更新全部262,144个参数。
  • LoRA(( r=8 )):仅需 ( 512 * 8 + 8 * 512 = 8,192 ) 个参数,即可捕捉主要变化。

4. 对原始权重 ( W ) 可以低秩分解吗?

不行。预训练模型的权重 ( W ) 通常接近满秩(奇异值分布平滑),低秩分解会丢失关键信息。而 ( ΔW ) 的秩天然较低,适合分解。

LoRA是如何更新参数的

本质上,LoRA仍然使用反向传播算法进行参数更新,但仅针对新增的低秩矩阵 ( A ) 和 ( B ),而保持原始权重 ( W ) 冻结。

参数更新过程

  1. 初始化
    • ( W ) 使用预训练模型的权重,梯度计算被禁用(不更新)。
    • ( A ) 用小的随机高斯分布初始化
    • ( B ) 初始化为全零矩阵,确保训练开始时 ( ΔW = 0 ),避免干扰原始模型。
  2. 前向传播
    • 输入数据 ( X ) 通过调整后的权重计算输出
    • 根据任务目标 计算损失函数 ( L )(如交叉熵损失)。
  3. 反向传播
    • 计算损失 ( L ) 对 ( A ) 和 ( B ) 的梯度
    • 不计算 ( W ) 的梯度(因其被冻结)。
  4. 参数更新
    • 使用优化器(如Adam)更新 ( A ) 和 ( B ):。其中 ( η ) 是学习率。
  5. 迭代优化
    • 重复步骤2-4,直到损失收敛或达到训练轮次。
    • 训练完成后,( A ) 和 ( B ) 捕捉了任务特定的调整信息。

推理部署选项

  • 合并权重:将 ( W' = W + A * B ) 合并为单一矩阵,直接用于推理(适合固定任务)。
  • 动态加载:保持 ( W ) 和 ( A * B ) 分离,灵活切换不同任务的LoRA模块(适合多任务场景)。

关键特点

  1. 参数高效:仅训练 ( A ) 和 ( B ),参数量从 ( d * k ) 降至 ( (d + k) * r )。
  2. 内存节省:无需存储全参数微调的梯度,显存占用大幅降低。
  3. 兼容性:原始模型 ( W ) 保持不变,支持多任务共享。

LoRA可以用在Transformer的哪些层

LoRA是"好钢要用在刀刃上"。并非模型的所有参数都需要微调,选择关键层进行适配即可达到接近全参数微调的效果。LoRA目前主要可以应用在transformer中的以下两类层:

Transformer是谷歌在2017年推出的深度学习模型,专门处理序列数据。简单来说,序列数据就像排队的小朋友,每个小朋友都有自己的位置和信息,Transformer能把这些信息处理得明明白白。后面有空我会专门出一个系列讲解一下。

1. 注意力层(Self-Attention)

Transformer的核心是多头注意力机制,每个注意力头包含4个权重矩阵:

  • ( W_q )(Query)
  • ( W_k )(Key)
  • ( W_v )(Value)
  • ( W_o )(Output)

LoRA通常应用在

  • ( W_q ) 和 ( W_v )(最高优先级):

    • 调整 ( W_q ) 可改变模型"关注哪些信息"。

    • 调整 ( W_v ) 可影响"如何编码关注的信息"。

  • ( W_o )(次优先级):

    • 调整输出投影矩阵,但收益通常不如 ( W_q ) 和 ( W_v ) 显著。

实验结论(来自LoRA原论文):

  • 仅微调 ( W_q ) 和 ( W_v ) 即可达到全参数微调效果的90%以上。
  • 添加 ( W_o ) 的LoRA对性能提升有限(<2%),但会增加参数量。

2. 前馈网络层(FFN)

FFN包含两个线性变换:

  • ( W_1 ):升维(通常放大4倍,如d_model → 4×d_model)
  • ( W_2 ):降维(4×d_model → d_model)

适用场景

  • 大模型(如GPT-3):添加FFN层的LoRA可进一步提升性能。
  • 复杂生成任务:调整FFN能增强任务特定的特征表达。

不推荐使用LoRA的层

  1. 嵌入层(Embedding)
    • 参数量大但微调收益低,冻结可节省资源。
  2. LayerNorm/Bias
    • 参数少,直接全参数微调成本低。
    • LayerNorm的缩放因子和偏置本身具有低秩特性,无需LoRA。

实际配置建议

模型规模 推荐LoRA目标层 典型rank (r)
小模型(如BERT) 仅 ( W_q ), ( W_v ) 8-16
大模型(如GPT-3) ( W_q ), ( W_v ), FFN的 ( W_1 ) 32-64
复杂生成任务 所有注意力矩阵 + FFN 64+

模块化设计优势

  • 任务切换:不同任务可独立配置LoRA模块(如翻译任务用( W_q ), ( W_v ),摘要任务额外启用FFN)。
  • 资源分配:对关键层分配更高秩(如( r=32 )),次要层用低秩(如( r=8 ))。

LoRA训练时需要调整哪些超参数

以 LLaMA-Factory 的配置为例,说明 LoRA 的关键超参数及其调参策略:

核心参数表
参数名 类型/范围 含义 建议值 默认值
finetuning_type ["full","freeze","lora"] 微调类型选择 必须设为 "lora" "lora"
lora_rank (r) 正整数 LoRA的秩,决定矩阵A/B的列数/行数 简单任务:8-16 中等任务:32 复杂任务:64+ 8
lora_alpha (α) 正整数 缩放系数,控制ΔW对原始权重W的影响强度 通常设为 lora_rank 的1-2倍(如r=16时α=32) None
lora_dropout 0.0-1.0 LoRA层的Dropout概率 大数据集:0.0 小数据集:0.05-0.1(防过拟合) 0.0
lora_target 逗号分隔的字符串 应用LoRA的模块名称(需匹配模型层名) 默认:"q_proj,v_proj" 复杂任务:"q_proj,k_proj,v_proj,o_proj" "all"
additional_target 逗号分隔的字符串 额外扩展的LoRA目标模块(如FFN层) 通常留空,大模型可加"ffn.w1,ffn.w2" None
调参技巧
  1. 秩(r)的选择

    • 从小开始:优先尝试r=8或16,逐步增加直至性能饱和。
    • 数据量关联
      • 小数据集(<5K样本):r=8
      • 大数据集(>50K样本):r=32+
  2. 目标层选择策略

    python 复制代码
    # 简单任务(如分类)
    lora_target = "q_proj,v_proj"
    
    # 复杂任务(如生成)
    lora_target = "q_proj,k_proj,v_proj,o_proj,ffn.w1,ffn.w2"
  3. 改进技术的适用场景

    • LoRA+ :训练速度要求高时启用(设lorapius_lr_ratio=8)。
    • DoRA :需要逼近全微调性能时开启(use_dora=true)。
    • rsLoRA :当r≥32时更稳定(use_rslora=true)。
参数影响对比
超参数 参数量影响 训练速度 性能影响
lora_rank 线性增加 略微下降 先升后平
lora_alpha 无影响 无影响 调节强度
use_dora=true 增加约10% 下降10%-20% 提升1%-3%
pissa_init=true 无影响 初始化耗时增加 收敛更快
经典配置示例
yaml 复制代码
# GLUE任务(BERT-base)
lora_rank: 16
lora_alpha: 32
lora_target: "query,value"
lora_dropout: 0.1

# GPT-3文本生成
lora_rank: 64
lora_alpha: 128
use_rslora: true
lora_target: "q_proj,v_proj,ffn.w1"

总结

LoRA是一种高效的大模型微调技术,它通过低秩矩阵分解显著地减少了参数量和计算资源的需求,同时又能保持接近全模型微调的性能。在接下来的文章中,我们将从实战角度出发,借由Llama-Factory来进行模型微调。我希望能帮助读者从零开始,全面掌握模型微调的知识和技巧。

感兴趣的朋友可以关注我的微信公众号 AI 博物院,获取第一时间更新!

相关推荐
幽络源小助理33 分钟前
springboot校园车辆管理系统源码 – SpringBoot+Vue项目免费下载 | 幽络源
vue.js·spring boot·后端
刀法如飞35 分钟前
一款开箱即用的Spring Boot 4 DDD工程脚手架
java·后端·架构
uzong1 小时前
后端系统设计文档模板
后端
幽络源小助理1 小时前
SpringBoot+Vue车票管理系统源码下载 – 幽络源免费项目实战代码
vue.js·spring boot·后端
uzong1 小时前
软件架构指南 Software Architecture Guide
后端
又是忙碌的一天1 小时前
SpringBoot 创建及登录、拦截器
java·spring boot·后端
勇哥java实战分享2 小时前
短信平台 Pro 版本 ,比开源版本更强大
后端
学历真的很重要2 小时前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
计算机毕设VX:Fegn08953 小时前
计算机毕业设计|基于springboot + vue二手家电管理系统(源码+数据库+文档)
vue.js·spring boot·后端·课程设计
上进小菜猪3 小时前
基于 YOLOv8 的智能杂草检测识别实战 [目标检测完整源码]
后端