LLM Developer DocsLLM Docs

Loading the search index ...

↑ ↓ move, Enter open, Esc close

GitHub

AdamW / Learning Rate / Clipping

The optimizer, the learning-rate schedule and gradient clipping as optim.py implements them, with their order in train.main.

optim.py

Three functions in optim.py turn a batch's mean gradient into changed parameters. This page documents them as they are coded. train.main runs them in this order:

train.pymainGitHub: train.py
            norm = clip_grad_norm(grads, config.grad_clip)            step += 1            lr = lr_schedule(step, config.warmup_steps, total_steps,                             config.lr_max, config.lr_min)            opt.step(params, grads, lr)

So clipping comes first, then the learning rate, then the optimizer step. How the Model Learns shows how they fit into one whole step. optim.py has no other optimizer, schedule or clipping mode.

The sections below are in the order of the code in optim.py.

AdamW

What is it?

AdamW updateImplemented

Optimizer

Adam.step changes every parameter once per batch. For each key it updates two moving averages, m and v, corrects them for their zero start, and divides one by the square root of the other. Keys with two or more dimensions also get weight decay. The class is named Adam, and train.main gives it a nonzero weight_decay, so what runs is AdamW.

Why does the model need it?

The gradient says which way each number should move, but its size varies a lot from one number to another and from one batch to the next. Dividing by the typical size in v gives each number a move of about lr, and m smooths the direction over recent batches. Weight decay shrinks the matrices slightly on each step, separately from those averages.

m and v

The class keeps two arrays per parameter, both starting at zero (optim.py:8). In general, these are called the first moment and the second moment. The code calls them m and v:

  • m is momentum. It is a running average of the gradient, so it holds the direction of recent batches. beta1 is how much of the old average it keeps on each step.
  • v is the second moment. It is a running average of the squared gradient, so it holds the typical size of the gradient for each number. beta2 is how much of the old average it keeps.

The update divides mhat by the square root of vhat. A number whose gradient is always large gets a large vhat, and the division brings its move back to about lr. So does a number whose gradient is always small. This is what the comment on that line says: every number moves about lr per step.

Mathematics

Powers, squares, square roots and divisions here apply to each number of the array separately.

Moving average of the gradient #Link to equation: Moving average of the gradient
mt=β1mt−1+(1−β1) gtm_t = \beta_1 m_{t-1} + (1 - \beta_1)\, g_t
Moving average of the squared gradient #Link to equation: Moving average of the squared gradient
vt=β2vt−1+(1−β2) gt2v_t = \beta_2 v_{t-1} + (1 - \beta_2)\, g_t^2

m and v start at zero, so on early steps they are too small. Bias correction divides them by the amount they fall short. self.t goes up by one at the start of each call, so t is 1 on the first step and 1 - beta1 ** t is never zero.

Bias correction of m #Link to equation: Bias correction of m
m^t=mt1−β1 t\hat m_t = \frac{m_t}{1 - \beta_1^{\,t}}
Bias correction of v #Link to equation: Bias correction of v
v^t=vt1−β2 t\hat v_t = \frac{v_t}{1 - \beta_2^{\,t}}
The update before decay #Link to equation: The update before decay
ut=m^tv^t+ϵu_t = \frac{\hat m_t}{\sqrt{\hat v_t} + \epsilon}
Decoupled weight decay #Link to equation: Decoupled weight decay
ut←ut+λ wt−1only where w has two or more dimensionsu_t \leftarrow u_t + \lambda\, w_{t-1} \qquad \text{only where } w \text{ has two or more dimensions}
The parameter update, in place #Link to equation: The parameter update, in place
wt=wt−1−ηt utw_t = w_{t-1} - \eta_t\, u_t

Decay is decoupled: it is added to update after m and v are computed, so it never enters those averages, and clipping never sees it. Since the gradient plays no part in it, a parameter whose gradient is zero still shrinks. Embeddings shows this on the unused rows of pos_emb.

What does each symbol mean?

SymbolMeaningIn codeShape
wwOne parameter arrayparams[k]same as the parameter
gtg_tIts gradient at this step, after averaging over the batch and after clippingg = grads[k]same as the parameter
mtm_tRunning average of the gradient, which holds the directionself.m[k]same as the parameter
vtv_tRunning average of the squared gradient, which holds the typical sizeself.v[k]same as the parameter
m^t,v^t\hat m_t, \hat v_tThe same averages after the correction for their zero startmhat, vhatsame as the parameter
utu_tThe updateupdatesame as the parameter
ttStep counter. It is 1 on the first callself.tscalar
β1,β2\beta_1, \beta_2How much of the old average each step keepsself.b1 from config.beta1, self.b2 from config.beta2scalar
ϵ\epsilonKeeps the division defined when v is 0self.eps from config.adam_epsscalar
λ\lambdaThe weight-decay rateself.wd from config.weight_decayscalar
ηt\eta_tThe learning rate for this steplrscalar

Which parameters get weight decay

The rule is one line, params[k].ndim >= 2 (optim.py:27). The rule depends only on the number of dimensions, so the two embedding tables get decay too, although config.py calls this "weight matrices". The table lists every name init_params creates. "Weight decay" comes from running the real Adam.step on all ones with zero gradients, where only decay can change a value.

KeyWeight decayndimShapeShape in config.py values
tok_embYes2(V, d)(V, 32)
pos_embYes2(T_max, d)(32, 32)
b{l}.ln1_gNo1(d,)(32,)
b{l}.ln1_bNo1(d,)(32,)
b{l}.wqYes2(d, d)(32, 32)
b{l}.bqNo1(d,)(32,)
b{l}.wkYes2(d, d)(32, 32)
b{l}.bkNo1(d,)(32,)
b{l}.wvYes2(d, d)(32, 32)
b{l}.bvNo1(d,)(32,)
b{l}.woYes2(d, d)(32, 32)
b{l}.boNo1(d,)(32,)
b{l}.ln2_gNo1(d,)(32,)
b{l}.ln2_bNo1(d,)(32,)
b{l}.ff1_wYes2(d, F)(32, 128)
b{l}.ff1_bNo1(F,)(128,)
b{l}.ff2_wYes2(F, d)(128, 32)
b{l}.ff2_bNo1(d,)(32,)
lnf_gNo1(d,)(32,)
lnf_bNo1(d,)(32,)
head_wYes2(d, V)(32, V)
head_bNo1(V,)(V,)

With num_layers = 2, the b{l}. keys appear once per block, so 15 tensors get decay and 23 do not.

Tiny numerical example

Two steps, with and without decayIllustrative ExampleThe numbers of test_adam_two_step_hand_trace: start at 1, gradients 0.5 then 0.25, and lr 0.1, which is far above lr_max so that the moves are easy to see. Other settings are beta1 = 0.9, beta2 = 0.999, adam_eps = 1e-8 and weight_decay = 0.01, the values in config.py. w has two dimensions and b has one.
ValueCodeStep 1, wStep 1, bStep 2, wStep 2, b
Beforeparams[k]110.8990.9
Gradientg = grads[k]0.50.50.250.25
Learning ratelr0.10.10.10.1
Counterself.t1122
Averageself.m[k]0.050.050.070.07
Average of squaresself.v[k]0.000250.000250.000312250.00031225
Correctedmhat0.50.50.368421050.36842105
Correctedvhat0.250.250.15620310.1562031
Before decaymhat / (np.sqrt(vhat) + self.eps)0.999999980.999999980.932179620.93217962
Updateupdate1.009999980.999999980.941169620.93217962
Afterparams[k] -= lr * update0.8990.90.804883040.80678204

Read the first column of each pair against the second. w and b start equal and get the same gradients, so self.m[k] and self.v[k] stay equal. They differ only in update, because w also has self.wd * params[k]. The values for b round to the 0.9000 and 0.8068 that test_adam_two_step_hand_trace asserts.

On the first step, the correction makes mhat equal to g and vhat equal to g squared. So update before decay is close to the sign of g, whatever its size. A test checks this (tests/test_optim.py:21).

One step for one number

One step for one numberIllustrative ExampleThe fields start at the two-step example's first step, and you can edit them. To see that example's second step, copy self.m[k], self.v[k], self.t and params[k] from the result back into the fields, and set grads[k] to its second gradient. The betas, eps and weight_decay stay at their config.py values.

Code, in orderResult
self.t += 11
self.m[k] = self.b1 * self.m[k] + (1 - self.b1) * g0.05
self.v[k] = self.b2 * self.v[k] + (1 - self.b2) * g * g0.00025
mhat = self.m[k] / (1 - self.b1 ** self.t)0.5
vhat = self.v[k] / (1 - self.b2 ** self.t)0.25
update = mhat / (np.sqrt(vhat) + self.eps)0.99999998
update = update + self.wd * params[k]1.00999998
params[k] -= lr * update0.899

Tensor shapes

There is one set of arrays per key, and each has the shape of its parameter. V depends on the corpus, so it stays a symbol.

One key with two dimensions, head_w, whose update includes decayoptim.py:28
  1. grads[k](d, V)(32, V)
  2. self.m[k](d, V)(32, V)
  3. self.v[k](d, V)(32, V)
  4. update(d, V)(32, V)
  5. params[k](d, V)(32, V)

d = emb_dim = 32. V depends on the corpus. It is not a value in config.py.

One key with one dimension, head_b, whose update has no decayoptim.py:25
  1. grads[k](V,)
  2. self.m[k](V,)
  3. self.v[k](V,)
  4. update(V,)
  5. params[k](V,)

V depends on the corpus. It is not a value in config.py.

Actual implementation

optim.py__init__GitHub: optim.py
    def __init__(self, params, beta1=0.9, beta2=0.999, eps=1e-8, weight_decay=0.0):        self.m = {k: np.zeros_like(v) for k, v in params.items()} # running average of the gradient (which direction to go)        self.v = {k: np.zeros_like(v) for k, v in params.items()} # running average of gradient squared (how big the gradients usually are)        self.t = 0 # step counter, needed for bias correction        self.b1, self.b2, self.eps, self.wd = beta1, beta2, eps, weight_decay
optim.pystepGitHub: optim.py
    def step(self, params, grads, lr):        self.t += 1 # first step is t=1, so 1 - b1**t is never zero        for k in params:            g = grads[k]            self.m[k] = self.b1 * self.m[k] + (1 - self.b1) * g # mostly old direction, a little new gradient            self.v[k] = self.b2 * self.v[k] + (1 - self.b2) * g * g # same idea, but for the squared gradient            mhat = self.m[k] / (1 - self.b1 ** self.t) # m starts at 0 so it is too small early on, this scales it back up            vhat = self.v[k] / (1 - self.b2 ** self.t)            update = mhat / (np.sqrt(vhat) + self.eps) # direction divided by typical size, so every number moves about lr per step            if params[k].ndim >= 2: # weight decay only for matrices, not biases or layer norm gamma/beta                update = update + self.wd * params[k] # shrink the weight a little, outside the m/v part (this is the "W" in AdamW)            params[k] -= lr * update # changes the array in place, nothing to return

params[k] -= lr * update changes the array in place, and step returns nothing. train.main creates the object once (train.py:51) and passes it the same params dictionary on every step.

The default for weight_decay in the class is 0.0, but train.main passes 0.01. So the pipeline always applies decay.

Code and math connection

EquationRoleLinesCode
Moving average of the gradientComputes itoptim.py:19self.m[k] = self.b1 * self.m[k] + (1 - self.b1) * g # mostly old direction, a little new gradient
Moving average of the squared gradientComputes itoptim.py:20self.v[k] = self.b2 * self.v[k] + (1 - self.b2) * g * g # same idea, but for the squared gradient
Bias correction of mComputes itoptim.py:22mhat = self.m[k] / (1 - self.b1 ** self.t) # m starts at 0 so it is too small early on, this scales it back up
Bias correction of vComputes itoptim.py:23vhat = self.v[k] / (1 - self.b2 ** self.t)
The update before decayComputes itoptim.py:25update = mhat / (np.sqrt(vhat) + self.eps) # direction divided by typical size, so every number moves about lr per step
Decoupled weight decayComputes itoptim.py:27if params[k].ndim >= 2: # weight decay only for matrices, not biases or layer norm gamma/beta
Decoupled weight decayComputes itoptim.py:28update = update + self.wd * params[k] # shrink the weight a little, outside the m/v part (this is the "W" in AdamW)
Decoupled weight decayTests ittests/test_optim.py:29assert np.allclose(params["b"], 1.0) # untouched
The parameter update, in placeComputes itoptim.py:30params[k] -= lr * update # changes the array in place, nothing to return
The parameter update, in placeCalls ittrain.py:66opt.step(params, grads, lr)
The parameter update, in placeTests ittests/test_optim.py:13assert np.isclose(params["w"][0], 0.8068, atol=1e-4)

Where is it used?

PDF reference

Section 4.6, Eq. 55, 56, 57, p. 13

Used by

Called by: train.main

Tested by: tests.test_optim.test_adam_first_step_is_lr_times_sign_of_gradient, tests.test_optim.test_adam_two_step_hand_trace, tests.test_optim.test_weight_decay_only_touches_matrices, tests.test_transformer.test_overfit_one_window

Status
Implemented

Implemented in

EquationRoleFileFunctionLinesGitHub
Moving average of the gradientComputes itoptim.pystep19GitHub: optim.py, line 19
Moving average of the squared gradientComputes itoptim.pystep20GitHub: optim.py, line 20
Bias correction of mComputes itoptim.pystep22GitHub: optim.py, line 22
Bias correction of vComputes itoptim.pystep23GitHub: optim.py, line 23
The update before decayComputes itoptim.pystep25GitHub: optim.py, line 25
Decoupled weight decayComputes itoptim.pystep27GitHub: optim.py, line 27
Decoupled weight decayComputes itoptim.pystep28GitHub: optim.py, line 28
The parameter update, in placeComputes itoptim.pystep30GitHub: optim.py, line 30

Learning-rate schedule

What is it?

Learning-rate scheduleImplemented

Optimizer

lr_schedule gives the learning rate for one step. Up to warmup_steps it rises in a straight line, reaching lr_max exactly at step warmup_steps. After that it follows a cosine curve down to lr_min, reaching it exactly at step total_steps.

Why does the model need it?

In general, large moves right after initialisation can do harm, and smaller moves near the end make the parameters settle. The schedule gives small steps first, the largest step at the end of warmup, and smaller steps toward the end.

Mathematics

Linear warmup #Link to equation: Linear warmup
η(t)=ηmax⁡⋅ttwarmt≤twarm\eta(t) = \eta_{\max} \cdot \frac{t}{t_{\text{warm}}} \qquad t \le t_{\text{warm}}
How far through the decay #Link to equation: How far through the decay
f=t−twarmttotal−twarmt>twarmf = \frac{t - t_{\text{warm}}}{t_{\text{total}} - t_{\text{warm}}} \qquad t > t_{\text{warm}}
Cosine decay #Link to equation: Cosine decay
η(t)=ηmin⁡+12(ηmax⁡−ηmin⁡)(1+cos⁡(πf))\eta(t) = \eta_{\min} + \tfrac{1}{2} (\eta_{\max} - \eta_{\min}) \big(1 + \cos(\pi f)\big)

What does each symbol mean?

SymbolMeaningIn codeShape
ttThe step. train.main adds one before the call, so the first value is 1stepscalar
twarmt_{\text{warm}}The last step of the warmupwarmup, from config.warmup_stepsscalar
ttotalt_{\text{total}}The last step of trainingtotal, from total_steps in train.mainscalar
ηmax⁡\eta_{\max}The largest learning ratelr_max, from config.lr_maxscalar
ηmin⁡\eta_{\min}The learning rate at the last steplr_min, from config.lr_minscalar
ffHow far through the decay this step is, from just above 0 to 1fracscalar

warmup_steps is 50, lr_max is 1e-3 and lr_min is 1e-4 in config.py. total_steps is computed by train.main from the number of windows, so it depends on the corpus. Training Loop shows that calculation.

Tiny numerical example

Edit the four values to see the curve change. The table lists the values at the key steps.

lr against stepIllustrative Examplewarmup_steps, lr_max and lr_min start at their config.py values. total_steps depends on the corpus, so its starting value is hand-picked. Both axes are linear, and the y axis starts at 0.

lr

0.00100: 050: 0.001200: 0.0001
0step200
Data table
steplr
00
10.00002
20.00004
30.00006
40.00008
50.0001
60.00012
70.00014
80.00016
90.00018
100.0002
110.00022
120.00024
130.00026
140.00028
150.0003
160.00032
170.00034
180.00036
190.00038
200.0004
210.00042
220.00044
230.00046
240.00048
250.0005
260.00052
270.00054
280.00056
290.00058
300.0006
310.00062
320.00064
330.00066
340.00068
350.0007
360.00072
370.00074
380.00076
390.00078
400.0008
410.00082
420.00084
430.00086
440.00088
450.0009
460.00092
470.00094
480.00096
490.00098
500.001
510.0009999
520.0009996
530.0009991
540.0009984
550.0009975
560.0009965
570.0009952
580.0009937
590.000992
600.0009902
610.0009881
620.0009859
630.0009834
640.0009808
650.000978
660.000975
670.0009718
680.0009684
690.0009648
700.0009611
710.0009572
720.0009531
730.0009488
740.0009443
750.0009397
760.0009349
770.0009299
780.0009248
790.0009195
800.0009141
810.0009084
820.0009027
830.0008967
840.0008906
850.0008844
860.000878
870.0008715
880.0008648
890.000858
900.0008511
910.000844
920.0008368
930.0008295
940.0008221
950.0008145
960.0008068
970.000799
980.0007911
990.0007831
1000.000775
1010.0007668
1020.0007585
1030.0007501
1040.0007416
1050.000733
1060.0007244
1070.0007157
1080.0007069
1090.000698
1100.0006891
1110.0006801
1120.000671
1130.0006619
1140.0006528
1150.0006436
1160.0006343
1170.000625
1180.0006157
1190.0006064
1200.000597
1210.0005877
1220.0005783
1230.0005688
1240.0005594
1250.00055
1260.0005406
1270.0005312
1280.0005217
1290.0005123
1300.000503
1310.0004936
1320.0004843
1330.000475
1340.0004657
1350.0004564
1360.0004472
1370.0004381
1380.000429
1390.0004199
1400.0004109
1410.000402
1420.0003931
1430.0003843
1440.0003756
1450.000367
1460.0003584
1470.0003499
1480.0003415
1490.0003332
1500.000325
1510.0003169
1520.0003089
1530.000301
1540.0002932
1550.0002855
1560.0002779
1570.0002705
1580.0002632
1590.000256
1600.0002489
1610.000242
1620.0002352
1630.0002285
1640.000222
1650.0002156
1660.0002094
1670.0002033
1680.0001973
1690.0001916
1700.0001859
1710.0001805
1720.0001752
1730.0001701
1740.0001651
1750.0001603
1760.0001557
1770.0001512
1780.0001469
1790.0001428
1800.0001389
1810.0001352
1820.0001316
1830.0001282
1840.000125
1850.000122
1860.0001192
1870.0001166
1880.0001141
1890.0001119
1900.0001098
1910.000108
1920.0001063
1930.0001048
1940.0001035
1950.0001025
1960.0001016
1970.0001009
1980.0001004
1990.0001001
2000.0001
Wheresteplr
Step 0, which train.main never uses00
Step 1, the first one used10.00002
The last warmup step500.001
The first step on the cosine510.000999901
The last step2000.0001

train.main adds one to step before calling the schedule (train.py:63), so step 0 is what the function would return, not a value training uses.

lr_max is reached at the last warmup step. At step warmup_steps, the test step <= warmup is still true (optim.py:33), so the function returns lr_max * step / warmup (optim.py:34), which is exactly lr_max. The next step is already on the cosine and slightly lower, as the table shows. The comment on lr_max in config.py says "reached right after warmup" (config.py:24). This page follows the code.

Tensor shapes

The schedule takes numbers and returns one number, so it has no tensors. Adam.step multiplies every update by that same number.

Actual implementation

optim.pylr_scheduleGitHub: optim.py
def lr_schedule(step, warmup, total, lr_max, lr_min):    if step <= warmup:        return lr_max * step / warmup # warm-up: ramp from near 0 up to lr_max, a fresh model is fragile    frac = (step - warmup) / (total - warmup) # how far through the decay we are, goes from 0 to 1    return lr_min + 0.5 * (lr_max - lr_min) * (1 + math.cos(math.pi * frac)) # cosine slide from lr_max down to lr_min

Code and math connection

EquationRoleLinesCode
Linear warmupComputes itoptim.py:33if step <= warmup:
Linear warmupComputes itoptim.py:34return lr_max * step / warmup # warm-up: ramp from near 0 up to lr_max, a fresh model is fragile
How far through the decayComputes itoptim.py:36frac = (step - warmup) / (total - warmup) # how far through the decay we are, goes from 0 to 1
Cosine decayComputes itoptim.py:37return lr_min + 0.5 * (lr_max - lr_min) * (1 + math.cos(math.pi * frac)) # cosine slide from lr_max down to lr_min
Cosine decayCalls ittrain.py:64lr = lr_schedule(step, config.warmup_steps, total_steps,
Cosine decayTests ittests/test_optim.py:35assert np.allclose(got, [1e-4, 5e-4, 1e-3, 5.5e-4, 1e-4])

Where is it used?

Source

optim.py, lines 32-37, lr_schedule

PDF reference

Section 4.6, Eq. 58, p. 14

Used by

Called by: train.main

Tested by: tests.test_optim.test_lr_schedule_values

Status
Implemented

Implemented in

EquationRoleFileFunctionLinesGitHub
Linear warmupComputes itoptim.pylr_schedule33GitHub: optim.py, line 33
Linear warmupComputes itoptim.pylr_schedule34GitHub: optim.py, line 34
How far through the decayComputes itoptim.pylr_schedule36GitHub: optim.py, line 36
Cosine decayComputes itoptim.pylr_schedule37GitHub: optim.py, line 37

Gradient clipping

What is it?

Gradient clippingImplemented

Optimizer

clip_grad_norm treats every gradient of every parameter as one long vector and measures its length. If that length is above the limit, it scales every tensor by the same factor, in place, so the length becomes the limit and the direction stays. It returns the length from before clipping.

Why does the model need it?

The comment on grad_clip in config.py gives the reason, that one bad batch must not blow up the model. A batch with an unusually large gradient still moves the parameters in its direction, but with a limited length.

Mathematics

Global norm, over all tensors #Link to equation: Global norm, over all tensors
∥g∥=∑k∑igk,i2\lVert g \rVert = \sqrt{\sum_{k} \sum_{i} g_{k,i}^2}
Scaling by one factor #Link to equation: Scaling by one factor
gk←gk⋅c∥g∥for every key k, only when ∥g∥>cg_k \leftarrow g_k \cdot \frac{c}{\lVert g \rVert} \qquad \text{for every key } k, \text{ only when } \lVert g \rVert > c

What does each symbol mean?

SymbolMeaningIn codeShape
gkg_kThe gradient tensor of one parametergrads[k]same as the parameter
gk,ig_{k,i}One number of that tensornot named in the codescalar
∥g∥\lVert g \rVertThe length of all gradients together. This is the value train.py prints as |g|normscalar
ccThe limitmax_norm, from config.grad_clipscalar

Tiny numerical example

Change the values or the limit. The two tensors start with one length of 3 and one of 4, which gives a norm of 5. With a limit of 1.0, each is scaled by the same factor.

norm is above max_norm, so every tensor is multiplied by max_norm / norm = 0.2.

Length of the whole gradientIllustrative ExampleHand-picked values that you can edit. max_norm starts at grad_clip from config.py.
Data table
LabelLength
norm before5.0000
max_norm1.0000
norm after1.0000
TensorBeforeAfter
a3, 00.6, 0
b0, 40, 0.8
  • norm = 5, the value clip_grad_norm returns
  • max_norm / norm = 0.2, the factor for every tensor

Tensor shapes

Clipping changes values, not shapes. head_w as one exampleoptim.py:48
  1. grads[k] before(d, V)(32, V)
  2. grads[k] after(d, V)(32, V)

d = emb_dim = 32. V depends on the corpus. It is not a value in config.py.

Actual implementation

optim.pyclip_grad_normGitHub: optim.py
def clip_grad_norm(grads, max_norm):    total = 0.0    for k in grads:        total += (grads[k] ** 2).sum() # add up the squares of every gradient number in every tensor    norm = np.sqrt(total) # length of the whole gradient as one big vector    if norm > max_norm:        for k in grads:            grads[k] *= max_norm / norm # shrink every tensor by the same factor, direction stays, length becomes max_norm    return float(norm) # the length before clipping, train.py prints it as |g|

The function has two results. It changes grads in place, and it returns the norm from before clipping as a Python float. train.main stores that as norm and prints it as |g| (train.py:67-68). So |g| can be above grad_clip, although the gradients that reach opt.step have a length of at most grad_clip.

It runs right after batch_loss_and_grads and before the learning rate and opt.step, so m and v only ever see clipped gradients.

Code and math connection

EquationRoleLinesCode
Global norm, over all tensorsComputes itoptim.py:43total += (grads[k] ** 2).sum() # add up the squares of every gradient number in every tensor
Global norm, over all tensorsComputes itoptim.py:44norm = np.sqrt(total) # length of the whole gradient as one big vector
Global norm, over all tensorsTests ittests/test_optim.py:41assert np.isclose(norm, 5.0) # norm BEFORE clipping
Scaling by one factorComputes itoptim.py:46if norm > max_norm:
Scaling by one factorComputes itoptim.py:48grads[k] *= max_norm / norm # shrink every tensor by the same factor, direction stays, length becomes max_norm
Scaling by one factorCalls ittrain.py:62norm = clip_grad_norm(grads, config.grad_clip)
Scaling by one factorTests ittests/test_optim.py:49assert np.allclose(grads["a"], [0.3, 0.4])

Where is it used?

Source

optim.py, lines 40-50, clip_grad_norm

PDF reference

Section 4.6, Eq. 59, p. 14

Used by

Called by: train.main

Tested by: tests.test_optim.test_clip_leaves_small_gradients_alone, tests.test_optim.test_clip_rescales_all_tensors_together

Status
Implemented

Implemented in

EquationRoleFileFunctionLinesGitHub
Global norm, over all tensorsComputes itoptim.pyclip_grad_norm43GitHub: optim.py, line 43
Global norm, over all tensorsComputes itoptim.pyclip_grad_norm44GitHub: optim.py, line 44
Scaling by one factorComputes itoptim.pyclip_grad_norm46GitHub: optim.py, line 46
Scaling by one factorComputes itoptim.pyclip_grad_norm48GitHub: optim.py, line 48