4.2.5Tokenization & Language Modeling

Embedding layers and tied weights

2,503 words11 min readdifficulty · medium

Overview

Embedding layers convert discrete token IDs into continuous vector representations, while weight tying shares parameters between the input embedding and output projection layers, reducing model size and improving generalization.


[!intuition] The Core Idea

Think of embedings as a lookup table where each token ID is a row number, and you retrieve that row's vector. The token "cat" (ID=42) always maps to the same learned vector e42Rd\mathbf{e}_{42} \in \mathbb{R}^{d}.

Why tie weights? The input embedding asks "what does this token mean?" and the output projection asks "which token should come next?" These are inverse operations—so using the same weight matrix (transposed) forces the model to learn a consistent representation space.

Analogy: If learning English→French and French→English translation, using the same dictionary for both directions ensures consistency.


[!definition] Embedding Layer

An embedding layer is a learned matrix ERV×d\mathbf{E} \in \mathbb{R}^{V \times d} where:

  • VV = vocabulary size
  • dd = embedding dimension
  • Each row E[i]\mathbf{E}[i] is the embedding vector for token ii

Operation: For input token ID x{0,1,,V1}x \in \{0, 1, \ldots, V-1\}, the embedding is: Embed(x)=E[x]=exRd\text{Embed}(x) = \mathbf{E}[x] = \mathbf{e}_x \in \mathbb{R}^d

This is not a matrix multiplication—it's an indexing operation (retrieval).


[!formula] Deriving the Embedding Operation

Goal: Convert token ID xx to vector ex\mathbf{e}_x.

Step 1: One-Hot Encoding (Conceptual)

Represent token xx as one-hot vector ox{0,1}V\mathbf{o}_x \in \{0,1\}^V where: ox[i]={1if i=x0otherwise\mathbf{o}_x[i] = \begin{cases} 1 & \text{if } i = x \\ 0 & \text{otherwise} \end{cases}

Step 2: Matrix Multiplication Interpretation

If we compute oxTE\mathbf{o}_x^T \mathbf{E}: oxTE=[0,,0,1,0,,0][e0vdotsexeV1]=ex\mathbf{o}_x^T \mathbf{E} = [0, \ldots, 0, 1, 0, \ldots, 0] \begin{bmatrix} — \mathbf{e}_0 — \\vdots \\ — \mathbf{e}_x — \\ \vdots \\ — \mathbf{e}_{V-1} — \end{bmatrix} = \mathbf{e}_x

Why this step? This shows embedings are mathematically equivalent to matrix multiplication with one-hot vectors, but we never actually create the one-hot (memory inefficient).

Step 3: Efficient Implementation

In practice:

# Conceptual: e_x = one_hot(x) @ E  # O(V*d) - slow!
e_x = E[x]  # O(1) indexing - fast!

Why this matters: For V=5000V=5000, d=512d=512, one-hot multiplication would waste 5000×512=2.56M5000 \times 512 = 2.56\text{M} multiplications (most against zeros). Direct indexing is instant.


[!formula] Weight Tying: Full Derivation

The Standard Architecture (No Tying)

Forward pass:

  1. Input embedding: h0=Ein[x]\mathbf{h}_0 = \mathbf{E}_{\text{in}}[x] where EinRV×d\mathbf{E}_{\text{in}} \in \mathbb{R}^{V \times d}
  2. Transformer layers: hL=Transformer(h0)\mathbf{h}_L = \text{Transformer}(\mathbf{h}_0)
  3. Output projection: z=hLWout+b\mathbf{z} = \mathbf{h}_L \mathbf{W}_{\text{out}} + \mathbf{b} where WoutRd×V\mathbf{W}_{\text{out}} \in \mathbb{R}^{d \times V}
  4. Softmax: P(xt+1xt)=softmax(z)P(x_{t+1} | x_{\leq t}) = \text{softmax}(\mathbf{z})

Total parameters: VdV \cdot d (input) +Vd+ V \cdot d (output) =2Vd= 2Vd

Weight Tying Constraint

Set Wout=EinT\mathbf{W}_{\text{out}} = \mathbf{E}_{\text{in}}^T (transpose). Now: z=hLEinT+b\mathbf{z} = \mathbf{h}_L \mathbf{E}_{\text{in}}^T + \mathbf{b}

Component-wise: The logit for token jj is: zj=hLej+bjz_j = \mathbf{h}_L \cdot \mathbf{e}_j + b_j

Interpretation: Score for token jj = similarity between context representation hL\mathbf{h}_L and token embedding ej\mathbf{e}_j.

Why this step? The dot product hLej\mathbf{h}_L \cdot \mathbf{e}_j measures how "aligned" the context is with token jj's meaning—high similarity → high probability.

Parameter reduction: Now only Vd+VVd + V parameters (embedings + bias), saving VdVVdVd - V \approx Vd parameters.

Gradient Flow with Tying

During backpropagation: LE=LEin+(LWout)T\frac{\partial \mathcal{L}}{\partial \mathbf{E}} = \frac{\partial \mathcal{L}}{\partial \mathbf{E}_{\text{in}}} + \left(\frac{\partial \mathcal{L}}{\partial \mathbf{W}_{\text{out}}}\right)^T

Why this step? Since E\mathbf{E} is used in two places (input and output), gradients from both paths accumulate. This provides richer supervision—embedings learn from both "what tokens mean in context" and "what tokens are likely next."


[!example] Example 1: Small Vocabulary Embedding

Setup: V=4V=4 tokens ["cat", "dog", "the", "ran"], d=3d=3 dimensions.

Embedding matrix: E=[0.20.50.80.30.40.70.90.10.20.60.30.5](cat, ID=0)(dog, ID=1)(the, ID=2)(ran, ID=3)\mathbf{E} = \begin{bmatrix} 0.2 & -0.5 & 0.8 \\ 0.3 & -0.4 & 0.7 \\ -0.9 & 0.1 & 0.2 \\ 0.6 & 0.3 & -0.5 \end{bmatrix} \begin{matrix} \text{(cat, ID=0)} \\ \text{(dog, ID=1)} \\ \text{(the, ID=2)} \\ \text{(ran, ID=3)} \end{matrix}

Query: Embed "dog" (ID=1).

Solution: Embed(1)=E[1]=[0.3,0.4,0.7]\text{Embed}(1) = \mathbf{E}[1] = [0.3, -0.4, 0.7]

Why this step? We just index row 1. No computation needed.


[!example] Example 2: Output Projection with Weight Tying

Context: After transformer, we have hL=[0.5,0.2,0.3]\mathbf{h}_L = [0.5, 0.2, -0.3].

Compute logits (using tied weights, Wout=ET\mathbf{W}_{\text{out}} = \mathbf{E}^T): z=hLET=[0.5,0.2,0.3][0.20.30.90.60.50.40.10.30.80.70.20.5]\mathbf{z} = \mathbf{h}_L \mathbf{E}^T = [0.5, 0.2, -0.3] \begin{bmatrix} 0.2 & 0.3 & -0.9 & 0.6 \\ -0.5 & -0.4 & 0.1 & 0.3 \\ 0.8 & 0.7 & 0.2 & -0.5 \end{bmatrix}

Calculate each logit:

  • z0=0.5(0.2)+0.2(0.5)+(0.3)(0.8)=0.10.10.24=0.24z_0 = 0.5(0.2) + 0.2(-0.5) + (-0.3)(0.8) = 0.1 - 0.1 - 0.24 = -0.24
  • z1=0.5(0.3)+0.2(0.4)+(0.3)(0.7)=0.150.080.21=0.14z_1 = 0.5(0.3) + 0.2(-0.4) + (-0.3)(0.7) = 0.15 - 0.08 - 0.21 = -0.14
  • z2=0.5(0.9)+0.2(0.1)+(0.3)(0.2)=0.45+0.020.06=0.49z_2 = 0.5(-0.9) + 0.2(0.1) + (-0.3)(0.2) = -0.45 + 0.02 - 0.06 = -0.49
  • z3=0.5(0.6)+0.2(0.3)+(0.3)(0.5)=0.3+0.06+0.15=0.51z_3 = 0.5(0.6) + 0.2(0.3) + (-0.3)(-0.5) = 0.3 + 0.06 + 0.15 = 0.51

Probabilities (after softmax): P(next token)=softmax([0.24,0.14,0.49,0.51])[0.19,0.21,0.15,0.40]P(\text{next token}) = \text{softmax}([-0.24, -0.14, -0.49, 0.51]) \approx [0.19, 0.21, 0.15, 0.40]

Interpretation: "ran" (ID=3) has highest probability because its embedding is most aligned with hL\mathbf{h}_L.

Why this step? Weight tying makes the model ask: "Which token embedding is closest to the context vector?" This is geometrically intuitive.


[!example] Example 3: Parameter Count Comparison

Scenario: GPT-2 small with V=50257V=50257, d=768d=768.

Without weight tying:

  • Input embedings: 50257×768=38,597,37650257 \times 768 = 38,597,376
  • Output projection: 768×50257=38,597,376768 \times 50257 = 38,597,376
  • Total: 77,194,75277,194,752 parameters

With weight tying:

  • Shared embeddings: 50257×768=38,597,37650257 \times 768 = 38,597,376
  • Output bias: 5025750257
  • Total: 38,647,63338,647,633 parameters

Savings: 77.2M38.6M=38.6M77.2M - 38.6M = 38.6M parameters (~50% reduction in embedding/projection).

Why this step? For large vocabularies, embedings dominate parameter count. Tying cuts this nearly in half without hurting (often improving!) performance.


[!mistake] Common Mistake 1: "Embedings are just random vectors"

The wrong intuition: Embeddings are arbitrary—any random initialization works.

Why it feels right: Initially, embedings are random. And even random embedings give some signal.

The fix: Embedings are learned representations. Through gradient descent:

  • Similar tokens (cat/dog) get similar vectors
  • Embedings capture semantic and syntactic relationships
  • The model learns a geometry where "king - man + woman ≈ queen"

Evidence: Pre-trained embedings (Word2Vec, GloVe) transfer across tasks because they capture real linguistic structure.


[!mistake] Common Mistake 2: "Weight tying forces input=output exactly"

The wrong intuition: With weight tying, Ein=Wout\mathbf{E}_{\text{in}} = \mathbf{W}_{\text{out}}, so input and output are identical.

Why it feels right: We use the same matrix, so they must be the same.

The fix: We tie Wout=EinT\mathbf{W}_{\text{out}} = \mathbf{E}_{\text{in}}^T (transpose!). The operations are:

  • Input: h0=E[x]\mathbf{h}_0 = \mathbf{E}[x] (select row xx)
  • Output: z=hLET\mathbf{z} = \mathbf{h}_L \mathbf{E}^T (compute all dot products)

These are inverse operations geometrically:

  • Input: "What vector represents this token?"
  • Output: "Which token vectors are closest to this context?"

Why this works: The output layer is asking "which embedding is most similar to my hidden state?" If embedings are well-learned, this is exactly what we want.


[!mistake] Common Mistake 3: "Embeddings are per-position"

The wrong intuition: Token "cat" at position 5 gets a different embedding than "cat" at position 10.

Why it feels right: Transformers use positional encodings, so position matters.

The fix: The token embedding is position-independent: "cat" always gets ecat\mathbf{e}_{\text{cat}}. Position is added separately: h0=E[x]+PositionalEncoding(pos)\mathbf{h}_0 = \mathbf{E}[x] + \text{PositionalEncoding}(\text{pos})

Why this step? Separating token and position information is cleaner—the embedding captures what the token is, positional encoding captures where it is.


[!recall]- Explain to a 12-Year-Old

Imagine you have a magic dictionary. Every word has a secret code (a list of numbers). When you look up "dog," you always get the same code, like [5, -2, 8].

Now, you're writing a story. You've written "The dog ran," and you want to predict the next word. Your brain (the transformer) thinks about the story and produces its own code, like [6, -1, 7].

To pick the next word, you compare your brain's code to every word's code in the dictionary. Whichever word's code is closest wins! Maybe "fast" has code [6, -1.5, 7.2]—super close—so you predict "fast."

Weight tying means using the same dictionary for looking up words at the start AND comparing at the end. Why? Because if "dog" and "pupy" have similar codes in the dictionary, you want your brain to treat them similarly when predicting too. It's like using one map for going to a place and coming back—more efficient and consistent!


[!mnemonic] Remember Weight Tying

"Shared Shoes": Input and output wear the same shoes (weight matrix), but one walks forward (embed tokens), the other walks backward (predict tokens). Same shoes, opposite directions. Saves money (parameters), still gets you there!


Key Formulas

Embedding Lookup

ex=E[x]where ERV×d\mathbf{e}_x = \mathbf{E}[x] \quad \text{where } \mathbf{E} \in \mathbb{R}^{V \times d}

Output Logits (Weight Tying)

zj=hLej+bj=k=1dhL,kej,k+bjz_j = \mathbf{h}_L \cdot \mathbf{e}_j + b_j = \sum_{k=1}^{d} h_{L,k} \cdot e_{j,k} + b_j

Probability Distribution

P(xt+1=jxt)=exp(zj)i=1Vexp(zi)P(x_{t+1} = j \mid x_{\leq t}) = \frac{\exp(z_j)}{\sum_{i=1}^{V} \exp(z_i)}


Connections


Practical Notes

When to use weight tying:

  • ✅ Standard language models (GPT, BERT)
  • ✅ Small to medium models where parameter count matters
  • ✅ When vocabulary is large (V>10kV > 10k)

When NOT to use:

  • ❌ Encoder-decoder models with different source/target vocabularies
  • ❌ Models where input/output dimensions differ (dindoutd_{\text{in}} \neq d_{\text{out}})
  • ❌ Some multimodal models (vision+language) where output is not token prediction

Implementation tip: Most frameworks (PyTorch, TensorFlow) make weight tying trivial:

self.output_proj.weight = self.embedding.weight  # Share reference

#flashcards/ai-ml

What is an embedding layer?
A learned lookup table (matrix ERV×d\mathbf{E} \in \mathbb{R}^{V \times d}) that converts discrete token IDs to continuous dd-dimensional vectors. Each row is one token's embedding.
What operation does an embedding layer perform?
Indexing: E[token_id] retrieves the corresponding row vector. It's NOT matrix multiplication in practice (though equivalent to multiplying by one-hot vector).
What is weight tying in language models?
Sharing the same weight matrix between input embedings and output projection by setting Wout=EinT\mathbf{W}_{\text{out}} = \mathbf{E}_{\text{in}}^T. Reduces parameters and improves consistency.
How much parameter savings does weight tying provide?
Approximately VdVd parameters (one full embedding matrix), which is ~50% of embedding+projection parameters for large vocabularies.
What is the output logit formula with weight tying?
zj=hLej+bjz_j = \mathbf{h}_L \cdot \mathbf{e}_j + b_j, where hL\mathbf{h}_L is the context vector and ej\mathbf{e}_j is token jj's embedding. It measures similarity.
Why does weight tying work mathematically?
Input embedding asks "what does token mean?" and output projection asks "which token is next?"—inverse operations. Using transposed weights enforces a consistent semantic space.
How do gradients flow with weight tying?
LE=LEin+(LWout)T\frac{\partial \mathcal{L}}{\partial \mathbf{E}} = \frac{\partial \mathcal{L}}{\partial \mathbf{E}_{\text{in}}} + (\frac{\partial \mathcal{L}}{\partial \mathbf{W}_{\text{out}}})^T. Embedings get gradients from both input lookup and output prediction paths.
Are token embeddings position-dependent?
No. Token embedings are position-independent; the same token always maps to the same vector. Position is encoded separately and added: h0=E[x]+PE(pos)\mathbf{h}_0 = \mathbf{E}[x] + \text{PE}(\text{pos}).
What's the computational complexity of embedding lookup?
O(1)O(1) for single token (direct indexing), O(Td)O(Td) for sequence of length TT (retrieve TT vectors of dimension dd). No matrix multiplication needed.
When should you NOT use weight tying?
When input and output vocabularies differ (e.g., translation), when input/output dimensions differ, or in some multimodal architectures where output isn't token prediction.

Concept Map

indexes into

retrieves row

multiplied with E equals

efficient version of

feeds into

projected by

softmax gives

shares params via

transposed reuse as

reduces params from 2Vd to Vd

Token ID x

Embedding matrix E VxD

One-hot vector

Embedding vector e_x

Direct indexing

Transformer layers

Output projection W_out

Weight tying

Next token probability

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Chalo, embeddings ka core idea samajhte hain simple tareeke se. Jab machine ko koi word milta hai, jaise "cat", to woh usse ek number (token ID) me convert karti hai — bolo 42. Ab ye number akela machine ko kuch nahi batata. Isliye humein chahiye ek lookup table — matlab ek badi matrix jisme har token ke liye ek row hoti hai, aur us row me ek continuous vector hota hai jo us word ka "meaning" capture karta hai. Toh embedding layer basically token ID ko uski row se jodkar vector nikaal deti hai. Interesting baat ye hai ki technically ye ek one-hot vector aur matrix ka multiplication ke barabar hai, lekin practically hum seedhe indexing (E[x]) karte hain kyunki multiplication me zyaadatar zeros ke saath fuzool calculations hoti hain — wahi kaam O(1) me ho jaata hai. Speed aur memory dono bachte hain.

Ab weight tying ka intuition samjho — ye sabse pyaari cheez hai. Model me do jagah embeddings kaam aati hain: ek input pe, jahan hum poochte hain "is token ka matlab kya hai?", aur ek output pe, jahan hum poochte hain "agla token kaunsa aana chahiye?". Ye dono actually inverse operations hain — ek meaning padhta hai, doosra meaning se prediction karta hai. Toh agar hum dono ke liye ek hi matrix (bas transpose karke) use karein, to model ko ek hi consistent representation space seekhna padta hai. Analogy yaad rakho: English-to-French aur French-to-English translation ke liye ek hi dictionary use karo — consistency guaranteed rehti hai. Output pe logit basically context vector aur token embedding ka dot product hota hai, matlab "context aur token ka meaning kitna aligned hai" — jitna zyada match, utni zyada probability.

Ye matter kyun karta hai? Do bade fayde hain. Pehla, parameters half ho jaate hain — bina tying ke 2Vd2Vd lagta, tying ke saath sirf VdVd (plus chhota bias). Bade vocabulary aur large models me ye lakhon-crore parameters bacha deta hai, matlab chhota model, kam memory, faster training. Doosra, generalization behtar hoti hai kyunki ek hi shared space dono kaam handle karta hai, to gradients dono jagah se aakar ek hi matrix ko refine karte hain — model zyada consistent aur robust ban jaata hai. Isliye GPT jaise real-world language models me weight tying ek standard trick hai. Chhoti si idea, par efficiency aur performance dono me bada impact deti hai.

Go deeper — visual, from zero

Test yourself — Tokenization & Language Modeling

Connections