使用奇异值分解微调 LLM

社区文章 发布于 2024 年 6 月 2 日

这篇博文将讨论一种通过截断奇异值分解 (SVD) 以参数开销较低的方式微调语言模型的方法。这项技术类似于LoRA,但可训练参数数量要少得多。

接下来,主要目标是重现最近论文LoRA-XS:使用极少量参数的低秩适应中的结果,并将其作为 Python 包轻松提供。

使用低秩适应 (LoRA) 进行训练

LoRA 的主要思想是将每个线性变换中的权重分解为两部分:一个不可训练的矩阵(包含初始权重)和一组可训练的权重,它们在低维潜在空间(通常是 2 的小幂次)之间进行变换。

image/png

上图中,原始权重矩阵是冻结的,只有两个投影矩阵是可训练的。

这项技术在允许更便宜的 GPU 设置上进行微调方面取得了令人难以置信的成功。通过严格将训练限制在低秩投影矩阵上,可以使用原始参数数量的约 1/1000 来微调模型的所有层。

仅训练奇异值

奇异值分解通过线性代数技术将矩阵分解为三个元素来近似矩阵。给定一个原始 m×nm \times n 权重矩阵 MM 和一个整数 qq,SVD 构建一个近似矩阵

M~=UΣV\tilde{M} = U\,\Sigma\,V

通过计算 q×nq \times n 矩阵 UUm×qm \times q 矩阵 VV,以及对角方阵 q×qq \times q 矩阵 Σ\Sigma

image/png

请注意,尽管 Σ\Sigma 是一个方阵,但唯一的非零元素位于对角线上,并被称为**奇异值**。 qq 的值是近似中选择的奇异值的数量。在本帖中,原始 m×nm \times n 矩阵的**秩** r 定义为 min(m,n)\min(m, n)

这是 SVD 训练的核心要素:*我们只训练奇异值,而不是训练整个权重矩阵 M。*

更具体地说,我们注意到矩阵 M 等价于

M=MUΣV+UΣVM = M - U\,\Sigma\,V + U\,\Sigma\,V

可以改写为

M=M+UΣVM = M' + U\,\Sigma\,V

其中 M=MUΣVM' = M - U\,\Sigma\,V

如果我们冻结 MM'UUVV 的参数,那么只有奇异值会在训练循环中更新。从内存角度来看,这比 LoRA 更便宜,因为矩阵 UUVV 保持不变。

image/png

上图中,只有奇异值是可训练的。所有其他参数都被冻结。

此方法是 **SVD 训练**技术,与 LoRA-XS 中描述的方法非常相似。主要区别在于 LoRA-XS 直接使用 MM,而不是 M=MUΣVM' = M - U\,\Sigma\,V

可训练参数的数量

接下来,我将对 Microsoft Research 开发的模型 Phi-3-mini-128k-instruct 进行一些实验。

使用 SVD 训练技术,可训练参数的数量取决于近似中使用的秩分数 q/rq/r。SVD 分解应用于所有线性层。下表展示了 LoRA 和 SVD 训练的典型超参数之间的差异。这些是使用 SVD 训练时常用的值

SVD 秩分数 可训练参数
0.1 239_283
0.2 278_886
0.5 397_824

这需要与以下常见的 LoRA 配置进行比较

低秩维度 可训练参数
8 12_864_000
16 25_728_000
32 51_456_000

请注意,低秩维度与 SVD 秩分数不直接对应。这是因为 SVD 近似应用于所有线性层——每个层具有不同的维度。秩分数为 0.1 会根据权重产生可变数量的 qq

为了便于比较,上述可训练参数的数量不包括两种情况下的 token 嵌入。该数字是一个常数,对于所考虑的模型,为 98,500,608。

SVD 训练库

一个利用 SVD 训练的简单库是 *svd-training*

pip install svd-training

此库需要对模型进行预处理,以生成矩阵 MM'UUΣ\SigmaVV

from transformers import AutoTokenizer, AutoModelForCausalLM
from svd_training.svd_model import SVDForCausalLM

filename = "mistralai/Mistral-7B-Instruct-v0.1"
tokenizer = AutoTokenizer.from_pretrained(filename)
model = AutoModelForCausalLM.from_pretrained(filename)

svd_model = SVDForCausalLM.create_from_model(model, rank_fraction=0.1) # Create the SVD model

### Train the model using your favourite training loop
...
###

svd_model.merge()  # Merge the SVD layers back into the model
svd_model.save_pretrained("svd_model/")  # Save the model

最终,所有元素都合并到原始权重矩阵中。合并操作后,模型可以像任何其他 HF 模型一样保存和加载。

在 Ultrachat 上进行微调

作为测试,上述 Phi-3 模型在 Ultrachat 数据集上进行了微调。此测试包括完整模型微调、8 维 LoRA 和秩分数为 0.1 的 SVD 训练。据我所知,Ultrachat 数据集不属于 Phi-3 原始训练集的一部分。

所有这些模型均使用标准的 SFTTrainer 和不同的学习率进行训练。这些是经过简短超参数扫描后为每个模型选择的值。

模型 学习率
完整模型微调(除了嵌入) 5e-5
8 维 LoRA 1e-4
SVD 训练,r=0.1 1e-2

Ultrachat 数据集上的评估损失如下(越低越好)

模型 Ultrachat 评估损失
原始 Phi-3-mini-128k-instruct 4.01
完整模型微调(除了嵌入) 2.60
8 维 LoRA 2.78
SVD 训练,r=0.1 2.67

最佳结果来自完整模型微调。次优结果来自 SVD 训练技术。

结论

SVD 训练方法似乎与**参数更重的 LoRA 表现不相上下**。这令人瞩目,因为 **SVD 使用的参数数量要少两个数量级**。虽然还需要更多工作来证实这些结果,但该方法非常有前景。

参数较少的模型显然对数据需求较少。这可能意味着 SVD 技术在小型数据集上进行微调时特别有用。未来的工作将更好地定义使该技术最成功的条件。

参考文献

[1] LoRA-XS: 使用极少量参数的低秩适应

[2] 在 Ultrachat 上 SVD 调整的 Phi-3 模型

社区

注册登录 发表评论