Zhanbo Chen
Back to home

CS546 Learning Notes: From Word Embedding to RNN, Understanding BPTT and Vanishing Gradients

Learning Notes/Algorithm
2026-09-12
15 min read
0 reads
AIMachine Learning

In brief

Last time I manually worked through the forward and backward propagation of Softmax, Cross-entropy, and a ReLU MLP. This time continuing the question: if the input is a sentence, how does text become numbers? When sentence lengths differ and word order varies, how does the model handle it? The main thread I worked through today is: token → embedding → RNN hidden state → prediction of next word → loss → backpropagation through time. The places most worth stopping to carefully work through are: the transpose in the embedding gradient, and why the gradient in RNN may become smaller the farther it propagates.

1. Embedding matrix: each word corresponds to one row

First, assume each word is a single token. Real tokenizers don't necessarily segment by complete words; we use words here just for convenience in hand calculation.

The vocabulary order is:

[I,like,cats,dogs,you][\text{I},\text{like},\text{cats},\text{dogs},\text{you}]

Token IDs start from 0, so the ID for cats is 2. The vocabulary size is V=5V=5, and each embedding has d=3d=3 numbers:

E∈R5×3E\in\mathbb R^{5\times3}

For example:

E=[0.2−0.10.70.50.3−0.2−0.40.80.10.6−0.50.90.00.2−0.3]E=\begin{bmatrix} 0.2&-0.1&0.7\\ 0.5&0.3&-0.2\\ -0.4&0.8&0.1\\ 0.6&-0.5&0.9\\ 0.0&0.2&-0.3 \end{bmatrix}

Embedding lookup retrieves the corresponding row based on token ID:

E[2]=[−0.4, 0.8, 0.1]E[2]=[-0.4,\ 0.8,\ 0.1]

This is index 2, which corresponds to the 3rd row written out.

The sentence I like cats has 3 tokens, and looking them up in the table yields a 3×33\times3 matrix. The two 3's represent the number of tokens and embedding dimension respectively, which happen to be the same.

InputShapeDimension Meaning
Complete embedding matrixV×dV\times dvocabulary size, vector dimension
A sentence of length TTT×dT\times dsequence length, vector dimension
BB sentences padded to length TTB×T×dB\times T\times dbatch, sequence length, vector dimension

Therefore, 2 sentences, each padded to 4 tokens, with each vector of length 3, the input tensor is 2×4×32\times4\times3. This is the common batch-first convention.

2. One-hot times matrix is embedding lookup

Write the one-hot for cats as a row vector:

x=[0,0,1,0,0]∈R1×5x=[0,0,1,0,0]\in\mathbb R^{1\times5}

Then:

x⏟1×5E⏟5×3=e⏟1×3=E[2]\underbrace{x}_{1\times5}\underbrace{E}_{5\times3} =\underbrace{e}_{1\times3} =E[2]

All other rows are multiplied by 0, only the cats row is multiplied by 1. Programs typically use direct lookup instead of actually creating a one-hot and performing many multiplications involving zeros.

The embedding matrix is usually a learnable parameter: later predictions generate a loss, gradients flow back, and the corresponding word vectors are updated.

3. Why is the gradient of embedding xTgex^Tg_e?

When I saw this transpose, I was confused again:

How is the transpose of xx derived? Can you work through it for me?

Start with a single element. The forward computation is e=xEe=xE, expanding the jj-th output component:

ej=∑i=15xiEije_j=\sum_{i=1}^{5}x_iE_{ij}

Here, the subscripts use mathematical notation starting from 1, so cats corresponds to i=3i=3; its program index is still 2.

Let the gradient flowing back be a row vector:

ge=∂L∂e=[g1,g2,g3]∈R1×3g_e=\frac{\partial L}{\partial e} =[g_1,g_2,g_3]\in\mathbb R^{1\times3}

where gj=∂L/∂ejg_j=\partial L/\partial e_j. The parameter EijE_{ij} directly affects only eje_j, and:

∂ej∂Eij=xi\frac{\partial e_j}{\partial E_{ij}}=x_i

The chain rule gives:

∂L∂Eij=∂L∂ej∂ej∂Eij=gjxi\frac{\partial L}{\partial E_{ij}} =\frac{\partial L}{\partial e_j} \frac{\partial e_j}{\partial E_{ij}} =g_jx_i

Arranging all elements back into a matrix:

∂L∂E=[x1g1x1g2x1g3x2g1x2g2x2g3⋮⋮⋮x5g1x5g2x5g3]=xTge\frac{\partial L}{\partial E} =\begin{bmatrix} x_1g_1&x_1g_2&x_1g_3\\ x_2g_1&x_2g_2&x_2g_3\\ \vdots&\vdots&\vdots\\ x_5g_1&x_5g_2&x_5g_3 \end{bmatrix} =x^Tg_e

The shapes also match:

(5×1)(1×3)=5×3(5\times1)(1\times3)=5\times3

This is called the outer product. The justification for the transpose comes from element-wise differentiation yielding xigjx_i g_j; shape is a tool for checking the result. When directly writing xgexg_e, the inner dimension 5≠15\neq1, and matrix multiplication won't even work.

For example, if ge=[2,−1,3]g_e=[2,-1,3], then:

∂L∂E=[0000002−13000000]\frac{\partial L}{\partial E} =\begin{bmatrix} 0&0&0\\ 0&0&0\\ 2&-1&3\\ 0&0&0\\ 0&0&0 \end{bmatrix}

If the learning rate is η=0.1\eta=0.1, the standard Gradient Descent update is:

E[2]new=[−0.4,0.8,0.1]−0.1[2,−1,3]=[−0.6,0.9,−0.2]\begin{aligned} E[2]_{\text{new}} &=[-0.4,0.8,0.1]-0.1[2,-1,3]\\ &=[-0.6,0.9,-0.2] \end{aligned}

I initially wrote the first number as 0.60.6. Actually −0.4−0.2=−0.6-0.4-0.2=-0.6; the second number increases because we subtract a negative gradient: 0.8−0.1(−1)=0.90.8-0.1(-1)=0.9.

"Only updating the looked-up row" here has conditions: EE is only used for input lookup, uses standard Gradient Descent, and has no other update sources like regularization. If there is weight decay, optimizer history, or shared embedding weights with the output layer, we cannot simply claim other rows remain unchanged.

If the same word appears twice, both lookups use the same row of parameters, so gradients from both positions sum together.

4. Where does the correct answer for next token prediction come from?

My question at that time was:

Previously there were definite labels; how do we provide the answer here?

First, let's distinguish: labels in regression tasks are usually numerical; one-hot is the label representation from earlier classification tasks. Next-token prediction in language models can also be viewed as a classification task over the vocabulary.

The raw text itself provides the answer. For:

[I,like,cats,today][\text{I},\text{like},\text{cats},\text{today}]

Shift the sequence by one position:

Time stepCurrent input tokenContext seen so farTarget
1IIlike
2likeI likecats
3catsI like catstoday

So the training input is [I, like, cats], and the target is [like, cats, today]. Although at step 2 we only read in like, the hidden state still carries information from I before it.

This way of constructing supervised signals from the data itself is called self-supervised learning. The target can conceptually be written as one-hot, but programs typically store token IDs directly.

We can also add <BOS> and <EOS> to represent sequence start and end:

Input<BOS>Ilikecatstoday
TargetIlikecatstoday<EOS>

The specific usage of special tokens depends on model and data processing conventions; not all implementations must use these two names. When adding new tokens, the vocabulary size must be computed accordingly.

During training, this example feeds the correct token from the source text at each step, called teacher forcing. During generation, the token selected from the previous step is fed back to the next step. The RNN's hidden state still needs to be computed sequentially over time; knowing all labels doesn't mean we can compute the hidden states of all time steps in parallel.

5. Similar embeddings are not humans manually specifying the meaning of each number

Each coordinate in an embedding typically has no human-assigned independent semantics. Vectors gradually adjust by completing training objectives; words that frequently appear in similar contexts may learn similar representations.

For example, cats and dogs are often used similarly, but this is a trend resulting from training, not guaranteed by one or two sentences.

It's especially important to distinguish between input embeddings and output layer weights: in the sample I like → cats, if the two don't share parameters, the input lookup uses I and like, and won't directly look up and update the cats row on the input side just because the target is cats. The output layer learns to predict cats through classification gradients over the vocabulary.

Therefore, "similar context corresponds to similar embedding" cannot be directly inferred from this single sample; we must look at the complete data, training objective, and parameter sharing scheme. Cosine similarity is commonly used when comparing word vectors; it mainly measures whether directions are similar.

6. Why can't we just add the embeddings of all words in a sentence?

Compare:

  • dog bites man
  • man bites dog

I initially thought their sum representations would be different, but vector addition satisfies commutativity:

edog+ebites+eman=eman+ebites+edoge_{\text{dog}}+e_{\text{bites}}+e_{\text{man}} =e_{\text{man}}+e_{\text{bites}}+e_{\text{dog}}

For this kind of fixed token embedding, direct summation loses word order. By arranging vectors into a matrix according to word order, we can preserve this difference in the input representation.

If each embedding has length 3, three words flattened have length 9, and five words have length 15. A standard MLP's first layer weight shape is fixed and cannot directly accept these two different lengths.

Padding to a fixed length can be used with MLP and preserve word order through fixed positions, but requires preprocessing length ranges. RNN provides another approach: read one token at a time and pass previous information to the next step.

7. RNN: current input plus previous hidden state

The basic RNN computation is:

ht=tanh⁡(Wxxt+Whht−1+bh)h_t=\tanh(W_xx_t+W_hh_{t-1}+b_h)

Starting from this section, xtx_t denotes the embedding column vector of the current token, not the one-hot row vector from the lookup derivation earlier. If EE stores one word vector per row, then xt=E[IDt]Tx_t=E[\mathrm{ID}_t]^T.

Let the embedding dimension be d=3d=3 and hidden dimension be H=4H=4:

SymbolShapeMeaning
xtx_t3×13\times1Embedding of current token
ht−1h_{t-1}、hth_t4×14\times1Hidden state from previous/current step
WxW_x4×34\times3Maps input to hidden space
WhW_h4×44\times4Maps history state to current computation
bhb_h4×14\times1Bias

All three terms yield 4×14\times1, so they can be added; tanh⁡\tanh acts element-wise without changing the shape.

Each time step shares the same set of Wx,Wh,bhW_x,W_h,b_h. Longer sequences increase the number of computation steps, but not the number of these parameters. Hidden state is a learned representation of historical information, with no guarantee of completely remembering all prior context.

8. Manual Verification: Why Can RNN Perceive Order?

Temporarily drop tanh⁡\tanh and simplify both input and hidden state to scalars:

ht=2xt+0.5ht−1,h0=0h_t=2x_t+0.5h_{t-1},\qquad h_0=0

Input sequence is [1,3][1,3]:

h1=2(1)+0.5(0)=2h_1=2(1)+0.5(0)=2 h2=2(3)+0.5(2)=7h_2=2(3)+0.5(2)=7

Swap the order, input [3,1][3,1]:

h1=2(3)+0.5(0)=6h_1=2(3)+0.5(0)=6 h2=2(1)+0.5(6)=5h_2=2(1)+0.5(6)=5

The same two inputs yield final states 7 and 5 respectively. Earlier inputs pass through the recurrent connection, so their effect on the final state differs from that of the current input, thus sequence order can influence the result.

This shows RNN can express word order differences, but does not mean any parameter setting will necessarily distinguish all different sequences.

9. We Can Already Distinguish Order—Why tanh⁡\tanh?

I recall the earlier ReLU: an MLP with all linear layers can be merged into a single linear transformation no matter how many layers. RNN has a similar problem.

Remove activation function and bias:

h1=Wxx1+Whh0h_1=W_xx_1+W_hh_0 h2=Wxx2+Whh1=Wxx2+WhWxx1+Wh2h0\begin{aligned} h_2 &=W_xx_2+W_hh_1\\ &=W_xx_2+W_hW_xx_1+W_h^2h_0 \end{aligned}

For a fixed number of steps, it remains a linear combination of inputs and initial state; with bias it becomes an affine transformation. Although it contains order information, this state computation cannot express nonlinear relationships.

After adding tanh⁡\tanh, nesting appears:

h2=tanh⁡(Wxx2+Whtanh⁡(Wxx1+Whh0+bh)+bh)h_2=\tanh\left(W_xx_2+W_h\tanh(W_xx_1+W_hh_0+b_h)+b_h\right)

This typically cannot be merged into a single linear layer anymore.

tanh⁡\tanh also constrains each hidden component to (−1,1)(-1,1). In the linear example ht=2ht−1h_t=2h_{t-1}, h0=1h_0=1, the state becomes 2,4,8,…2,4,8,\ldots; hidden states after tanh⁡\tanh do not grow unboundedly this way. However, bounded forward state does not guarantee stable backward gradients.

10. Output Next Word from Hidden State

The output layer reuses the Softmax classification from before:

zt=Woht+bo,pt=softmax⁡(zt)z_t=W_oh_t+b_o,\qquad p_t=\operatorname{softmax}(z_t)

If H=4H=4 and vocabulary size V=5V=5:

Wo⏟5×4ht⏟4×1+bo⏟5×1=zt⏟5×1\underbrace{W_o}_{5\times4}\underbrace{h_t}_{4\times1} +\underbrace{b_o}_{5\times1} =\underbrace{z_t}_{5\times1}

ptp_t is likewise 5×15\times1, with each element being the probability of the corresponding word—all non-negative and sum to 1.

Using the five-word vocabulary, if the target is cats:

pt=[0.1,0.1,0.4,0.3,0.1]T,yt=[0,0,1,0,0]Tp_t=[0.1,0.1,0.4,0.3,0.1]^T,\qquad y_t=[0,0,1,0,0]^T

Cross-entropy at a single position is:

Lt=−log⁡pt(cats)=−log⁡0.4L_t=-\log p_t(\text{cats})=-\log0.4

The gradient is:

∂Lt∂zt=pt−yt=[0.1,0.1,−0.6,0.3,0.1]T\frac{\partial L_t}{\partial z_t}=p_t-y_t =[0.1,0.1,-0.6,0.3,0.1]^T

Higher probability for the correct word means lower loss. Treating logits as independent variables, the negative gradient direction would increase the correct word's logit and decrease others'. But actual training updates shared parameters, which does not guarantee that after updating the entire model or a batch, every sample's logits strictly change in this direction.

Multiple positions' objectives can be summed:

L=∑t=1TLtL=\sum_{t=1}^{T}L_t

Or averaged; when averaging, the corresponding total gradient should also be divided by TT. With padding, loss is typically computed only over valid targets.

11. BPTT: Chain Rule After Unrolling Over Time

Suppose we only look at three time steps, with total loss:

L=L1+L2+L3L=L_1+L_2+L_3

h1h_1 affects L1L_1, and also affects L2L_2 through h2h_2, and affects L3L_3 through h2,h3h_2,h_3. Therefore:

∂L∂h1=∂L1∂h1+∂L2∂h1+∂L3∂h1\frac{\partial L}{\partial h_1} =\frac{\partial L_1}{\partial h_1} +\frac{\partial L_2}{\partial h_1} +\frac{\partial L_3}{\partial h_1}

While h2h_2 only receives contributions from L2,L3L_2,L_3. In the forward graph of a basic unidirectional RNN, there is no path from h2h_2 back to affect L1L_1.

This is Backpropagation Through Time (BPTT): unroll the RNN over time, then apply ordinary backpropagation.

In the scalar case, one path from L3L_3 is:

∂L3∂h1=∂L3∂h3∂h3∂h2∂h2∂h1\frac{\partial L_3}{\partial h_1} =\frac{\partial L_3}{\partial h_3} \frac{\partial h_3}{\partial h_2} \frac{\partial h_2}{\partial h_1}

Still the familiar rule: multiply along the path, add across paths.

In the vector case, we cannot freely permute matrix order. Define Jt=∂ht/∂ht−1∈RH×HJ_t=\partial h_t/\partial h_{t-1}\in\mathbb R^{H\times H}, and write the gradient of loss with respect to state as a column vector, then the contribution flowing back from L3L_3 to h1h_1 is:

∇h1L3=J2TJ3T∇h3L3\nabla_{h_1}L_3=J_2^TJ_3^T\nabla_{h_3}L_3

12. Why Must Gradients of Shared Parameters Be Summed?

The same WhW_h is reused across different time steps. During backpropagation, contributions from each use to the total loss must be accumulated to update the same parameter.

To make "each step's contribution" clear, define the pre-activation quantity:

at=Wxxt+Whht−1+bha_t=W_xx_t+W_hh_{t-1}+b_h

Let δt=∂L/∂at∈RH×1\delta_t=\partial L/\partial a_t\in\mathbb R^{H\times1}, which already includes contributions from current and subsequent losses, then:

∂L∂Wh=∑t=1Tδtht−1T\frac{\partial L}{\partial W_h} =\sum_{t=1}^{T}\delta_t h_{t-1}^T

Each term has shape (H×1)(1×H)=H×H(H\times1)(1\times H)=H\times H. As with embeddings, an outer product appears again.

After this complete sequence's backpropagation, ordinary Gradient Descent updates:

Wh←Wh−η∂L∂WhW_h\leftarrow W_h-\eta\frac{\partial L}{\partial W_h}

We don't treat it as three independently learned WhW_h just because we unrolled three time steps. Similarly, do not double-count gradients that already include subsequent influences.

13. Vanishing and Exploding Gradients: Consequences of Repeated Multiplication

Start with the simplest scalar RNN:

ht=wht−1h_t=wh_{t-1}

The local derivative at each step is ww. Assume the gradient of end-point loss with respect to h3h_3 is 1, then:

∂L3∂h0=1×w3\frac{\partial L_3}{\partial h_0}=1\times w^3

When w=0.5w=0.5:

0.53=0.125,0.510≈0.000980.5^3=0.125,\qquad 0.5^{10}\approx0.00098

When w=2w=2:

210=10242^{10}=1024

The former shows vanishing gradient, the latter exploding gradient. Here we use h0h_0 to observe how the signal changes across time, not claiming the fixed zero initial state must be a parameter to update.

Vanishing gradients mean the learning signal from later losses may be extremely weak for early computations, making long-term dependencies hard to learn. If early positions also have their own loss, they may still receive strong local gradients; what vanishes is the long-distance contribution we care about, not all gradients necessarily becoming zero.

In a real tanh RNN:

Jt=∂ht∂ht−1=diag⁡(1−ht2)WhJ_t=\frac{\partial h_t}{\partial h_{t-1}} =\operatorname{diag}(1-h_t^2)W_h

Here ht2h_t^2 denotes element-wise square, diag⁡\operatorname{diag} places these activation derivatives on the diagonal. The entire JtJ_t has shape H×HH\times H.

Because:

tanh⁡′(a)=1−tanh⁡2(a)\tanh'(a)=1-\tanh^2(a)

When hth_t approaches 11 or −1-1, the derivative approaches 0, called saturation. Gradients passing through these directions tend to get compressed.

The final manual calculation: end-point gradient is 1, local derivatives at five consecutive steps are all 0.2:

1×0.25=0.000321\times0.2^5=0.00032

This time it's correct. A local derivative that doesn't seem particularly small, when multiplied repeatedly, can still rapidly weaken learning signals from far away.

In the matrix case, it depends on the product of Jacobians; we cannot just check if some element of WhW_h is greater than 1. Although each step shares WhW_h, activation derivatives vary with state, so the complete Jacobians at different time steps are usually different.

ZB

Zhanbo Chen

Java Backend & AI Agent Developer

Back to home
Comments
CS546 Learning Notes: From Word Embedding to RNN, Understanding BPTT and Vanishing Gradients | Zhanbo