Lab 8: A Low-Rank Update and a Forgetting Demo
Factor a 4×4 update, then train XOR then AND and watch the first task drop.
Work in a Jupyter notebook with PyTorch. Do notes 8.1–8.3 first. CPU is enough. No pretrained LLMs.
import torch
from torch import nn
import torch.nn.functional as F
1. Frozen , low-rank
torch.manual_seed(0)
W = torch.randn(4, 4)
A = nn.Parameter(torch.randn(1, 4) * 0.1) # r = 1
B = nn.Parameter(torch.zeros(4, 1))
x = torch.randn(4)
Treat W as frozen (no requires_grad on a buffer).
-
Print
W @ xand(W + B @ A) @ x. They must match at init becauseBis zero. -
Target
y = torch.tensor([1.0, -1.0, 0.5, 0.0]). Train onlyA,Bfor 400 Adam steps,lr=5e-2, MSE on(W + B @ A) @ xversusy. Print the residualB @ Aand its rank (torch.linalg.matrix_rank). Rank should be . -
Freeze
A,Band try to fit the sameyby training a copy ofW(optional). The point of (2) is: a rank-1 bump can move the output without touchingW.
2. Sequential XOR, then AND (forgetting)
Two-bit inputs, labels XOR, then the same net trained on AND.
bits = torch.tensor([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])
xor = torch.tensor([[0.], [1.], [1.], [0.]])
and_ = torch.tensor([[0.], [0.], [0.], [1.]])
class Tiny(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(nn.Linear(2, 8), nn.Tanh(), nn.Linear(8, 1))
def forward(self, z):
return self.net(z)
-
Train
Tinyon XOR to mean accuracy 1.0 (BCEWithLogitsLoss, a few hundred steps). Print predictions. -
Continue training the same weights on AND until AND accuracy is 1.0. Re-evaluate XOR. It should collapse toward chance or toward AND’s pattern. That is catastrophic forgetting.
-
Replay: from a fresh XOR-trained net, train on batches that are 50% AND rows and 50% XOR rows. Report XOR and AND accuracies. XOR should survive better than in (2).
3. Write-up
Four sentences: why B=0 keeps the base map, what rank cannot represent, what forgetting looked like on XOR, and why a per-task LoRA would not have required replay in (2).