A prediction task on unlabelled text (guess the neighbours from the centre word, or the centre from its neighbours) trains an embedding table; the full softmax over the vocabulary makes each step touch tens of millions of weights, and negative sampling cuts that to a few thousand while still learning co-occurrence statistics.
The previous note found that library-trained embedding tables give similar vectors to words used in similar contexts, without explaining why. Words that occur in the same contexts tend to have similar meanings (Harris, 1954; Firth, 1957), and a model that must predict a word’s neighbours through a narrow bottleneck has to give words with the same neighbours similar bottleneck vectors. Word2Vec (Mikolov et al., 2013a, 2013b) turns that into two small networks, made trainable on billions of tokens by hierarchical softmax or negative sampling; this note develops negative sampling, the one used in practice.
Notation. The corpus is a token sequence w1,…,wT over a vocabulary V; d is the embedding dimension. For a position t the centre word is wt and the context is the window Ct=(wt+j)j∈Jt with Jt={j:1≤∣j∣≤m,1≤t+j≤T}, up to 2m neighbours with repeats kept; m is the window radius. Each word w has two vectors: an input (centre) vector vw∈Rd, a row of Win∈R∣V∣×d, and an output (context) vector uw∈Rd, a row of Wout∈R∣V∣×d. Win is the embedding table E of the previous note. σ(x)=1/(1+e−x); k is the number of negative samples and Pn the distribution they are drawn from.
Baseline: fit the embeddings with a labelled task#
The previous note listed sources for the table. Training it as a layer of a classifier works (the CNN note measures it) but learns only about words in the labelled set and only what helps predict the label. Labelled text is scarce; unlabelled text is almost unlimited. What should a model predict when there is no label?
The key idea needs only a two-layer linear network. For an input word c and a target word o:
take the one-hot vector ec∈{0,1}∣V∣ of c;
multiply by the first layer: h=Win⊤ec=vc∈Rd, the row of c (no bias, no non-linearity);
score every vocabulary word against h: s=Wouth∈R∣V∣, so sw=uw⋅vc;
turn the scores into a distribution with a softmax:
p(o∣c)=∑w∈Vexp(uw⋅vc)exp(uo⋅vc).
The network is fitted with the cross-entropy −logp(o∣c) on (input, target) pairs cut from the corpus; then all but the first layer is thrown away, and the row vc is the embedding. In code the one-hot product is a lookup: a batch of ids [B] becomes [B,d], then [B,∣V∣]. The model has 2∣V∣d parameters.
Figure
The Word2Vec network: a row lookup, a dot product with every output vector, a softmax
Toy sizes |V| = 8 and d = 3, centre word coffee, observed context word drink. The one-hot vector of coffee times W_in reads out one row, v_coffee; there is no bias and no non-linearity, so the hidden vector h is that row. Each row u_w of W_out is dotted with h to give one score per vocabulary word, and the softmax turns the 8 scores into a distribution over the context word (scores and bars are computed from toy vectors). Training raises the probability of drink. After training only W_in is kept, as the embedding table; W_out and the softmax exist only to provide the training signal. For CBOW the input is the 2m context words and h is the mean of their rows.
Two tables are needed because with one, a word’s score against itself, ∥vw∥2, would be large, while a word rarely occurs in its own window; two tables let uw⋅vw be small (Goldberg and Levy, 2014). Libraries return Win (wv in gensim).
The network leaves open which words are inputs and which are targets. Word2Vec defines two choices on the same windows.
Continuous bag of words (CBOW) predicts the centre word from its context: the mean of the context input vectors is classified among the ∣V∣ possible centre words:
“Bag” because the mean ignores word order; each position gives one example.
Skip-gram reverses the direction: the centre word predicts each context word separately, with the softmax of the previous section, minimizing
LSG=−T1t=1∑Tj∈Jt∑logp(wt+j∣wt).
Each position gives up to 2m (centre, context) pairs.
Figure
One window, two prediction tasks
The sentence “we drink hot coffee every morning” with window radius m = 2 and centre word coffee (amber); the word “we” is outside the window. Skip-gram (top) turns the window into four separate training pairs, each asking the model to predict one neighbour from coffee. CBOW (bottom) turns the same window into a single example: the four neighbour vectors are averaged and the average has to predict coffee. Sliding the centre over all six positions gives 2, 3, 4, 4, 3, 2 neighbours per position (fewer at the sentence edges), so 18 skip-gram updates against 6 CBOW updates.
Worked example: training pairs. In we drink hot coffee every morning (T=6, m=2) the centre coffee gives four skip-gram pairs and one CBOW example, in which the mean of the four context vectors predicts coffee (figure above). Position i has min(m,i−1)+min(m,T−i) neighbours, so the sentence gives 2+3+4+4+3+2=18 skip-gram pairs against 6 CBOW examples.
A neural language model (Bengio et al., 2003) also starts with an embedding table but predicts the next word, like the n-gram model, through a costly non-linear hidden layer. Word2Vec only wants the table, so it drops the non-linearity and uses context on both sides.
CBOW computes one output distribution per position and skip-gram up to 2m, so CBOW is cheaper by about the window size, but in the comparison of Mikolov et al. (2013a), §3.1–3.2 and Table 3 (640-dimensional vectors), skip-gram was far better on semantic analogies (55% against 24%) and slightly worse on syntactic ones (59% against 64%). It is also reported to be better for rare words (word2vec project page), whose vectors CBOW only updates through a mean with up to 2m−1 others.
For each centre the effective radius is drawn uniformly from {1,…,m} (Mikolov et al., 2013a, §3.2), so a neighbour at distance j is used with probability (m−j+1)/m (1,0.8,0.6,0.4,0.2 for m=5): near neighbours count more without an explicit weight.
Write the skip-gram loss of one pair (c,o) as −logp(o∣c)=−uo⋅vc+log∑w∈Vexp(uw⋅vc). The derivative of the log-sum-exp with respect to vc is the softmax-weighted average of the uw, and with respect to one uw it is p(w∣c)vc:
The first line reads “observed minus expected”: a step pulls vc towards the context word that occurred and away from the one the model expects. The second is non-zero for everyw, so one pair writes to all ∣V∣ rows of Wout, and the normalizer alone needs ∣V∣ dot products (Rong, 2014).
With ∣V∣=200,000 and d=300 the model has 2∣V∣d=1.2×108 parameters. One pair costs ∣V∣d=6×107 multiply-adds for the scores and updates ∣V∣d+d=60,000,300 parameters, and a corpus of 109 tokens with m=5 gives up to 1010 pairs per pass. The model is small; the normalization makes training infeasible.
Figure
Parameters touched by one training pair as the vocabulary grows
Both axes are logarithmic; d = 300. The full softmax reads and updates every output row, so its cost grows in proportion to |V|. Hierarchical softmax replaces the |V|-way normalization by about log₂|V| binary decisions along a tree. Negative sampling with k = 5 updates the centre vector, the observed context vector and 5 sampled vectors, (k + 2)d = 2,100 parameters, whatever the vocabulary size. At |V| = 200,000 (vertical line) the full softmax touches 60,000,300 parameters per pair, about 28,600 times more.
Hierarchical softmax (Morin and Bengio, 2005) puts the vocabulary at the leaves of a binary (Huffman, Mikolov et al., 2013b, §2.1) tree, so a prediction takes about log2∣V∣ binary decisions, 18 for 200,000 words. Negative sampling drops the normalization; it is the default in practice and the subject of the rest of this note.
Softmax versus negative sampling on the output side of skip-gram
Toy sizes |V| = 8 and d = 3; the words are those of the worked example below: centre coffee, observed context drink, negatives car and tax. With the full softmax (first panel) the probability of drink is normalized over all |V| words, so one training pair reads and updates every row of W_out. With negative sampling (second panel) the pair (coffee, drink) is a positive example with target 1 and k = 2 sampled words, car and tax, are negatives with target 0 (targets in brackets); each is an independent logistic classifier on its score, and only these k + 1 output rows and v_coffee are read and updated. The rest of the vocabulary is not visited. At |V| = 200,000, d = 300 and k = 5 this is 2,100 parameters per pair instead of 60,000,300.
For an observed pair (c,o) and noise words n1,…,nk∼Pn, the skip-gram negative-sampling (SGNS) loss is
LNS(c,o)=−logσ(uo⋅vc)−i=1∑klogσ(−uni⋅vc).
σ(uw⋅vc) is the model’s probability that a pair is real and σ(−x)=1−σ(x), so each term of the sum is the log-probability that a noise pair is recognized as fake. With the scores so=uo⋅vc and si=uni⋅vc, and dxdlogσ(x)=1−σ(x), the gradients are
This is “observed minus expected” with the expectation over ∣V∣ words replaced by k sampled words, each weighted by how plausible the model finds it; all other rows have zero gradient.
CBOW uses the same loss with the context mean ht in place of the centre vector and the centre word as the positive:
Its gradients are those above with ht for vc. The chain rule through the mean would send 1/∣Jt∣ of ht‘s gradient to each context vector; word2vec.c (word2vec project page) and gensim (cbow_mean=1) add all of it, in effect a larger learning rate for the input table.
A pair updates (k+2)d parameters, 2,100 for d=300 and k=5. Mikolov et al. (2013b), §2.2, report that k between 5 and 20 works for small training sets and 2 to 5 for large ones; gensim uses k=5.
∂L/∂ucar=0.562(1.0,0.5)=(0.562,0.281) and ∂L/∂utax=0.599(1.0,0.5)=(0.599,0.299).
One step x←x−η∂L/∂x gives vcoffee=(0.977,0.442), udrink=(0.925,0.662), ucar=(−0.481,0.759) and utax=(0.301,−0.550); the two figures below show the vectors and the scores moving, and the loss falls from 2.026 to 1.611. Four rows changed; with 200,000 words the other 199,997 output rows were not read.
Figure
One negative-sampling step, drawn in the plane
The worked example with d = 2, k = 2 negatives and learning rate 0.5. Dashed: the four vectors before the step; solid: after it; short arrows: how each output tip moved (v_coffee moves too little for an arrow). The centre vector v_coffee moves (1.000, 0.500) → (0.977, 0.442). The observed context vector u_drink moves along v_coffee, so their dot product grows; the negatives u_car and u_tax move against v_coffee, so theirs shrink. Every gradient is a multiple of a vector already in the picture: an output row moves only along v_coffee, and v_coffee moves along a weighted sum of the three output rows. No other row of the vocabulary is read.Figure
The two loss terms of negative sampling as functions of the score
Green: −log σ(s), the loss of the observed pair; its slope σ(s) − 1 vanishes once the score is large, so a pair that is already recognized stops moving. Red: −log σ(−s), the loss of each negative pair; its slope σ(s) grows with the score, so a negative that the model wrongly finds plausible gets the largest push. Points are the worked example (d = 2, k = 2, learning rate 0.5): hollow before the step, filled after it. The observed pair (coffee, drink) moves from score 1.10 to 1.20, the negatives car and tax from 0.25 and 0.40 to −0.13 and 0.05; the total loss falls from 2.026 to 1.611.
For contrast, the full softmax over a toy six-word vocabulary with the same vcoffee: drink(0.8,0.6), hot(0.7,0.1), morning(0.3,0.5), car(−0.2,0.9), tax(0.6,−0.4) and cup(0.9,0.2). The scores are 1.10,0.75,0.55,0.25,0.40,1.00, the probabilities 0.243,0.171,0.140,0.104,0.121,0.220, and the loss −log0.243=1.414. The gradient on vcoffee, ∑wp(w∣c)uw−udrink=(−0.193,−0.277), needs all six rows and pushes down cup, a plausible context ranked second; negative sampling pushes a word down only when it is drawn.
Negatives are drawn from the unigram distribution raised to the power 3/4 and renormalized (Mikolov et al., 2013b, §2.2):
Pn(w)=∑w′∈Vcount(w′)3/4count(w)3/4.
With toy counts the 600, coffee 30 and espresso 3, sampling in proportion to the counts gives probabilities 0.948,0.047,0.005, and the 3/4 power gives 0.889,0.094,0.017: espresso becomes 3.5 times more likely to be drawn.
Figure
Raising the unigram distribution to the power 3/4
A toy vocabulary of 10 words whose counts follow Zipf's law: count proportional to 1/rank. Grey: negatives drawn in proportion to frequency (α = 1). Blue: the Word2Vec choice α = 3/4. Dashed: uniform sampling (α = 0). The power 3/4 takes probability from the most frequent words and gives it to the rare ones: the most frequent word is drawn 10 times as often as the rarest with α = 1, 5.6 times as often with α = 3/4. The value 3/4 was chosen empirically.
Pairs with the or a are numerous and uninformative. Before windows are formed, each occurrence of a word w with relative frequency f(w) is discarded with probability
Pdiscard(w)=1−θ/f(w)
for a threshold θ≈10−5, and never when f(w)≤θ (Mikolov et al., 2013b, §2.3). For toy frequencies 0.05, 10−3 and 10−4 the discard probabilities are 0.986, 0.9 and 0.68. Removing a token brings the words around it closer, so subsampling also widens the effective window. gensim uses a variant of this rule with threshold sample, default 10−3.
Negative sampling optimizes a different objective from the softmax, a simplification of noise-contrastive estimation (Gutmann and Hyvärinen, 2010; Mnih and Teh, 2012), which classifies with the corrected logit uo⋅vc−log(kPn(o)) and whose optimum is logP(o∣c). Negative sampling uses the raw score (Mikolov et al., 2013b, §2.2), so its optimum is shifted: if d is large enough for every score to be set independently, it is the pointwise mutual information shifted by logk (Levy and Goldberg, 2014, §3.1):
With the 3/4 power this is a smoothed PMI: rare context words get a larger Pn(o) and a smaller value (Levy, Goldberg and Dagan, 2015, “context distribution smoothing”).
Figure
The per-pair negative-sampling objective and its minimum
The objective of one pair (c, o) as a function of its score x = u_o · v_c, divided by the number of terms so that the curves share an axis. Toy counts: the centre word c occurs in #(c) = 1000 pairs, P_n(o) = 0.01 and k = 5, so o is drawn as a negative of c 50 times in expectation. Blue: o occurs with c 200 times, PMI = log 20 = 3.00. Amber: 10 times, PMI = 0.00, as often as chance predicts. Each curve has its minimum (filled point) at PMI − log k, 1.39 and −1.61; the arrows below the axis show the shift by log 5 = 1.61 from the PMI (open marker). Dotted: a pair that never co-occurs keeps only the negative term −log σ(−x), which is non-negative and decreases towards its infimum 0 as x → −∞ without reaching it, so there is no finite minimizer and its optimal score is −∞.
So SGNS factorizes the shifted PMI matrix into WinWout⊤ of rank d, and this compression puts words with similar PMI rows close together. Pairs that never co-occur have PMI=−∞ (dotted curve), so there it stays approximate for any d.
We train a model with d=32, window radius 5 and minimum count 5 on about half a million Quora questions, tokenized with stopwords removed but punctuation such as ? kept. Everything else is a gensim default (table below), so this is CBOW with negative sampling, k=5, and subsampling.
The vector of word has 32 coordinates between −4.09 and 4.18 (norm 10.7), and its nearest cosine neighbours, listed in the previous note, are sentence (0.836), words (0.796) and verb (0.787). Singular and plural forms sit together (word/words, phrase/phrases) because the text was not lemmatized. The neighbours are topical as much as grammatical because, without stopwords and with ? subsampled, a radius-5 window covers more of a question than five raw tokens.
A second CBOW model, 100-dimensional with minimum count 4 and window radius 5, is trained on the AG News training articles only, so that no test words leak in; it supplies the input vectors of the next note.
Radius; larger windows give more topical neighbours, smaller ones more syntactic ones (Jurafsky and Martin, §5.7)
5
5
min_count
Drops rarer words; sets ∣V∣
5
5 (Quora), 4 (AG News)
sg
0 = CBOW, 1 = skip-gram
0
default
negativek
Negatives per pair; 0 with hs=1 gives hierarchical softmax
5
default
ns_exponent
Power of the counts in Pn
0.75
default
sample
Subsampling threshold for frequent words
10−3
default
epochs, alpha
Passes over the corpus; initial learning rate
5, 0.025
default
With tuned window, subsampling, negatives and noise exponent, count-based methods and SGNS perform much more alike than the original comparisons suggested (Levy, Goldberg and Dagan, 2015).
Two later static embeddings have notes of their own. GloVe (Pennington, Socher and Manning, 2014), which gave the Twitter vectors of the previous note, fits uw⋅vc+bw+bc≈logXwc to the co-occurrence matrix X by weighted least squares; like SGNS it fits a function of co-occurrence counts with dot products, so the two end up close (GloVe note). FastText (Bojanowski et al., 2017) trains SGNS but represents a word as the sum of its character n-gram vectors (where → <wh, whe, her, ere, re>), so related words share parameters and an unseen word still gets a vector (FastText note).
Static. One vector per word type: bank is a single compromise between the river and the money. The BERT note removes this.
Closed vocabulary. Words below min_count or absent from the corpus get no vector; FastText and subword tokenization address this.
Window-bound. Only neighbours within m positions shape a vector, and CBOW ignores their order; negation and long-range dependencies do not enter.
Similarity means substitutability.good and bad, hot and cold, Monday and Tuesday share contexts and end up close, the wrong geometry for sentiment.
Data-hungry. Half a million questions suffice for frequent words such as word, not for rare ones; the analogies of the previous note use billions of tokens.
Sensitive, not reproducible. Preprocessing, window, subsampling and k change the neighbours, and multithreaded runs with one seed differ.
Skip-gram predicts the context from the centre, CBOW the centre from the context. The network is linear; its first layer Win is the embedding table, the output layer only a training signal.
The full softmax updates all ∣V∣ output rows per pair; negative sampling classifies the real pair against k pairs drawn from the 3/4-power unigram distribution and updates (k+2)d parameters.
At its optimum SGNS sets uo⋅vc=PMI(c,o)−logk: the vectors are a low-rank compression of co-occurrence statistics.
gensim trains CBOW with k=5 negatives by default; skip-gram needs sg=1.
week01_word_embeddings.ipynb. Cell numbers count every cell, markdown included, from 1. Quoted numbers come from the run saved with the course; cells changed since then for current library versions have no saved output.
Data (cells 4–31). Cell 4 downloads the Quora file only if it is missing (the repository already has it); cell 6 reads data/quora.txt from disk. Cells 13–22 build data_tok: wordpunct_tokenize, lower-casing (an exercise in cell 15, with the solution filled in), NLTK stopword removal, punctuation kept. The lemmatized and stemmed variants of the 537,272 questions (cells 28–31) are not used by Word2Vec.
Training (cells 53–58). Markdown introduces skip-gram and gensim (Alammar, 2019); cell 58 trains Word2Vec(data_tok, vector_size=32, min_count=5, window=5), which with the defaults is CBOW with 5 negatives and the summed context update (cbow_mean=1) described above, not skip-gram.
Neighbours (cells 59–60). The vector of word and its nearest neighbours, the only saved outputs of this model: no loss curve, training time, vocabulary size or evaluation score was saved. The norm 10.7 quoted above is computed from the printed coordinates.
Analogies (cells 61–66). Pretrained 100-dimensional GloVe Twitter vectors and the analogies reported in the previous note.
week02_NN_text_classification.ipynb, cells 13–15, trains a 100-dimensional CBOW model (min_count=4, window=5) on the AG News training texts only, so no test words leak in, and turns each article into a sequence of vectors for the next note.
Word2Vec learns from one window at a time and never looks at the corpus as a whole. The next note counts every pair of words once into a co-occurrence matrix and fits vectors to the logarithms of those counts directly, and shows that the two approaches end up factorizing nearly the same matrix.
T. Mikolov, K. Chen, G. Corrado, J. Dean. Efficient Estimation of Word Representations in Vector Space. ICLR Workshop, 2013. CBOW and skip-gram, their training complexity (§3.1–3.2) and comparison (Table 3), the random window radius (§3.2).
D. Jurafsky, J. H. Martin. Speech and Language Processing, 3rd edition draft of August 19, 2026, chapter 5 “Embeddings”, §5.5 “Word2vec” and §5.7 “Semantic properties of embeddings”. Textbook treatment of skip-gram with negative sampling; the effect of the window size.
T. Mikolov et al. word2vec. Project page and reference C implementation (word2vec.c), Google Code Archive, 2013. Practical comparison of skip-gram and CBOW (“better for infrequent words”); the CBOW update that adds the whole hidden-layer gradient to every context vector.