7.0. LoRA Improvements in fine-tuning

Ulepszenia LoRA

Użycie LoRA znacznie redukuje obliczenia potrzebne do dostrajania już wytrenowanych modeli.

LoRA umożliwia efektywne dostrajanie dużych modeli poprzez zmianę tylko małej części modelu. Redukuje liczbę parametrów, które musisz wytrenować, oszczędzając pamięć i zasoby obliczeniowe. Dzieje się tak, ponieważ:

  1. Redukuje liczbę parametrów do wytrenowania: Zamiast aktualizować całą macierz wag w modelu, LoRA dzieli macierz wag na dwie mniejsze macierze (nazywane A i B). To sprawia, że trening jest szybszy i wymaga mniej pamięci, ponieważ mniej parametrów musi być aktualizowanych.

  2. Dzieje się tak, ponieważ zamiast obliczać pełną aktualizację wag warstwy (macierzy), przybliża ją do iloczynu 2 mniejszych macierzy, co redukuje aktualizację do obliczenia:\

2. **Zachowuje oryginalne wagi modelu bez zmian**: LoRA pozwala na zachowanie oryginalnych wag modelu, a jedynie aktualizuje **nowe małe macierze** (A i B). To jest pomocne, ponieważ oznacza, że oryginalna wiedza modelu jest zachowana, a ty tylko dostosowujesz to, co jest konieczne. 3. **Efektywne dostrajanie specyficzne dla zadania**: Kiedy chcesz dostosować model do **nowego zadania**, możesz po prostu wytrenować **małe macierze LoRA** (A i B), pozostawiając resztę modelu w niezmienionej formie. To jest **znacznie bardziej efektywne** niż ponowne trenowanie całego modelu. 4. **Efektywność przechowywania**: Po dostrojeniu, zamiast zapisywać **cały nowy model** dla każdego zadania, musisz tylko przechowywać **macierze LoRA**, które są bardzo małe w porównaniu do całego modelu. To ułatwia dostosowanie modelu do wielu zadań bez użycia zbyt dużej ilości pamięci.

Aby zaimplementować LoraLayers zamiast liniowych podczas dostrajania, proponowany jest tutaj ten kod 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)

References

Last updated