本仓库包含 Python 包 loralib 的源代码,以及如何将其与 PyTorch 模型(例如 Hugging Face 中的模型)集成的多个示例。目前我们仅支持 PyTorch。有关 LoRA 的详细描述,请参阅我们的论文。
LoRA:大语言模型的低秩自适应
Edward J. Hu*, Yelong Shen*, Phillip Wallis, Zeyuan Allen-Zhu, Yuanzhi Li, Shean Wang, Lu Wang, Weizhu Chen
论文:https://arxiv.org/abs/2106.09685
视频讲解:https://www.youtube.com/watch?v=DhRoTONcyZE
2023年2月更新:LoRA 现已获得 Hugging Face 的 State-of-the-art Parameter-Efficient Fine-Tuning (PEFT) 库支持。
LoRA 通过冻结原始权重并学习一对低秩分解矩阵,来减少可训练参数的数量。这极大地降低了针对特定任务适配大语言模型时的存储需求,并且能够在部署时高效地进行任务切换,而不会引入推理延迟。LoRA 的性能也优于其他几种适配方法,包括适配器、前缀微调和全参数微调。
我们在 GLUE 基准测试上,使用 RoBERTa (Liu et al., 2019) 的 base 和 large 版本以及 DeBERTa (He et al., 2020) XXL 1.5B 模型,获得了与全参数微调相当或更优的结果,同时仅需训练和存储一小部分参数。点击下方数字可下载 RoBERTa 和 DeBERTa 的 LoRA 检查点。
| RoBERTa base 全参数微调 |
RoBERTa base LoRA |
DeBERTa XXL 全参数微调 |
DeBERTa XXL LoRA |
||
|---|---|---|---|---|---|
| 可训练参数量 | 125M | 0.8M | 1.5B | 4.7M | |
| MNLI (m-Acc/mm-Acc) | 87.6 | 87.5±.3/86.9±.3 | 91.7/91.9 | 91.9±.1/91.9±.2 | |
| SST2 (Acc) | 94.8 | 95.1±.2 | 97.2 | 96.9±.2 | |
| MRPC (Acc) | 90.2 | 89.7±.7 | 92.0 | 92.6±.6 | |
| CoLA (Matthew's Corr) | 63.6 | 63.4±1.2 | 72.0 | 72.4±1.1 | |
| QNLI (Acc) | 92.8 | 93.3±.3 | 96.0 | 96.0±.1 | |
| QQP (Acc) | 91.9 | 90.8±.1 | 92.7 | 92.9±.1 | |
| RTE (Acc) | 78.7 | 86.6±.7 | 93.9 | 94.9±.4 | |
| STSB (Pearson/Spearman Corr) | 91.2 | 91.5±.2/91.3±.2 | 92.9/92.6 | 93.0±.2/92.9±.3 | |
| 平均值 | 86.40 | 87.24 | 91.06 | 91.32 |
注意:要使用 LoRA 检查点,你仍然需要来自 Hugging Face 的原始预训练检查点。
全参数微调的数据取自 Liu et al. (2019) 和 He et al. (2020)。我们包含了实验结果的置信区间。请按照 examples/NLU/ 中的说明来复现我们的结果。
在 GPT-2 上,LoRA 与全参数微调以及其他高效调优方法(如 适配器 (Houlsby et al., 2019) 和 前缀调优 (Li and Liang, 2021))相比表现优异。我们在 E2E NLG Challenge、DART 和 WebNLG 上进行了评估:
| 方法 | 可训练参数量 | E2E (BLEU) | DART (BLEU) | WebNLG (BLEU-U/S/A) | |
|---|---|---|---|---|---|
| GPT-2 M (全参数微调) | 354.92M | 68.2 | 46.0 | 30.4/63.2/47.6 | |
| GPT-2 M (适配器) | 0.37M | 66.3 | 42.4 | 45.1/54.5/50.2 | |
| GPT-2 M (前缀调优) | 0.35M | 69.7 | 45.7 | 44.1/63.1/54.4 | |
| GPT-2 M (LoRA) | 0.35M | 70.4±.1 | 47.1±.2 | 46.7±.4/62.1±.2/55.3±.2 | |
| GPT-2 L (全参数微调) | 774.03M | 68.5 | 46.5 | 41.7/64.6/54.2 | |
| GPT-2 L (适配器) | 0.88M | 69.1±.1 | 45.7±.1 | 49.8±.0/61.1±.0/56.0±.0 | |
| GPT-2 L (前缀调优) | 0.77M | 70.3 | 46.5 | 47.0/64.2/56.4 | |
| GPT-2 L (LoRA) | 0.77M | 70.4±.1 | 47.5±.1 | 48.4±.3/64.0±.3/57.0±.1 |
非 LoRA 基线(GPT-2 large 上的适配器除外)取自 Li and Liang (2021)。我们包含了实验结果的置信区间。
下载 GPT-2 LoRA 检查点:
* GPT-2 Medium E2E (1.5 MB)
* GPT-2 Medium DART (1.5 MB)
* GPT-2 Medium WebNLG (1.5 MB)
* GPT-2 Large E2E (2.3 MB)
* GPT-2 Large DART (2.3 MB)
* GPT-2 Large WebNLG (2.3 MB)
请按照 examples/NLG/ 中的说明来复现我们的结果。
(此仓库的初始版本已存档在分支 "snapshot-9-15-2021" 中)
本仓库包含以下几个目录:
* loralib/ 包含 loralib 包的源代码,需要安装此包才能运行我们提供的示例;
* examples/NLG/ 包含使用我们的包在 GPT-2 中实现 LoRA 的示例,可用于复现论文中的结果;
* examples/NLU/ 包含使用我们的包在 RoBERTa 和 DeBERTa 中实现 LoRA 的示例,可在 GLUE 基准测试上产生有竞争力的结果;
* 查看我们如何在 GPT-2、RoBERTa 和 DeBERTa v2 中使用 loralib。
安装 loralib 非常简单:
bash
pip install loralib
# 或者
# pip install git+https://github.com/microsoft/LoRA
你可以选择通过替换为 loralib 中实现的对应模块来适配某些层。目前我们支持 nn.Linear、nn.Embedding 和 nn.Conv2d。我们还支持 MergedLinear,用于单个 nn.Linear 代表多个层的情况,例如在某些注意力 qkv 投影的实现中(更多信息请参阅附加说明)。
```python
# ===== 替换前 =====
# layer = nn.Linear(in_features, out_features)
# ===== 替换后 ======
import loralib as lora
# 添加一对秩 r=16 的低秩自适应矩阵
layer = lora.Linear(in_features, out_features, r=16)
```
python
import loralib as lora
model = BigModel()
# 这将为名称中不包含字符串 "lora_" 的所有参数设置 requires_grad=False
lora.mark_only_lora_as_trainable(model)
# 训练循环
for batch in dataloader:
...state_dict。python
# ===== 替换前 =====
# torch.save(model.state_dict(), checkpoint_path)
# ===== 替换后 =====
torch.save(lora.lora_state_dict(model), checkpoint_path)load_state_dict 加载检查点时,请确保设置 strict=False。python
# 首先加载预训练检查点
model.load_state_dict(torch.load('ckpt_pretrained.pt'), strict=False)
# 然后加载 LoRA 检查点
model.load_state_dict(torch.load('ckpt_lora.pt'), strict=False)虽然我们在示例中专注于一个简单而有效的设置,即仅适配 Transformer 中的 q 和 v 投影,但 LoRA 可以应用于预训练权重的任何子集。我们鼓励你探索不同的配置,例如通过将 nn.Embedding 替换为 lora.Embedding 来适配嵌入层,和/或适配 MLP 层。很可能最优配置会因模型架构和任务的不同而有所变化。
某些 Transformer 实现使用单个 nn.Linear 作为查询、键和值的投影矩阵。如果希望限制对单个矩阵更新的秩,则必须将其拆分为三个独立的矩阵或使用 lora.MergedLinear。如果选择拆分该层,请确保相应地修改检查点。
# ===== 替换前 =====
# qkv_proj = nn.Linear(d_model, 3*d_model)
# ===== 替换后 =====
# 将其拆分(记得相应地修改预训练检查点)
q_proj = lora.Linear(d_model, d_model, r=8)
k_proj = nn.Linear(d_model, d_model)
v_proj = lora.Linear(d_model, d_model, r=8)
# 或者,使用 lora.MergedLinear(推荐)
qkv_proj = lora.MergedLinear(d_model, 3*d_model, r=8, enable_lora=[True, False, True])
lora 中使其易于尝试。你可以在调用 mark_only_lora_as_trainable 时通过传递 "all" 或 "lora_only" 给 bias= 参数来将某些偏置标记为可训练。保存检查点时,记得将相应的 bias= 参数传递给 lora_state_dict。# ===== 替换前 =====
# lora.mark_only_lora_as_trainable(model) # 不训练任何偏置向量
# ===== 替换后 =====
# 训练与我们所应用 LoRA 的模块相关的所有偏置向量
lora.mark_only_lora_as_trainable(model, bias='lora_only')
# 或者,我们可以训练模型中的 *所有* 偏置向量,包括 LayerNorm 的偏置
lora.mark_only_lora_as_trainable(model, bias='all')
# 保存检查点时,使用相同的 bias= 参数('all' 或 'lora_only')
torch.save(lora.lora_state_dict(model, bias='all'), checkpoint_path)
model.eval() 将触发 LoRA 参数与相应预训练参数的合并,从而消除后续前向传递的额外延迟。再次调用 model.train() 将撤销合并。可以通过向 LoRA 层传递 merge_weights=False 来禁用此功能。如有任何问题,请联系我们或发布 issue。
关于 loralib 包的问题:
* Edward Hu (edward@edwardjhu.com)
* Phillip Wallis (phwallis@microsoft.com)
* Weizhu Chen (wzchen@microsoft.com)
GPT-2 示例相关问题:
* Phillip Wallis (phwallis@microsoft.com)
* Yelong Shen (yeshe@microsoft.com)
RoBERTa/DeBERTa 示例相关问题:
* Lu Wang (luw@microsoft.com)
我们按字母顺序感谢以下人员提供了宝贵的反馈:Jianfeng Gao, Jade Huang