Normalize by root mean square
RMS normalization is a lighter version of layer normalization, and many recent language models use it. It skips the step of subtracting the mean. It only measures how large the values in a row are, with a number called the root mean square, and divides by it. Rows of any size come out at a standard size, and the work per row is smaller.
Learn it
Words to know
- normalization
- Rescaling a group of numbers to a standard size, which keeps training stable.
- root mean square
- A measure of size: square the numbers, take their mean, then take the square root.
- 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
- Square every value in the row. Squares are never negative, so positive and negative values both count toward the size.
- Take the mean of the squares, add
epsilon, and take the square root. This is the root mean square, or RMS. - Divide each value in the row by that number. Nothing is subtracted, so signs and zeros stay as they were.
Worked example
Input rows = [[3, 4]] epsilon = 1e-8 Output [[0.848528, 1.13137]]
| What | Calculation | Value |
|---|---|---|
| Squares | 3×3 and 4×4 | 9 and 16 |
| Mean of squares | (9 + 16) / 2 | 12.5 |
| Root mean square | sqrt(12.5) | 3.5355 |
| Result | 3 / 3.5355 and 4 / 3.5355 | 0.8485 and 1.1314 |
Both results stay positive and keep their ratio of 3 to 4. Layer normalization would turn the same row into -1 and 1.
Why it matters
Several recent open language models, Llama among them, use RMS normalization in place of layer normalization inside each transformer block. It does the same job with less arithmetic.
Your turn
Using PyTorch, divide each row of a table by its root mean square.
rows- a table of numbers; every row has at least one value
epsilon- a positive number added to the mean of squares; tiny in practice
- Returns
- A table of the same shape, as nested Python lists: each row divided by
sqrt(mean(row ** 2) + epsilon).
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 = [[3, 4]] epsilon = 1e-8 Output [[0.848528, 1.13137]]
The mean of the squares 9 and 16 is 12.5. Its square root is about 3.5355, and each value is divided by that.
Example 2
Input rows = [[0, 0]] epsilon = 0.00001 Output [[0, 0]]
An all-zero row stays all zero. Epsilon keeps the division from being 0 divided by 0.
Example 3
Input rows = [[3, 4], [6, 8]] epsilon = 1e-8 Output [[0.848528, 1.13137], [0.848528, 1.13137]]
Each row is divided by its own root mean square, about 3.5355 and 7.0711, so both rows come out the same.
Example 4
Input rows = [[3, 3]] epsilon = 7 Output [[0.75, 0.75]]
Epsilon is large here to show where it goes: the mean of squares, 9, plus 7 is 16, and its square root is 4.
Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1: Where to start
There is no mean to subtract this time. You need one number per row: its root mean square.
Hint 2: The key step
Square the tensor with
x * x, then take the mean along the last dimension withkeepdim=True.Hint 3: The outline
Make
xafloat64tensor. Setmean_square = (x * x).mean(-1, keepdim=True). Return(x / torch.sqrt(mean_square + 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
- The mean of the squared values in each row. Squaring first makes a value of -3 count as much as a value of 3. The result has shape
[rows, 1]. - Divide each row by its root mean square. After this the mean of the squares in each row is 1, or very close to it, whatever size the row had before. Only a row of zeros stays at 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. The mean of the squared values in each row
mean_square = (x * x).mean(-1, keepdim=True)
# 2. Divide each row by its root mean square
normalized = x / torch.sqrt(mean_square + epsilon)
return normalized.tolist()Watch out
- Square first, then average. The square of the mean is a different number: for
[3, -3]it is 0, while the root mean square is 3. - Do not subtract the mean. That would turn this into layer normalization.
Check yourself
What does RMS normalization leave out that layer normalization does?
Subtracting the mean. It only divides by the size of the row.
Why are the values squared before averaging?
So that negative and positive values both add to the size. A plain average of 3 and -3 would be 0.