Fine-tuning

Layer 3 · Code

Fine-tuning

A LoRA linear layer implemented from scratch, then a full PEFT + TRL SFT script with a chat template, masking, and evaluation.

13 min read110 XP

1. LoRA from scratch — the whole idea in code

python
import torch
import torch.nn as nn

class LoRALinear(nn.Module):
    def __init__(self, base: nn.Linear, r: int = 16, alpha: int = 32, dropout: float = 0.05):
        super().__init__()
        self.base = base
        for p in self.base.parameters():
            p.requires_grad = False          # freeze the pretrained weight entirely

        self.A = nn.Parameter(torch.randn(r, base.in_features) * 0.01)   # small random init
        self.B = nn.Parameter(torch.zeros(base.out_features, r))         # zero init -> no-op at start
        self.scale = alpha / r
        self.drop = nn.Dropout(dropout)

    def forward(self, x):
        base_out = self.base(x)                              # frozen path, no grad
        lora_out = self.drop(x) @ self.A.T @ self.B.T         # low-rank path, trainable
        return base_out + lora_out * self.scale

    @torch.no_grad()
    def merge_(self):
        """Fold the adapter into the base weight for zero-overhead inference."""
        self.base.weight.data += (self.B @ self.A) * self.scale

    def trainable_parameters(self):
        return [self.A, self.B]


def inject_lora(model: nn.Module, target_names=("q_proj", "k_proj", "v_proj", "o_proj"), r=16, alpha=32):
    """Walk the model and replace target nn.Linear modules with LoRALinear in place."""
    for name, module in model.named_modules():
        for child_name, child in module.named_children():
            if any(t in child_name for t in target_names) and isinstance(child, nn.Linear):
                setattr(module, child_name, LoRALinear(child, r=r, alpha=alpha))
    return model
A drop-in replacement for nn.Linear that adds a trainable low-rank update.