Lab: Word2Vec’s Static Embedding Problem
Word2Vec and GloVe produce one vector per word. That is their strength — compact, reusable — and their fundamental weakness: a word with multiple meanings is forced into a single point in space.
This notebook uses tiny synthetic vectors to show the problem before we move to contextual models.
import numpy as np
def cosine(a, b):
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
# Hand-designed 4-dim vectors. 'bank' is forced to sit between two meanings.
embeddings = {
"river": np.array([1.0, 0.0, 0.0, 0.0]),
"stream": np.array([0.9, 0.1, 0.0, 0.0]),
"money": np.array([0.0, 1.0, 0.0, 0.0]),
"cash": np.array([0.1, 0.9, 0.0, 0.0]),
"bank": np.array([0.5, 0.5, 0.0, 0.0]), # one static compromise
"cat": np.array([0.0, 0.0, 1.0, 0.1]),
"dog": np.array([0.0, 0.0, 0.9, 0.2]),
}
1. Polysemy: one vector cannot be in two places at once
In a real corpus, bank appears near river and near money. Word2Vec averages the pressure and lands somewhere in between.
print("Similarity of 'bank' to other words:")
for w in ["river", "stream", "money", "cash", "cat", "dog"]:
print(f" {w:8s}: {cosine(embeddings['bank'], embeddings[w]):.3f}")
Similarity of 'bank' to other words:
river : 0.707
stream : 0.781
money : 0.707
cash : 0.781
cat : 0.000
dog : 0.000
bank ends up roughly similar to both river-words and money-words, but it is not a clean member of either cluster. More importantly, downstream layers receive the exact same vector for bank regardless of whether the sentence is about a river or a bank account.
2. Antonyms can look like synonyms
Words that appear in the same contexts get pushed together, even if their meanings are opposite. hot and cold often appear in the same slot (“it is ___ today”), so a static embedding can place them closer than intuition expects.
# Synthetic extreme: two words share a context slot but are opposites.
# They point in nearly the same direction, while 'river' points elsewhere.
embeddings["hot"] = np.array([0.20, 0.60, 0.05, 0.00])
embeddings["cold"] = np.array([0.18, 0.55, -0.03, 0.00])
print(f"cosine(hot, cold) = {cosine(embeddings['hot'], embeddings['cold']):.3f}")
print(f"cosine(hot, river) = {cosine(embeddings['hot'], embeddings['river']):.3f}")
cosine(hot, cold) = 0.991
cosine(hot, river) = 0.315
hot and cold are closer to each other than hot is to river, even though hot and river are not opposites. The model only knows usage patterns, not semantics.
Takeaway
Static embeddings are a useful first compression of distributional similarity, but they cannot represent context-dependent meaning. The next step — attention and Transformers — replaces the single vector with a representation that changes based on neighboring tokens.