09 / 37 · Distributed Representations
GloVe: embeddings from global co-occurrence counts
Count once how often every pair of words occurs together, then fit vectors whose dot products reproduce the logarithms of those counts: the ratio argument behind the model, the weighted least-squares loss, a run on 537,272 Quora questions next to Word2Vec on the same text, and what GloVe and skip-gram have in common.
On this page 13
Word2Vec slides a window over the corpus and updates two vectors for every pair it sees, millions of small stochastic steps. Every pair of words that occurs together a thousand times is visited a thousand times. GloVe (Pennington, Socher and Manning, 2014) starts from the opposite end: count every pair once into a co-occurrence matrix, then fit vectors to that matrix directly. Training then costs time proportional to the number of distinct pairs, not to the length of the corpus, and the objective says explicitly what the dot products are supposed to equal.
Notation. is the vocabulary. is the (weighted) number of times word occurs in the context of word ; ; . Each word has a word vector , a context vector and two biases , .
Baseline: factorize the count matrix#
Methods older than Word2Vec already worked with the co-occurrence matrix: latent semantic analysis (Deerwester et al., 1990) reduces a word–document matrix with a truncated SVD, and HAL-style models reduce a word–word matrix. Applied to raw counts, they inherit two problems. The most frequent words (the, and, ?) co-occur with everything, so their counts dominate any least-squares fit while carrying little meaning; and a single squared error treats a difference of 10 between counts of 20 and 30 like the same difference between 10,020 and 10,030. Transforms such as the logarithm or square root help, and in the GloVe paper an SVD of log counts reaches 60.1% on word analogies where GloVe reaches 71.7% (both 300-dimensional, 6 billion tokens). The question is which function of the counts the vectors should reproduce, and with which weights.
Ratios carry the meaning#
The paper’s example compares ice and steam on 6 billion tokens. , gives , while water, related to both, gives and fashion, related to neither, . On 537,272 Quora questions, counted as described in the Code section, the same pattern appears for tea and coffee:
| Context | Ratio | ||
|---|---|---|---|
| green | 12.2 | ||
| starbucks | 0.129 | ||
| drink | 1.00 | ||
| india | 1.16 |
Green is about tea, Starbucks about coffee, drink about both and India about neither, and the ratio sorts them, while the raw probabilities of drink (0.020) and India (0.002) differ by an order of magnitude for reasons of frequency alone. The corpus decides the senses: on Quora, is 63 times , because most questions about Steam are about the game store.
Probability ratios and pair weights in GloVe
From ratios to a model#
GloVe asks for vectors such that some function of the difference and a context vector reproduces the ratio. Taking the difference compares the two words, taking the dot product with turns vectors into a number, and asking that a difference of vectors map to a ratio of probabilities, , leaves :
The term depends on alone and becomes a bias ; a context bias restores the symmetry. The model is
A dot product plus two biases should equal the log count. Differences of vectors then encode log ratios, which is where the linear analogy structure of the embeddings note comes from.
Weighted least squares#
The model is fitted by least squares over the pairs that occur, with a weight per pair:
with and in all experiments of the paper. The weight does three jobs. Pairs that never occur have and are left out, so the sum runs over the nonzero entries only. Rare pairs are noisy and get small weights: , . Frequent pairs are capped at weight 1, so the pairs of ? with everything cannot take over the fit.
Worked example. Take a pair with , so and . If the current vectors give , the error is and the pair contributes to . The gradient with respect to is : the update moves toward until their dot product, with the biases, matches the log count.
Training follows from the form of . The matrix is built in one pass over the corpus; the paper uses a window of ten words on each side, with a pair at distance adding to the count. Then AdaGrad samples nonzero entries for 50 passes (100 for 300-dimensional vectors), with initial learning rate 0.05. Words and contexts play symmetric roles, so the two sets of vectors differ only through their random initialization, and the final vector of a word is , which the paper finds slightly better than either.
A run on Quora questions#
To see GloVe next to the Word2Vec model of the previous note, train both on the same text: 537,272 Quora questions, lower-cased, split into words and punctuation, with English stopwords removed, 3.9 million tokens. Words seen fewer than 5 times are dropped, which leaves . The co-occurrence matrix, window 10 with weighting, has 6,781,251 nonzero entries, 0.88% of the possible pairs; only 0.15% of the entries reach . Fifty AdaGrad passes with take about a minute on a laptop CPU, and the weighted loss per entry falls from 0.082 after the first pass to 0.036, 0.030, 0.023 and 0.020 after passes 5, 10, 25 and 50.
Nearest neighbours by cosine, GloVe against CBOW Word2Vec with and a 5-word window on the same tokens:
| Word | GloVe | Word2Vec (CBOW) |
|---|---|---|
| python | java 0.891, php 0.847, django 0.842 | javascript 0.967, java 0.942, php 0.937 |
| coffee | tea 0.834, starbucks 0.802, drinking 0.769 | tea 0.900, beer 0.875, starbucks 0.857 |
| word | meaning 0.888, ’? 0.880, origin 0.874 | words 0.806, sentence 0.803, pronounce 0.797 |
| learn | programming 0.840, learning 0.821, easy 0.812 | learning 0.817, beginner 0.750, teach 0.743 |
Both models find the same topics. GloVe’s neighbours lean toward what the word appears with in a question (learn → programming, python), Word2Vec’s toward what can replace it (learn → teach, beginner), partly because GloVe’s window here is twice as wide. GloVe also lets punctuation tokens such as ’? into the neighbour lists: it has no subsampling of frequent tokens, and the cap limits their weight per pair but not the number of pairs they appear in.
GloVe and skip-gram#
The two methods are closer than their training procedures suggest. The Word2Vec note shows that skip-gram with negative sampling drives toward the shifted pointwise mutual information ; GloVe drives toward , and the biases can absorb the and terms that turn a log count into a PMI. Both factorize a log co-occurrence matrix with dot products. They differ in the weighting of pairs, in whether the matrix is built explicitly, and in the cost: GloVe pays once per corpus for counting and then per nonzero entry, Word2Vec per token per epoch.
The GloVe paper reports analogy accuracies of 71.7% for GloVe against 65.7% for CBOW and 69.1% for skip-gram (300 dimensions, 6 billion tokens), and 75.0% for GloVe on 42 billion tokens. Levy, Goldberg and Dagan (2015) later showed that much of such differences comes from choices outside the algorithm (window size, subsampling, smoothing of the context distribution, how the two sets of vectors are combined) and that with these matched no method is consistently better. Today GloVe is used mostly through its downloadable vectors, such as the Twitter vectors used in the embeddings note.
What changed#
| Word2Vec (SGNS) | GloVe | |
|---|---|---|
| Input | windows streamed over the corpus | co-occurrence counts, built once |
| Objective | logistic loss on positive and sampled pairs | weighted least squares on |
| What dot products approximate | shifted PMI (implicitly) | log count minus biases (explicitly) |
| Training cost | proportional to corpus length × epochs | proportional to nonzero pairs × passes |
| Memory | two vector tables | the count matrix too (6.8 M entries for 27,789 words here) |
| Frequent words | subsampled | capped by |
Limitations#
- One vector per word type. Like Word2Vec, GloVe gives bank one compromise vector; contextual vectors come with BERT.
- Closed vocabulary. A word absent from the counts has no vector, and a word seen five times gets a vector fitted to a handful of pairs. The next note builds vectors from parts of words.
- The count matrix. Its size grows with the vocabulary and window; for large vocabularies counting needs care with memory, which Word2Vec never pays.
- Hyperparameters beyond the algorithm. Window, weighting, , and the combination change the result as much as the choice between GloVe and Word2Vec.
What you should remember#
- GloVe fits over the nonzero co-occurrence counts, by weighted least squares.
- The motivation is the ratio : it separates what distinguishes two words from what they share, and log-linear dot products turn it into a vector difference.
- The weight ignores zeros, damps rare pairs and caps frequent ones.
- Skip-gram and GloVe both factorize a log co-occurrence matrix; their differences are in weighting and cost, and in practice smaller than the hyperparameters around them.
Notebook#
No course notebook trains GloVe. week01_word_embeddings.ipynb downloads the pretrained 100-dimensional GloVe Twitter vectors (cell 62; cell numbers count every cell from 1) and runs the analogies discussed in the embeddings note. The numbers of this note come from the run in the Code section, on the course’s own Quora file (seminars/data/quora.txt).
Code#
GloVe in about forty lines of NumPy and PyTorch, on the Quora questions:
import re, collections, numpy as np, torch
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS
tok = re.compile(r"\w+|[^\w\s]+")
lines = [l.strip() for l in open('quora.txt', encoding='utf-8') if l.strip()]
sents = [[t for t in tok.findall(l.lower()) if t not in ENGLISH_STOP_WORDS] for l in lines]
cnt = collections.Counter(t for s in sents for t in s)
vocab = [w for w, c in sorted(cnt.items(), key=lambda x: (-x[1], x[0])) if c >= 5]
idx = {w: i for i, w in enumerate(vocab)}; V = len(vocab)
print(len(sents), sum(map(len, sents)), V)
# co-occurrence counts: window 10 on each side, a pair at distance d adds 1/d
ids = [np.array([idx[t] for t in s if t in idx]) for s in sents]
ids = [a for a in ids if len(a)]
flat = np.concatenate(ids); sid = np.repeat(np.arange(len(ids)), [len(a) for a in ids])
keys, vals = [], []
for d in range(1, 11):
same = sid[:-d] == sid[d:] # both words in the same question
a, b = flat[:-d][same], flat[d:][same]
keys += [a * V + b, b * V + a]; vals += [np.full(len(a), 1 / d)] * 2
uk, inv = np.unique(np.concatenate(keys), return_inverse=True)
X = torch.tensor(np.bincount(inv, weights=np.concatenate(vals)), dtype=torch.float32)
I, J = torch.from_numpy(uk // V), torch.from_numpy(uk % V)
print(len(X), (X >= 100).float().mean().item())
# weighted least squares with AdaGrad
torch.manual_seed(0)
dim = 32
W = torch.nn.Parameter((torch.rand(V, dim) - 0.5) / dim); Wt = torch.nn.Parameter((torch.rand(V, dim) - 0.5) / dim)
b = torch.nn.Parameter(torch.zeros(V)); bt = torch.nn.Parameter(torch.zeros(V))
opt = torch.optim.Adagrad([W, Wt, b, bt], lr=0.05)
f = torch.clamp(X / 100, max=1.0) ** 0.75; logX = torch.log(X)
for epoch in range(50):
perm, total = torch.randperm(len(X)), 0.0
for s in range(0, len(X), 100_000):
p = perm[s:s + 100_000]; i, j = I[p], J[p]
err = (W[i] * Wt[j]).sum(1) + b[i] + bt[j] - logX[p]
loss = (f[p] * err ** 2).sum()
opt.zero_grad(); loss.backward(); opt.step(); total += loss.item()
print(epoch + 1, total / len(X))
E = (W + Wt).detach().numpy() # final vectors: w + w~
E /= np.linalg.norm(E, axis=1, keepdims=True)
sim = E @ E[idx['python']]
print([(vocab[k], round(float(sim[k]), 3)) for k in np.argsort(-sim)[1:4]])
The run prints 537,272 questions, 3,883,212 tokens and , then 6,781,251 nonzero entries of which 0.15% reach , the losses quoted above (0.082 after the first pass, 0.020 after the fiftieth) and java 0.891, php 0.847, django 0.842 for python. The tea and coffee rows come from the same X: is divided by the row sum , 1,702.8 for tea and 1,954.4 for coffee.
Next#
GloVe and Word2Vec both stop at the edge of their vocabulary: a misspelling, a rare inflection or a new word gets nothing. The next note keeps the skip-gram objective and builds every word’s vector from the vectors of its character n-grams, so related forms share parameters and unseen words still get a vector.
References#
- J. Pennington, R. Socher, C. D. Manning. GloVe: Global Vectors for Word Representation. EMNLP, 2014. Table 1 (ice and steam), §3 (the model and the weighting function), §4.2 (training), Table 2 (analogies).
- O. Levy, Y. Goldberg, I. Dagan. Improving Distributional Similarity with Lessons Learned from Word Embeddings. TACL, 3, 2015.
- S. Deerwester, S. T. Dumais, G. W. Furnas, T. K. Landauer, R. Harshman. Indexing by Latent Semantic Analysis. JASIS, 41(6), 1990.