Skip to content

← Parameter-Efficient FT step 4 of 4

Medium End-to-End

Train LoRA Adapter End-to-End

Train a low-rank correction to a fixed linear regression model. The base matrix stays unchanged while two smaller matrices, a and b, learn a correction a @ b. Use explicit gradients and full-batch SGD, without an optimizer or automatic differentiation.

Inputs and forward pass

Implement train_lora(x, y, w_base, a0, b0, lr, n_steps):

  • x has shape (N, d_in) and y has shape (N, d_out).
  • w_base has shape (d_in, d_out).
  • a0 has shape (d_in, r) and b0 has shape (r, d_out).
  • lr is a nonnegative learning rate; n_steps is a nonnegative integer.

All matrix dimensions are positive. Start from the supplied a0 and b0. This exercise uses row-vector orientation and a correction scale of one:

effective_w = w_base + a @ b
prediction = x @ effective_w
loss = mean((prediction - y)**2)

There is no bias, dropout or extra alpha / r factor. The correction has rank at most r; choosing a small r reduces the number of trainable parameters. Neither w_base nor any input array should be modified.

Gradients and update order

The mean includes all N * d_out squared errors. At each step, compute:

G = (2 / (N * d_out)) * x.T @ (prediction - y)
grad_a = G @ b.T
grad_b = a.T @ G

G is the gradient with respect to the effective matrix, with shape (d_in, d_out). The chain rule gives grad_a of shape (d_in, r) and grad_b of shape (r, d_out).

Compute both gradients from the same current a and b, then update:

a = a - lr * grad_a
b = b - lr * grad_b

Do not use the updated a to calculate grad_b. Repeat exactly n_steps times, recomputing predictions and gradients each time. Do not stop early.

Output and checks

Return one vector: all entries of final a, flattened in row-major order, followed by all entries of final b in the same order. Its length is d_in*r + r*d_out. Do not return the effective matrix or the loss.

For zero steps or zero learning rate, return the initial factors in that format. If both initial factors are zero, both gradients stay zero. Use the supplied initialization as-is; this edge case is not a recommended way to initialize a trainable correction.

For a one-step check, take x = [[1, 0], [0, 1]], y = [[1, 0], [0, 1]], zero w_base, a0 = [[1], [0.5]], b0 = [[0.2, -0.4]], and lr = 0.1. The gradients are grad_a = [[0], [0.25]] and grad_b = [[-0.375, -0.5]]. Return [1, 0.475, 0.2375, -0.35].