Negative Sampling and Training Word2Vec

Suppose you want to teach a machine that "king" and "queen" share meaning. You must do this using only raw text and no labels. The Word2Vec Skip-gram model tries to do this by predicting context words from a center word. But the naive way of scoring that prediction requires a softmax over the entire vocabulary. This vocabulary can span tens of thousands to millions of terms. It must be computed for every single training example. With a vocabulary of 1,000,000 words and a corpus of billions of tokens, a full softmax turns into an intractable amount of matrix-vector work. This work gets repeated billions of times. Negative sampling solves this by reframing the learning problem entirely. Instead of asking "which of these million words is the correct context word?", it asks something cheaper. It asks a much cheaper binary question repeated many times: "is this particular (center, context) pair a real co-occurrence, or did I just make it up?" This turns an expensive multi-class classification into a handful of cheap binary classifications. It is one of the key engineering ideas that made training high-quality word embeddings feasible. This became possible on web-scale corpora in 2013. This shift is not just a speed hack. It is a principled approximation to a well-defined objective. It is closely related to matrix factorization of a pointwise mutual information matrix. It comes paired with a second trick: subsampling of frequent words. This trick improves both training speed and embedding quality. It does this by down-weighting uninformative high-frequency tokens like "the" and "of." This article gives a precise, step-by-step account of how negative sampling works inside Word2Vec. It also covers subsampling in the same detail. This includes full pseudocode, and a complexity analysis. That analysis explains exactly why the trick is asymptotically cheaper. It also includes a worked numerical trace, and the historical context that led to this design. It sets up the ground for understanding GloVe and later Embedding Geometry and Analogies.

Key takeaways
  • Full softmax over a vocabulary of size VV costs O(V)O(V) per training step; negative sampling replaces it with O(k)O(k) binary decisions, k≪Vk \ll V.
  • Negative sampling turns "predict the right word among VV" into "distinguish real pairs from sampled fake pairs" using logistic regression.
  • Negative words are drawn from a unigram distribution raised to the power 0.750.75, which boosts rare words relative to their raw frequency.
  • Subsampling randomly discards frequent words (like "the") during training, controlled by a threshold hyperparameter, speeding up training and improving embeddings for rare words.
  • The resulting objective is a biased but effective approximation to full softmax skip-gram, and is provably related to factorizing a shifted PMI matrix.
  • Both tricks are what made word2vec practical at web scale, directly enabling the pipeline that leads to GloVe and to studying Embedding Geometry and Analogies.
The core insight

Negative sampling replaces an expensive VV-way softmax classification with a cheap set of binary real-vs-fake classifications, making skip-gram training linear in the number of negative samples instead of the vocabulary size.

The idea

The Word2Vec Skip-gram model defines, for a center word wcw_c and a context word wow_o, a probability

P(wo∣wc)=exp⁡(vwo⊤vwc)∑w∈Vexp⁡(vw⊤vwc)P(w_o \mid w_c) = \frac{\exp(\mathbf{v}_{w_o}^\top \mathbf{v}_{w_c})}{\sum_{w \in V} \exp(\mathbf{v}_{w}^\top \mathbf{v}_{w_c})}

where vwc\mathbf{v}_{w_c} is the "input" embedding of the center word and vw\mathbf{v}_w ranges over "output" embeddings of every word in the vocabulary VV. Training by maximum likelihood requires computing the denominator — the partition function — over the entire vocabulary at every step, since its gradient touches every output vector. This is the full softmax bottleneck: each single training example costs O(∣V∣)O(|V|) time and touches O(∣V∣)O(|V|) parameters.

Negative sampling (introduced alongside skip-gram as "Skip-gram with Negative Sampling", SGNS) replaces this multi-class objective with a set of independent binary classification problems. For each observed (center, context) pair, treat it as a positive example with label 11. Then sample kk random words from the vocabulary (that are not the true context word) as negative examples with label 00. Train a logistic classifier — using the dot product of the two embeddings as the score — to distinguish real pairs from fake ones. The key terminology: the true pair is called a positive sample, the randomly drawn words are negative samples, and kk (typically 5–20 for small corpora, 2–5 for very large ones) is the negative sampling rate. A second, complementary idea is subsampling of frequent words. Words like "the", "a", "is" occur so often that they contribute little information about any specific center word's meaning, yet they dominate training time and drown out the gradient signal from rarer, more meaningful words. Subsampling randomly drops instances of a word from training with a probability that grows with its corpus frequency, controlled by a threshold hyperparameter tt (commonly 10−510^{-5}). This both speeds up training (fewer tokens processed) and improves embedding quality (better-balanced gradient signal across the vocabulary).

How it works

The negative sampling objective

For a positive pair (wc,wo)(w_c, w_o), define the sigmoid score

σ(x)=11+e−x.\sigma(x) = \frac{1}{1 + e^{-x}}.

The per-pair SGNS objective, sampling kk negatives wn1,…,wnkw_{n_1}, \dots, w_{n_k} from a noise distribution Pn(w)P_n(w), is

J(wc,wo)=log⁡σ(vwo⊤vwc)+∑i=1kEwni∼Pn[log⁡σ(−vwni⊤vwc)].J(w_c, w_o) = \log \sigma(\mathbf{v}_{w_o}^\top \mathbf{v}_{w_c}) + \sum_{i=1}^{k} \mathbb{E}_{w_{n_i}\sim P_n}\Big[\log \sigma(-\mathbf{v}_{w_{n_i}}^\top \mathbf{v}_{w_c})\Big].

We maximize this quantity (or equivalently minimize its negation) via stochastic gradient ascent/descent. Note the sign flip −vwni⊤vwc-\mathbf{v}_{w_{n_i}}^\top \mathbf{v}_{w_c} inside σ\sigma for negatives: we want the dot product for a fake pair to be small (score close to 00), which is equivalent to wanting σ(−score)\sigma(-\text{score}) to be close to 11.

The noise distribution

Negatives are not drawn uniformly nor from the raw unigram frequency f(w)f(w), but from a smoothed unigram distribution:

Pn(w)=f(w)0.75∑w′f(w′)0.75.P_n(w) = \frac{f(w)^{0.75}}{\sum_{w'} f(w')^{0.75}}.

Raising frequencies to the power 0.750.75 flattens the distribution: very frequent words become relatively less likely to be sampled compared to their raw frequency, and rare words become relatively more likely. This empirically produces better embeddings — negatives that are neither trivially easy (extremely common, obviously unrelated words) nor unhelpfully obscure.

Subsampling of frequent words

Before pairs are even generated, each token wiw_i in the training stream is kept with probability

P(keep wi)=(tf(wi)+1)⋅tf(wi),P(\text{keep } w_i) = \left(\sqrt{\frac{t}{f(w_i)}} + 1\right)\cdot\frac{t}{f(w_i)},

a common formulation used in the reference implementation (an equivalent simplified variant is P(keep)=min⁡ ⁣(1,t/f(wi))P(\text{keep}) = \min\!\big(1, \sqrt{t / f(w_i)}\big) used in some expositions). Both forms share the essential shape: keep-probability decreases smoothly as frequency f(wi)f(w_i) grows, with negligible discarding below the threshold tt and aggressive discarding well above it.

Step-by-step training loop

The full pipeline, combining skip-gram context generation, subsampling, and negative sampling: ``` INPUT: corpus (stream of tokens), vocabulary V with counts, embedding dim d, window size m, negatives k, subsample threshold t, learning rate eta, epochs E 1. Build vocabulary V from corpus; compute frequency f(w) for each w. 2. Precompute noise distribution: P_n(w) = f(w)^0.75 / sum_{w'} f(w')^0.75 for all w in V Build an alias table (or cumulative table) for O(1)-ish sampling from P_n. 3. Initialize input embeddings U[w] and output embeddings W[w] for all w in V, each a d-dimensional vector (small random init). 4. for epoch in 1..E: for each token position i in corpus: w_c = token at position i // --- Subsampling step --- keep_prob = compute_keep_probability(f(w_c), t) if random() > keep_prob: continue // discard this token instance // --- Dynamic window --- window = random_integer(1, m) // shrink window randomly per example context_positions = [i-window, ..., i-1, i+1, ..., i+window] (clipped to valid corpus bounds) for j in context_positions: w_o = token at position j // positive context word // --- Positive update --- score = dot(U[w_c], W[w_o]) g_pos = (1 - sigmoid(score)) // gradient coefficient grad_U = g_pos W[w_o] grad_W_pos = g_pos U[w_c] // --- Negative samples --- grad_U_total = grad_U for s in 1..k: w_n = sample_from(P_n) such that w_n != w_o score_n = dot(U[w_c], W[w_n]) g_neg = -sigmoid(score_n) grad_U_total += g_neg W[w_n] W[w_n] += eta g_neg U[w_c] // update negative's vector // --- Apply updates --- W[w_o] += eta grad_W_pos U[w_c] += eta * grad_U_total 5. return U // (or average of U and W, or U alone, per convention) ``` The gradient coefficients above follow directly from differentiating JJ. For the positive term log⁡σ(s)\log \sigma(s) with s=vwo⊤vwcs = \mathbf{v}_{w_o}^\top \mathbf{v}_{w_c},

∂∂slog⁡σ(s)=1−σ(s),\frac{\partial}{\partial s}\log \sigma(s) = 1 - \sigma(s),

and for a negative term log⁡σ(−sn)\log \sigma(-s_n),

∂∂snlog⁡σ(−sn)=−σ(sn).\frac{\partial}{\partial s_n}\log \sigma(-s_n) = -\sigma(s_n).

Both use the identity σ′(x)=σ(x)(1−σ(x))\sigma'(x) = \sigma(x)(1-\sigma(x)) and the chain rule; multiplying by ∂s/∂vwc=vwo\partial s/\partial \mathbf{v}_{w_c} = \mathbf{v}_{w_o} (or the symmetric term for the output vector) gives exactly the vector updates in the pseudocode.

Figure
SGNS as a small binary classifier over sampled pairs
Skip-gram with Negative Sampling — one training step center word w_c = "king" lookup U[w_c] U[w_c] ∈ ℝ^d positive w_o "queen" label=1 negative "banana" 0 negative "purple" 0 negative "run" 0 noise dist. P_n(w) ∝ f(w)^0.75 draws k negatives σ(dot)→1 σ(dot)→0 (×3) Objective (this step) log σ(v_king·v_queen) + Σ log σ(−v_king·v_neg_i)

Complexity & correctness

Time complexity per training pair. Full softmax requires computing vwc⊤vw\mathbf{v}_{w_c}^\top \mathbf{v}_w for every w∈Vw \in V to normalize, costing O(∣V∣⋅d)O(|V| \cdot d) per (center, context) pair, where dd is the embedding dimension. Negative sampling instead computes only k+1k+1 dot products (one positive, kk negatives), costing O(k⋅d)O(k \cdot d), independent of ∣V∣|V|. Since kk is a small constant (commonly 5–20) while ∣V∣|V| can be 10510^5 to 10610^6, this is a reduction from linear-in-vocabulary to constant-in-vocabulary per pair — the entire reason SGNS scales to web-sized corpora. For an epoch over a corpus of NN tokens with average window size mm, the total cost is O(N⋅m⋅k⋅d)O(N \cdot m \cdot k \cdot d) for negative sampling versus O(N⋅m⋅∣V∣⋅d)O(N \cdot m \cdot |V| \cdot d) for full softmax. With N=109N = 10^9, m=5m=5, k=10k=10, d=300d=300, ∣V∣=106|V| = 10^6: negative sampling costs on the order of 109×5×10×300≈1.5×101310^9 \times 5 \times 10 \times 300 \approx 1.5\times10^{13} floating point multiply-adds per epoch, while full softmax costs roughly 109×5×106×300≈1.5×101810^9 \times 5 \times 10^6 \times 300 \approx 1.5\times10^{18} — a factor of 10510^5 difference, matching the ratio ∣V∣/k|V|/k.

Space. Both approaches store the same parameters: two ∣V∣×d|V| \times d matrices (input and output embeddings), so space is O(∣V∣⋅d)O(|V| \cdot d) regardless of the training method — negative sampling only changes the per-step compute, not the parameter count.

Why the approximation is sound. SGNS is not merely "softmax but cheaper" — it is a different but related objective with its own justification. Consider a single negative sample per positive pair for simplicity, drawn from the true unigram distribution. It has been shown (Levy & Goldberg, 2014) that maximizing the SGNS objective in expectation, for the case kk negative samples drawn from the unigram distribution, drives the dot product between vectors to satisfy

vw⊤vc≈PMI(w,c)−log⁡k,\mathbf{v}_{w}^\top \mathbf{v}_{c} \approx \mathrm{PMI}(w, c) - \log k,

where PMI(w,c)=log⁡P(w,c)P(w)P(c)\mathrm{PMI}(w,c) = \log \frac{P(w,c)}{P(w)P(c)} is the pointwise mutual information between word ww and context cc over the corpus. This means SGNS is, in expectation, implicitly performing a low-rank factorization of a shifted PMI matrix — the same statistical quantity that count-based distributional semantics methods have long used, but learned via cheap stochastic binary classification rather than an explicit matrix decomposition. This connection is exactly what motivates comparing SGNS to GloVe, which factorizes a log-count matrix explicitly.

Correctness sketch via gradient balance. At a stationary point of the SGD dynamics, for each (center, context) pair the expected gradient must vanish. Setting the derivative of JJ with respect to the score s=vw⊤vcs = \mathbf{v}_w^\top \mathbf{v}_c to zero and using Pn(w)∝P_n(w) \propto smoothed frequency, one finds the balance condition

σ(s)=P(w,c)P(w,c)+k⋅P(w) Pn(c),\sigma(s) = \frac{P(w,c)}{P(w,c) + k \cdot P(w)\, P_n(c)},

which after algebra recovers s≈log⁡P(w,c)P(w)Pn(c)−log⁡ks \approx \log\frac{P(w,c)}{P(w) P_n(c)} - \log k, i.e., the shifted-PMI identity above (with the caveat that in the real algorithm PnP_n, not the raw unigram PP, is used for negatives, and the 0.750.75 exponent slightly perturbs this ideal relationship in practice, which empirically improves embedding quality on rare words).

Honest tradeoffs. Negative sampling is a biased, approximate objective, not a strict lower bound on the true softmax log-likelihood in the way that e.g. hierarchical softmax approximations sometimes are; its quality depends on the number of negatives kk, the smoothing exponent, and the noise distribution shape. Too few negatives leads to underfitting the "what is not a valid context" signal; too many negatives increases cost roughly linearly with little added benefit past a point (diminishing returns are well documented empirically, with k≈5k \approx 5–2020 typically sufficient). Subsampling is also a lossy heuristic: it discards information, and its keep-probability formula, while empirically well-tuned, is not derived from a clean statistical principle — it is a practical corpus-frequency correction, not a probabilistically unbiased importance-sampling scheme.

Worked example

Consider a tiny toy corpus (ignore real-world scale, this is for tracing the mechanics): ``` "the cat sat on the mat the dog barked at the cat" ``` Vocabulary (with counts): the:4, cat:2, sat:1, on:1, mat:1, dog:1, barked:1, at:1. Total tokens N=13N=13.

Step 1 — noise distribution. Compute f(w)0.75f(w)^{0.75} for each word (raw frequency f(w)=count(w)/Nf(w) = \text{count}(w)/N):

wordcountf(w)f(w)f(w)0.75f(w)^{0.75}Pn(w)P_n(w) (normalized)
the40.30770.42850.257
cat20.15380.26030.156
sat10.07690.15800.095
on10.07690.15800.095
mat10.07690.15800.095
dog10.07690.15800.095
barked10.07690.15800.095
at10.07690.15800.095

Note "the" drops from raw share 0.30770.3077 to smoothed share 0.2570.257 — still likely to be sampled as a negative, but proportionally less dominant than its raw frequency would suggest, exactly the intended flattening effect.

Step 2 — subsampling decision for "the". Using the simplified rule P(keep)=min⁡(1,t/f(w))P(\text{keep}) = \min(1, \sqrt{t/f(w)}) with t=10−5t = 10^{-5}:

P(keep "the")=10−5/0.3077≈3.25×10−5≈0.0057.P(\text{keep "the"}) = \sqrt{10^{-5} / 0.3077} \approx \sqrt{3.25\times10^{-5}} \approx 0.0057.

So roughly 99.4% of occurrences of "the" are dropped from training — it will almost never be used as a center word or (in most implementations) removed from context windows either, freeing training time for informative pairs like (cat, sat) or (dog, barked). Contrast with "cat" (f=0.1538f=0.1538): P(keep)=10−5/0.1538≈0.0081P(\text{keep}) = \sqrt{10^{-5}/0.1538}\approx 0.0081 — still heavily subsampled since even "cat" is common in this tiny corpus, illustrating that subsampling is aggressive whenever tt is small relative to corpus size; in real multi-billion-token corpora, t=10−5t=10^{-5} produces much milder, more realistic discarding for mid-frequency words while still hammering stopwords.

Step 3 — one SGNS update. Suppose after subsampling we still process the pair center = "cat", context = "sat" (positive), with k=2k=2 negatives sampled from PnP_n: suppose we draw "barked" and "on". Let embeddings be 2-dimensional for hand-tracing, with current values

vcat=[0.10,−0.20],vsat=[0.05,0.30],vbarked=[−0.10,0.10],von=[0.20,0.05].\mathbf{v}_{cat} = [0.10, -0.20],\quad \mathbf{v}_{sat} = [0.05, 0.30],\quad \mathbf{v}_{barked} = [-0.10, 0.10],\quad \mathbf{v}_{on} = [0.20, 0.05].

Positive score: s=vsat⋅vcat=(0.05)(0.10)+(0.30)(−0.20)=0.005−0.06=−0.055s = \mathbf{v}_{sat}\cdot\mathbf{v}_{cat} = (0.05)(0.10) + (0.30)(-0.20) = 0.005 - 0.06 = -0.055. σ(−0.055)≈0.4863\sigma(-0.055) \approx 0.4863. Gradient coefficient gpos=1−0.4863=0.5137g_{pos} = 1 - 0.4863 = 0.5137 (we want this dot product to increase). Negative 1 ("barked"): sn1=(−0.10)(0.10)+(0.10)(−0.20)=−0.01−0.02=−0.03s_{n1} = (-0.10)(0.10) + (0.10)(-0.20) = -0.01 - 0.02 = -0.03. σ(−0.03)≈0.4925\sigma(-0.03)\approx 0.4925, so gneg1=−0.4925g_{neg1} = -0.4925 (push this dot product down further — it should be even more negative). Negative 2 ("on"): sn2=(0.20)(0.10)+(0.05)(−0.20)=0.02−0.01=0.01s_{n2} = (0.20)(0.10) + (0.05)(-0.20) = 0.02 - 0.01 = 0.01. σ(0.01)≈0.5025\sigma(0.01)\approx 0.5025, gneg2=−0.5025g_{neg2} = -0.5025 (this one is positive, meaning the model currently thinks "on" is a plausible context for "cat" — the gradient will push it down harder). With learning rate η=0.1\eta = 0.1:

Δvcat=η(gpos vsat+gneg1 vbarked+gneg2 von)\Delta \mathbf{v}_{cat} = \eta\Big(g_{pos}\,\mathbf{v}_{sat} + g_{neg1}\,\mathbf{v}_{barked} + g_{neg2}\,\mathbf{v}_{on}\Big)
=0.1(0.5137[0.05,0.30]+(−0.4925)[−0.10,0.10]+(−0.5025)[0.20,0.05])= 0.1\Big(0.5137[0.05,0.30] + (-0.4925)[-0.10,0.10] + (-0.5025)[0.20,0.05]\Big)
=0.1([0.0257,0.1541]+[0.0493,−0.0493]+[−0.1005,−0.0251])=0.1[−0.0255,0.0797]=[−0.00255,0.00797].= 0.1\big([0.0257, 0.1541] + [0.0493,-0.0493] + [-0.1005,-0.0251]\big) = 0.1 [-0.0255, 0.0797] = [-0.00255, 0.00797].

New vcat≈[0.0975,−0.1920]\mathbf{v}_{cat} \approx [0.0975, -0.1920]. The update nudged "cat" only slightly since the sigmoid values were all near 0.50.5 (an uninitialized, nearly-random embedding stage) — this is expected early in training and is the correct behavior: large, confident updates only occur once dot products diverge from zero, which is exactly what repeated SGD steps are designed to produce over many epochs.

Common pitfalls & traps

PitfallWhy it happensFix
Sampling the true context word as a "negative"Naive uniform/unigram sampling can accidentally draw the actual positive wordExplicitly reject-and-resample if a drawn negative equals wow_o
Using raw unigram frequency for PnP_n instead of the 0.750.75-power smoothed versionCopying softmax-style sampling without the empirical correctionAlways raise frequencies to 0.750.75 before normalizing
Setting kk far too large "for better accuracy"Assuming more negatives always helps, ignoring diminishing returnsUse k≈5k \approx 5–2020; benchmark on a validation analogy/similarity task
Forgetting to subsample before windowingApplying subsampling only to center words but leaving all context tokens in-windowApply subsampling uniformly to the corpus stream before generating windows
Confusing input and output embedding matrices at inferenceSome pipelines only need UU, others sum or average U+WU+WFollow the convention documented for the specific pretrained model; don't silently mix them
Treating SGNS score as a true probabilityThe sigmoid output is calibrated for the binary sub-task, not for ranking against the full vocabularyDo not interpret σ(score)\sigma(\text{score}) as P(wo∣wc)P(w_o \mid w_c) directly; use dot-product rankings instead

Pioneers & history

Origins · 1 minTomas Mikolov and how this was discovered
TM
Tomas Mikolov
b. 1984 · Google Brain / Facebook AI Research, developer of Word2Vec and negative sampling

Tomas Mikolov, together with colleagues Ilya Sutskever, Kai Chen, Greg Corrado, and Jeffrey Dean at Google, introduced both the skip-gram/CBOW architectures and the negative sampling training trick in 2013. Mikolov's earlier work on recurrent neural network language models had already made him attentive to the crushing cost of softmax normalization over large vocabularies; negative sampling was the pragmatic solution that let embedding training scale from academic-sized corpora to the entire Google News dataset (about 100 billion words) in days rather than months on the hardware of the time.

History
2003
— Bengio et al. propose neural probabilistic language models, establishing distributed word representations learned via neural networks.
2013
— Mikolov et al. publish "Efficient Estimation of Word Representations in Vector Space", introducing skip-gram and CBOW.
2013
— Mikolov et al. publish "Distributed Representations of Words and Phrases and their Compositionality", introducing negative sampling and subsampling.
2014
— Levy and Goldberg formally connect SGNS to implicit factorization of a shifted PMI matrix, and Pennington et al. release GloVe as an explicit-factorization alternative.
2018
onward — Contextual embeddings (ELMo, BERT) subsume static word vectors for many downstream tasks, though SGNS-style negative sampling ideas persist in contrastive learning broadly.

Where this matters

Negative sampling's core idea — replace an expensive full-normalization objective with cheap binary discrimination against sampled negatives — extends far beyond Word2Vec. It underlies contrastive learning broadly, including modern sentence and document embedding methods, retrieval systems (dense passage retrieval for search engines, where a query must be scored against millions of documents using in-batch or hard negatives instead of a full corpus softmax), and recommendation systems (candidate generation models that must rank against catalogs of millions of items). The Skip-gram-style objective and its negative sampling training recipe are reused, with modifications, in graph embedding methods (node2vec, DeepWalk) that treat graph random walks the same way skip-gram treats sentences. Pretrained static word embeddings trained this way (word2vec, fastText) are still used as fast, low-resource baselines and as initialization or auxiliary features in some production NLP pipelines where full contextual transformer models are too costly to serve. Studying this mechanism directly prepares the ground for GloVe's explicit matrix-factorization alternative and for understanding Embedding Geometry and Analogies, since the shifted-PMI interpretation of SGNS explains why linear analogy structure (king − man + woman ≈ queen) emerges from these training objectives at all.

Ask the tutor: How does changing the negative sampling exponent from 0.75 to 1.0 (raw frequency) change the embeddings learned for rare words?

Why not just use full softmax if it's the mathematically "correct" objective?

Full softmax is not wrong, just computationally prohibitive at scale. Each gradient step requires normalizing over the entire vocabulary, meaning cost per training example grows linearly with vocabulary size, often 10510^5–10610^6 words. With corpora of billions of tokens, this becomes computationally infeasible on commodity hardware. Negative sampling and related tricks like hierarchical softmax were developed specifically to make training tractable while approximately preserving the objective's useful properties — namely, that words with similar contexts end up with similar embedding vectors. The tradeoff is a biased, approximate objective in exchange for orders-of-magnitude speedup, which in practice yields embeddings of comparable or even better empirical quality on analogy and similarity benchmarks.

How is negative sampling different from hierarchical softmax, another common Word2Vec speedup?

Both address the softmax normalization bottleneck but differently. Hierarchical softmax organizes the vocabulary into a binary tree (often a Huffman tree weighted by frequency) and computes the probability of a word as the product of binary decisions along the tree path from root to leaf, costing O(log⁡∣V∣)O(\log |V|) per example rather than O(∣V∣)O(|V|). Negative sampling instead abandons the tree structure entirely and reframes the task as binary classification against kk sampled negatives, costing O(k)O(k) per example. Empirically, negative sampling with small kk tends to train faster and produce better embeddings for frequent words, while hierarchical softmax can be preferable for rare words, since it still models a proper (if tree-structured) probability distribution.

Does the number of negative samples kk matter a lot in practice?

Yes, though with diminishing returns. Increasing kk generally improves embedding quality up to a point because the model sees more contrastive signal about what is not a valid context per positive example, but each additional negative also costs additional compute linearly. The original word2vec paper found kk between 5 and 20 worked well for smaller datasets, while kk between 2 and 5 sufficed for very large datasets (billions of tokens), since with enough total training pairs, the model already sees enough negative evidence across the whole corpus even with few negatives per individual example. Choosing kk is thus a tradeoff tuned against total corpus size and compute budget.

What does subsampling actually remove, and could it hurt rare-word learning?

Subsampling removes tokens probabilistically, most aggressively removing instances of very frequent words like function words. It does not remove rare words from the vocabulary or corpus at all — rare words are essentially always kept, since their keep-probability is close to 1 under the standard formula. In fact, subsampling helps rare-word learning indirectly: by removing a huge share of "the", "of", "a" tokens from context windows, the remaining windows shift to contain relatively more rare and informative words as both centers and contexts, increasing the density of useful training signal for those rare words per unit of training time.

Is the dot product used for scoring the same as cosine similarity?

No, though they are related. SGNS scores pairs using the raw dot product vwo⊤vwc\mathbf{v}_{w_o}^\top \mathbf{v}_{w_c}, not the cosine similarity, which additionally normalizes by vector norms. During training this matters because the model can increase confidence in a positive pair either by aligning the vectors' directions or by simply growing their magnitudes, and in practice both effects occur to some extent. When embeddings are evaluated for word similarity or analogy tasks afterward, however, cosine similarity is almost universally used instead of raw dot product, since it discounts frequency-driven magnitude effects and better reflects semantic direction, a distinction relevant to later study of Embedding Geometry and Analogies.

How does negative sampling relate to ideas used outside NLP, like in recommendation systems?

The same reframing — expensive multi-class prediction over huge output spaces replaced by binary discrimination against sampled negatives — appears throughout machine learning wherever the "correct answer" must be chosen from an enormous set of candidates. Recommendation systems predicting which of millions of items a user will click use exactly this trick: sample a handful of non-clicked items as negatives rather than normalizing over the whole catalog. Retrieval-based question answering and dense passage retrieval systems do the same over document collections. This makes negative sampling one of the most widely reused computational tricks originating from Word2Vec, well beyond its original NLP context.

Can negative sampling be applied to CBOW as well as skip-gram?

Yes. In Continuous Bag-of-Words (CBOW), the model predicts a center word from the average (or sum) of its surrounding context word embeddings, rather than skip-gram's per-context-word prediction. Negative sampling applies identically: instead of a full softmax over the vocabulary to score the true center word against the averaged context vector, the model samples kk negative center words and trains a binary classifier the same way, using the same sigmoid-based objective and the same f(w)0.75f(w)^{0.75} noise distribution. The gradient derivations are essentially identical, only the vector being scored (averaged context vs. single center) differs.

Before this

Where this leads