找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3967

积分

0

好友

548

主题
发表于 17 小时前 | 查看: 2| 回复: 0

像 LLaMA、Mistral、Qwen 这类大语言模型,参数动辄数十亿甚至上百亿。若想在自定义数据上进行全量微调,成本极其高昂:一个 650 亿参数的模型,仅以 float16 精度加载就需消耗约 130GB 显存,通常需要多块顶级 GPU 跑上数天甚至数周。然而,对于大多数特定的下游任务,我们往往不需要动用模型全部的参数能力。

因此,研究者们提出了一个核心问题:能否只调整模型的一小部分参数,同时获得与全量微调相近甚至更好的效果?答案是肯定的。这类方法被统称为参数高效微调。在众多 PEFT 技术中,LoRA、QLoRA 和 DoRA 分别从不同角度解决了上述问题,极大地降低了微调门槛。如果你想深入了解这些技术或与其他开发者交流,欢迎来 云栈社区 探讨。

LoRA、QLoRA与DoRA技术概念图

LoRA——低秩自适应

论文:LoRA: Low-Rank Adaptation of Large Language Models, Hu et al. (ICLR 2022)

核心思想

LoRA 的思路非常直观:冻结预训练模型原有的权重矩阵 W,然后在其旁边并联两个小的、可训练的矩阵 AB。前向传播的计算公式变为:

output = W·x + (B·A)·x × (alpha/r)

训练过程中,我们只更新 ABW 保持不变。这里的秩 r 是分解的瓶颈维度,通常取值为 4、8 或 16。alpha 是一个缩放因子,用于控制 LoRA 更新的强度。

参数量的减少是惊人的。以一个维度为 (4096, 4096) 的权重矩阵为例,原始参数量是 1677 万。如果采用秩 r=8 的 LoRA,矩阵 A 的维度为 (8, 4096),参数量为 32,768;矩阵 B 的维度为 (4096, 8),参数量同样为 32,768。两者相加仅 65,536 个参数,相比原始矩阵减少了 99.6%

LoRA 论文的一个关键洞见是:在微调过程中,大部分有意义的权重更新本就集中在一个低维子空间内。因此,将更新约束在低秩矩阵上,并不会造成明显的性能损失。

以下是微软官方实现中,Linear 层的 LoRA 版本核心代码:

# Source: github.com/microsoft/LoRA — loralib/layers.py
class Linear(nn.Linear, LoRALayer):
    def __init__(
        self,
        in_features: int,
        out_features: int,
        r: int = 0,
        lora_alpha: int = 1,
        lora_dropout: float = 0.,
        merge_weights: bool = True,
        **kwargs
    ):
        nn.Linear.__init__(self, in_features, out_features, **kwargs)
        LoRALayer.__init__(self, r=r, lora_alpha=lora_alpha,
                           lora_dropout=lora_dropout,
                           merge_weights=merge_weights)
        if r > 0:
            self.lora_A = nn.Parameter(
                self.weight.new_zeros((r, in_features))
            )
            self.lora_B = nn.Parameter(
                self.weight.new_zeros((out_features, r))
            )
            self.scaling = self.lora_alpha / self.r
            # Freeze the pretrained weights
            self.weight.requires_grad = False

    def forward(self, x: torch.Tensor):
        if self.r > 0 and not self.merged:
            # Original frozen weights + low-rank update
            result = F.linear(x, self.weight, bias=self.bias)
            result += (
                self.lora_dropout(x)
                @ self.lora_A.transpose(0, 1)
                @ self.lora_B.transpose(0, 1)
            ) * self.scaling
            return result
        else:
            return F.linear(x, self.weight, bias=self.bias)

在实际应用中,我们通常用 LoRA 层替换注意力机制中的投影层,以下是一个典型示例:

# Source: github.com/microsoft/LoRA — README [1]
import loralib as lora

# Before: standard attention projection
# qkv_proj = nn.Linear(d_model, 3*d_model)

# After: apply LoRA to Q and V, freeze K
qkv_proj = lora.MergedLinear(
    d_model, 3*d_model,
    r=8,
    enable_lora=[True, False, True] # Q=LoRA, K=frozen, V=LoRA
)

# Mark only LoRA parameters as trainable
lora.mark_only_lora_as_trainable(model)

基准测试结果(来自 LoRA 论文)

论文在 GPT-2(自然语言生成)和 RoBERTa/DeBERTa(GLUE 基准)上进行了评测。下表展示了 GPT-2 在 E2E NLG Challenge 上的表现,对比了 LoRA 与全量微调及其他 PEFT 方法:

| Method               | Trainable Params | BLEU | NIST | MET  | ROUGE-L | CIDEr |
|----------------------|-----------------|------|------|------|---------|-------|
| Full Fine-Tuning     | 117M            | 68.2 | 8.62 | 46.2 | 71.0    | 2.47  |
| Adapter (Houlsby)    | 1.0M            | 66.3 | 8.41 | 45.0 | 69.8    | 2.40  |
| Prefix Tuning        | 0.35M           | 68.1 | 8.59 | 46.3 | 70.8    | 2.47  |
| LoRA (r=4)           | 0.77M           | 70.4 | 8.85 | 46.8 | 71.8    | 2.53  |

值得注意的是,LoRA 不仅追平了全量微调的性能,在仅训练不到 1% 参数的情况下,多项指标反而实现了反超。论文将这一现象归因于低秩约束本身带来的正则化效应,有助于防止模型过拟合。

关键超参数

  • r:定义了低秩矩阵的瓶颈维度,通常在 4 到 16 之间,这个范围足以覆盖绝大多数场景。r 越大,模型的容量越高,但可训练参数也越多。
  • Alpha (lora_alpha):缩放因子。LoRA 更新的有效学习率可以近似为 alpha/r。通常设置 alpha = 2r 是一个不错的起点。
  • 目标模块 (Target Modules):决定将 LoRA 适配器应用到哪些层。最常见的选择是注意力机制中的 q_proj(查询投影)和 v_proj(值投影)。

QLoRA——量化 LoRA

论文:QLoRA: Efficient Finetuning of Quantized LLMs, Dettmers et al. (NeurIPS 2023)

LoRA 尚未彻底解决的问题

LoRA 虽然大幅减少了可训练参数,但基础模型仍需完整地加载到显存中。一个 650 亿参数的模型,即使以 float16 精度加载也需要约 130GB 显存,这通常需要多块 A100 级别的 GPU。对于大多数研究者和开发者而言,这依然是难以企及的门槛。

QLoRA 的核心思路是:先将基础模型量化到极低的 4 位精度,再在其之上以 16 位精度运行 LoRA 适配器进行训练。这能极大缓解 模型训练 对海量计算资源的依赖。

三大技术创新

QLoRA 论文同时引入了三项相互配合的技术:

  1. NF4 (4-bit NormalFloat):一种专门针对正态分布权重设计的 4 位数据类型。由于神经网络权重天然近似服从正态分布,NF4 从信息论角度看是最优的编码方式,其存储效率比标准的 INT4 和 FP4 更高。
  2. 双重量化:量化常数(用于将 4 位值转换回浮点的参数)本身也会占用内存。QLoRA 进一步将这些量化常数也进行量化,为大型模型(如 650 亿参数)额外节省了约 3GB 内存。
  3. 统一内存分页:针对使用梯度检查点技术时可能出现的内存峰值,QLoRA 利用 NVIDIA 的统一内存机制。当某条序列的计算在 GPU 上触发内存不足风险时,自动将优化器状态卸载到 CPU 内存中,待需要时再换回,从而平滑训练过程。

代码示例

以下是使用 Hugging Face transformerspeft 库实现 QLoRA 的典型代码:

# Source: github.com/artidoro/qlora — qlora.py [2]
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
import torch

# Step 1: Define 4-bit quantization config (NF4)
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",         # NormalFloat4
    bnb_4bit_compute_dtype=torch.bfloat16, # Compute in bf16
    bnb_4bit_use_double_quant=True,    # Double quantization
)

# Step 2: Load base model in 4-bit
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b-hf",
    quantization_config=bnb_config,
    device_map="auto",
)

# Step 3: Prepare for k-bit training (handles frozen layer casting)
model = prepare_model_for_kbit_training(model)

# Step 4: Apply LoRA adapters in 16-bit on top
lora_config = LoraConfig(
    r=64,
    lora_alpha=16,
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)

在整个训练过程中,基础模型的权重始终保持为 NF4(4位、冻结状态),只有 LoRA 适配器的权重以 bf16 精度参与训练和更新。

基准测试结果(来自 QLoRA 论文)

QLoRA 技术催生了 Guanaco 系列模型。在由 GPT-4 进行评估的 Vicuna 基准测试中,Guanaco-65B 模型在单块 48GB 显存的 GPU 上仅训练了 24 小时,其性能就达到了 ChatGPT 的 99.3%

更关键的是,4 位量化相比 16 位 LoRA 带来的性能损失微乎其微:

| Method                 | Model | Memory Usage | Vicuna Score vs ChatGPT |
|------------------------|-------|--------------|-------------------------|
| Full Fine-Tuning (fp16)| 65B   | >780 GB      | —                       |
| LoRA (fp16)            | 65B   | ~130 GB      | —                       |
| QLoRA (NF4)            | 65B   | ~48 GB       | 99.3%                   |
| QLoRA (NF4)            | 33B   | ~24 GB       | 97.8%                   |
| QLoRA (NF4)            | 7B    | ~5 GB        | ~87%                    |

Guanaco-7B 模型仅占用约 5GB 显存,但在 Vicuna 基准上以超过 20 个百分点的巨大优势碾压了 Alpaca-65B。这正是 QLoRA 的革命性所在:它使得使用一块消费级 GPU 微调超大规模语言模型成为现实,极大地降低了技术门槛。

DoRA——权重分解低秩自适应

论文:DoRA: Weight-Decomposed Low-Rank Adaptation, Liu et al. (ICML 2024, Oral)

尽管 LoRA 表现出色,但与全量微调之间始终存在一道精度鸿沟,尤其是在秩 r 取值较低时更为明显。研究表明,LoRA 难以完整复现全参数更新时的梯度学习行为。

问题出在哪里?DoRA 论文通过一种新颖的权重分解分析给出了答案。

核心洞察

任何一个权重矩阵 W 都可以被分解为两个分量:

W = m × (V / ||V||_c)

其中,m幅度分量,一个标量向量,反映了每个输出神经元权重的“大小”;V / ||V||_c方向分量,即权重矩阵的单位方向向量。

DoRA 论文发现,在全量微调过程中,幅度和方向是以一种灵活且耦合的方式同步更新的。然而,LoRA 的更新模式将两者绑定在一起,主要只改变了方向,这在一定程度上限制了模型的表达能力。

DoRA 的解决方案是将两者解耦:在冻结的基础分解结构之上,让 LoRA 仅作用于方向分量,同时将幅度 m 作为一个独立的、可学习的标量参数进行更新。

W' = (m + Δm) × ((V + ΔV_LoRA) / ||V + ΔV_LoRA||_c)

通过这种方式,DoRA 的学习模式更接近于全量微调。最重要的是,由于幅度和方向在部署前可以合并回单个权重矩阵,因此在推理阶段不会引入任何额外的开销。

代码实现

以下是 DoRA 前向传播逻辑的简化实现,展示了其核心的分解机制:

# Source: github.com/NVlabs/DoRA — adapted from DoRA paper implementation [3]
import torch
import torch.nn as nn
import torch.nn.functional as F

class DoRALayer(nn.Module):
    def __init__(self, d_in, d_out, rank, lora_alpha):
        super().__init__()
        # Frozen pretrained weight
        self.weight = nn.Parameter(
            torch.randn(d_out, d_in), requires_grad=False
        )
        # Learnable magnitude (one scalar per output neuron)
        self.m = nn.Parameter(
            self.weight.norm(p=2, dim=1, keepdim=True)
        )
        # LoRA matrices for directional updates (trainable)
        std = 1 / torch.sqrt(torch.tensor(rank).float())
        self.lora_A = nn.Parameter(torch.randn(d_in, rank) * std)
        self.lora_B = nn.Parameter(torch.zeros(rank, d_out))
        self.rank = rank
        self.scaling = lora_alpha / rank

    def forward(self, x):
        # Compute the directional update from LoRA
        lora_update = (self.lora_A @ self.lora_B).T * self.scaling
        # Adapted weight = base weight + LoRA update
        adapted = self.weight + lora_update
        # Column-wise normalization → unit direction vectors
        column_norms = adapted.norm(p=2, dim=1, keepdim=True)
        V_normalized = adapted / column_norms
        # Scale by learned magnitude
        effective_weight = self.m * V_normalized
        return F.linear(x, effective_weight)

更便捷的是,通过 HuggingFace PEFT 库(peft >= 0.9.0)可以轻松启用 DoRA,它与标准 LoRA 配置的唯一区别在于一个标志位:

# Source: HuggingFace PEFT documentation — DoRA is supported from peft>=0.9.0 [3]
from peft import LoraConfig, get_peft_model

lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
    use_dora=True,   # ← This single flag enables DoRA
)
model = get_peft_model(model, lora_config)

基准测试结果(来自 DoRA 论文)

DoRA 论文在 8 个常识推理数据集上进行了评测。在 LLaMA-7B 和 LLaMA2-7B 上,以相同的秩 r 和可训练参数量,与 LoRA 进行公平对比:

| Method         | BoolQ | PIQA | HellaSwag | WinoGrande | ARC-e | ARC-c | OBQA | Avg   |
|----------------|-------|------|-----------|------------|-------|-------|------|-------|
| Full FT        | 69.4  | 82.3 | 89.7      | 82.4       | 79.8  | 60.7  | 81.6 | 77.99 |
| LoRA (r=32)    | 68.9  | 80.9 | 90.0      | 82.1       | 78.2  | 59.8  | 80.0 | 77.13 |
| DoRA (r=32)    | 70.0  | 83.6 | 91.0      | 83.0       | 81.4  | 65.8  | 83.4 | 79.75 |

在全部 8 个数据集上,DoRA 全面超越了 LoRA。差距在复杂推理任务上尤其显著,例如在 ARC-c 数据集上,DoRA 比 LoRA 高出 6 个百分点。论文还在视觉-语言任务上进行了测试,DoRA 同样全面优于 LoRA。

横向对比与选型指南

内存需求对比

方法 LLaMA 7B LLaMA 13B LLaMA 33B LLaMA 65B
Full Fine-Tuning (fp16) ~28 GB ~52 GB ~130 GB ~260 GB
LoRA (fp16) ~14 GB ~26 GB ~65 GB ~130 GB
QLoRA (NF4) ~5 GB ~8 GB ~20 GB ~48 GB
DoRA (fp16) ~14 GB ~26 GB ~65 GB ~130 GB

注:DoRA 因为多了一个幅度向量(每个目标层的每个输出神经元对应一个标量),会有极少量额外开销,但在实践中可忽略不计。

性能与资源综合对比

方法 常识推理表现 指令微调表现 内存需求 训练速度
Full Fine-Tune 基准 基准 极高
LoRA 略低于全量微调 可比
QLoRA 与 LoRA 相当 可达 ChatGPT 99.3%* 中等(量化开销)
DoRA 显著优于 LoRA 优于 LoRA 快(接近 LoRA)

*基于 Guanaco-65B 在 Vicuna 基准上的结果。

各场景下的最佳选择

该用 LoRA 的场景

  • 硬件条件:拥有 16GB 以上显存,目标模型大小不超过 130 亿参数左右。
  • 需求:追求稳定可靠、经过广泛实战验证的方案。LoRA 是所有后续 PEFT 方法的基石,HuggingFace 的 PEFT 生态围绕其构建,工具链最为成熟。
  • 建议:如果你是 PEFT 的初学者,从 LoRA 入手会是最省心、学习曲线最平缓的选择。

该用 QLoRA 的场景

  • 硬件条件:显存有限(如 24GB 或更低),但又希望微调 300 亿参数以上的超大模型。
  • 需求:QLoRA 使得在单块 48GB GPU 上微调 LLaMA-65B 成为现实。如果你只有 24GB 显存的卡,也能尝试 330 亿参数的模型。对于使用免费 Colab T4(15GB 显存)又想折腾 200 亿+ 模型的开发者,QLoRA 几乎是唯一可行的方案。这在很大程度上解决了 人工智能 开发中的算力瓶颈问题。

该用 DoRA 的场景

  • 硬件条件:与 LoRA 相同。
  • 需求:在完全相同的参数预算和训练成本下,追求最高的微调精度。DoRA 是 LoRA 的“即插即用”式升级品,在配置中只需添加 use_dora=True 即可,推理零成本,且在复杂推理任务上提升尤为明显。

该用 QLoRA + DoRA(QDoRA)的场景

  • 硬件条件:显存紧张。
  • 需求:既要最大限度地节省显存,又不想在精度上做过多妥协。这种组合已获得官方支持,实验数据表明其效果优于单纯的 QLoRA。部分早期实验甚至显示 QDoRA 能够持平乃至超越全量微调的结果。

总结

当前的微调技术生态已形成清晰的层次:LoRA 作为坚实底座,以其简单、快速和成熟的生态,满足大多数场景的需求;QLoRA 则打开了使用消费级显卡微调超大模型的大门,在显著降低显存占用的同时保持了优异性能;而 DoRA 更像是一次“免费升级”——在与 LoRA 成本几乎相同的前提下,提供了更优的效果,仅需一行代码即可启用。

这三种方法各有侧重,互为补充。作为实践者,你无需纠结于孰优孰劣,只需根据自己手头的硬件条件和对任务精度的要求,选择最贴合的那一个。无论是想快速上手,还是挑战极限,总有一款 PEFT 方案适合你。




上一篇:荣耀MWC发布Robot Phone与Magic V6:折叠屏补强,云台手机定义新物种
下一篇:企业网络边界安全管理制度模板:防火墙、IDS配置与访问控制(免费下载)
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-2 22:46 , Processed in 0.491826 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表