7.0. Поліпшення LoRA у тонкому налаштуванні

Reading time: 2 minutes

Поліпшення LoRA

tip

Використання LoRA значно зменшує обчислення, необхідні для тонкого налаштування вже навчених моделей.

LoRA дозволяє ефективно тонко налаштовувати великі моделі, змінюючи лише невелику частину моделі. Це зменшує кількість параметрів, які потрібно навчати, економлячи пам'ять та обчислювальні ресурси. Це відбувається тому, що:

  1. Зменшує кількість навчальних параметрів: Замість оновлення всієї вагової матриці в моделі, LoRA ділить вагову матрицю на дві менші матриці (названі A та B). Це робить навчання швидшим і вимагає менше пам'яті, оскільки потрібно оновити менше параметрів.

  2. Це відбувається тому, що замість обчислення повного оновлення ваги шару (матриці), воно апроксимує його до добутку 2 менших матриць, зменшуючи оновлення для обчислення:\

  1. Зберігає оригінальні ваги моделі незмінними: LoRA дозволяє зберігати оригінальні ваги моделі такими ж, і лише оновлює нові маленькі матриці (A та B). Це корисно, оскільки означає, що оригінальні знання моделі зберігаються, і ви лише налаштовуєте те, що необхідно.
  2. Ефективне тонке налаштування для конкретних завдань: Коли ви хочете адаптувати модель до нового завдання, ви можете просто навчати маленькі матриці LoRA (A та B), залишаючи решту моделі без змін. Це набагато ефективніше, ніж повторне навчання всієї моделі.
  3. Ефективність зберігання: Після тонкого налаштування, замість збереження цілої нової моделі для кожного завдання, вам потрібно зберігати лише матриці LoRA, які є дуже маленькими в порівнянні з усією моделлю. Це полегшує адаптацію моделі до багатьох завдань без використання занадто багато пам'яті.

Для реалізації LoraLayers замість лінійних під час тонкого налаштування, тут пропонується цей код https://github.com/rasbt/LLMs-from-scratch/blob/main/appendix-E/01_main-chapter-code/appendix-E.ipynb:

python
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)

Посилання