LoRA 為 Low-Rank Adaptation 的縮寫
在訓練模型時,不對整個模型所有參數都進行訓練
放一個小模型在旁邊,與大模型的輸出合併運算
從而減少反向梯度所需消耗的記憶體使用量
若 Batch Size 為 \(32\)
則我們的輸入是一個 \(32 \times 100\) 的矩陣 \(I\)
而模型的權重為 \(100 \times 100\) 的矩陣 \(W\)
最後輸出的結果為 \(I \times W\) 為 \(32\times 100\times 100\)
這樣更新參數量大小為 \(100 \times 100=10000\)
LoRA 將計算拆解為 \(A,B\) 兩個矩陣
分別為 \(100\times10\) 跟 \(10\times100\) 的矩陣
然後計算 \(I\times A\times B\) 得到 \(32\times100\times100\)
但更新參數量只有 \(100\times10+10\times100=2000\)
如此一來就減少了八成的參數量
LoRA 為何不會增加參數量呢?
為何矩陣 \(W,A,B\) 可以合併呢?
原本的計算為 \(I\times W+I\times A\times B\)
根據分配律,可以改成 \(I\times (W+A\times B)\)
\(A \in \mathbb{R}^{100 \times 10}\times B\in \mathbb{R}^{10 \times 100}=W' \in \mathbb{R}^{100\times 100}\)
\(W \in \mathbb{R}^{100\times 100}+W' \in \mathbb{R}^{100\times 100}=W'' \in \mathbb{R}^{100\times 100}\)
\(W''\) 的大小與原本的 \(W\) 一樣
所以參數量並沒有增加,且三個矩陣能夠合併
pip install peft
原本的訓練程式碼
model = ModelCls.from_pretrained("gpt2") args = TrainingArguments(...) trainer = Trainer(model, args) trainer.train()
使用 LoRA 的訓練程式碼
model = ModelCls.from_pretrained("gpt2") model = get_peft_model(model, LoraConfig(r=8)) args = TrainingArguments(...) trainer = Trainer(model, args) trainer.train()
Comparison | SFT | LoRA | Delta |
---|---|---|---|
Parameters | 4M | 9K | 99.78% |
GPU Memory | 2560 MiB | 2380 MiB | 7.03% |
Time Cost | 150s | 120s | 20.00% |
Comparison | SFT | LoRA | Delta |
---|---|---|---|
Parameters | 109M | 300K | 99.72% |
GPU Memory | 22 GiB | 19 GiB | 13.64% |
Time Cost | ~45m | ~37m | 17.78% |
Epoch | SFT-Loss | SFT-Acc | LoRA-Loss | LoRA-Acc |
---|---|---|---|---|
1.0 | 0.3401 | 0.8574 | 0.4117 | 0.8134 |
2.0 | 0.3080 | 0.8707 | 0.3968 | 0.8174 |
3.0 | 0.3050 | 0.8762 | 0.3538 | 0.8442 |
4.0 | 0.3056 | 0.8790 | 0.3457 | 0.8492 |
5.0 | 0.3084 | 0.8776 | 0.3392 | 0.8523 |
.vscode-server
換個位置"remote.SSH.serverInstallPath": {
"trainer57": "/mnt/ssd4t/.vscode-server",
"trainer32": "/mnt/ssd2t/.vscode-server"
}