7.0. LoRA Improvements in fine-tuning

Melhorias do LoRA

O uso do LoRA reduz muito a computação necessária para ajustar modelos já treinados.

LoRA torna possível ajustar grandes modelos de forma eficiente, mudando apenas uma pequena parte do modelo. Isso reduz o número de parâmetros que você precisa treinar, economizando memória e recursos computacionais. Isso ocorre porque:

  1. Reduz o Número de Parâmetros Treináveis: Em vez de atualizar toda a matriz de pesos no modelo, o LoRA divide a matriz de pesos em duas matrizes menores (chamadas A e B). Isso torna o treinamento mais rápido e requer menos memória porque menos parâmetros precisam ser atualizados.

  2. Isso ocorre porque, em vez de calcular a atualização completa de pesos de uma camada (matriz), ele a aproxima a um produto de 2 matrizes menores, reduzindo a atualização a calcular:\

2. **Mantém os Pesos do Modelo Original Inalterados**: O LoRA permite que você mantenha os pesos do modelo original os mesmos e apenas atualize as **novas matrizes pequenas** (A e B). Isso é útil porque significa que o conhecimento original do modelo é preservado, e você só ajusta o que é necessário. 3. **Ajuste Fino Eficiente Específico para Tarefas**: Quando você deseja adaptar o modelo a uma **nova tarefa**, pode apenas treinar as **pequenas matrizes LoRA** (A e B) enquanto deixa o resto do modelo como está. Isso é **muito mais eficiente** do que re-treinar o modelo inteiro. 4. **Eficiência de Armazenamento**: Após o ajuste fino, em vez de salvar um **novo modelo completo** para cada tarefa, você só precisa armazenar as **matrizes LoRA**, que são muito pequenas em comparação com o modelo inteiro. Isso facilita a adaptação do modelo a muitas tarefas sem usar muito armazenamento.

Para implementar LoraLayers em vez de Linear durante um ajuste fino, este código é proposto aqui 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)

Referências

Last updated