Easyword2vec

Frequent-Word Subsampling

Word2Vec

Easy

Problem

Compute the keep probability associated with the Word2Vec frequent-word subsampling rule. The paper gives the discard probability as 1-\sqrt{t/f(w)}, so the corresponding keep probability is clipped to the valid probability range.

f(w)=\frac{\operatorname{count}(w)}{\sum_{u=1}^{V}\operatorname{count}(u)}

P_{\mathrm{keep}}(w)=\min\!\left(1,\sqrt{\frac{t}{f(w)}}\right)

Here, f(w) is the corpus frequency of word w, t is the subsampling threshold, and V is the vocabulary size. Return the keep probabilities in vocabulary order as a float64 PyTorch tensor with shape (V).

Theory

Frequent-word subsampling is the preprocessing trick from Mikolov et al. (2013), "Distributed Representations of Words and Phrases and their Compositionality", that randomly discards very common words during Skip-gram training. Words like "the", "a", and "of" appear millions of times yet carry almost no useful co-occurrence signal, so the authors keep each occurrence with a probability that shrinks as the word becomes more frequent.


Why Frequent Words Hurt Training

Word2Vec learns embeddings by predicting context words from a center word (Skip-gram) over a sliding window. The number of training pairs a word generates is roughly proportional to how often it appears in the corpus. This creates two problems for the most common words.

The paper observes that "the vector representations of frequent words do not change significantly after training on several million examples." In other words, extra exposure to "the" is wasted compute. Subsampling redirects that compute toward rarer, more informative words and, as a side effect, lets the effective context window reach further across the meaningful words that remain.


The Keep-Probability Formula

Let \text{count}(w) be the number of times word w occurs in the corpus and let N = \sum_w \text{count}(w) be the total token count. The frequency of w is:

f(w) = \frac{\text{count}(w)}{N}

Each occurrence of w is then kept with probability:

P_{\text{keep}}(w) = \min\!\left(1, \sqrt{\frac{t}{f(w)}}\right)

where:

The original paper states the rule as discarding word w_i with probability P(w_i) = 1 - \sqrt{t / f(w_i)}. Keeping is the complement, so P_{\text{keep}}(w) = \sqrt{t / f(w)}, clamped at 1. Both phrasings describe the same operation. This problem computes the keep-probability directly.


Reading Each Term

The behavior of the formula is easiest to understand by splitting the vocabulary at the threshold t.

The threshold t acts as a soft cutoff: words below it are untouched, words above it are thinned in proportion to how far above they sit.


Why the Square Root

A natural alternative would be to keep words with probability t / f(w), with no square root. That decays too fast. If "the" has frequency f = 5 \times 10^{-2} and t = 10^{-5}, then t / f = 2 \times 10^{-4}, so only one in five thousand occurrences survives. That is so aggressive it can erase a word almost entirely and starve nearby words of context.

The square root softens the decay. Under \sqrt{t / f}, the same "the" is kept with probability \sqrt{2 \times 10^{-4}} \approx 0.014, about one in seventy occurrences. Frequent words are still heavily thinned, but not annihilated. The paper describes the formula as "heuristically chosen" because it "aggressively subsamples words whose frequency is greater than t while preserving the ranking of the frequencies." The square root keeps the relative ordering of frequencies intact while compressing their dynamic range.

There is also a useful scaling property. If one word is four times as frequent as another (and both are above t), its keep-probability is only half as large, since \sqrt{1/4} = 1/2. The square root turns a multiplicative gap in frequency into a smaller multiplicative gap in keep-probability. A linear rule like t/f would instead turn that same four-times frequency gap into a four-times difference in keep-probability, magnifying rather than dampening the imbalance the technique is meant to reduce.


Effect on Speed and Embedding Quality

Subsampling has two reinforcing effects reported in the paper.

A subtle bonus is window widening. When intervening function words are dropped, the surviving context words sit closer together inside the sliding window, so meaningful words that were just outside the window can now co-occur. This lets a fixed window capture longer-range semantic relationships.


How It Fits Into Skip-gram

Subsampling is a corpus preprocessing step, applied before any embedding update. Understanding where it sits in the pipeline clarifies why it is computed once, up front.

  1. Count pass: scan the corpus once and tally \text{count}(w) for every word, plus the grand total N.

  2. Probability pass: compute P_{\text{keep}}(w) for each word in the vocabulary using the formula above. This is a one-time vector operation over the vocabulary, not over the corpus, so it is cheap.

  3. Streaming pass: walk the corpus token by token. For each occurrence of word w, draw a uniform random number r \in [0, 1) and keep the token only if r < P_{\text{keep}}(w). Dropped tokens are removed before the sliding window forms training pairs.

Because the keep-probability depends only on global frequency, it is identical for every occurrence of a given word. Two occurrences of "the" are equally likely to be dropped, independently of position. The randomness lives in the third pass; this problem isolates the deterministic second pass, which is the piece that has to be numerically exact.


Choosing the Threshold t

The threshold t is the single knob that controls how aggressive subsampling is. It is the frequency at which a word transitions from "always kept" to "partially dropped".

The paper reports that a value around t = 10^{-5} works well for the large news corpus they trained on. The right value scales with corpus size and vocabulary distribution: a corpus where one word dominates needs a different cutoff than a flatter distribution. Because the formula preserves frequency ranking for any positive t, sweeping t changes how much is dropped without reshuffling which words are considered most common.


Comparison With Simpler Strategies


Worked Example (t = 10^{-5})

Take a toy corpus with three words and counts [100, 50, 10]. The total is N = 160.

  1. Frequencies: f = [100/160, 50/160, 10/160] = [0.625, 0.3125, 0.0625].

  2. Ratios t / f: with t = 10^{-5}, these are [1.6 \times 10^{-5}, 3.2 \times 10^{-5}, 1.6 \times 10^{-4}].

  3. Square roots: \sqrt{t/f} = [0.004, 0.005657, 0.012649] (rounded).

  4. Clamp at 1: all three are already below 1, so P_{\text{keep}} = [0.004, 0.005657, 0.012649].

The most frequent word is kept only about 0.4 percent of the time, while the least frequent of the three survives roughly three times as often. Notice how the keep-probabilities track the frequencies: word 1 is twice as frequent as word 2, and its keep-probability (0.004) is about 1/\sqrt{2} \approx 0.707 times that of word 2 ($$), exactly the square-root scaling described above. Now contrast a rare word: if its frequency were f = 10^{-6} (below t), then t/f = 10 and \sqrt{10} \approx 3.16, which the \min clamps to 1, so every occurrence is kept.

As a second check, consider four words with equal counts [25, 25, 25, 25]. Each has frequency f = 0.25, so each keep-probability is \sqrt{10^{-5} / 0.25} = \sqrt{4 \times 10^{-5}} \approx 0.006325. Equal-frequency words receive identical keep-probabilities, which confirms the formula depends only on relative frequency and treats symmetric inputs symmetrically.


Implementation Notes

The computation is a short, fully vectorized pipeline over the count tensor.

In practice Word2Vec applies the keep-probability per occurrence by drawing a uniform random number and dropping the token if it exceeds P_{\text{keep}}. This problem stops at computing the deterministic per-word probabilities, which is the part that must be exactly correct before any sampling happens.


Pitfalls


Examples

Example 1

Input
counts = [100,50,10], t = 0.00001
Output
[0.004,0.005657,0.012649]
Explanation
The most frequent word receives the smallest keep probability after counts become corpus frequencies.

Example 2

Input
counts = [25,25,25,25], t = 0.001
Output
[0.063246,0.063246,0.063246,0.063246]

Example 3

Input
counts = [10000,1,1], t = 0.001
Output
[0.031626,1,1]

Hints

  1. Divide counts by counts.sum() before applying the threshold.
  2. Use torch.clamp with max=1.0 after the square root.

Requirements

Constraints

Starter Code

import torch

def subsample_keep_probs(counts: torch.Tensor,
                         t: float = 1e-5) -> torch.Tensor:
    """
    Returns the float64 keep probability for every vocabulary word.
    """
    pass

Test Cases

CaseMatches
Frequent wordspublic
Equal countspublic
Rare word clippingpublic