LLM Developer DocsLLM Docs

Loading the search index ...

↑ ↓ move, Enter open, Esc close

GitHub

Mathematics Reference

Every documented operation with its equation, its direction, its code and its tensor shapes. Generated from the concept data.

Each row comes from one concept record. The count compares those records with the rows of the mathematics inventory, which lists every operation the code implements. Shapes are for one sequence, and V stays a symbol because it depends on the corpus.

61 of 61 operations documented

Showing 61 of 61

Name and directionEquationFile and functionTensor shapesLinks
Causal maskImplementedDirectionForwardEquation
Causal mask #Link to equation: Causal mask
Sij←{Sijj≤i−∞j>iS_{ij} \leftarrow \begin{cases} S_{ij} & j \le i \\ -\infty & j > i \end{cases}
File and functiontransformer.pyattention_forwardTensor shapesThe mask has one layer of shape (T, T). NumPy applies it to every headS, scaled (H, T, T)(2, 16, 16)np.triu(..., 1) (T, T)(16, 16)S, masked (H, T, T)(2, 16, 16)LinksExplanationNumerical exampletransformer.py:70-86
Merging headsImplementedDirectionForwardEquation
Heads side by side #Link to equation: Heads side by side
O=merge⁡(AVh)=[A(0)V(0)⋯A(H−1)V(H−1)]O = \operatorname{merge}(A V_h) = \begin{bmatrix} A^{(0)} V^{(0)} & \cdots & A^{(H-1)} V^{(H-1)} \end{bmatrix}
File and functiontransformer.pymerge_headsTensor shapesThe transpose, then the reshape, in merge_headsA @ Vh (H, T, dh)(2, 16, 16)transpose (T, H, dh)(16, 2, 16)O (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:65-67
Output projectionImplementedDirectionForwardEquation
Output projection #Link to equation: Output projection
out=OWo+1bo⊤\mathrm{out} = O W_o + \mathbf{1} b_o^\top
File and functiontransformer.pyattention_forwardTensor shapesThe output has the shape of the input XnO (T, d)(16, 32)wo (d, d)(32, 32)out (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:70-86
Q, K, V projectionsImplementedDirectionForwardEquation
Query projection #Link to equation: Query projection
Q=XnWq+1bq⊤Q = X_n W_q + \mathbf{1} b_q^\top
Key projection #Link to equation: Key projection
K=XnWk+1bk⊤K = X_n W_k + \mathbf{1} b_k^\top
Value projection #Link to equation: Value projection
V=XnWv+1bv⊤V = X_n W_v + \mathbf{1} b_v^\top
File and functiontransformer.pyattention_forwardTensor shapesOne projection. Q, K and V all have this shapeXn (T, d)(16, 32)wq (d, d)(32, 32)Q (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:70-86
Scaling by sqrt(d_h)ImplementedDirectionForwardEquation
Scaled scores #Link to equation: Scaled scores
S=QhKh⊤dhS = \frac{Q_h K_h^\top}{\sqrt{d_h}}
File and functiontransformer.pyattention_forwardTensor shapesScaling divides by one number, so the shape does not changeQh @ Kh^T (H, T, T)(2, 16, 16)S (H, T, T)(2, 16, 16)LinksExplanationNumerical exampletransformer.py:70-86
ScoresImplementedDirectionForwardEquation
Scores, one matrix per head #Link to equation: Scores, one matrix per head
QhKh⊤,(QhKh⊤)ij=∑c(Qh)ic(Kh)jcQ_h K_h^\top, \qquad (Q_h K_h^\top)_{ij} = \sum_{c} (Q_h)_{ic} (K_h)_{jc}
File and functiontransformer.pyattention_forwardTensor shapesScores, before scaling. Both this step and the next are on one line of codeQh (H, T, dh)(2, 16, 16)Kh.transpose(0, 2, 1) (H, dh, T)(2, 16, 16)Qh @ Kh^T (H, T, T)(2, 16, 16)LinksExplanationNumerical exampletransformer.py:70-86
Softmax per rowImplementedDirectionForwardEquation
Weights, one row at a time #Link to equation: Weights, one row at a time
A=softmax⁡(S),∑jAij=1A = \operatorname{softmax}(S), \qquad \sum_{j} A_{ij} = 1
A masked cell has a weight of exactly 0 #Link to equation: A masked cell has a weight of exactly 0
exp⁡(−∞)=0⇒Aij=0 for j>i\exp(-\infty) = 0 \quad\Rightarrow\quad A_{ij} = 0 \text{ for } j > i
File and functiontransformer.pyattention_forwardTensor shapesSoftmax keeps the shape. It normalises along the last axisS (H, T, T)(2, 16, 16)A (H, T, T)(2, 16, 16)LinksExplanationNumerical exampletransformer.py:70-86
Splitting into headsImplementedDirectionForwardEquation
Split each matrix into heads #Link to equation: Split each matrix into heads
Qh, Kh, Vh=split⁡(Q,H), split⁡(K,H), split⁡(V,H)Q_h,\ K_h,\ V_h = \operatorname{split}(Q, H),\ \operatorname{split}(K, H),\ \operatorname{split}(V, H)
File and functiontransformer.pysplit_headsTensor shapesThe reshape, then the transpose, in split_headsQ (T, d)(16, 32)reshape (T, H, dh)(16, 2, 16)transpose (H, T, dh)(2, 16, 16)LinksExplanationNumerical exampletransformer.py:60-62
Weighted valuesImplementedDirectionForwardEquation
Weighted values, one matrix per head #Link to equation: Weighted values, one matrix per head
AVhA V_h
The same product, for one row Derived from implementation#Link to equation: The same product, for one row
(AVh)i=∑j≤iAij(Vh)j(A V_h)_{i} = \sum_{j \le i} A_{ij} (V_h)_{j}
File and functiontransformer.pyattention_forwardTensor shapesWeighted values have the shape of the valuesA (H, T, T)(2, 16, 16)Vh (H, T, dh)(2, 16, 16)A @ Vh (H, T, dh)(2, 16, 16)LinksExplanationNumerical exampletransformer.py:70-86
Shuffling and batchingDerived from implementationDirectionNeither (not differentiated)Equation
Batches in one epoch #Link to equation: Batches in one epoch
batches per epoch=⌈WB⌉\text{batches per epoch} = \left\lceil \frac{W}{B} \right\rceil
File and functiondata.pyiterate_batchesTensor shapesFrom all windows to one sequencetr_x (W, T)(W, 16)xb (B, T)(16, 16)x (T,)(16,)LinksExplanationNumerical exampledata.py:36-43
Sliding windowsImplementedDirectionNeither (not differentiated)Equation
Where windows start Derived from implementation#Link to equation: Where windows start
s∈{0, k, 2k, … },s<N−Ts \in \{0,\, k,\, 2k,\, \dots\}, \qquad s < N - T
Inputs #Link to equation: Inputs
xt(s)=idss+t,t=0,…,T−1x^{(s)}_{t} = \text{ids}_{s+t}, \qquad t = 0, \dots, T-1
Targets #Link to equation: Targets
yt(s)=idss+t+1,t=0,…,T−1y^{(s)}_{t} = \text{ids}_{s+t+1}, \qquad t = 0, \dots, T-1
Targets are inputs shifted by one #Link to equation: Targets are inputs shifted by one
yt(s)=xt+1(s),t=0,…,T−2y^{(s)}_{t} = x^{(s)}_{t+1}, \qquad t = 0, \dots, T-2
File and functiondata.pymake_windowsTensor shapesInputs of the training windowstrain_ids (N,)tr_x (W, T)(W, 16)Targets of the training windowstrain_ids (N,)tr_y (W, T)(W, 16)LinksExplanationNumerical exampledata.py:24-33
The splitImplementedDirectionNeither (not differentiated)Equation
Where the stream is cut #Link to equation: Where the stream is cut
cut=⌊(1−ρ) N⌋\text{cut} = \lfloor (1 - \rho)\, N \rfloor
File and functiondata.pysplit_idsTensor shapesNone declaredLinksExplanationNumerical exampledata.py:19-21
Window countDerived from implementationDirectionNeither (not differentiated)Equation
Number of windows #Link to equation: Number of windows
W=⌈N−Tk⌉,N>TW = \left\lceil \frac{N - T}{k} \right\rceil, \qquad N > T
File and functiondata.pymake_windowsTensor shapesNone declaredLinksExplanationNumerical exampledata.py:24-33
Positional embeddingImplementedDirectionForwardEquation
The first T rows of the position table #Link to equation: The first T rows of the position table
(P[:T])t=Pt,t=0,…,T−1\left(P[:T]\right)_t = P_t, \quad t = 0, \ldots, T - 1
File and functiontransformer.pyembed_forwardTensor shapesThe table is cut to the length of the sequencepos_emb (Tmax, d)(32, 32)pos_emb[:T] (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:52-53
Token embeddingImplementedDirectionForwardEquation
Row t of the lookup is the table row of the t-th id #Link to equation: Row t of the lookup is the table row of the t-th id
(E[ids])t=Eit\left(E[\text{ids}]\right)_t = E_{i_t}
File and functiontransformer.pyembed_forwardTensor shapesFrom ids to vectorsids (T,)(16,)tok_emb[ids] (T, d)(16, 32)The table itself. V depends on the corpusrows (V,)tok_emb (V, d)(V, 32)LinksExplanationNumerical exampletransformer.py:52-53
Autoregressive loopImplementedDirectionNeither (not differentiated)Equation
The last window ids #Link to equation: The last window ids
x=ids[−w:]x = \mathrm{ids}[-w:]
Logits of the last position only #Link to equation: Logits of the last position only
Z=forward(x),z=ZT−1Z = \mathrm{forward}(x), \qquad z = Z_{T-1}
Sample and append #Link to equation: Sample and append
ids←ids+[ sample_next(z,τ,k,ρ) ]\mathrm{ids} \leftarrow \mathrm{ids} + [\,\mathrm{sample\_next}(z, \tau, k, \rho)\,]
File and functiongenerate.pygenerateTensor shapesOne step of the loopx (T,)(16,)logits (T, V)(16, V)logits[-1] (V,)LinksExplanationNo numerical examplegenerate.py:28-35
Checking every parameterImplementedDirectionNeither (not differentiated)Equation
Worst relative error for one parameter name #Link to equation: Worst relative error for one parameter name
worst⁡[n]=max⁡s=1,…,S rel⁡ ⁣(∂L∂θn[is], L(θn[is]+h)−L(θn[is]−h)2h)\operatorname{worst}[n] = \max_{s = 1,\ldots,S}\ \operatorname{rel}\!\left(\frac{\partial L}{\partial \theta_n}[i_s],\ \frac{L(\theta_n[i_s] + h) - L(\theta_n[i_s] - h)}{2h}\right)
File and functiongradcheck.pycheck_paramsTensor shapesOne name, at the sizes in config.py. The test itself uses a smaller ModelConfigW = pos_emb (Tmax, d)(32, 32)grads["pos_emb"] (Tmax, d)(32, 32)LinksExplanationNumerical examplegradcheck.py:19-33
Central differenceImplementedDirectionNeither (not differentiated)Equation
Central difference for one entry #Link to equation: Central difference for one entry
gi=f(x+h ei)−f(x−h ei)2hg_i = \frac{f(x + h\, e_i) - f(x - h\, e_i)}{2h}
File and functiongradcheck.pynumerical_gradTensor shapesNone declaredLinksExplanationNumerical examplegradcheck.py:3-14
Relative errorImplementedDirectionNeither (not differentiated)Equation
Relative error with the epsilon guard #Link to equation: Relative error with the epsilon guard
rel⁡(a,b)=∣a−b∣max⁡(∣a∣, ∣b∣, ε)\operatorname{rel}(a, b) = \frac{|a - b|}{\max\left(|a|,\, |b|,\, \varepsilon\right)}
File and functiongradcheck.pyrelative_errorTensor shapesNone declaredLinksExplanationNumerical examplegradcheck.py:16-17
HeadsImplementedDirectionForwardEquation
Width of one head #Link to equation: Width of one head
dh=dH,d mod H=0d_h = \frac{d}{H}, \qquad d \bmod H = 0
Head h owns one block of columns #Link to equation: Head h owns one block of columns
Mt,i(h)=Mt,  hdh+iM^{(h)}_{t,i} = M_{t,\; h d_h + i}
File and functiontransformer.pysplit_headstransformer.pymerge_headsTensor shapesSplit, for the query matrixQ (T, d)(16, 32)Qh (H, T, dh)(2, 16, 16)Merge, after the weighted valuesA @ Vh (H, T, dh)(2, 16, 16)O (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:60-62transformer.py:65-67
Matrix multiplicationDerived from implementationDirectionForwardEquation
One entry of the product #Link to equation: One entry of the product
(XW)tj=∑kXtkWkj(XW)_{tj} = \sum_{k} X_{tk} W_{kj}
The shape rule #Link to equation: The shape rule
X∈RT×a,  W∈Ra×c  ⇒  XW∈RT×cX \in \mathbb{R}^{T \times a},\; W \in \mathbb{R}^{a \times c} \;\Rightarrow\; XW \in \mathbb{R}^{T \times c}
A linear layer, with b added to every row #Link to equation: A linear layer, with b added to every row
Ytj=(XW)tj+bjY_{tj} = (XW)_{tj} + b_j
File and functionops.pylinear_forwardTensor shapesQuery projection, width stays the sameXn (T, d)(16, 32)Q (T, d)(16, 32)First feed-forward layer, width growsXn (T, d)(16, 32)Z (T, F)(16, 128)Linear head, width becomes the vocabulary sizeXf (T, d)(16, 32)logits (T, V)(16, V)LinksExplanationNumerical exampleops.py:7-8
Cross-entropy, backwardImplementedDirectionBackwardEquation
The derivative of the loss at one position #Link to equation: The derivative of the loss at one position
∂ℓt∂logitstv=Ptv−1[v=yt]\frac{\partial \ell_t}{\partial \text{logits}_{tv}} = P_{tv} - \mathbf{1}[v = y_t]
The combined backward, where E_y is one-hot at each correct token #Link to equation: The combined backward, where E_y is one-hot at each correct token
dZ=P−EyTdZ = \frac{P - E_y}{T}
File and functionops.pycross_entropy_backwardTensor shapesThe start of the backward passprobs (T, V)(16, V)dZ (T, V)(16, V)LinksExplanationNumerical exampleops.py:36-40
Cross-entropyImplementedDirectionForwardEquation
The same shift as softmax, written inline #Link to equation: The same shift as softmax, written inline
ztv=logitstv−max⁡klogitstkz_{tv} = \text{logits}_{tv} - \max_{k} \text{logits}_{tk}
Log-softmax #Link to equation: Log-softmax
log⁡ptv=ztv−log⁡∑keztk\log p_{tv} = z_{tv} - \log \sum_{k} e^{z_{tk}}
Mean over positions #Link to equation: Mean over positions
L=−1T∑tlog⁡pt, ytL = -\frac{1}{T} \sum_{t} \log p_{t,\, y_t}
The probabilities it returns #Link to equation: The probabilities it returns
Ptv=elog⁡ptvP_{tv} = e^{\log p_{tv}}
File and functionops.pycross_entropy_forwardTensor shapesFrom logits to probabilities. The loss is a single numberlogits (T, V)(16, V)logp (T, V)(16, V)probs (T, V)(16, V)LinksExplanationNumerical exampleops.py:29-34
LayerNorm, backwardImplementedDirectionBackwardEquation
Gradient on the normalised row #Link to equation: Gradient on the normalised row
dX^ti=dYti γid\hat{X}_{ti} = dY_{ti}\, \gamma_i
Gradient on the input #Link to equation: Gradient on the input
dXtj=st(dX^tj−mean⁡i ⁣(dX^ti)−X^tj mean⁡i ⁣(dX^tiX^ti))dX_{tj} = s_t \left( d\hat{X}_{tj} - \operatorname{mean}_i\!\left(d\hat{X}_{ti}\right) - \hat{X}_{tj}\, \operatorname{mean}_i\!\left(d\hat{X}_{ti} \hat{X}_{ti}\right) \right)
Gradient on the scale, summed over rows #Link to equation: Gradient on the scale, summed over rows
dγi=∑tdYti X^tid\gamma_i = \sum_{t} dY_{ti}\, \hat{X}_{ti}
Gradient on the shift, summed over rows #Link to equation: Gradient on the shift, summed over rows
dβi=∑tdYtid\beta_i = \sum_{t} dY_{ti}
File and functionops.pylayer_norm_backwardTensor shapesThe final LayerNormdXf (T, d)(16, 32)dX (T, d)(16, 32)dg (d,)(32,)db (d,)(32,)LayerNorm before attention, inside a blockdXn (T, d)(16, 32)dX_branch (T, d)(16, 32)dg (d,)(32,)db (d,)(32,)LinksExplanationNumerical exampleops.py:49-57
Deriving the backward formulaDerived from implementationDirectionBackwardEquation
Step 1: from the output to the normalised row #Link to equation: Step 1: from the output to the normalised row
∂L∂X^ti=∂L∂Yti ∂Yti∂X^ti=dYti γi=dX^ti\frac{\partial L}{\partial \hat{X}_{ti}} = \frac{\partial L}{\partial Y_{ti}}\, \frac{\partial Y_{ti}}{\partial \hat{X}_{ti}} = dY_{ti}\, \gamma_i = d\hat{X}_{ti}
Step 2: the derivative of the mean #Link to equation: Step 2: the derivative of the mean
∂μt∂Xtj=1d\frac{\partial \mu_t}{\partial X_{tj}} = \frac{1}{d}
Step 3: the derivative of the variance, where the mu term cancels #Link to equation: Step 3: the derivative of the variance, where the mu term cancels
∂σt2∂Xtj=2d∑i(Xti−μt)(δij−1d)=2d(Xtj−μt)\frac{\partial \sigma_t^2}{\partial X_{tj}} = \frac{2}{d} \sum_{i} \left(X_{ti} - \mu_t\right)\left(\delta_{ij} - \frac{1}{d}\right) = \frac{2}{d}\left(X_{tj} - \mu_t\right)
Step 4: the derivative of s, with epsilon absorbed into s #Link to equation: Step 4: the derivative of s, with epsilon absorbed into s
∂st∂Xtj=−12(σt2+ϵ)−3/22d(Xtj−μt)=−1d st3(Xtj−μt)\frac{\partial s_t}{\partial X_{tj}} = -\frac{1}{2}\left(\sigma_t^2 + \epsilon\right)^{-3/2} \frac{2}{d}\left(X_{tj} - \mu_t\right) = -\frac{1}{d}\, s_t^3 \left(X_{tj} - \mu_t\right)
Step 5: the product rule on the normalised row #Link to equation: Step 5: the product rule on the normalised row
∂X^ti∂Xtj=st(δij−1d)+(Xti−μt)∂st∂Xtj=st(δij−1d−1d X^tiX^tj)\frac{\partial \hat{X}_{ti}}{\partial X_{tj}} = s_t\left(\delta_{ij} - \frac{1}{d}\right) + \left(X_{ti} - \mu_t\right)\frac{\partial s_t}{\partial X_{tj}} = s_t\left(\delta_{ij} - \frac{1}{d} - \frac{1}{d}\,\hat{X}_{ti}\hat{X}_{tj}\right)
Step 6: the chain rule over all features of the row #Link to equation: Step 6: the chain rule over all features of the row
dXtj=∑idX^ti ∂X^ti∂Xtj=st(dX^tj−1d∑idX^ti−X^tj 1d∑idX^tiX^ti)dX_{tj} = \sum_{i} d\hat{X}_{ti}\, \frac{\partial \hat{X}_{ti}}{\partial X_{tj}} = s_t \left( d\hat{X}_{tj} - \frac{1}{d}\sum_{i} d\hat{X}_{ti} - \hat{X}_{tj}\, \frac{1}{d}\sum_{i} d\hat{X}_{ti}\hat{X}_{ti} \right)
The parameters, each of which is shared by every row #Link to equation: The parameters, each of which is shared by every row
∂Yti∂γi=X^ti,∂Yti∂βi=1\frac{\partial Y_{ti}}{\partial \gamma_i} = \hat{X}_{ti},\quad \frac{\partial Y_{ti}}{\partial \beta_i} = 1
File and functionops.pylayer_norm_forwardops.pylayer_norm_backwardTensor shapesEach row has its own matrix of derivatives. The code never builds itdXhat row (d,)(32,)dX/dXhat matrix (d, d)(32, 32)dX row (d,)(32,)LinksExplanationNumerical exampleops.py:42-47ops.py:49-57
LayerNormImplementedDirectionForwardEquation
Mean of one row, over the last axis #Link to equation: Mean of one row, over the last axis
μt=1d∑iXti\mu_t = \frac{1}{d} \sum_{i} X_{ti}
Biased variance of one row. It divides by d #Link to equation: Biased variance of one row. It divides by d
σt2=1d∑i(Xti−μt)2\sigma_t^2 = \frac{1}{d} \sum_{i} \left(X_{ti} - \mu_t\right)^2
Inverse standard deviation #Link to equation: Inverse standard deviation
st=1σt2+ϵs_t = \frac{1}{\sqrt{\sigma_t^2 + \epsilon}}
The normalised row #Link to equation: The normalised row
X^ti=(Xti−μt)st\hat{X}_{ti} = \left(X_{ti} - \mu_t\right) s_t
Scale and shift, one pair per feature #Link to equation: Scale and shift, one pair per feature
Yti=γiX^ti+βiY_{ti} = \gamma_i \hat{X}_{ti} + \beta_i
File and functionops.pylayer_norm_forwardTensor shapesBefore attentionX (T, d)(16, 32)Xn (T, d)(16, 32)Before the feed-forward layerX1 (T, d)(16, 32)Xn2 (T, d)(16, 32)At the end of the model, before the headX (T, d)(16, 32)Xf (T, d)(16, 32)LinksExplanationNumerical exampleops.py:42-47
Linear layer, backwardImplementedDirectionBackwardEquation
The derivatives of one output entry Derived from implementation#Link to equation: The derivatives of one output entry
∂Ytj∂Wkj=Xtk,∂Ytj∂bj=1,∂Ytj∂Xtk=Wkj\frac{\partial Y_{tj}}{\partial W_{kj}} = X_{tk},\quad \frac{\partial Y_{tj}}{\partial b_{j}} = 1,\quad \frac{\partial Y_{tj}}{\partial X_{tk}} = W_{kj}
Gradient on the weights #Link to equation: Gradient on the weights
dW=X⊤dYdW = X^{\top} dY
Gradient on the bias, the sum of dY over rows #Link to equation: Gradient on the bias, the sum of dY over rows
db=∑tdYtdb = \sum_{t} dY_{t}
Gradient on the input #Link to equation: Gradient on the input
dX=dY W⊤dX = dY\, W^{\top}
File and functionops.pylinear_backwardTensor shapesLinear headdZ (T, V)(16, V)dW (d, V)(32, V)db (V,)dXf (T, d)(16, 32)Second feed-forward layerdOut (T, d)(16, 32)dW2 (F, d)(128, 32)db2 (d,)(32,)dA (T, F)(16, 128)First feed-forward layerdZ (T, F)(16, 128)dW1 (d, F)(32, 128)db1 (F,)(128,)dXn (T, d)(16, 32)LinksExplanationNumerical exampleops.py:10-14
Linear layerImplementedDirectionForwardEquation
The linear layer, with b added to every row of XW #Link to equation: The linear layer, with b added to every row of XW
Y=XW+bY = XW + b
File and functionops.pylinear_forwardTensor shapesAttention output projection, width stays the sameO (T, d)(16, 32)out (T, d)(16, 32)First feed-forward layer, width growsXn (T, d)(16, 32)Z (T, F)(16, 128)Second feed-forward layer, width shrinks backA (T, F)(16, 128)output (T, d)(16, 32)Linear head, width becomes the vocabulary sizeXf (T, d)(16, 32)logits (T, V)(16, V)LinksExplanationNumerical exampleops.py:7-8
PerplexityImplementedDirectionNeither (not differentiated)Equation
Perplexity #Link to equation: Perplexity
perplexity=eL\text{perplexity} = e^{L}
File and functionops.pyperplexityTensor shapesNone declaredLinksExplanationNumerical exampleops.py:59-60
ReLU, backwardImplementedDirectionBackwardEquation
The derivative as the code uses it, with 0 at exactly Z = 0 Derived from implementation#Link to equation: The derivative as the code uses it, with 0 at exactly Z = 0
∂Ati∂Zti={1Zti>00Zti≤0\frac{\partial A_{ti}}{\partial Z_{ti}} = \begin{cases} 1 & Z_{ti} > 0 \\ 0 & Z_{ti} \le 0 \end{cases}
The mask-based backward #Link to equation: The mask-based backward
dZ=dA⊙1[Z>0]dZ = dA \odot \mathbf{1}[Z > 0]
File and functionops.pyrelu_backwardTensor shapesInside the feed-forward backwarddA (T, F)(16, 128)dZ (T, F)(16, 128)LinksExplanationNumerical exampleops.py:19-20
ReLUImplementedDirectionForwardEquation
ReLU, on each entry #Link to equation: ReLU, on each entry
Ati=max⁡(0, Zti)A_{ti} = \max(0,\, Z_{ti})
File and functionops.pyrelu_forwardTensor shapesInside the feed-forward layerZ (T, F)(16, 128)A (T, F)(16, 128)LinksExplanationNumerical exampleops.py:16-17
Softmax, backwardImplementedDirectionBackwardEquation
The derivative of softmax. The code never builds this matrix #Link to equation: The derivative of softmax. The code never builds this matrix
∂yi∂xj=yi(δij−yj)\frac{\partial y_i}{\partial x_j} = y_i\left(\delta_{ij} - y_j\right)
The backward line, which multiplies dY by that derivative #Link to equation: The backward line, which multiplies dY by that derivative
dxj=yj(dYj−∑iyi dYi)dx_j = y_j\left(dY_j - \sum_i y_i\, dY_i\right)
File and functionops.pysoftmax_backwardTensor shapesAttentiondA (H, T, T)(2, 16, 16)dS (H, T, T)(2, 16, 16)LinksExplanationNumerical exampleops.py:26-27
SoftmaxImplementedDirectionForwardEquation
Shift by the maximum, then exponentiate #Link to equation: Shift by the maximum, then exponentiate
ei=exp⁡(xi−max⁡kxk)e_i = \exp\left(x_i - \max_k x_k\right)
Normalize #Link to equation: Normalize
pi=ei∑kekp_i = \frac{e_i}{\sum_k e_k}
Why the shift is safe. The factor from any constant c cancels #Link to equation: Why the shift is safe. The factor from any constant c cancels
exi−c∑kexk−c=e−c exie−c∑kexk=exi∑kexk\frac{e^{x_i - c}}{\sum_k e^{x_k - c}} = \frac{e^{-c}\, e^{x_i}}{e^{-c} \sum_k e^{x_k}} = \frac{e^{x_i}}{\sum_k e^{x_k}}
File and functionops.pysoftmax_forwardTensor shapesAttention weightsS (H, T, T)(2, 16, 16)A (H, T, T)(2, 16, 16)Samplinglogits / temperature (V,)p (V,)LinksExplanationNumerical exampleops.py:22-24
Xavier uniform initialisationImplementedDirectionNeither (not differentiated)Equation
The limit #Link to equation: The limit
a=6fan_in+fan_outa = \sqrt{\frac{6}{\text{fan\_in} + \text{fan\_out}}}
Each entry is drawn evenly between -a and a #Link to equation: Each entry is drawn evenly between -a and a
Wij∼U[−a, a]W_{ij} \sim U[-a,\, a]
The variance this gives. The code sets a and never computes this #Link to equation: The variance this gives. The code sets a and never computes this
Var⁡(Wij)=a23=2fan_in+fan_out\operatorname{Var}(W_{ij}) = \frac{a^2}{3} = \frac{2}{\text{fan\_in} + \text{fan\_out}}
File and functionops.pyxavier_uniformTensor shapesA query, key, value or output matrixfan_in, fan_out (d, d)(32, 32)w{q,k,v,o} (d, d)(32, 32)The first feed-forward matrixfan_in, fan_out (d, F)(32, 128)ff1_w (d, F)(32, 128)LinksExplanationNumerical exampleops.py:3-5
AdamW updateImplementedDirectionNeither (not differentiated)Equation
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
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
File and functionoptim.pystepTensor shapesOne key with two dimensions, head_w, whose update includes decaygrads[k] (d, V)(32, V)self.m[k] (d, V)(32, V)self.v[k] (d, V)(32, V)update (d, V)(32, V)params[k] (d, V)(32, V)One key with one dimension, head_b, whose update has no decaygrads[k] (V,)self.m[k] (V,)self.v[k] (V,)update (V,)params[k] (V,)LinksExplanationNumerical exampleoptim.py:13-30
Gradient clippingImplementedDirectionNeither (not differentiated)Equation
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
File and functionoptim.pyclip_grad_normTensor shapesClipping changes values, not shapes. head_w as one examplegrads[k] before (d, V)(32, V)grads[k] after (d, V)(32, V)LinksExplanationNumerical exampleoptim.py:40-50
Learning-rate scheduleImplementedDirectionNeither (not differentiated)Equation
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)
File and functionoptim.pylr_scheduleTensor shapesNone declaredLinksExplanationNumerical exampleoptim.py:32-37
Random choiceImplementedDirectionNeither (not differentiated)Equation
One draw from the final probabilities #Link to equation: One draw from the final probabilities
P(next=i)=piP(\mathrm{next} = i) = p_i
File and functiongenerate.pysample_nextTensor shapesThe array before the draw. The draw itself returns one int, with no axesp after top-k (V,)p after top-p (V,)LinksExplanationNo numerical examplegenerate.py:7-26
Greedy guardImplementedDirectionNeither (not differentiated)Equation
Greedy guard #Link to equation: Greedy guard
τ<10−6  ⟹  next=arg⁡max⁡izi\tau < 10^{-6} \implies \mathrm{next} = \arg\max_i z_i
File and functiongenerate.pysample_nextTensor shapesFrom the model's logits to the row sample_next reads. The result is one int, with no axeslogits (T, V)(16, V)logits[-1] (V,)LinksExplanationNo numerical examplegenerate.py:7-26
TemperatureImplementedDirectionNeither (not differentiated)Equation
Softmax of the logits divided by temperature #Link to equation: Softmax of the logits divided by temperature
p=softmax(zτ)p = \mathrm{softmax}\left(\frac{z}{\tau}\right)
File and functiongenerate.pysample_nextTensor shapesTemperature keeps the shapelogits (V,)p (V,)LinksExplanationNo numerical examplegenerate.py:7-26
Top-kDerived from implementationDirectionNeither (not differentiated)Equation
The cut-off #Link to equation: The cut-off
c=k-th largest value of p, counting repeatsc = k\text{-th largest value of } p \text{, counting repeats}
Zero everything below the cut-off #Link to equation: Zero everything below the cut-off
pi′={pipi≥c0otherwisep'_i = \begin{cases} p_i & p_i \geq c \\ 0 & \text{otherwise} \end{cases}
Renormalise #Link to equation: Renormalise
pi′′=pi′∑jpj′p''_i = \frac{p'_i}{\sum_j p'_j}
File and functiongenerate.pysample_nextTensor shapesTop-k changes values, not the shape. Removed tokens stay as zerosp before (V,)p after (V,)LinksExplanationNo numerical examplegenerate.py:7-26
Top-pDerived from implementationDirectionNeither (not differentiated)Equation
Sort #Link to equation: Sort
π=token indices sorted by p, largest first\pi = \text{token indices sorted by } p \text{, largest first}
Cumulative sum #Link to equation: Cumulative sum
Cm=∑j=0mpπjC_m = \sum_{j=0}^{m} p_{\pi_j}
The smallest prefix that reaches top_p #Link to equation: The smallest prefix that reaches top_p
s=min⁡{m:Cm≥ρ},keep π0,…,πss = \min \{ m : C_m \geq \rho \}, \qquad \text{keep } \pi_0, \dots, \pi_s
Renormalise #Link to equation: Renormalise
qi=pi∑m=0spπm for kept i,qi=0 otherwiseq_i = \frac{p_i}{\sum_{m=0}^{s} p_{\pi_m}} \text{ for kept } i, \qquad q_i = 0 \text{ otherwise}
File and functiongenerate.pysample_nextTensor shapesTop-p sorts and sums over all V tokens. Only keep is shorter, and p keeps its shapeorder (V,)cum (V,)p (V,)LinksExplanationNo numerical examplegenerate.py:7-26
Encode and decodeImplementedDirectionNeither (not differentiated)Equation
The id of a token #Link to equation: The id of a token
id(τ)={stoi[τ]if τ∈stoistoi[ <unk> ]otherwise\text{id}(\tau) = \begin{cases} \text{stoi}[\tau] & \text{if } \tau \in \text{stoi} \\ \text{stoi}[\,\texttt{<unk>}\,] & \text{otherwise} \end{cases}
Decoding joins the strings #Link to equation: Decoding joins the strings
decode(i1,…,in)=itos[i1] ∥ ⋯ ∥ itos[in]\text{decode}(i_1, \dots, i_n) = \text{itos}[i_1] \,\Vert\, \cdots \,\Vert\, \text{itos}[i_n]
File and functiontokenizer.pyencodetokenizer.pydecodetokenizer.pyapply_mergesTensor shapesThe ids of the corpus, as a list and then as an arraytok.encode(text) (N,)ids (N,)LinksExplanationNumerical exampletokenizer.py:86-90tokenizer.py:92-96tokenizer.py:63-73
The vocabularyImplementedDirectionNeither (not differentiated)Equation
The order of the vocabulary #Link to equation: The order of the vocabulary
itos=[ <unk> ]+[ characters, first seen first ]+[ new merged tokens, in merge order ]\text{itos} = [\,\texttt{<unk>}\,] + [\,\text{characters, first seen first}\,] + [\,\text{new merged tokens, in merge order}\,]
File and functiontokenizer.pybuild_tokenizerTensor shapesNone declaredLinksExplanationNumerical exampletokenizer.py:99-119
Pre-tokens and word countsImplementedDirectionNeither (not differentiated)Equation
How often the pre-token w occurs #Link to equation: How often the pre-token w occurs
nw=∣{ i:pi=w }∣n_w = \left|\{\, i : p_i = w \,\}\right|
File and functiontokenizer.pycount_wordsTensor shapesNone declaredLinksExplanationNumerical exampletokenizer.py:6-10
Applying a mergeImplementedDirectionNeither (not differentiated)Equation
Replace an adjacent pair with one token #Link to equation: Replace an adjacent pair with one token
[ …, a∗, b∗, … ]  →  [ …, a∗b∗, … ][\,\dots,\, a^*,\, b^*,\, \dots\,] \;\to\; [\,\dots,\, a^* b^*,\, \dots\,]
File and functiontokenizer.pymerge_tokensTensor shapesNone declaredLinksExplanationNumerical exampletokenizer.py:12-22
Pair frequency and the best pairImplementedDirectionNeither (not differentiated)Equation
Pair frequency, weighted by word count #Link to equation: Pair frequency, weighted by word count
f(a,b)=∑wnw ∣{ i:siw=a,  si+1w=b }∣f(a,b) = \sum_{w} n_w \,\bigl|\{\, i : s^{w}_{i} = a,\; s^{w}_{i+1} = b \,\}\bigr|
The best pair #Link to equation: The best pair
(a∗,b∗)=arg max⁡(a,b)f(a,b)(a^*, b^*) = \operatorname*{arg\,max}_{(a,b)} f(a,b)
File and functiontokenizer.pytrain_bpetokenizer.pytrain_bpe_on_textTensor shapesNone declaredLinksExplanationNumerical exampletokenizer.py:24-61tokenizer.py:76-77
Mini-batch loss and gradientDerived from implementationDirectionNeither (not differentiated)Equation
The batch size, read from this batch #Link to equation: The batch size, read from this batch
B=number of rows of xbB = \text{number of rows of } x_b
How the code accumulates the loss #Link to equation: How the code accumulates the loss
L←L+LiBi=1,…,B,starting from L=0L \leftarrow L + \frac{L_i}{B} \qquad i = 1, \dots, B, \quad \text{starting from } L = 0
How the code accumulates each gradient #Link to equation: How the code accumulates each gradient
gˉk←gˉk+gk,iBfor every key k,starting from zeros\bar g_k \leftarrow \bar g_k + \frac{g_{k,i}}{B} \qquad \text{for every key } k, \quad \text{starting from zeros}
The result, the batch mean loss #Link to equation: The result, the batch mean loss
L=1B∑i=1BLiL = \frac{1}{B} \sum_{i=1}^{B} L_i
The result, the mean gradient #Link to equation: The result, the mean gradient
gˉk=1B∑i=1Bgk,i\bar g_k = \frac{1}{B} \sum_{i=1}^{B} g_{k,i}
File and functiontrain.pybatch_loss_and_gradsTensor shapesA batch goes through the model one window at a timexb (B, T)(16, 16)x (T,)(16,)logits inside loss_and_grads (T, V)(16, V)One key as an example. Averaging does not change a gradient's shapeg["head_w"] (d, V)(32, V)grads["head_w"] (d, V)(32, V)LinksExplanationNumerical exampletrain.py:12-24
Step countsDerived from implementationDirectionNeither (not differentiated)Equation
Steps per epoch #Link to equation: Steps per epoch
Sepoch=⌈WB⌉S_{\text{epoch}} = \left\lceil \frac{W}{B} \right\rceil
Total steps #Link to equation: Total steps
Stotal=E⋅SepochS_{\text{total}} = E \cdot S_{\text{epoch}}
File and functiontrain.pymainTensor shapesNone declaredLinksExplanationNumerical exampletrain.py:36-81
Validation lossDerived from implementationDirectionNeither (not differentiated)Equation
Mean cross-entropy over validation windows #Link to equation: Mean cross-entropy over validation windows
v=1Wv∑j=1WvLjv = \frac{1}{W_v} \sum_{j=1}^{W_v} L_j
File and functiontrain.pyvalidation_lossTensor shapesValidation windows go through the model one at a timeinputs (W, T)(W, 16)x (T,)(16,)logits (T, V)(16, V)LinksExplanationNumerical exampletrain.py:26-34
Attention backwardImplementedDirectionBackwardEquation
Gradient on the output weights #Link to equation: Gradient on the output weights
dWo=O⊤ dOutdW_o = O^\top\, d\mathrm{Out}
Gradient on the output bias #Link to equation: Gradient on the output bias
dbo=1⊤dOutdb_o = \mathbf{1}^\top d\mathrm{Out}
Through the output projection, then the head split #Link to equation: Through the output projection, then the head split
dOh=split⁡(dOut Wo⊤,H)dO_h = \operatorname{split}(d\mathrm{Out}\, W_o^\top, H)
Gradient on the values #Link to equation: Gradient on the values
dVh=A⊤dOhdV_h = A^\top dO_h
Gradient on the weights #Link to equation: Gradient on the weights
dA=dOhVh⊤dA = dO_h V_h^\top
Through softmax, softmax_backward(A, dA) #Link to equation: Through softmax, softmax_backward(A, dA)
dSij=Aij(dAij−∑kAik dAik)dS_{ij} = A_{ij}\Big(dA_{ij} - \sum_{k} A_{ik}\, dA_{ik}\Big)
A masked cell gets no gradient Derived from implementation#Link to equation: A masked cell gets no gradient
Aij=0⇒dSij=0A_{ij} = 0 \quad\Rightarrow\quad dS_{ij} = 0
Gradient on the queries #Link to equation: Gradient on the queries
dQh=dS KhdhdQ_h = \frac{dS\, K_h}{\sqrt{d_h}}
Gradient on the keys #Link to equation: Gradient on the keys
dKh=dS⊤QhdhdK_h = \frac{dS^\top Q_h}{\sqrt{d_h}}
Heads side by side again Derived from implementation#Link to equation: Heads side by side again
dQ, dK, dV=merge⁡(dQh), merge⁡(dKh), merge⁡(dVh)dQ,\ dK,\ dV = \operatorname{merge}(dQ_h),\ \operatorname{merge}(dK_h),\ \operatorname{merge}(dV_h)
Gradient on the three weight matrices #Link to equation: Gradient on the three weight matrices
dWn=Xn⊤dMn,n∈{q,k,v}dW_n = X_n^\top dM_n, \qquad n \in \{q, k, v\}
Gradient on the three biases #Link to equation: Gradient on the three biases
dbn=1⊤dMn,n∈{q,k,v}db_n = \mathbf{1}^\top dM_n, \qquad n \in \{q, k, v\}
Gradient on the input, summed over q, k and v #Link to equation: Gradient on the input, summed over q, k and v
dXn=dQ Wq⊤+dK Wk⊤+dV Wv⊤dX_n = dQ\, W_q^\top + dK\, W_k^\top + dV\, W_v^\top
The key bias has a gradient of zero #Link to equation: The key bias has a gradient of zero
dbk=1⊤dK=0db_k = \mathbf{1}^\top dK = 0
File and functiontransformer.pyattention_backwardTensor shapesFrom dOut to the valuesdOut (T, d)(16, 32)dOh (H, T, dh)(2, 16, 16)dVh (H, T, dh)(2, 16, 16)dV (T, d)(16, 32)From dOh through the weights and scores to the queries. Keys go the same waydOh (H, T, dh)(2, 16, 16)dA (H, T, T)(2, 16, 16)dS (H, T, T)(2, 16, 16)dQh (H, T, dh)(2, 16, 16)dQ (T, d)(16, 32)Parameter gradient and input gradient, for one projectiondM (T, d)(16, 32)grads wn (d, d)(32, 32)dXn (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:92-109
Residual connections, backwardImplementedDirectionBackwardEquation
Through the feed-forward branch #Link to equation: Through the feed-forward branch
dXn2=FFN⁡back(dY)dX_{n2} = \operatorname{FFN}^{\text{back}}(dY)
Through the second LayerNorm #Link to equation: Through the second LayerNorm
dX1branch=LN⁡2back(dXn2)dX_1^{\text{branch}} = \operatorname{LN}_2^{\text{back}}(dX_{n2})
Second add, reversed #Link to equation: Second add, reversed
dX1=dY+dX1branchdX_1 = dY + dX_1^{\text{branch}}
Through the attention branch #Link to equation: Through the attention branch
dXn=Attention⁡back(dX1)dX_n = \operatorname{Attention}^{\text{back}}(dX_1)
Through the first LayerNorm #Link to equation: Through the first LayerNorm
dXbranch=LN⁡1back(dXn)dX^{\text{branch}} = \operatorname{LN}_1^{\text{back}}(dX_n)
First add, reversed #Link to equation: First add, reversed
dX=dX1+dXbranchdX = dX_1 + dX^{\text{branch}}
What each branch receives Derived from implementation#Link to equation: What each branch receives
∂Y∂Ff=I  ⇒  dFf=dY,∂X1∂Att=I  ⇒  dAtt=dX1\frac{\partial Y}{\partial \mathrm{Ff}} = I \;\Rightarrow\; d\mathrm{Ff} = dY, \qquad \frac{\partial X_1}{\partial \mathrm{Att}} = I \;\Rightarrow\; d\mathrm{Att} = dX_1
Why the identity path carries dY unchanged Derived from implementation#Link to equation: Why the identity path carries dY unchanged
Y=X1+Ff(X1)  ⇒  dX1=dY(I+∂Ff∂X1)=dY+dY∂Ff∂X1Y = X_1 + \mathrm{Ff}(X_1) \;\Rightarrow\; dX_1 = dY \left( I + \frac{\partial \mathrm{Ff}}{\partial X_1} \right) = dY + dY \frac{\partial \mathrm{Ff}}{\partial X_1}
File and functiontransformer.pyblock_backwardTensor shapesEvery gradient in the block has the same shapedY (T, d)(16, 32)dXn2 (T, d)(16, 32)dX1_branch (T, d)(16, 32)dX1 (T, d)(16, 32)dXn (T, d)(16, 32)dX_branch (T, d)(16, 32)dX (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:141-152
Pre-LN block, forwardImplementedDirectionForwardEquation
First LayerNorm, on a copy #Link to equation: First LayerNorm, on a copy
Xn=LN⁡1(X)X_n = \operatorname{LN}_1(X)
Attention reads the copy #Link to equation: Attention reads the copy
Att=Attention⁡(Xn)\mathrm{Att} = \operatorname{Attention}(X_n)
First residual add #Link to equation: First residual add
X1=X+AttX_1 = X + \mathrm{Att}
Second LayerNorm, on a copy #Link to equation: Second LayerNorm, on a copy
Xn2=LN⁡2(X1)X_{n2} = \operatorname{LN}_2(X_1)
The feed-forward layer reads the copy #Link to equation: The feed-forward layer reads the copy
Ff=FFN⁡(Xn2)\mathrm{Ff} = \operatorname{FFN}(X_{n2})
Second residual add #Link to equation: Second residual add
Y=X1+FfY = X_1 + \mathrm{Ff}
The block on one line #Link to equation: The block on one line
X1=X+Attention⁡(LN⁡1(X)),Y=X1+FFN⁡(LN⁡2(X1))X_1 = X + \operatorname{Attention}(\operatorname{LN}_1(X)), \qquad Y = X_1 + \operatorname{FFN}(\operatorname{LN}_2(X_1))
File and functiontransformer.pyblock_forwardTensor shapesEvery array in the block has the same shapeX (T, d)(16, 32)Xn (T, d)(16, 32)att (T, d)(16, 32)X1 (T, d)(16, 32)Xn2 (T, d)(16, 32)ff (T, d)(16, 32)Y (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:131-139
Parameter countImplementedDirectionNeither (not differentiated)Equation
Scalars in one block #Link to equation: Scalars in one block
Nblock=4d2+2dF+F+9dN_{\text{block}} = 4d^2 + 2dF + F + 9d
Scalars in the whole model #Link to equation: Scalars in the whole model
N=(2d+1)V+(Tmax⁡+2)d+L(4d2+2dF+F+9d)N = (2d+1)V + (T_{\max}+2)d + L\left(4d^2 + 2dF + F + 9d\right)
File and functiontransformer.pycount_paramstransformer.pyinit_paramsTensor shapesThe two feed-forward weight matrices of one blockff1_w (d, F)(32, 128)ff2_w (F, d)(128, 32)LinksExplanationNumerical exampletransformer.py:44-45transformer.py:17-42
Embedding backwardImplementedDirectionBackwardEquation
The chain rule for one entry of the token table Derived from implementation#Link to equation: The chain rule for one entry of the token table
∂L∂Evj=∑tdXtj(0) ∂Xtj(0)∂Evj,∂Xtj(0)∂Evj={1it=v0otherwise\frac{\partial L}{\partial E_{vj}} = \sum_{t} dX^{(0)}_{tj}\, \frac{\partial X^{(0)}_{tj}}{\partial E_{vj}}, \qquad \frac{\partial X^{(0)}_{tj}}{\partial E_{vj}} = \begin{cases} 1 & i_t = v \\ 0 & \text{otherwise} \end{cases}
Gradient accumulation per token row #Link to equation: Gradient accumulation per token row
dEv=∑t : it=vdXt(0)dE_v = \sum_{t\,:\, i_t = v} dX^{(0)}_t
Gradient on the position table #Link to equation: Gradient on the position table
dPt={dXt(0)t<T0t≥TdP_t = \begin{cases} dX^{(0)}_t & t < T \\ 0 & t \geq T \end{cases}
File and functiontransformer.pyembed_backwardTensor shapesInto the token table. Rows of ids not in this window stay 0dX0 (T, d)(16, 32)grads["tok_emb"] (V, d)(V, 32)Into the position table. Rows from T onward stay 0dX0 (T, d)(16, 32)grads["pos_emb"] (Tmax, d)(32, 32)LinksExplanationNumerical exampletransformer.py:55-57
Embedding sumImplementedDirectionForwardEquation
One row of the sum #Link to equation: One row of the sum
Xt(0)=Eit+PtX^{(0)}_t = E_{i_t} + P_t
The whole sum, as the code writes it #Link to equation: The whole sum, as the code writes it
X(0)=E[ids]+P[:T]X^{(0)} = E[\text{ids}] + P[:T]
File and functiontransformer.pyembed_forwardTensor shapesBoth terms have the same shape, and so does their sumtok_emb[ids] (T, d)(16, 32)pos_emb[:T] (T, d)(16, 32)X0 (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:52-53
Feed-forward network, backwardImplementedDirectionBackwardEquation
Gradient on the ReLU output #Link to equation: Gradient on the ReLU output
dA=dOut W2⊤dA = d\mathrm{Out}\, W_2^\top
Gradient on the second weights #Link to equation: Gradient on the second weights
dW2=A⊤dOutdW_2 = A^\top d\mathrm{Out}
Gradient on the second bias #Link to equation: Gradient on the second bias
db2=1⊤dOutdb_2 = \mathbf{1}^\top d\mathrm{Out}
Through ReLU #Link to equation: Through ReLU
dZ=dA⊙1[Z>0]dZ = dA \odot \mathbb{1}[Z > 0]
Gradient on the input #Link to equation: Gradient on the input
dXn=dZ W1⊤dX_n = dZ\, W_1^\top
Gradient on the first weights #Link to equation: Gradient on the first weights
dW1=Xn⊤dZdW_1 = X_n^\top dZ
Gradient on the first bias #Link to equation: Gradient on the first bias
db1=1⊤dZdb_1 = \mathbf{1}^\top dZ
File and functiontransformer.pyffn_backwardTensor shapesBack through the three stepsdOut (T, d)(16, 32)dA (T, F)(16, 128)dZ (T, F)(16, 128)dXn (T, d)(16, 32)Parameter gradients of the second layerdW2 (F, d)(128, 32)db2 (d,)(32,)Parameter gradients of the first layerdW1 (d, F)(32, 128)db1 (F,)(128,)LinksExplanationNumerical exampletransformer.py:118-128
Feed-forward network, forwardImplementedDirectionForwardEquation
Widen #Link to equation: Widen
Z=XnW1+1b1⊤Z = X_n W_1 + \mathbf{1} b_1^\top
ReLU, element by element #Link to equation: ReLU, element by element
A=ReLU⁡(Z)A = \operatorname{ReLU}(Z)
Narrow #Link to equation: Narrow
Ff=AW2+1b2⊤\mathrm{Ff} = A W_2 + \mathbf{1} b_2^\top
The same three steps for one row x #Link to equation: The same three steps for one row x
FFN⁡(x)=ReLU⁡(xW1+b1) W2+b2\operatorname{FFN}(x) = \operatorname{ReLU}(x W_1 + b_1)\, W_2 + b_2
File and functiontransformer.pyffn_forwardTensor shapesWiden, ReLU, narrowXn (T, d)(16, 32)Z (T, F)(16, 128)A (T, F)(16, 128)out (T, d)(16, 32)LinksExplanationNumerical exampletransformer.py:112-116
Whole model, forwardImplementedDirectionForwardEquation
The sequence must fit the position table #Link to equation: The sequence must fit the position table
T≤Tmax⁡T \le T_{\max}
Blocks in sequence #Link to equation: Blocks in sequence
X(l+1)=Block⁡l ⁣(X(l)),l=0,…,L−1X^{(l+1)} = \operatorname{Block}_l\!\big(X^{(l)}\big), \qquad l = 0, \dots, L - 1
Final LayerNorm #Link to equation: Final LayerNorm
Xf=LN⁡f ⁣(X(L))X_f = \operatorname{LN}_f\!\big(X^{(L)}\big)
The head #Link to equation: The head
Z=XfWh+1bh⊤Z = X_f W_h + \mathbf{1} b_h^\top
The last two steps together #Link to equation: The last two steps together
Logits=LN⁡f ⁣(X(L)) Wh+bh\mathrm{Logits} = \operatorname{LN}_f\!\big(X^{(L)}\big)\, W_h + b_h
File and functiontransformer.pyforwardTensor shapesOne sequence through the modelids (T,)(16,)X (T, d)(16, 32)Xf (T, d)(16, 32)logits (T, V)(16, V)LinksExplanationNumerical exampletransformer.py:155-168
Starting values of the parametersImplementedDirectionNeither (not differentiated)Equation
Starting values, by kind of parameter #Link to equation: Starting values, by kind of parameter
W∼Xavier uniform,b=0,γ=1,β=0W \sim \text{Xavier uniform},\quad b = \mathbf{0},\quad \gamma = \mathbf{1},\quad \beta = \mathbf{0}
File and functiontransformer.pyinit_paramsTensor shapesThe head's weights and biashead_w (d, V)(32, V)head_b (V,)LinksExplanationNumerical exampletransformer.py:17-42
Whole model, backwardImplementedDirectionBackwardEquation
Order of calls, the head #Link to equation: Order of calls, the head
dXf, dWh, dbh=Head⁡back(dZ)dX_f,\ dW_h,\ db_h = \operatorname{Head}^{\text{back}}(dZ)
Order of calls, the final LayerNorm #Link to equation: Order of calls, the final LayerNorm
dX(L), dγf, dβf=LN⁡fback(dXf)dX^{(L)},\ d\gamma_f,\ d\beta_f = \operatorname{LN}_f^{\text{back}}(dX_f)
Order of calls, the blocks in reverse #Link to equation: Order of calls, the blocks in reverse
dX(l)=Block⁡lback ⁣(dX(l+1)),l=L−1,…,0dX^{(l)} = \operatorname{Block}_l^{\text{back}}\!\big(dX^{(l+1)}\big), \qquad l = L - 1, \dots, 0
Order of calls, the embeddings #Link to equation: Order of calls, the embeddings
d tok_emb, d pos_emb=Embed⁡back ⁣(dX(0))d\,\mathrm{tok\_emb},\ d\,\mathrm{pos\_emb} = \operatorname{Embed}^{\text{back}}\!\big(dX^{(0)}\big)
File and functiontransformer.pyloss_and_gradsTensor shapesThe gradient from the logits to the first block's inputdZ (T, V)(16, V)dXf (T, d)(16, 32)dX (T, d)(16, 32)Parameter gradients of the headhead_w (d, V)(32, V)head_b (V,)Parameter gradients of the embedding tablestok_emb (V, d)(V, 32)pos_emb (Tmax, d)(32, 32)LinksExplanationNumerical exampletransformer.py:171-190