Key ideas
- Sparse representations. Character-level text gives one one-hot column per character (small alphabet, long sequences); word-level gives one per word (more meaning, but vectors as long as the vocabulary). Summing the one-hot vectors of a text gives a bag-of-words count vector, which loses word order; adding n-gram features restores some local order but makes the vocabulary explode. Any two different one-hot vectors have cosine similarity 0, so they cannot express that two words are similar1.
- Dense embeddings. Each word gets a learned real vector of a small, fixed size
(typically a few hundred, far below ). An embedding layer (
nn.Embedding) takes integer token ids and returns rows of a weight matrix: exactly one-hot , without building the one-hot vector. An embedding bag aggregates (sum, mean or max) the embeddings of all words into one fixed-size vector for a text classifier7. - word2vec is self-supervised1: the "labels" are words of the raw text itself. Skip-gram predicts each context word from the center word; CBOW predicts the center word from the average of its context-word vectors. Every word gets two vectors, one for its role as center word and one for its role as context word; skip-gram typically keeps the center-word vectors as embeddings, CBOW the context-word vectors (in both cases the input side). The prediction task is only a means to learn them.
- Why similar words get similar vectors. Words that occur in similar contexts are pushed toward high dot products with the same context vectors (the distributional hypothesis), so their vectors align. This also pulls antonyms such as "hot" and "cold" together, because they occur in the same kinds of contexts8.
- Approximate training2. The softmax normalizes over the whole vocabulary, so one gradient costs . Negative sampling turns it into binary decisions (true pair vs. noise words), a cost linear in ; hierarchical softmax walks a binary tree, about steps.
- Data tricks3: frequent words are subsampled (randomly dropped), and noise words are drawn with probability proportional to frequency, which flattens the unigram distribution so rarer words are drawn more often.
- GloVe4 fits global co-occurrence counts with a weighted squared loss on . Because , center and context vectors play symmetric roles, and the final vector of a word is the sum of the two.
- fastText5 adds
<and>to a word and represents it by the sum of the vectors of its character -grams () plus the whole word, so rare and unseen words get vectors built from shared pieces ("where" →<wh,whe, …,where>,<where>). - Static vs. contextual6. word2vec, GloVe and fastText give a word the same vector in every sentence, so the senses of "bank" or "crane" are blended. ELMo, GPT and BERT compute context-sensitive representations from the whole sequence; only a bidirectional model sees clues that come after the word.
Formulas
- Skip-gram1:
- Negative sampling loss for one pair2:
- Subsampling3: discard each occurrence of with probability , , = share of all tokens.
- GloVe4:
- fastText5:
- Cosine similarity lies in 1; for unit vectors , so nearest by distance equals nearest by cosine.
Worked example
Subsampling with : "the" makes up of the tokens, so each occurrence is dropped with probability ; a word with is never dropped.
Noise distribution: two words with counts 81 and 1 get weights and , so the frequent word is drawn with probability instead of .
In code
import torch
import torch.nn as nn
torch.manual_seed(0)
emb = nn.Embedding(num_embeddings=5, embedding_dim=3) # |V| = 5, d = 3
ids = torch.tensor([2, 4]) # token ids, not strings
one_hot = nn.functional.one_hot(ids, num_classes=5).float()
print(torch.allclose(emb(ids), one_hot @ emb.weight)) # True: lookup = one-hot @ W
bag = nn.EmbeddingBag.from_pretrained(emb.weight, mode="mean")
print(bag(ids.unsqueeze(0)).shape) # torch.Size([1, 3]) for any length
print(torch.allclose(bag(ids.unsqueeze(0)), emb(ids).mean(0))) # True
Common pitfalls
- The embedding size is a small hyperparameter, not the vocabulary size; a 20,000-word vocabulary does not mean 20,000-dimensional word2vec vectors.
- CBOW: context → center word; skip-gram: center → context words. Don't swap them.
- A trained CBOW or skip-gram model is not a text generator or classifier; what you keep is the embedding matrix.
- word2vec has no vector for a word outside its training vocabulary; fastText can compose one from character n-grams.
- One static vector per word type: "model" (fashion, statistical, scale) has a single blended vector. A left-to-right model cannot disambiguate a word whose clue comes later in the sentence.
- High similarity does not mean synonymy: antonyms share contexts and end up close.
- Cosine similarity can be negative; embedding entries have both signs.
- In CBOW with negative sampling, the noise words replace the center word, scored against the averaged context vector.
References
- Dive into Deep Learning — Sec. 15.1 Word Embedding (word2vec)
- Dive into Deep Learning — Sec. 15.2 Approximate Training (15.2.1 Negative Sampling, 15.2.2 Hierarchical Softmax)
- Dive into Deep Learning — Sec. 15.3.2 Subsampling and 15.3.4 Negative Sampling
- Dive into Deep Learning — Sec. 15.5 Word Embedding with Global Vectors (GloVe)
- Dive into Deep Learning — Sec. 15.6.1 The fastText Model
- Dive into Deep Learning — Sec. 15.8.1 From Context-Independent to Context-Sensitive
- PyTorch documentation: torch.nn.EmbeddingBag — Class description (mode "sum", "mean" or "max"; input = indices into the embedding matrix)
- Natural Language Processing with Deep Learning (Ruhr University Bochum, 2025/26) — Lecture 5 handout, frames 'Distributional hypothesis' and 'Limitations of word embeddings (1)' (antonyms)