Normalize a feature row
As numbers pass through many layers, they can drift: some rows of values become huge, others tiny, and training becomes unstable. Normalization pulls them back to a standard size. Layer normalization does this for one example at a time. It looks at the features of that example, moves them so their average is 0, and scales them so their spread is 1. Other examples in the batch play no part.
Learn it
Words to know
- normalization
- Rescaling a group of numbers to a standard size, which keeps training stable.
- batch
- A group of examples that the model processes together in one step.
- variance
- A measure of spread: the average of the squared distances from the mean.
- epsilon
- A tiny number added before dividing or taking a root, so that the calculation is safe when the other part is 0.
How it works
- For one row, find the mean of its values.
- Find the variance: the average of the squared distances from the mean. Divide by the number of values
N, not byN - 1. - Subtract the mean from each value, then divide by
sqrt(variance + epsilon). - In PyTorch,
dim=-1means the last dimension, the features of each row.keepdim=Truekeeps the result as a column, so it lines up with the rows when you subtract and divide.
Worked example
Input rows = [[1, 2, 3], [10, 20, 30]] epsilon = 0 Output [[-1.22474, 0, 1.22474], [-1.22474, 0, 1.22474]]
| Row | Mean | Variance | Square root | Normalized |
|---|---|---|---|---|
| [1, 2, 3] | 2 | 0.6667 | 0.8165 | [-1.2247, 0, 1.2247] |
| [10, 20, 30] | 20 | 66.6667 | 8.165 | [-1.2247, 0, 1.2247] |
| feature 0 | feature 1 | feature 2 | |
|---|---|---|---|
| example 0 | 1 | 2 | 3 |
| example 1 | 10 | 20 | 30 |
The second row is ten times the first, yet both come out the same. Layer normalization keeps the pattern inside a row and throws away its overall size.
Why it matters
Transformer blocks normalize their values this way, or with the lighter RMS version two lessons on, so a GPT can stack many blocks without its numbers drifting. You use it again in chapter 6.
Your turn
Using PyTorch, apply layer normalization to each row of a table.
rows- a table of numbers; every row has at least one value
epsilon- a number added to the variance, 0 or more, so the division is safe; tiny in practice
- Returns
- A table of the same shape, as nested Python lists. Each row has its own mean subtracted and is divided by
sqrt(variance + epsilon). For the variance, divide the sum of the squared distances byN, not byN - 1.
This lesson needs PyTorch, which does not run in the browser. The app gives you the exercise as a file with its tests, to run on your own computer.
Example 1
Input rows = [[1, 2, 3], [10, 20, 30]] epsilon = 0 Output [[-1.22474, 0, 1.22474], [-1.22474, 0, 1.22474]]
Both rows have the same pattern, low, middle, high, so both normalize to the same three numbers.
Example 2
Input rows = [[1, 3]] epsilon = 0 Output [[-1, 1]]
The mean is 2 and the variance is 1, so the values 1 and 3 become -1 and 1.
Example 3
Input rows = [[1, 3]] epsilon = 3 Output [[-0.5, 0.5]]
The same row with a large epsilon, to show where it goes: the variance 1 plus 3 is 4, and its square root is 2.
Example 4
Input rows = [[2, 2]] epsilon = 0.00001 Output [[0, 0]]
All values are equal, so the variance is 0. Epsilon keeps the division from being 0 divided by 0, and the result is 0.
Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1: Where to start
You need two numbers per row, a mean and a variance. Ask PyTorch for both along the last dimension.
Hint 2: The key step
Pass
keepdim=Trueto both, so each comes back with shape[rows, 1]. Passunbiased=Falsetovarso it divides byN.Hint 3: The outline
Make
xfromrowsas afloat64tensor. Setmean = x.mean(-1, keepdim=True)andvariance = x.var(-1, unbiased=False, keepdim=True). Return((x - mean) / torch.sqrt(variance + epsilon)).tolist().
Solution
From here on the page gives the method away. If you are still solving, go back to the hints.
Step by step
- One mean and one variance per row, kept as columns.
keepdim=Truegives shape[rows, 1]. PyTorch can then subtract that column from every value in the matching row. - Centre each row on 0 and scale its spread to 1. Subtracting the mean centres the row. Dividing by the square root of the variance makes its spread 1, and epsilon protects against a variance of 0.
The same numbered steps are the comments in the solution.
Python
import torch
def solve(rows, epsilon):
x = torch.tensor(rows, dtype=torch.float64)
# 1. One mean and one variance per row, kept as columns
mean = x.mean(-1, keepdim=True)
variance = x.var(-1, unbiased=False, keepdim=True)
# 2. Centre each row on 0 and scale its spread to 1
normalized = (x - mean) / torch.sqrt(variance + epsilon)
return normalized.tolist()Watch out
x.var()divides byN - 1unless you passunbiased=False. For the row[1, 2, 3]that gives a variance of 1 instead of 0.6667, and every answer is off.- Without
keepdim=Truethe means have shape[rows]. PyTorch then raises a shape error, or worse, lines them up with the columns when the table is square. - PyTorch's
nn.LayerNormthen multiplies each feature by a learned weight and adds a learned bias. They start at 1 and 0, so a new layer gives the same numbers as this exercise.
Check yourself
Which numbers does layer normalization use to normalize one value?
Only the values in the same row: the features of that one example. Other examples in the batch are not used.
What is epsilon for?
It keeps the division safe when all values in a row are equal and the variance is 0.