Lora

Lora

LoRA (Low-Rank Adaptation) 是一种参数高效微调方法,主要目标是通过低秩矩阵分解高效地微调预训练语言模型。

  • 核心思想

    • 冻结预训练模型的原始权重 WWW,通过引入低秩矩阵 AAA 和 BBB 来表示权重更新: $W′=W+ΔW,ΔW=\frac{\alpha}{r} AB$ 其中:
      • $A \in \mathbb{R}^{d \times r}$
      • $B \in \mathbb{R}^{r \times d}$
      • r 是低秩分解的秩,通常 $r \ll d$,以减少参数量。
      • $\alpha$为LoRA 的缩放因子。
  • 特点

    • 高效微调:只需学习两个小矩阵 A 和 B 的参数,而不需要调整整个预训练模型权重。
    • 参数量小:相比全参数微调,LoRA 显著减少了需要调整的参数量,适合低资源场景。
    • 通用性强:可以应用于各种预训练模型(如 Transformer)。

Lora的一些参数

learning_rate = 3e-4
batch_size = 128
micro_batch_size = 1
max_iters = 50000  # train dataset size
weight_decay = 0.01
lora_r = 8
lora_alpha = 16
lora_dropout = 0.05
lora_query = True
lora_key = False
lora_value = True
lora_projection = False
lora_mlp = False
lora_head = False
warmup_steps = 100
  • lora_r = 8

    • 含义:LoRA 的低秩分解秩值 r。
    • 作用:控制低秩矩阵 A 和 B 的维度,直接决定 LoRA 的参数量。
    • 解释:较小的 r 减少训练参数,适合资源有限场景;较大的 r 提供更强的表达能力。
  • lora_alpha = 16

    • 含义:LoRA 的缩放因子。
    • 作用:调整低秩更新矩阵$\Delta W = \frac{\alpha}{r} AB$ 的整体影响强度。
    • 解释lora_alpha = 16 是常见值,确保更新矩阵对模型权重有适当的影响。
  • lora_dropout = 0.05

    • 含义:LoRA 的 Dropout 概率。
    • 作用:在训练时随机丢弃一部分低秩矩阵的值,增加正则化,防止过拟合。
    • 解释0.05 表示有 5% 的权重会在每次更新时被随机置零。
  • lora_query = True

    • 含义:是否对注意力机制中的 Query 矩阵应用 LoRA。
    • 作用:为 Query 矩阵引入低秩更新,以调整模型的查询能力。
    • 解释True 表示对 Query 矩阵使用 LoRA。
  • lora_key = False

    • 含义:是否对 Key 矩阵应用 LoRA。
    • 解释False 表示不对 Key 矩阵进行 LoRA 更新,保留预训练模型的权重。
  • lora_value = True

    • 含义:是否对 Value 矩阵应用 LoRA。
    • 作用:为 Value 矩阵引入低秩更新。
    • 解释True 表示对 Value 矩阵应用 LoRA,这是常见的配置。
  • lora_projection = False

    • 含义:是否对注意力机制中的投影矩阵应用 LoRA。
    • 解释False 表示不对投影矩阵更新,保持默认设置。
  • lora_mlp = False

    • 含义:是否对 Transformer 中的 MLP 层应用 LoRA。
    • 解释False 表示不对 MLP 层应用低秩更新。
  • lora_head = False

    • 含义:是否对注意力机制中的头部进行 LoRA 更新。
    • 解释False 表示不单独对注意力头应用 LoRA。

Memory Savings with QLoRA

QLoRA 的核心组成

1. 权重量化
  • 低比特量化(4-bit Quantization)

    • 将预训练模型的全精度权重(通常是 16-bit 或 32-bit 浮点数)量化为 4-bit 表示。
    • 量化过程使用方法如 NF4 (Normalized Float 4),这是 QLoRA 的核心量化技术:
      • NF4 是一种针对权重的非对称量化方法,通过对权重进行归一化和重新映射,保留其分布的关键特性。
      • 与传统的整数量化相比,NF4 能更好地保持权重的动态范围,从而在低比特表示下减少性能损失。
        NF4 的量化过程
Step 1: 权重归一化

对每个张量 W 的权重进行归一化:

  • 归一化公式为: \(W' = \frac{W}{\sigma}, \quad \sigma = \text{max}(|W|)\) 或: \(W' = \frac{W - \mu}{\sigma}, \quad \mu = \text{mean}(W), \, \sigma = \text{std}(W)\)
  • 归一化后,权重值被映射到一个统一的范围,例如 [−1,1]。
Step 2: 非线性量化映射

对归一化后的权重 W′ 进行非线性映射:

  • 使用预设的非线性分段函数对权重分布进行量化。
  • 映射公式可以是对数或指数函数,例如: $Q(W’)=sign(W’)⋅log(1+k∣W’∣)$ 其中 k是非线性映射的调节因子。
Step 3: 离散化到 4-bit

将映射后的权重 Q(W′)离散化到 4-bit 表示:

  • 预先定义 16 个离散值作为量化区间。
  • 每个权重值映射到最近的离散值。
  • 例如假设使用 线性分布(均匀间隔)的量化表,量化值为:

\(q = \{-1.0, -0.87, -0.73, -0.60, -0.47, -0.33, -0.20, -0.07, 0.07, 0.20, 0.33, 0.47, 0.60, 0.73, 0.87, 1.0\}\)

  • 表中的 16 个值均匀覆盖 [−1.0,1.0]。
  • 每个值对应一个整数索引 $i = 0, 1, \dots, 15$ 如: \(W=\left[\begin{array}{ll} 0.8 & -0.6 \\ 0.1 & -0.9 \end{array}\right]\) 查找相近的量化值 \(\begin{array}{|l|l|l|} \hline \text { 原始权重值 } & \text { 最近量化值 } & \text { 索引 } \\ \hline 0.8 & 0.87 & 14 \\ \hline-0.6 & -0.6 & 3 \\ \hline 0.1 & 0.07 & 8 \\ \hline-0.9 & -0.87 & 1 \\ \hline \end{array}\)

    此矩阵占用 4-bit 的存储空间,大大降低了存储需求。

Step 4: 反量化(推理时)

在推理过程中,反量化恢复权重的近似值:

  • 假设量化表:
  • \[\begin{array}{|l|l|l|} \hline \text { 原始权重值 } & \text { 最近量化值 } & \text { 索引 } \\ \hline 0.8 & 0.87 & 14 \\ \hline-0.6 & -0.6 & 3 \\ \hline 0.1 & 0.07 & 8 \\ \hline-0.9 & -0.87 & 1 \\ \hline \end{array}\]
  • 利用量化表,将 4-bit 表示还原为对应的浮点数值。
2. LoRA 微调

在权重量化后,模型的核心权重$Q_{\text{4-bit}}$​ 被冻结,QLoRA 插入了 低秩适配(Low-Rank Adaptation)矩阵 A 和 B,用于学习任务相关的调整。

假设我们进行文本分类任务,QLoRA 在量化权重的基础上进行以下操作:

  1. 在训练过程中,只更新插入的矩阵 A 和 B。

    • 示例:假设 A 和 B 的维度分别为 $[128 \times 4]$ 和 $[4 \times 128]$,远小于原始模型权重。
  2. 更新后的权重表示为:

    $W_{\text{fine-tuned}} = Q_{\text{4-bit}} + A \cdot B$

这样,仅调整少量参数即可完成模型的微调,显著降低计算和存储开销。

Lora微调的一些代码

在 Colab Notebook 中微调您自己的 Llama 2 模型

Lora的cofig以及在SFT

from peft import LoraConfig, PeftModel

# The model that you want to train from the Hugging Face hub
model_name = "NousResearch/llama-2-7b-chat-hf"

# The instruction dataset to use
dataset_name = "mlabonne/guanaco-llama2-1k"

# Fine-tuned model name
new_model = "llama-2-7b-miniguanaco"

# Load LoRA configuration
peft_config = LoraConfig(
    lora_alpha=lora_alpha,
    lora_dropout=lora_dropout,
    r=lora_r,
    bias="none",
    task_type="CAUSAL_LM",
)

# Set supervised fine-tuning parameters 
trainer = SFTTrainer( model=model, train_dataset=dataset, peft_config=peft_config, dataset_text_field="text", max_seq_length=max_seq_length, tokenizer=tokenizer, args=training_arguments, packing=packing, )

加载新的llama-2-miniguanaco

我们现在如何存储新llama-2-7b-miniguanaco模型?我们需要将 LoRA 中的权重与基础模型合并。不幸的是,据我所知,没有直接的方法可以做到这一点:我们需要以 FP16 精度重新加载基础模型,然后使用库peft合并所有内容。微调需要FP32保证精度。

# Reload model in FP16 and merge it with LoRA weights
base_model = AutoModelForCausalLM.from_pretrained(
    model_name,
    low_cpu_mem_usage=True,
    return_dict=True,
    torch_dtype=torch.float16,
    device_map=device_map,
)
model = PeftModel.from_pretrained(base_model, new_model)
model = model.merge_and_unload() #模型权重的合并(w+deltaw)

# Reload tokenizer to save it
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
Tags: LLM
Share: X (Twitter) Facebook LinkedIn