""" Generic LoRA (Low-Rank Adaptation, Hu et al. 2021) wrapper -- module-agnostic, works on any nn.Module built from nn.Linear layers. Used here to fine-tune the value head under self-play with a capacity-constrained update instead of a full-parameter one, which empirically fine-tunes more reliably on a small, self-generated data distribution without regressing. """ import torch from torch import nn class LoRALinear(nn.Module): """Wraps an existing nn.Linear, freezing its original weight/bias and adding a trainable low-rank delta: output = frozen_linear(x) + scaling * (x @ A^T) @ B^T `B` is initialized to ZERO, so the wrapped layer's output is byte- identical to the original before any training happens.""" def __init__(self, linear, rank=4, alpha=1.0): super().__init__() assert isinstance(linear, nn.Linear), f"LoRALinear only wraps nn.Linear, got {type(linear)}" self.linear = linear for p in self.linear.parameters(): p.requires_grad = False self.rank = rank self.scaling = alpha / rank device = linear.weight.device dtype = linear.weight.dtype self.lora_A = nn.Parameter(torch.randn(rank, linear.in_features, device=device, dtype=dtype) * 0.01) self.lora_B = nn.Parameter(torch.zeros(linear.out_features, rank, device=device, dtype=dtype)) def forward(self, x): base = self.linear(x) delta = (x @ self.lora_A.t()) @ self.lora_B.t() return base + self.scaling * delta def lora_parameters(self): return [self.lora_A, self.lora_B] def merge_into_base(self): """Fold the current LoRA delta into the frozen base weight and reset B to zero -- used to "commit" a trained adapter back into a plain nn.Linear-equivalent state so the saved checkpoint is an ordinary state_dict, loadable with zero LoRA-awareness downstream.""" with torch.no_grad(): delta_w = self.scaling * (self.lora_B @ self.lora_A) self.linear.weight.add_(delta_w) self.lora_B.zero_() def apply_lora(module, rank=4, alpha=1.0): """Recursively replace every nn.Linear submodule of `module` with a LoRALinear wrapper. Returns the new LoRA parameters for the caller's optimizer. Modifies `module` in place.""" lora_params = [] for name, child in list(module.named_children()): if isinstance(child, nn.Linear): wrapped = LoRALinear(child, rank=rank, alpha=alpha) setattr(module, name, wrapped) lora_params.extend(wrapped.lora_parameters()) else: lora_params.extend(apply_lora(child, rank=rank, alpha=alpha)) return lora_params