Chapter 3 · Applied Natural Language Processing
pump, pumps and impeller are three mutually orthogonal directionspump and pumps are separate features: train on the pump failed, test on the pumps failed, and the weight learned for pump does not carry over to pumps by itselfIn linear-algebra terms: multiply the one-hot vectors by a learned matrix. Each word gets 100 or 300 numbers, and we aim for words used in similar ways to end up close. That short, dense vector is an embedding.
| Kind | Example | Does a word get the same vector everywhere? |
|---|---|---|
| Static word embedding | word2vec, GloVe, fastText | Yes — one vector per word form |
| Contextual token embedding | BERT | No — each token’s vector is computed from its context in the sequence; one word can be several tokens |
| Sentence / document embedding | SBERT and similar | One vector for the whole sentence or document |
This chapter is about static word embeddings, with a short introduction to document vectors. Contextual representations belong to Chapter 7, and the use of sentence and document representations in retrieval to Chapter 19.
“You shall know a word by the company it keeps.” — J.R. Firth, 1957
That makes it possible to learn representations from large unlabelled corpora.
| eats | rules | royal | sweet | the | |
|---|---|---|---|---|---|
king |
1 | 1 | 0 | 0 | 4 |
queen |
1 | 1 | 1 | 0 | 3 |
apple |
2 | 0 | 0 | 1 | 0 |
banana |
2 | 0 | 0 | 1 | 0 |
king ≈ queen and apple ≈ banana: the hypothesis is already workingking row is the. Raw counts are affected by the association and also by how frequent the words are overall.\[\mathrm{PMI}(w, c) = \log \frac{P(w, c)}{P(w)\,P(c)}\]
\[\mathrm{PPMI}(w, c) = \max\!\left(0,\ \log \frac{c_{wc}\, N}{c_{w\ast}\, c_{\ast c}}\right)\]
All four numbers are read off \(C\): the pair count \(c_{wc}\), the row total \(c_{w\ast}\), the column total \(c_{\ast c}\) and \(N = 144\) — not the 51 tokens but all (word, neighbour) pairs.
| pair | observed | expected | ratio | PMI |
|---|---|---|---|---|
apple–sweet |
1 | \(7 \times 6 / 144 = 0.29\) | 3.43 | 1.23 |
king–the |
4 | \(9 \times 22 / 144 = 1.38\) | 2.91 | 1.07 |
Two words each occur 10 times in a million-token corpus and never co-occur. Why clip the negative PMI values instead of keeping them?
king → queen 0.55. But apple → man 0.79: the two rows carry high weights in the an and eats columns, and man occurs in a single sentence\[\mathrm{PPMI} \approx U_d \Sigma_d V_d^\top, \qquad \text{embedding} = U_d \Sigma_d\]
| word pair | PPMI rows, 19-D | SVD, 5-D | what happened |
|---|---|---|---|
apple – banana |
0.40 | 1.00 | never share a sentence; shared contexts link them, and compression makes the link decisive |
king – queen |
0.55 | 0.90 | already right, and got stronger |
apple – man |
0.79 | 0.85 | the one-sentence accident — not deleted, but no longer first |
SVD does not break the wrong link; it strengthens the right one enough to overtake it. The ordering improves; the absolute values do not.
Deerwester et al. 1990 — latent semantic analysis, more than twenty years before word2vec
man and woman are close in this view; their contexts are not identicalWe set up the prediction task to learn vectors we can reuse. A transformer’s input embedding table (Chapter 7) works similarly; its rows belong to tokens.
One window on the pump failed during the night shift: centre failed, width 2.
the pump during the → average → failed
failed → its own vector → the pump during the
Both learn \(\mathbf{W}\) (word → vector) and \(\mathbf{W}'\) (scores partners); the pretrained vectors you download are mostly rows of \(\mathbf{W}\). The rest of the chapter follows skip-gram, which is also what negative sampling is most often paired with.
\[P(y = 1 \mid w, c;\, K, q) = \sigma(\mathbf{v}_w \cdot \mathbf{u}_c), \qquad \sigma(z) = \frac{1}{1 + e^{-z}}\]
\[\mathcal{L} = -\log \sigma(\mathbf{v}_w \cdot \mathbf{u}_c) - \sum_{k=1}^{K} \log \sigma(-\mathbf{v}_w \cdot \mathbf{u}_{n_k})\]
Mikolov et al. 2013
Four dimensions in place of 300. Centre failed: \(\mathbf{v} = (0.4,\ -0.2,\ 0.7,\ 0.1)\). Positive context pump: \(\mathbf{u} = (0.5,\ 0.1,\ 0.6,\ -0.3)\). Sampled negative opera: \(\mathbf{u} = (-0.3,\ 0.6,\ -0.2,\ 0.4)\).
| pair | \(y\) | \(\mathbf{v} \cdot \mathbf{u}\) | \(\sigma(\mathbf{v} \cdot \mathbf{u})\) | loss | \(\sigma - y\) |
|---|---|---|---|---|---|
failed–pump |
1 | 0.57 | 0.639 | 0.448 | −0.361 |
failed–opera |
0 | −0.34 | 0.416 | 0.538 | 0.416 |
failed·pump 0.570 → 0.590; failed·opera −0.340 → −0.359\[\frac{\partial \mathcal{L}}{\partial \mathbf{v}_w} = \bigl(\sigma(z) - y\bigr)\,\mathbf{u}_c, \qquad \frac{\partial \mathcal{L}}{\partial \mathbf{u}_c} = \bigl(\sigma(z) - y\bigr)\,\mathbf{v}_w\]
| choice | what it sets | typical value | effect |
|---|---|---|---|
| Number of negatives (\(K\)) | negatives per positive pair | 5–20 for a small corpus, 2–5 for a large one in the original work | changes the balance between cost and training signal |
| Context window | candidate neighbours and weighting by distance | for example at most 5, with a dynamic window | narrow windows can bring out functional relations more, wide ones topical relations |
| Subsampling (\(t\)) | probability of keeping very frequent tokens | for example \(t = 10^{-3}\) | reduces the share of frequent words in training |
| Noise distribution | probability of drawing each negative | \(q(w) \propto f(w)^{0.75}\) | raises the relative sampling share of rare words |
The update itself is simple. What decides quality: which pairs you show, how often, and against what.
| share of corpus | example | kept | discarded |
|---|---|---|---|
| 5% | the |
0.16 | 84% |
| 1% | of, and |
0.42 | 58% |
| 0.1% (\(= t\)) | mid-frequency | 1.00 | 0% |
| 0.01% and below | the tail | 1.00 | 0% |
In our run 26.6% of the in-vocabulary tokens go: 6,397,826 → 4,698,743.
Levy et al. (2015): hyperparameters of this kind can affect comparison results substantially.
NumPy, no framework; trained on the first 40 million characters of text8. The update step:
v = Wc[c] # (B, D)
u = Wo[targets] # (B, K+1, D)
score = sigmoid(np.einsum("bkd,bd->bk", u, v)) # (B, K+1)
err = (score - labels).astype(np.float32) # predicted − observed
gv = np.einsum("bk,bkd->bd", err, u)
gu = err[:, :, None] * v[:, None, :]
np.clip(gv, -1.0, 1.0, out=gv)
np.clip(gu, -1.0, 1.0, out=gu)
np.add.at(Wc, c, -lr * gv)
np.add.at(Wo, targets.ravel(), (-lr * gu).reshape(-1, D))err = score - labels is predicted minus observed; every gradient is that difference times the other vector. After three epochs: monday → friday 0.87, france → spain 0.79, physics → mechanics 0.82.
Levy and Goldberg (2014): how skip-gram with negative sampling relates to matrix factorization.
\[\mathbf{v}_w \cdot \mathbf{u}_c = \mathrm{PMI}(w, c) - \log K\]
Levy et al. (2015): with matched hyperparameters, on the tasks they examined, the performance gap largely closes.
Baroni et al. 2014 · Levy & Goldberg 2014 · Levy, Goldberg & Dagan 2015
Fix one pair and treat \(z = \mathbf{v}_w \cdot \mathbf{u}_c\) as a free number. If the negatives are drawn from the context marginal \(P(c)\), the pair’s contribution to the objective to be maximized is:
\[P(w,c)\,\log \sigma(z) \;+\; K\,P(w)P(c)\,\log \sigma(-z)\]
Set the derivative to zero, \(P(w,c)\bigl(1 - \sigma(z)\bigr) = K\,P(w)P(c)\,\sigma(z)\), and solve:
\[z = \log \frac{P(w,c)}{K\,P(w)P(c)} = \mathrm{PMI}(w,c) - \log K\]
The derivation assumes negatives drawn from \(P(c)\) and a \(z\) that can be chosen independently for every pair. In the actual model the dot products are built from shared vectors; SGNS minimizes a sampling-weighted logistic loss, PPMI–SVD the squared error of a clipped matrix. Both the losses and the targets can differ.
\[\mathcal{L} = \sum_{(w,c)\,:\,X_{wc} > 0} f(X_{wc}) \left(\mathbf{v}_w \cdot \mathbf{u}_c + b_w + b_c - \log X_{wc}\right)^2\]
\(f(x) = (x/x_{\max})^{3/4}\) for \(x < x_{\max} = 100\), else 1
| \(X_{wc}\) | \(f(X_{wc})\) |
|---|---|
| 1 | 0.03 |
| 10 | 0.18 |
| 100 | 1.00 |
| 100,000 | 1.00 |
Pennington et al. 2014
Pretrained GloVe: 400,000 vectors, 100 dimensions, trained on Wikipedia 2014 + Gigaword 5 (6 billion tokens).
| query | nearest neighbours | what it shows |
|---|---|---|
king |
prince 0.77, queen 0.75, son 0.70 | — |
pump |
pumps 0.84, pumping 0.77, pumped 0.71 | pumps is a neighbour, not an orthogonal axis |
turkey |
ankara 0.75, turkish 0.72, greece 0.71 | words about the country stand out |
python |
monty 0.69, php 0.59, perl 0.58, flipper 0.51 | neighbours from different usages together |
bank |
banks 0.81, banking 0.75, credit 0.70 | words about finance stand out |
The defining limitation of a static embedding: one vector per word form, whatever the context. That limitation is one of the important reasons for the context-dependent token representations of Chapter 7.
| word | cosine |
|---|---|
| queen | 0.7699 |
| monarch | 0.6843 |
| throne | 0.6756 |
| daughter | 0.6595 |
| princess | 0.6521 |
| word | cosine |
|---|---|
| king | 0.8361 |
| queen | 0.7699 |
| monarch | 0.6843 |
| throne | 0.6756 |
| daughter | 0.6595 |
gensim normalizes the inputs and removes them from the candidates. With the scores fixed, the unfiltered nearest neighbour is king; queen is first only without the inputs. The candidate set is part of the task.
mice − mouse + goose: the expected geese is third in the unfiltered list and second, behind cows, once the inputs are removedmadrid − spain + germany: munich comes ahead of berlin. The expected answer is not first; that does not prove the model carries no information about the capital relationTwo Wikipedia samples of comparable size:
| Turkish | English | |
|---|---|---|
| tokens | 1,458,213 | 1,828,840 |
| distinct word forms | 138,578 | 78,140 |
| forms seen after 1 million tokens | 112,644 | 52,347 |
| forms seen exactly once (hapax) | 50.0% | 46.7% |
| test tokens outside the training vocabulary | 7.33% | 4.72% |
ev, evim, evimde, evimdeki, evlerimizdekiler are five separate vocabulary entries. In the held-out Turkish portion about one token in fourteen falls outside the training vocabulary. The numbers belong to one sample and one tokenization choice.
\[\mathbf{v}_w = \mathbf{1}_{\{w \in \mathcal{V}\}}\,\mathbf{z}_w + \sum_{g \in G(w)} \mathbf{z}_g\]
<pu inside pump, pumps, pumped, pumping, punch … Not every piece is learned from plentiful data; the n-grams are mapped to a fixed number of hash bucketspumps, seen in training: <pu pum ump mps ps> <pumps>
pumpage, never seen: <pu pum ump mpa pag age ge> — word2vec: KeyError
Drawn with 3-grams so it stays readable; fastText’s default is every n-gram of length 3 to 6, so a real pumpage decomposes into 22 pieces, not 7.
Out-of-vocabulary words: two Turkish forms the Turkish Wikipedia sample never contained, then two typos. word2vec raises KeyError; fastText still gives a vector:
| word | fastText’s nearest neighbours |
|---|---|
kitaplarımızdaki |
kitapları 0.91, kitapların 0.91, kitaplıkları 0.90 |
bilgisayarcılık |
bilgisayara 0.96, bilgisayarlı 0.96, bilgisayar 0.96 |
philosphy |
philosophy 0.96, philosophie 0.95, philosophies 0.94 |
governmentt |
government 0.98, governments 0.94, governmental 0.93 |
Shared character pieces help with some typos as well: in these examples the out-of-vocabulary forms get vectors close to the expected words. That result is not guaranteed for every typo.
| query | fastText | word2vec |
|---|---|---|
kitap (book) |
kitapta 0.96, kitaba 0.95, kitab 0.94 | eseri 0.81, makale 0.80, eseriyle 0.80 |
yaz (summer; also “write”) |
yao 0.78, yağ 0.77, yağdı 0.77 | kış 0.86, olimpiyatları 0.84, aylarında 0.83 |
cosine of kitap–kitle |
0.667 | 0.446 |
kitap: inflections or semantic neighbours? It depends on your task. yaz: spelling neighbours come to the front in fastTextfastText is a strong candidate where out-of-vocabulary forms and inflection matter. Character sharing can also create unwanted closeness: evaluate by coverage and task performance.
Chapter 2’s four 20 Newsgroups categories, same split, same logistic regression on top:
| representation | accuracy | dimensions |
|---|---|---|
| TF-IDF (Ch. 2 baseline) | 0.853 | 32,014 |
| averaged GloVe | 0.820 | 100 |
| idf-weighted averaged GloVe | 0.822 | 100 |
| doc2vec (PV-DBOW, 40 epochs) | 0.839 | 100 |
| averaged GloVe + doc2vec | 0.845 | 200 |
On this split TF-IDF has the highest accuracy. In the paired test its gap to averaged GloVe is hard to explain by chance: 135 documents against 84, p = 0.0007.
The standard error of one accuracy (≈ 0.010) is not that of the difference between two models. The two GloVe averages: 66 against 63 (p = 0.86). No such calculation for the doc2vec rows: their predictions cannot be reproduced exactly.
np.allclose is True, the bit-level comparison is False (largest difference \(1.2 \times 10^{-7}\))Averaging does not keep the word order that determines which part failed: chapter 2’s bag-of-words problem, now in 100 dimensions. Word order needs a model that reads the sequence (Chapters 6 and 7).
| pair | cosine |
|---|---|
good – bad |
0.770 |
hot – cold |
0.725 |
increase – decrease |
0.881 |
cat – dog |
0.880 |
cat – car |
0.311 |
teacher – student |
0.808 |
hot and cold appear next to the same wordsDo not read the raw cosine of static vectors directly as a measure of sentiment or polarity.
4,000 unit vectors from the top 50,000 GloVe words
| statistic | value |
|---|---|
| mean cosine, random pairs | 0.029 |
| standard deviation | 0.119 |
| pairs with cosine > 0 | 56.9% |
| norm of the mean vector | 0.171 |
| mean cosine after centring | −0.000 |
Frequent words have longer vectors on average: mean norm 6.19 for the top 100 words, 3.62 for ranks 100,000–400,000.
| term | rank | nearest neighbours |
|---|---|---|
water |
430 | natural 0.70, dry 0.68, salt 0.68 |
turbine |
15,747 | turbines 0.80, compressor 0.78, generator 0.72 |
cavitation |
109,044 | aliasing 0.56, barotrauma 0.54, attenuation 0.54 |
impeller |
124,835 | armature 0.62, evaporator 0.61, nozzle 0.60 |
eutectic |
159,022 | azeotrope 0.61, azeotropic 0.55, tiwai 0.54 |
Evaluate domain terms separately. Neighbour lists are a good first check; choose the method by comparing pretrained, domain-adapted and subword representations with task metrics.
Same corpus, same settings, two seeds (text8, 6,794,527 tokens)
| measurement | value |
|---|---|
| same word, raw coordinates: mean cosine | −0.016 |
| after orthogonal (Procrustes) alignment | +0.951 |
| top-10 neighbour overlap, 1,000 most frequent | 62.2% |
| top-10 neighbour overlap, 1,000 rarest | 46.6% |
| largest cosine change under an orthogonal map | 5.5 × 10⁻⁷ |
Same corpus + different seed → geometrically different, functionally similar spaces. Do not argue from one neighbour list of one run.
Projection onto woman − man
| word | projection |
|---|---|
homemaker |
+0.363 |
nurse |
+0.329 |
receptionist |
+0.281 |
teacher |
+0.152 |
doctor |
+0.109 |
scientist |
−0.046 |
programmer |
−0.083 |
engineer |
−0.123 |
The sign shows the direction of the projection on the chosen axis; it does not measure all of social bias.
Bolukbasi et al. 2016 · Caliskan et al. 2017 · Gonen & Goldberg 2019
Google analogies: 0.633 overall
| category | accuracy |
|---|---|
| capital-common-countries | 0.939 |
| capital-world | 0.889 |
| nationality-adjective | 0.879 |
| family | 0.816 |
| city-in-state | 0.308 |
| opposite | 0.201 |
| currency | 0.152 |
| GloVe · 6 billion tokens | from scratch · 6.8 million tokens | |
|---|---|---|
| WordSim-353 | 0.533 · 0.0% skipped | 0.627 · 11.9% skipped |
| the 311 common pairs | 0.549 | 0.627 |
| Analogies, questions scored | 0.633 · 19,486 | 0.200 · 9,282 |
| Analogies, all 19,544 questions | 0.631 | 0.095 |
dummy4unknown=True) halves the small model’s scoredenar: in the vocabulary (rank 394,434), outside the default limit of 300,000Give the coverage and the number of examples scored together with the score. The direction of the effect of skipped examples depends on which examples are skipped.
| method | reach for it when | watch for |
|---|---|---|
| PPMI + SVD | you want to inspect the steps of the computation, or set up a count-based baseline on a modest vocabulary | sparse count matrix, low-rank factors and solver memory; no row for out-of-vocabulary forms |
| word2vec | you want to learn word vectors from a domain corpus — measuring against fastText | performance depends on sampling, settings and coverage; the final vectors alone are not enough to continue training |
| GloVe | you want a quick start with released vectors that cover the language and domain you need | training needs the co-occurrence data prepared; coverage of a released vocabulary varies with language and domain |
| fastText | the OOV rate, inflectional variety or spelling variability is high | the subword table needs extra memory; similar spelling can create unwanted closeness (kitap/kitle 0.667) |
Evaluate the coverage gain on task performance and hard negatives (the measurement in Chapter 4). For documents: on the single split of this experiment none of the dense methods beat the TF-IDF row; that finding does not cover every classification or retrieval task.
*2vec uses the same objective (Barkan & Koenigstein 2016)An embedding layer maps discrete identities to learnable continuous vectors. What is differentiated is not the identity itself but the vector parameters that stand for it.
| limitation | evidence from this chapter | common way to address or mitigate it |
|---|---|---|
| One static vector per form | python: neighbours from different usages |
representations computed from context (Ch. 7) |
| Unordered combination | same tokens, different order → same mean | sequence models (Ch. 6) |
| Vocabulary coverage | KeyError; OOV 7.3% on the Turkish sample |
subword representations |
| Similarity and oppositeness mixed | increase/decrease 0.881 |
evaluation with task-suited labels |
| Uneven directions | mean cosine 0.029 (top-50,000 sample) | testing centring on the task |
| Rare / out-of-domain words | neighbours of some domain terms | domain evaluation, adaptation |
| Social associations | homemaker +0.363 |
several metrics; testing interventions |
A static embedding is a type-level lookup table: word form → one vector. A contextual model computes the token’s representation from the surroundings it can access.
Your embedding has 300 dimensions. Why 300 and not 30 or 3,000?
Why negative sampling instead of a softmax over the vocabulary?
You have 50,000 support tickets in Turkish. word2vec or fastText?
kitap/kitle)Cosine similarity between two documents’ averaged vectors is 0.95. Are they about the same thing?
Should you fine-tune pretrained embeddings on your own corpus, or train from scratch?
KeyedVectors“Embeddings replaced TF-IDF.” True or false?
Three of the chapter’s twelve questions: one on the objective, one on the geometry, one on system design.
General preparation resources: Huyen 2021 and the AIMLInterviews repository
| resource | why |
|---|---|
| Mikolov et al. 2013a, 2013b | skip-gram and CBOW; then negative sampling and subsampling — read the second alongside the chapter’s code |
| Rong 2014 · Goldberg & Levy 2014 | the gradients derived step by step; the reasoning behind the negative-sampling objective |
| Levy & Goldberg 2014 · Levy et al. 2015 | the SGNS–PMI relation and the effect of hyperparameters |
| Bolukbasi et al. 2016, then Gonen & Goldberg 2019 | read together: the proposed operations, then an examination of what remains |
| Jurafsky & Martin, Chapter 5 | word representations in the August 19, 2026 draft; Appendix J for PPMI; free |
| Stanford CS224N | notes of the word-vectors lecture; the assignments change from year to year |
| Elena Voita, NLP Course For You | skip-gram and negative sampling, with visuals |
| fastText pretrained vectors | released vectors for 157 languages including Turkish |
Uygulamalı Doğal Dil İşleme · Applied NLP