7.0. LoRA Improvements in fine-tuning
Reading time: 2 minutes
LoRA Improvements
tip
Використання LoRA значно зменшує обчислення, необхідні для додаткового налаштування вже навчених моделей.
LoRA дозволяє ефективно додатково налаштовувати великі моделі, змінюючи лише невелику частину моделі. Це зменшує кількість параметрів, які потрібно навчати, економлячи пам'ять та обчислювальні ресурси. Це відбувається тому, що:
-
Зменшує кількість навчальних параметрів: Замість оновлення всієї матриці ваг у моделі, LoRA ділить матрицю ваг на дві менші матриці (названі A та B). Це робить навчання швидшим і вимагає менше пам'яті, оскільки потрібно оновити менше параметрів.
-
Це відбувається тому, що замість обчислення повного оновлення ваги шару (матриці), воно апроксимує його до добутку 2 менших матриць, зменшуючи оновлення для обчислення:\
 (1).png)
- Зберігає оригінальні ваги моделі незмінними: LoRA дозволяє зберігати оригінальні ваги моделі такими ж, і лише оновлює нові маленькі матриці (A та B). Це корисно, оскільки означає, що оригінальні знання моделі зберігаються, і ви лише налаштовуєте те, що необхідно.
- Ефективне специфічне налаштування завдань: Коли ви хочете адаптувати модель до нового завдання, ви можете просто навчити маленькі матриці LoRA (A та B), залишаючи решту моделі без змін. Це набагато ефективніше, ніж повторне навчання всієї моделі.
- Ефективність зберігання: Після додаткового налаштування, замість збереження цілої нової моделі для кожного завдання, вам потрібно зберігати лише матриці LoRA, які є дуже маленькими в порівнянні з усією моделлю. Це полегшує адаптацію моделі до багатьох завдань без використання занадто багато пам'яті.
Для реалізації LoraLayers замість лінійних під час додаткового налаштування, тут пропонується цей код https://github.com/rasbt/LLMs-from-scratch/blob/main/appendix-E/01_main-chapter-code/appendix-E.ipynb:
import math
# Create the LoRA layer with the 2 matrices and the alpha
class LoRALayer(torch.nn.Module):
def __init__(self, in_dim, out_dim, rank, alpha):
super().__init__()
self.A = torch.nn.Parameter(torch.empty(in_dim, rank))
torch.nn.init.kaiming_uniform_(self.A, a=math.sqrt(5)) # similar to standard weight initialization
self.B = torch.nn.Parameter(torch.zeros(rank, out_dim))
self.alpha = alpha
def forward(self, x):
x = self.alpha * (x @ self.A @ self.B)
return x
# Combine it with the linear layer
class LinearWithLoRA(torch.nn.Module):
def __init__(self, linear, rank, alpha):
super().__init__()
self.linear = linear
self.lora = LoRALayer(
linear.in_features, linear.out_features, rank, alpha
)
def forward(self, x):
return self.linear(x) + self.lora(x)
# Replace linear layers with LoRA ones
def replace_linear_with_lora(model, rank, alpha):
for name, module in model.named_children():
if isinstance(module, torch.nn.Linear):
# Replace the Linear layer with LinearWithLoRA
setattr(model, name, LinearWithLoRA(module, rank, alpha))
else:
# Recursively apply the same function to child modules
replace_linear_with_lora(module, rank, alpha)