Concepts / Implementing Linear Layers in PyTorch

Implementing Linear Layers in PyTorch

A LoRA layer is a PyTorch module with two learnable matrices, A and B, and a scaling factor alpha, and its forward method computes alpha times x @ A @ B.

  • Programming

Predicting the First Output of a LoRA Layer

This article starts from a working PyTorch class rather than from a definition. You will read the class once, predict how it behaves, and then check that prediction against the explanation. The goal is a mental model you can test against the code, so that when a LoRA layer produces an unexpected result you already know which line to examine first.

A LoRA layer, in the form used here, is a PyTorch module with two learnable matrices called A and B and a scalar setting called alpha. The module takes an input, passes it through A and then through B, scales the result by alpha, and returns it. Everything else in this article explains why those few lines are arranged the way they are.

What do you think happens?

A LoRA layer has just been constructed, and no training step has run yet. What does the layer return the first time it is called?

  • A random, noisy vector that changes the model's output from the first step

  • A vector of zeros, so the adaptation adds nothing to the pretrained output

  • The input x unchanged, because the layer is a pass-through until training starts

Reveal answer
Answer:

A vector of zeros, so the adaptation adds nothing to the pretrained output.

The answer depends on one line in the constructor: B is filled with zeros. Since the forward computation multiplies by B, the LoRA output is zero at creation time. The rest of this article explains why that choice was deliberate.

Reading the Constructor: A, B, and alpha

Two details of the constructor matter most. The first is the pair of shapes: A is in_dim by rank, and B is rank by out_dim. Multiplying x by A and then by B only works because the inner dimension of the first product matches the outer dimension of the second, and that shared inner size is the rank. The second is that A and B receive different initial values, which the next sections examine closely.

python
Walk through it
  1. 1
    The class inherits from nn.Module, so PyTorch tracks the parameters it registers.
  2. 2
    self.A is wrapped in nn.Parameter, which marks it as learnable, and its values are drawn with Kaiming uniform initialization.
  3. 3
    self.B is also a learnable parameter, but it is created with torch.zeros, so every entry starts at zero.
  4. 4
    alpha is stored as a plain setting. It scales the output but does not add entries to A or B.
  5. 5
    forward multiplies x by A, then by B, then scales the product by alpha, and returns it.
Notes on the code
  • A is a learnable parameter with shape in_dim by rank, filled with Kaiming uniform values
  • B is a learnable parameter with shape rank by out_dim, filled with zeros
  • alpha is a scalar setting, not a learned matrix
  • forward computes alpha times x @ A @ B, so the input passes through A, then B, then is scaled

The class is a compact expression of the forward computation alpha times x @ A @ B. The matrices A and B are the learned part of the adaptation, and alpha is the dial that controls how much their result is allowed to affect the output. Because the class is short, you can follow one input through it line by line, which is exactly what the next sections do.

Why B Starts at Zero

The A matrix is filled with random values drawn using Kaiming uniform initialization, the same scheme PyTorch uses for Linear layers. Random values in A give the layer a non-trivial starting point, so gradients can flow into it during training. The B matrix, by contrast, is filled with zeros.

Combining these two choices gives a starting state in which the adaptation's output is exactly zero. Because the forward computation multiplies x by A and then by B, any zero in B makes the whole product zero, regardless of what A contains. The layer begins by adding nothing to the pretrained output and then learns how much to add. Initializing both matrices randomly would instead inject arbitrary noise into the model from the first step.

B at creationall zerosLoRA outputzeroFinal outputequals pretrained outputB after traininglearned nonzero valuesLoRA outputnonzero correctionFinal outputpretrained plus correctionzero productadds nothingnonzero productadds correction
What is the layer's output before any training, compared with its output after training updates B?

Tracing Shapes Through A and B

Follow one input through the forward method. The input x enters A, the result enters B, and that result is multiplied by alpha. The diagram below shows each step as one operation, and the nodes show the width of the value after each step.

Input xin_dim valuesMultiply by Ain_dim to rankIntermediaterank valuesMultiply by Brank to out_dimScale by alphasame widthLoRA outputout_dim valuesx @ Arank-sized result@ Btimes alphareturned
How do the shapes change as x passes through A, then B, and why does the rank act as a bottleneck?

The rank-sized intermediate value is the only place where the input is compressed. The input is first projected down to rank values and then projected back up to out_dim. Because the rank is usually much smaller than the input and output sizes, the adaptation stays small. The final shape always matches out_dim, so the LoRA output can be combined with the output of a layer of that width.

Rank Versus Alpha

Rank and alpha are the two settings that most affect a LoRA layer, and they do different jobs. The rank sets the inner dimension shared by A and B, and that single number determines how many extra parameters LoRA adds to a layer. The source describes this as a balance: a larger rank gives the adaptation more capacity to change the layer, at the cost of more parameters, while a smaller rank keeps the layer cheap but limits what it can represent.

Alpha works differently. Rank decides how many parameters exist, while alpha decides how strongly the low-rank output is allowed to influence the result. In the class, alpha multiplies the whole adaptation result. Doubling alpha doubles the contribution of the LoRA path to the output while leaving A and B exactly as they are. A small alpha keeps the adaptation quiet, and a large alpha lets it dominate. Alpha is therefore a dial on the strength of the update, not a dial on its size in parameter terms.

SettingWhat it controlsAdds parameters?Changes the LoRA output how?
rankInner dimension shared by A and BYes, in_dim times rank plus rank times out_dimChanges capacity, and the width of the intermediate value
alphaScale on the whole adaptation resultNoMultiplies the contribution without changing A or B

How rank and alpha differ

Combining the LoRA Output with the Pretrained Layer

A LoRA layer is not usually used alone. The typical goal is to add it to an existing Linear layer, so that weight updates are applied to the pretrained weights rather than replacing them. The original layer computes its output from the pretrained weights W. The LoRA layer computes a low-rank approximation of the weight update, which the source writes as delta W. The final output is the sum of the two results.

Formula
Input xPretrained Woriginal weightsSumadds the two outputsAdapted outputLoRA pathalpha times x A Bpretrained pathadaptation pathWxLoRA outputfinal output
How does the input x flow through the frozen pretrained weights and the LoRA path, and where do the two results get added together?

Both results are computed for the same input, and they meet at the summing step. Read this way, the adapted layer is the original layer plus a correction. The pretrained weights remain intact, and the learned part lives in A and B. When B is still zero, the correction term is zero and the adapted layer returns exactly the pretrained output.

Common Misreadings of the LoRA Layer

Most LoRA bugs in this implementation come from a small number of misreadings. Each mistake below shows where the symptom appears and which part of the reasoning to check.

  • Common mistake

    Expecting a new LoRA layer to change the model's output immediately

    What it looks like

    A freshly created adaptation produces a different prediction from the pretrained layer before any training has run.

    Why it fails

    B starts at zero, so the LoRA output is zero and the final output equals the pretrained output.

    Do this instead

    Check whether B has been changed. If the output differs before training, look at the initialization of B first.

  • Common mistake

    Treating alpha as the number of extra parameters

    Why it fails

    Alpha is a scaling factor applied to the whole adaptation result. It does not add any entries to A or B.

    Do this instead

    Count parameters from in_dim, out_dim, and rank. Use alpha to reason about how strongly the adaptation is applied.

  • Common mistake

    Thinking a larger rank only makes the update stronger

    Why it fails

    Rank sets the inner dimension shared by A and B, so it changes the parameter count and capacity. Strength is controlled by alpha.

    Do this instead

    Increase rank when more capacity is needed, and adjust alpha when the size of the contribution is the concern.

  • Common mistake

    Expecting the LoRA output to have rank-sized width

    What it looks like

    Someone looks for a rank-wide output and finds a mismatch when adding it to the pretrained output.

    Why it fails

    Rank is only the intermediate size. The output after B always has out_dim values.

    Do this instead

    Trace the shapes: in_dim to rank through A, then rank to out_dim through B.

  • Common mistake

    Replacing the pretrained layer instead of adding to it, which discards the original weights rather than learning a correction

Counting Parameters for a Small LoRA Layer

MEDIUM

A LoRA layer has in_dim 10, out_dim 6, and rank 2. How many parameters does the adaptation add, and what is the width of its output? Then explain what happens to the LoRA output if B is still all zeros.

Hints
  • A has in_dim times rank entries, and B has rank times out_dim entries
  • The output width is set by B, not by the rank
  • Zeros in B make the product x @ A @ B zero, regardless of A

Key Takeaways

  • A LoRA layer is a PyTorch module with learnable matrices A (in_dim by rank) and B (rank by out_dim) and a scalar alpha, and its forward method computes alpha times x @ A @ B.
  • B is initialized to zeros while A is filled with Kaiming uniform values, so a new adaptation contributes nothing to the first output and then learns how much to add.
  • Rank sets the inner dimension shared by A and B and therefore the number of extra parameters, while alpha scales the adaptation's output without adding parameters.
  • A LoRA layer is typically added to an existing Linear layer, and the final output is the original output plus the LoRA output, which approximates the weight update delta W.

Related concepts

More concepts starting with I