14

Embeddings: meaning as geometry

How does a machine turn meaning into distances?

11 minExecutive path

Embeddings: meaning as geometry

With voice. You can pause and jump to any step.

Lesson complete

Subtitles

A customer writes “I want my money back,” and the right article talks about a “refund.” They share not one word. How can a machine see that they mean the same thing?

Voice

No voice: the lesson advances on its own with subtitles.

The voice for this lesson is being recorded: for now it advances on its own with subtitles.

0:00 / 0:00

What you will achieve

  • Explain why a dense, learned vector represents meaning better than one slot per word (one-hot).
  • Compute the cosine similarity between two vectors and interpret neighbors, 2D maps, and meaning arithmetic, with their limits.
  • Recognize how the biases in text travel into vector space, and how to evaluate a semantic search with your own cases.

Key ideas

  1. An embedding is a dense, learned vector: a row of a table tuned by data, instead of a fixed slot per word.
  2. What shows up in similar contexts ends up with similar vectors: meaning becomes angle and distance.
  3. Cosine similarity compares directions, not lengths. A 2D map is a projection and loses information: use it to explore, not to measure.
  4. Meaning arithmetic is real but fragile: the input words must be excluded, and it works best on clean relations, such as country and capital.
  5. Vectors inherit the biases of the text. Semantic search rescues paraphrases, and its quality is measured with your own cases.

What many people believe

Common belief “King − man + woman = queen” proves that the model understands what royalty and gender are.

What is true It is a real geometric regularity, but a fragile one: it only comes out when the input words are excluded, it fails on many relations, and the same vectors carry the biases of the text. Measuring is not understanding.

Check yourself

Four questions. No grade, no account: they just show whether the idea stuck.

ConceptWhy does a dense, learned vector represent meaning better than a one-hot vector?
CalculationTwo vectors are a = (3, 4) and b = (4, 3). What is their cosine similarity?
ConceptA model trained on web text places “nurse” closer to the female words than “engineer.” What is the best explanation?
ApplicationYour team wants a semantic search for support, and three vendors promise to “understand meaning.” What do you do first?

Under the hoodFor those who want the math and the code. You do not need it to follow the course.

An embedding is a row of a matrix

Let VV be the vocabulary size and dd the dimension of the vectors. The table is a matrix E∈RV×dE\in\mathbb{R}^{V\times d}. The word with index ii is the one-hot vector xi∈{0,1}Vx_i\in\{0,1\}^V (a one at position ii, zeros elsewhere) and its embedding is

ei=xi⊤E=Ei,:e_i = x_i^{\top}E = E_{i,:}

Multiplying by a one-hot equals picking a row: it is a linear layer with no activation (lesson 7) whose V⋅dV\cdot d weights are learned. With V=400,000V=400{,}000 and d=100d=100 that is 40,000,00040{,}000{,}000 numbers. In practice nothing is multiplied: the row is looked up. The same mechanism opens language models: in the smallest GPT-2 the table is 50,257×768≈38.650{,}257\times 768\approx 38.6 million parameters (lessons 20 and 22).

Cosine similarity

cos⁡(a,b)=a⋅b∥a∥ ∥b∥=cos⁡θ∈[−1,1]\cos(a,b)=\frac{a\cdot b}{\lVert a\rVert\,\lVert b\rVert}=\cos\theta\in[-1,1]

If both vectors have length 1, cos⁡(a,b)=a⋅b\cos(a,b)=a\cdot b and ∥a−b∥2=2−2cos⁡(a,b)\lVert a-b\rVert^2=2-2\cos(a,b): ranking by cosine and ranking by Euclidean distance gives the same order. Example: a=(3,4)a=(3,4) and b=(4,3)b=(4,3) give cos⁡=12+125⋅5=0.96\cos=\frac{12+12}{5\cdot 5}=0.96, an angle of about 16∘16^\circ. With cos⁡=0.88\cos=0.88 (“dog” and “cat”) the angle is arccos⁡0.88≈28∘\arccos 0.88\approx 28^\circ.

Where do the numbers come from?

Skip-gram (Mikolov et al., 2013) maximizes the average probability of the neighbors of each word wtw_t, with a window of size cc:

1T∑t=1T  ∑−c≤j≤c,  j≠0log⁡p(wt+j∣wt),p(o∣i)=exp⁡ ⁣(vo′⊤ vi)∑w=1Vexp⁡ ⁣(vw′⊤ vi)\frac1T\sum_{t=1}^{T}\;\sum_{-c\le j\le c,\;j\ne 0}\log p(w_{t+j}\mid w_t),\qquad p(o\mid i)=\frac{\exp\!\big(v'^{\top}_{o}\,v_i\big)}{\sum_{w=1}^{V}\exp\!\big(v'^{\top}_{w}\,v_i\big)}

Each word has an input vector vv (its embedding) and an output vector v′v'. The denominator runs over the whole vocabulary, which is why the paper proposes approximations such as negative sampling. GloVe (Pennington et al., 2014) instead fits

J=∑i,j=1Vf(Xij) (wi⊤w~j+bi+b~j−log⁡Xij)2,f(x)={(x/100)3/4x<1001otherwiseJ=\sum_{i,j=1}^{V} f(X_{ij})\,\big(w_i^{\top}\tilde w_j+b_i+\tilde b_j-\log X_{ij}\big)^2,\qquad f(x)=\begin{cases}(x/100)^{3/4}&x<100\\ 1&\text{otherwise}\end{cases}

where XijX_{ij} counts how often jj appears near ii in the corpus, trained with AdaGrad. Two roads, one idea: words that share contexts end up with similar vectors.

The arithmetic, step by step

The question “aa is to a∗a^* as bb is to ...” is answered with

x∗=arg⁡max⁡x∉{a, a∗, b} cos⁡ ⁣(x,  a∗−a+b)x^{*}=\arg\max_{x\notin\{a,\,a^*,\,b\}}\ \cos\!\big(x,\;a^*-a+b\big)

With length-1 vectors, ∥a∗−a+b∥\lVert a^*-a+b\rVert does not depend on xx, so this equals maximizing cos⁡(x,a∗)−cos⁡(x,a)+cos⁡(x,b)\cos(x,a^*)-\cos(x,a)+\cos(x,b). The exclusion is not a detail: Linzen (2016) measured that, without it, the closest word was bb in 93% of cases and a∗a^* in 5%. In this lesson, with GloVe’s 100 numbers and choosing among its 400,000 words:

Questions With exclusion Without exclusion The closest was one of the 3 inputs
Capital–country (342) 80.1% (274) 68.1% 19.3%
Gender (90) 85.6% (77) 55.6% 41.1%

I picked the pairs, and they are clean relations; the Mikolov et al. set (19,544 questions, 14 types, counting only the exact word, not synonyms) is harder: their best model reached 65.6%. With only the lesson’s 880 words the exam is far easier (94.4% and 85.6%): there are fewer candidates to get wrong. Linzen adds that the method mixes the consistency of the offset with neighborhood structure: it does not measure “relations” alone.

How the bias is measured in the lesson

With FF = {woman, girl, mother, wife, sister, daughter} and MM = {man, boy, father, husband, brother, son}, the score of a word ww is

s(w)=1∣F∣∑f∈Fcos⁡(w,f)  −  1∣M∣∑m∈Mcos⁡(w,m)s(w)=\frac{1}{|F|}\sum_{f\in F}\cos(w,f)\;-\;\frac{1}{|M|}\sum_{m\in M}\cos(w,m)

Positive means closer to the female words. Sets are used rather than a pair because “man” has several senses. It is a simplified version of the association tests of Caliskan et al. (2017). Result over the 52 professions in the vocabulary: from −0.16-0.16 (“boss”) to +0.10+0.10 (“receptionist”); 26 objects with no gender (table, tree, river…) fall between −0.05-0.05 and +0.03+0.03. The Spearman correlation with the “she” − “he” score in the full GloVe is 0.91. The score mixes gender with topic (in the text, the female words show up more next to “dress” or “soap”): it shows the slope, it is not a verdict about any person. If each vector has its component along g=Fˉ−Mˉg=\bar F-\bar M (normalized) subtracted, the score on that axis is 0 by construction; even so, guessing whether each profession was above or below the median of the original score, with the nearest cosine centroid and leaving one out at a time, gets 41 of 52 right before and 35 of 52 after (chance: 26). It is a minimal experiment, with few words, in the spirit of Gonen and Goldberg (2019), who showed that methods for “debiasing” mostly hide the bias rather than remove it.

Semantic search

The average of a text with words w1,…,wnw_1,\dots,w_n is q=1n∑kewkq=\frac1n\sum_k e_{w_k}; documents are ranked by cos⁡(q,Dj)\cos(q,D_j). Comparing against NN documents costs O(N⋅d)O(N\cdot d) per query, and with millions of them approximate indexes are used. Sentence vectors (Reimers and Gurevych, 2019) are computed once per document: finding the most similar pair among 10,000 sentences with BERT takes about 50 million inferences (about 65 hours); with sentence vectors, about 5 seconds. The third column of the scene uses paraphrase-multilingual-MiniLM-L12-v2 (Apache-2.0, 384 dimensions, more than 50 languages; Reimers and Gurevych, 2020), with the mean of the output vectors and cosine. Its scores were computed offline for the 36 test questions.

With 36 questions, 21 hits (the vector average on the English questions) are 58.3% with a 95% Wilson interval of 42% to 73%: a margin of about ±15 points. That is why lesson 34 insists on sample size.

In code

With toy vectors (invented numbers) the whole mechanism is visible:

import numpy as np

# Toy embedding table: one row per word, 3 numbers each (invented; a real model uses hundreds).
words = ["king", "queen", "man", "woman", "bread"]
E = np.array([
    [4.0, 3.0,  1.0],   # king
    [4.0, 0.5, -2.0],   # queen
    [4.0, 0.0,  1.0],   # man
    [4.0, 0.0, -1.0],   # woman
    [0.0, 0.5,  3.0],   # bread
])
E = E / np.linalg.norm(E, axis=1, keepdims=True)   # length 1: the dot product is the cosine
row = {w: i for i, w in enumerate(words)}

def cosine(a, b):
    return float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))

def closest(v, exclude=()):
    pairs = [(w, round(cosine(v, E[i]), 2)) for w, i in row.items() if w not in exclude]
    return sorted(pairs, key=lambda p: -p[1])[:3]

print(round(cosine(E[row["king"]], E[row["queen"]]), 2))          # king and queen
v = E[row["king"]] - E[row["man"]] + E[row["woman"]]               # king - man + woman
print(closest(v))                                                   # without excluding
print(closest(v, exclude=["king", "man", "woman"]))                 # excluding the three inputs
# 0.68
# [('king', 0.89), ('queen', 0.87), ('woman', 0.81)]
# [('queen', 0.87), ('bread', -0.18)]

And with the real GloVe vectors (the file glove.6B.100d.txt comes from the Stanford page, see the sources; reading it takes a few seconds):

import numpy as np

words, rows = [], []
with open("glove.6B.100d.txt", encoding="utf8") as f:
    for line in f:
        w, _, rest = line.partition(" ")
        words.append(w)
        rows.append(np.asarray(rest.split(), dtype=np.float32))
E = np.stack(rows)
E /= np.linalg.norm(E, axis=1, keepdims=True)   # length 1: the dot product is the cosine
idx = {w: i for i, w in enumerate(words)}

def closest(v, k=5, exclude=()):
    s = E @ (v / np.linalg.norm(v))
    for w in exclude:
        s[idx[w]] = -1
    top = np.argsort(-s)[:k]
    return [(words[i], round(float(s[i]), 2)) for i in top]

print(closest(E[idx["dog"]], 5, exclude=["dog"]))
v = E[idx["king"]] - E[idx["man"]] + E[idx["woman"]]
print(closest(v, 3))
print(closest(v, 3, exclude=["king", "man", "woman"]))
# [('cat', 0.88), ('dogs', 0.83), ('pet', 0.74), ('puppy', 0.72), ('horse', 0.71)]
# [('king', 0.84), ('queen', 0.77), ('monarch', 0.68)]
# [('queen', 0.77), ('monarch', 0.68), ('throne', 0.68)]

Where this lesson’s data come from

Vectors: GloVe 6B with 100 dimensions (Wikipedia 2014 and Gigaword 5, 6 billion words, 400,000 vocabulary, English only), published by Stanford under the Public Domain Dedication and License v1.0, which allows redistribution. They are normalized to length 1 and stored in 8 bits (maximum cosine error 0.0095). The map is t-SNE (van der Maaten and Hinton, 2008) with perplexity 20, cosine metric, and seed 0: Wattenberg et al. (2016) warn that in a t-SNE the size of clusters and the distance between them may mean nothing. The 10 support articles and the 36 test questions were written by the lesson’s author: they are illustrative. Everything can be regenerated with the scripts in the lesson folder (build-vectors.py, eval-search.py, eval-analogies.py, measure-data.py).

What comes next

The vectors in this lesson are static: one word, one vector. Attention (lesson 21) and the transformer (lesson 22) produce a different vector for each word depending on the sentence. Lesson 15 asks whether, besides representing what exists, a network can generate something new; lesson 18 puts image and text vectors in one shared space, and lesson 31 uses semantic search to give a model knowledge of its own.

Decision lens

What this idea means if your job is to decide, not to code.

  • Semantic search and vector databases: every document becomes a vector once, and every question on the spot. Ask which embedding model is used, how many dimensions, in which languages, and under what license: if the model changes, the whole index must be recomputed.
  • Measure quality with your own cases: a few dozen real questions, each with its correct document, and count how often it comes first or among the top five. With thirty-six cases the margin is about fifteen points; with more, the comparison sharpens.
  • Embeddings inherit the biases of the text. If they rank people (résumés, candidates, customers), ask how bias was measured on data from that use; “we fixed it” is not enough, because removing one direction usually hides the bias rather than removing it.
  • Warning signs: a 2D map presented as proof of quality, a demo with hand-picked questions, the phrase “it understands meaning” with no measured results, or an English model applied to Spanish texts without checking it.

Terms in this lesson

Full transcript
CommentsThis lesson’s public conversation: questions, facts to check, ideas.

Comments live in GitHub Discussions, in the course’s public repository; writing one requires a GitHub account. They load from giscus.app when you open this section. The course reviewers read them, and if a change is accepted, it is applied in both languages. Open the thread on GitHub

Sources