Part II opened with the distributional hypothesis, in Firth’s slogan that “you shall know a word by the company it keeps”, and we turned it into geometry by counting co-occurrences and reducing the matrix with an SVD.
Those count based vectors work, and they also carry the whole matrix’s baggage, being large, being sparse, and having to be rebuilt from scratch whenever the corpus changes.
Around 2013 a different idea took over, and every modern system inherits it. Do not count contexts and then compress, but learn a small dense vector for each word directly by training a model to predict context.
The distributional hypothesis is unchanged and only the machinery is new. That machinery turns out to be faster and smaller, and it produces geometry with structure nobody put there on purpose.
The canonical method is word2vec, from Mikolov and colleagues in 2013, which trains vectors through a deliberately trivial prediction task. There are two formulations and they are mirror images of each other.
| one word | CBOW | skip-gram |
|---|---|---|
| , | ||
| , where |
||
| , where |
||
Slide a window across the corpus, take the word at the centre, then try to predict each of the words around it one at a time. That is the entire supervised signal, with a centre word in and one context word out, and no labels are needed because the corpus labels itself.
Formally, for a corpus and a window of words on each side, skip-gram maximises
CBOW, for continuous bag of words, reverses every arrow by taking the surrounding words and predicting the one in the middle. The context words are combined before they reach the hidden layer, and the combination is a simple average:
That average is where the name comes from, because the context is a bag and order inside the window is discarded, so the cat sat and sat cat the give the same hidden vector.
Take “the quick brown fox jumps” with a window of two words on each side, where both formulations see the same windows and make different training data from them.
| centre | context words | skip-gram pairs | CBOW example |
|---|---|---|---|
| the | quick, brown | (the, quick), (the, brown) | (quick brown) the |
| quick | the, brown, fox | (quick, the), (quick, brown), … | (the brown fox) quick |
| brown | the, quick, fox, jumps | (brown, the), (brown, quick), … | (the quick fox jumps) brown |
| fox | quick, brown, jumps | (fox, quick), (fox, brown), … | (quick brown jumps) fox |
| jumps | brown, fox | (jumps, brown), (jumps, fox) | (brown fox) jumps |
The counts differ, and that difference explains everything else about the two. Skip-gram produces 14 training pairs from this one sentence, one per centre and context combination, while CBOW produces 5, one per centre word.
| CBOW | Skip-gram | |
|---|---|---|
| direction | context centre | centre context |
| examples per centre | 1 | up to |
| speed | faster | slower |
| rare words | weaker | stronger |
| context handling | averaged into one | each predicted separately |
The averaging in Equation (9.2) is what costs CBOW its accuracy on rare words, because a rare word in a context of four is one quarter of one hidden vector and the three common words beside it smooth its contribution away.
Skip-gram gives that rare word its own training pair with every neighbour, so it gets chances to shape its own vector, and it pays for this in time, since more pairs means more updates. The rule of thumb is CBOW for large corpora where speed matters, and skip-gram when the tail matters.
The model has almost no parameters, holding one vector per word or strictly two. Every word has an input vector , a row of , used when is the centre or context being fed in, and it also has an output vector , a column of , used when is the word being predicted.
Training nudges those vectors so that words appearing in similar contexts end up with similar vectors, and when training finishes the output vectors are usually thrown away, leaving the input matrix as what people mean by “the embeddings”.
word2vec is a neural network with one hidden layer and no nonlinearity in it, and writing it out properly takes four steps. Let the vocabulary be size and the embedding size , with two weight matrices, where is and holds the input vectors while is and holds the output, or context, vectors.
The input is one hot for the centre word . So
which is just row of . No arithmetic happens here, because multiplying a one hot vector by a matrix selects a row, and that row is the word’s embedding.
For each candidate output word , take the dot product of the hidden vector with that word’s context vector:
Turn the scores into a distribution:
We want to maximise the probability of the true context word , so we minimise its negative log probability, which is cross entropy against a one hot target:
The two terms pull in opposite directions, pushing up the score of the right word while pushing down the total of all scores.
Everything now follows from the chain rule, and the first derivative is the one you already know from Chapter 8. Let be the target, for the true context word and otherwise, and differentiating Equation (9.6) gives the prediction minus target rule again:
That is the error at output unit , and every remaining gradient is multiplied by whatever it was attached to.
| gradient | chain rule | result |
|---|---|---|
| output weights | ||
| hidden layer | ||
| input weights |
Two consequences follow. The output gradient touches every word in the vocabulary, because is nonzero for all , and that is the cost we are about to attack. The input gradient is multiplied by , which is one hot, so only one row of moves per training example and the centre word’s embedding is updated while no other is.
Now count the work. Equation (9.5) has a denominator summing over the whole vocabulary, and Equation (9.7) produces an error for every one of those words, so with in the hundreds of thousands and billions of training examples this is ruinous. Both fixes below attack the same denominator.
The first fix changes the shape of the output layer, replacing independent output units with a binary tree whose leaves are the words. Reaching any word in a balanced tree takes decisions, so for a million words that is about twenty steps rather than a million.
Each internal node carries its own vector , and there are no output vectors for words at all any more, only for the nodes on the way to them.
Write for the number of nodes on the path from the root to word . Write for the th node on that path. Then
where the indicator is
The whole construction rests on one identity:
At every node the probability of going left and the probability of going right sum to one, so the probabilities of all leaves sum to one automatically and we get a proper distribution without ever computing a normalising sum.
To see that working, take and a balanced tree of depth 3, where the path to cat goes left, then right, then left, and the hidden vector is .
| node | ||||
|---|---|---|---|---|
Multiply the three: .
Three sigmoid evaluations replaced a softmax over eight words, and you can check the identity at the first node, where and sum to exactly .
The loss is then the negative log of Equation (9.8):
For the path above, .
Differentiating gives a familiar shape:
and the centre word is updated with .
Only the nodes on the path are touched, and every other node in the tree is left alone. In practice the tree is a Huffman tree built from word frequencies, so that frequent words sit on short paths and cost least to compute.
The output layer is the problem, because predicting a context word means a softmax over the entire vocabulary, and with a vocabulary in the hundreds of thousands computing that denominator for every training example is ruinous.
word2vec’s decisive trick is negative sampling, which does not ask which of all words is the context but instead asks a handful of much easier yes or no questions. Is this the real context word? Yes. Is this randomly drawn word? No, it is a negative.
Train the vectors to say yes to true pairs and no to a few random ones, and a -way softmax becomes a handful of binary decisions per example. That is what made word2vec fast enough to train on billions of words on one machine.
Chapter 7 promised this exponent would return, so here it is. The negatives come from the unigram counts raised to the power , rather than uniformly or from the raw unigram distribution:
Why bend the distribution at all? Draw negatives from the raw counts and nearly every negative is the, which teaches the model almost nothing, while drawing them uniformly makes rare words appear as negatives far more often than they ever appear as real words. The power sits between the two, and here is what it does to a five word vocabulary.
| word | count | ||||
|---|---|---|---|---|---|
| the | 1000 | 0.5910 | 177.8 | 0.5301 | |
| of | 600 | 0.3546 | 121.2 | 0.3614 | |
| dog | 50 | 0.0296 | 18.8 | 0.0561 | |
| cat | 40 | 0.0236 | 15.9 | 0.0474 | |
| aardvark | 2 | 0.0012 | 1.7 | 0.0050 |
In the last column the most frequent word is sampled slightly less than its share and the rarest more than four times its share, because raising counts to a power below one compresses the range, which lifts the tail and trims the head.
The same exponent did the same job for PMI in Chapter 7, where it smoothed the context distribution to stop rare words earning inflated scores. Two methods, one correction, and no coincidence, since word2vec can be shown to be implicitly factorising a shifted PMI matrix.
The update is the prediction minus target rule from Chapter 8 applied to a dot product, and nothing new is needed.
Take a centre word cat with vector , the true context word sat with vector , and one sampled negative the with , at a learning rate of .
For each pair the model scores , squashes it with a sigmoid, then compares with the label, which is for the true context and for a negative.
| pair | label | gradient | ||
|---|---|---|---|---|
| cat, sat (positive) | 1 | |||
| cat, the (negative) | 0 |
The signs are the whole story. The positive pair gets a negative gradient, which pulls the two vectors together, while the negative pair gets a positive one, which pushes them apart.
Both sides of every pair move, so there are two update rules and not one. Each context vector is pulled towards or away from the centre word,
and the centre word is moved once, by the sum of the pulls from every pair it took part in,
Forgetting the second rule is the usual slip, and it changes the answer below.
| vector | arithmetic | new value |
|---|---|---|
The centre word’s own gradient is , which is the positive context pulling it one way and the negative pushing it the other, added together.
Did it work? Recompute both scores with all three updated vectors.
| before | after | |
|---|---|---|
| , want it up | ||
| , want it down |
True pairs were pulled together and random pairs pushed apart. Repeat that a few billion times and the geometry of Part II falls out.
Try it yourself.
code/worked_examples/word2vec.pyprints both tables above.--exponentlets you change the and watch the sampling distribution flatten or sharpen.
Written out, the loss for one centre word with negatives is
where is the score of a candidate word.
Apply Equation (9.10) and the second term becomes , which is the form usually printed, and both say the same thing: make the true pair score high and the sampled pairs score low.
Compare this with the full softmax loss of Equation (9.6), where the sum over has become a sum over , and is typically 5 to 20.
Put the three side by side. Let be the corpus size, the embedding dimension, and the window size.
| method | cost |
|---|---|
| Full softmax | |
| Hierarchical softmax | |
| Negative sampling |
For the full softmax costs a million operations per prediction, hierarchical softmax costs about twenty, and negative sampling costs , which does not grow with the vocabulary at all.
Both fixes are approximations and they approximate different things. Hierarchical softmax still computes a genuine probability distribution, merely factored over a tree, whereas negative sampling abandons the distribution entirely and solves a set of binary classification problems instead. In practice negative sampling won, because it is simpler and trains faster on frequent words.
Negative sampling is also a pattern this book meets again, in the form of pulling true pairs together and pushing random pairs apart. The contrastive training behind dense retrieval in Chapter 21 is the same idea one level up, with positive and negative pairs shaping a representation.
Something unplanned fell out of these vectors. Take the vector for king, subtract man, add woman, and the nearest vector to the result is queen.
The relationship from male to female turns out to be, approximately, a direction in the space, and the same displacement carries uncle to aunt and he to she, while plural formation is another direction and capital of country is another.
Nobody designed this. It emerged because consistent semantic relationships produce consistent contextual differences, and training recorded them as consistent geometric ones, which makes analogy solving arithmetic:
excluding , and themselves from the answer.
Take a toy space with three interpretable dimensions, namely how royal a word is, how male and how female.
| word | royal | male | female |
|---|---|---|---|
| king | 1.00 | 0.90 | 0.10 |
| man | 0.10 | 0.90 | 0.10 |
| woman | 0.10 | 0.10 | 0.90 |
| queen | 0.95 | 0.15 | 0.85 |
| prince | 0.90 | 0.85 | 0.15 |
| throne | 0.80 | 0.45 | 0.45 |
| child | 0.20 | 0.50 | 0.50 |
Compute the target, where subtracting man from king strips the male component while keeping the royal one, and adding woman puts a female component back:
Now rank every word by cosine to that target.
| word | cosine | |
|---|---|---|
| queen | the answer | |
| throne | royal, but not gendered | |
| woman | query word, must be excluded | |
| child | ||
| prince | royal, but male | |
| king | query word, must be excluded | |
| man |
Queen wins at and the runners up are interpretable, since throne is royal but carries no gender while prince is royal but male. Both are near, and both are wrong for the right reasons.
Note where woman lands, which is third, ahead of four other words. On real spaces a query word very often comes first, because stays close to , so forgetting the exclusion will make your analogy scores look far better than they are. It is the single most common way to overstate an embedding.
The analogy above used a famous example on a space built to make it work, so here is a more honest use of the same arithmetic, which doubles as a diagnosis of the corpus. Take a space trained on technology news and ask for the nearest neighbours of apple.
| neighbour | cosine | |
|---|---|---|
| mac | ||
| samsung | ||
| iphone | ||
| android | ||
| orange | the first fruit, far behind | |
| juice |
The top four are technology and the first fruit trails at , and it is orange, which is a phone network as well as a fruit.
This tells us something about the corpus rather than about English, because apple is overwhelmingly a company here. The fruit sense exists in the vector and is buried under the company sense, since the training text talked about one far more than the other.
Now subtract the strongest technology association and see what is left:
Rank every word by cosine to .
| neighbour of | cosine | |
|---|---|---|
| fruit | ||
| banana | ||
| juice | ||
| orange | ||
| mac | now pointing the other way | |
| samsung |
The list has inverted completely, with fruits filling the top while the technology words have gone negative, meaning they now point away from what is left. Nothing was told to the model about fruit or about companies, and subtracting one word removed a whole semantic dimension so that the sense which had been buried came to the surface.
The reason it works is that a vector is a mixture of the senses and associations the corpus gave the word. Apple carries a large technology component and a smaller fruit one, while iphone carries the technology component and no fruit at all, so subtracting cancels what they share and keeps what only the first has. The technology part very nearly annihilates, the fruit part survives untouched, and the residue is the minority sense.
This is a practical diagnostic and worth more than analogy scores. It reveals the dominant sense of a polysemous word, which is a fact about your training data, so that if apple comes out as a company your corpus is technology news, and if it comes out as a fruit your corpus is recipes or agriculture. The same probe on bank, java, python or amazon will tell you what your text was about before you read a line of it.
It also shows the limit of a single vector per word, because both senses are jammed into one point and pulling them apart takes arithmetic plus a guess about what to subtract. Chapter 15 solves this properly by letting the sentence decide which sense is present.
Try it yourself.
code/worked_examples/word2vec.py --appleprints both tables.--subtracttakes any word, so you can strip a different association and see what surfaces.
word2vec learns from local windows one at a time and never sees the corpus as a whole, so GloVe (Pennington et al. 2014) asks whether that is wasteful, given that the global co-occurrence counts are sitting right there.
GloVe’s founding observation concerns ratios of counts rather than counts themselves. Let be the number of times word occurs in the context of word , so that the conditional probability is
Take a single probability such as and it is hard to interpret, because there is nothing to compare against. Now take two words and probe them with a third, comparing against for various probe words , and the ratio is immediately readable.
| probe | solid | gas | water | fashion |
|---|---|---|---|---|
| related to ice? | yes | no | both | neither |
| related to steam? | no | yes | both | neither |
| large | small |
The ratio is large when the probe belongs to the first word, small when it belongs to the second, and near one when it belongs to both or to neither. That is the discrimination raw probabilities could not give. Water and fashion both score near one for opposite reasons, and the ratio quite properly refuses to distinguish them, since neither tells ice from steam.
If ratios carry the meaning, the model should predict them. GloVe arranges for the dot product of two word vectors to approximate the log of the co-occurrence count, because differences of logs are ratios.
That gives a weighted least squares problem:
Each piece earns its place. Here is the vector for the word and the vector for the context, so every word again gets two exactly as word2vec has input and output vectors, and the final embedding is usually since both carry signal.
The biases and absorb how common each word is on its own, because without them the model would have to encode raw frequency in the vectors, which is the pollution PMI existed to remove in Chapter 7.
The target is rather than , because a log turns the multiplicative structure of counts into an additive one, so a difference of dot products lands on a ratio of probabilities. That is the whole design.
The last piece is , and without it the objective would be dominated by whichever pairs happened to be most frequent.
It does three jobs at once. At it gives , so pairs that never co-occur contribute nothing, which matters because is undefined and most of the matrix is zeros. Between and it rises, so rarer pairs count less than common ones, which suppresses noise from pairs seen once or twice. Above it is flat at , so a pair seen ten thousand times gets no more weight than one seen a hundred times and the cannot dominate the fit.
That exponent is for the third time in this book, after PMI smoothing and negative sampling. Three different models, three different mechanisms, and the same correction, which is to compress the range so the head does not swamp the tail.
At release in 2014 it beat the alternatives on three tasks, namely word analogy, word similarity and named entity recognition, and it captured syntactic and semantic relationships equally well.
It shares word2vec’s fatal limitation, though, since GloVe produces static embeddings of one vector per word, and contextual models have since displaced it for most tasks.
Both models so far treat a word as an atom, so a word is either in the vocabulary with a vector of its own or unknown and has nothing. Chapter 3 showed why that fails, because Heaps’ law guarantees the vocabulary never closes and unknown words keep arriving forever.
FastText (Bojanowski et al. 2017) answers the same pressure that subword tokenisation answered in Chapter 5, but at the embedding rather than the tokeniser.
Break each word into overlapping character n-grams, marking the
boundaries with < and > so that a prefix
can be told from the same letters appearing mid-word. For apple
with
:
<ap app ppl
ple le>
FastText normally uses several values of together, so here is technical at and at .
| 3-grams | <te, tec,
ech, chn, hni, nic,
ica, cal, al> |
| 4-grams | <tec, tech,
echn, chni, hnic,
nica, ical, cal> |
Every n-gram gets its own learned vector , and the word’s vector is their sum, with the whole word included in the set as well:
Written out for our example,
Everything else is skip-gram with negative sampling, unchanged, and only the definition of a word vector has moved.
The first gain is that unknown words get real vectors. Meet
technicality for the first time and FastText still produces
something sensible, because its n-grams tec,
ech, chn and nic were all trained
on technical and its relatives, so the sum lands near them.
word2vec and GloVe can only return an unknown token here, whereas
FastText returns a vector in roughly the right neighbourhood.
The second is that morphology comes free, since walk, walks, walked and walking share most of their n-grams and their vectors are therefore close by construction rather than by luck. This matters far more in some languages than in English, because a morphologically rich language such as Tamil, Finnish or Turkish generates enormous numbers of inflected forms, most of them individually rare, so treating each as an unrelated atom wastes almost all the evidence while sharing subword vectors pools it.
The cost is twofold. The n-gram table is larger than a word table and a word vector must be assembled at query time rather than looked up, and FastText also cannot separate unrelated words that happen to share spelling, since nothing tells it that ear in earth has no connection to ear in hearing.
FastText itself is little used now, while its central move is everywhere. Every modern tokeniser builds its vocabulary from frequent character sequences rather than whole words, whether BPE in Chapter 5, WordPiece or SentencePiece, and all of them answer the open vocabulary problem the same way, by composing rare things from common pieces. FastText made that argument at the embedding layer first.
Part II has now built dense word vectors three separate times. Chapter 7 factored a PPMI matrix with an SVD, and this chapter trained word2vec by gradient descent and then fitted GloVe to global counts.
Three methods, three chapters and three sets of vectors that behave alike, which is not a coincidence, and the reason is the most useful idea in Part II. All three are matrix factorisations of the same co-occurrence statistics, differing in which matrix, which loss and which optimiser, but not differing in kind.
Every method here produces two matrices, a word matrix of size and a context matrix of the same size, where row of is the vector and row of is the context vector . Every method then asks the dot product of a word and a context to reproduce some association score:
That is the whole family, so choosing a method means choosing three things: the matrix , the weight each cell carries, and how you solve for and .
| SVD of PPMI | word2vec (SGNS) | GloVe | |
|---|---|---|---|
| target | |||
| cell weight | all cells equal | the counts | |
| zero cells | fitted as | pushed down | dropped |
| solved by | closed form | stochastic gradient | gradient on counts |
| needs | the whole matrix | a stream of windows | the whole matrix |
The middle column is the surprising one, because nothing in word2vec’s training loop mentions PMI, and the next subsection shows why PMI turns up anyway.
Recall the negative sampling objective of Equation (9.15), take one word and one context, and write for their dot product.
Over the whole corpus that pair is seen times as a real pair, and it also turns up as a sampled negative, because a word is the centre of windows, each draws negatives, and each negative is with probability , so the pair appears as a negative times in expectation. Collect the two contributions:
Now differentiate and set the result to zero. Using ,
Write and . Then , and since , this gives . Substituting , where is the number of pairs in the corpus:
This is the result of Levy and Goldberg (2014), that the optimum of skip-gram with negative sampling is a PMI matrix shifted down by . Two hundred dimensions of gradient descent, and the answer was in Chapter 7 all along.
The shift has a plain reading, since more negatives means a higher bar. With every entry moves down by , so a pair needs stronger evidence before its dot product goes positive.
The claim is checkable on the eight-word matrix of Chapter 7. Train the objective directly, then compare each learned dot product against .
| pair | learned | difference | |
|---|---|---|---|
| cat, vet | |||
| vet, vet | |||
| dog, fur | |||
| fuel, tyre | |||
| vet, feed | |||
| pet, feed |
Across all observed pairs the mean absolute difference is with , and the largest is . The network was never shown a PMI value.
GloVe looks like a different animal, because Equation (9.19) fits rather than an association score, and it is the biases that close the gap.
Start from the definition of PMI and expand it in raw counts. Write for a row total, for a column total, and for the grand total:
Now read GloVe’s objective again, where it asks for and the two biases are free parameters fitted from the data. Set
and the right hand side becomes exactly Equation (9.27), so GloVe’s dot product is fitting PMI up to whatever the biases absorb.
That is what the biases were introduced to do. The GloVe section justified them as removing raw frequency from the vectors, and Equation (9.27) says what removing raw frequency means, namely subtracting a row effect and a column effect, which is the arithmetic PMI performs.
The fitted numbers bear that out in the aggregate, though not cell by cell. Here are the eight biases from the same eight-word matrix, beside each word’s row total.
| word | bias | row total |
|---|---|---|
| dog | ||
| car | ||
| fuel | ||
| truck | ||
| pet | ||
| cat | ||
| drive | ||
| vet |
The broad tendency is there, in that vet, with the smallest row total, takes the smallest bias. The relationship is plainly not monotone, though. Dog has a smaller row total than cat and truck and a larger bias than either, while pet, drive and fuel share a row total of and take biases of , and .
Two things explain the spread. The split between and is not identified, because adding a constant to every and subtracting it from every leaves the objective unchanged, so only the sum of the two is pinned down. And at the vectors themselves absorb part of the row effect, leaving the bias to carry only the remainder. The biases do the job Equation (9.27) describes between them, and no single bias is a readout of one word’s frequency.
So three chapters of machinery converge on one target. SVD fits PPMI, word2vec fits PMI shifted by , and GloVe fits PMI with the shift absorbed into two bias terms.
That also settles a puzzle from earlier, because the exponent appears in PMI smoothing, in negative sampling and in GloVe’s weighting function, and it is the same correction applied to the same statistic, three times over.
Sharing a target does not make three methods interchangeable. The differences sit in the two remaining columns of the table, which are how each cell is weighted and what happens to the zeros.
Weighting first. The SVD minimises squared error over every cell equally, so a pair seen once counts as much as a pair seen ten thousand times, whereas word2vec weights each pair by how often it occurred, since the objective sums over actual windows, and GloVe states its weighting outright in and can therefore tune it.
The zeros matter more and they are easy to miss. Most of a co-occurrence matrix is zeros, and the three methods treat them in three different ways. PPMI writes an explicit and the SVD fits it like any other cell, while word2vec never fits a zero cell directly, although negative sampling still drags it down because an unseen pair is drawn as a negative and never as a positive, and GloVe sets and drops those cells entirely.
There is a case where that difference bites. Take the matrix from Chapter 7, with four animal words, four vehicle words and no shared context at all, compress it to two dimensions by each method, then ask for nearest neighbours.
| query | SVD of PPMI | word2vec | GloVe |
|---|---|---|---|
| dog | cat | cat | truck |
| car | truck | truck | dog |
| vet | dog | pet | fuel |
| fuel | car | drive | vet |
Two methods get all four right while GloVe gets all four wrong, and it is not a training failure, since its fit to is good at a mean absolute error of .
The explanation is the zeros. On this matrix every fact that separates an animal from a vehicle lives in a cell that is zero, and GloVe drops those cells, so it is never shown a single piece of evidence that a dog is unlike a truck. The two groups are fitted on disjoint contexts with nothing tying them together, so their relative positions are left to the initialiser.
Fill each zero with a count of and GloVe recovers all four. Real corpora have almost no structural zeros, because in a large enough corpus most pairs eventually co-occur once, which is why this never became a practical problem and also why the toy exposes the mechanism so cleanly.
The remaining difference is cost. The SVD needs the whole matrix in memory and a full decomposition, which is cubic in the smaller dimension, whereas word2vec needs only a stream of windows and never builds the matrix at all. That, rather than vector quality, is why the learned methods took over in 2013.
The factorisation view also settles what the embedding dimension means. In the SVD picture the top singular values carry most of the semantic structure, and Chapter 7 used their decay to choose through the contribution ratio of Equation (7.12).
word2vec’s hidden layer dimension plays the identical role, being a budget for how many directions you are willing to keep, and the lectures put it as a parallel between hidden units and significant eigenvalues.
Choosing is therefore the bias variance trade off in disguise, since too few dimensions lose semantic information while too many fit noise. That predicts something you can check, because raising past the number of significant singular values should buy little, and in practice it does. The useful tends to land near the count of large singular values, which is what the scree plot of Chapter 7 was showing all along.
Try it yourself.
code/worked_examples/factorisation.pyproduces every number in this section, on the matrix Chapter 7 already used. Run it plain for all four steps.--sgns --k 1shows the shift vanishing when there is one negative, and--k 15shows it growing.--compareprints the neighbour table above, and--compare --fill 1removes the structural zeros and watches GloVe come back into line.
An embedding space is an object you can interrogate, and learning to read one is a practitioner’s core skill. Every question you can ask reduces to one operation, the cosine similarity of Chapter 4:
It ranges from for the same direction through for orthogonal to for opposite. We use the angle rather than the raw dot product for a specific reason, which is that vector length in these spaces tracks word frequency more than meaning, while direction is where the semantics live.
From cosine, three tools follow. Nearest neighbours are the words of highest cosine to a target, excluding the target itself, and they reveal what a word means to the space, so the neighbours of bank expose whether your corpus was about rivers or money. Analogies, through Equation (9.16), probe whether a relationship is encoded as a clean direction.
Failures are as informative as successes. On a corpus of a few thousand documents many analogies return nonsense, because the relationship was never witnessed often enough to become a stable direction, and knowing that a bad analogy usually means thin data rather than a broken method is exactly the forensic skill Project 3 trains.
The geometry records whatever the corpus contains, including its social regularities. The apple probe above was a mild instance, and what follows is the same fact with higher stakes.
Suppose a body of text systematically places occupation words near one gender’s pronouns, in which case the space will place them near that gender’s vector. No single sentence is biased, and the aggregate association is real and measurable.
Measuring it needs only cosine, and the tool is the Word Embedding Association Test (Caliskan et al. 2017). In the simplified form this course uses, it scores the differential association of target words with two attribute sets and :
A positive means the targets sit closer on average to set , a negative means closer to , and the magnitude is the strength. Point it at institutional against narrative vocabulary and it separates registers, while pointing it at gendered attributes exposes occupational stereotypes in web scale spaces.
The number is honest and it demands honesty back, so three cautions are in order. It measures the corpus through the model, so change either and changes. The word lists are a design choice, so report them with the score or the score means nothing. And a near zero result means these lists do not separate, which is not the same as the space being unbiased.
Any system built on these vectors inherits their associations silently, whether search, screening or classification, which is why measuring them is part of knowing what you have built.
For all their elegance, these embeddings share one fatal limitation, which is that each word gets exactly one vector, forever. The bank of a river and the bank that holds money collapse into a single point, and that point is the blurred average of both senses, correct for neither.
A word’s meaning depends on its sentence, and a lookup table cannot represent that because it committed to the vector before the sentence existed. Repairing this is the achievement of the contextual embeddings and transformers of Part IV, which let a word’s representation depend on its context.
The tools you build here do not go away, because cosine, nearest neighbours and association tests are exactly how those larger models are probed and audited. Learning to read a space by hand now is learning to read every space later.
Mikolov et al. (2013) introduce negative sampling. Pennington et al.
(2014) on GloVe and Bojanowski et al. (2017) on FastText are the
siblings. Caliskan et al. (2017) is the WEAT paper and a model of
careful measurement. Rong (2014) explains word2vec’s parameter updates
step by step. Le and Mikolov (2014) extends the idea from words to whole
sentences and documents. Arora et al. (2016) analyses the linear
algebraic structure that lets a single vector encode several senses of a
word. Levy and Goldberg (2014) proves that negative sampling factorises
a shifted PMI matrix, and Levy et al. (2015) shows that much of
word2vec’s reported advantage over counting was down to hyperparameters
rather than method. The implementation most people train with is
gensim’s Word2Vec, whose arguments are this
chapter’s choices under other names, and in any framework the two update
rules above are what a single call to backward()
produces.
Build the toolkit. Start from a trained embedding space. PPMI plus SVD over a corpus of your choice works well. Implement three functions: cosine similarity, nearest neighbours, and analogy solving. Make sure your analogy function excludes the three query words from the answer. Omitting that exclusion is the most common way to overstate performance.
The exclusion trap, measured. Run twenty analogies twice, once with the query words excluded and once without. Report how much the score improves when you cheat. Which query word wins most often, and why is it usually rather than or ?
Bend the sampling distribution. Using Equation (9.12), recompute the five word table in this chapter with exponents , and . At which exponent does aardvark get sampled ten times its share? What would go wrong at each extreme?
Embedding forensics. Take five word vectors, add a little random noise to each, and identify the original words using only cosine and nearest neighbours. Report how much noise your identification survives, and which words are easiest and hardest to recover.
Audit the analogies. Run twenty analogy queries of your own and record which succeed and which fail. Group the failures and propose an explanation for each group: frequency effects, polysemy, a missing relation direction. Analogies are honestly hit or miss. Your job is to say when and why.
Derive the shift. Starting from Equation (9.24), differentiate with
respect to
and recover Equation (9.26). Then state what
happens to the target as
grows, and why
makes the shift disappear. Verify one row of the table numerically with
factorisation.py --sgns --k 1.
Break GloVe on purpose. Build a small co-occurrence matrix whose two word groups share no context. Fit GloVe to it and report the nearest neighbours. Explain, using , why the groups are not separated. Then add a single count to every empty cell and report what changes. Which of SVD and word2vec would you expect to survive the original matrix, and why?
Measure a bias. Choose two target sets and two attribute sets, for example two families of names against two families of adjectives. Compute a WEAT score with Equation (9.29). Interpret its sign and magnitude, and state one thing the number does not license you to conclude.
Autograd will derive your update rules. Build the
single training step of this chapter in torch: a centre
vector at
for cat, context vectors for sat and the, and
the negative sampling objective over one positive pair and one negative
pair. Call backward() and take one step at
.
You should land on
,
and a centre vector at
,
all three of which this chapter computes by hand. If your context
vectors move and your centre vector does not, you have implemented
Equation (9.13)
and forgotten Equation (9.14).
Each one reproduces the tables above. Open it in Colab, change an input and watch which numbers move.
word2vec.pyword2vec, one step at a time
The 0.75 exponent, a single training step worked in full, and the analogy with its trap.
standard library only Run it in Colab Notebook Sourcefactorisation.pyThree methods, one matrix
SVD, word2vec and GloVe factorising the same count matrix, and disagreeing about it.
pip install numpy Run it in Colab Notebook SourceFrom Natural Language Processing: From Corpus Statistics to Grounded LLMs, by Ramaseshan Ramachandran.