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 的缩放因子。
- 冻结预训练模型的原始权重 WWW,通过引入低秩矩阵 AAA 和 BBB 来表示权重更新: $W′=W+ΔW,ΔW=\frac{\alpha}{r} AB$ 其中:
-
特点:
- 高效微调:只需学习两个小矩阵 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 在量化权重的基础上进行以下操作:
-
在训练过程中,只更新插入的矩阵 A 和 B。
- 示例:假设 A 和 B 的维度分别为 $[128 \times 4]$ 和 $[4 \times 128]$,远小于原始模型权重。
-
更新后的权重表示为:
$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"