Where we left off
Last week: a word is its row of PPMI weights over contexts. Two rows:
espressocappuccinocup01.57brew1.920morning01.51shot2.270foam03.32roast2.300screen00⋯⋯⋯
The rest of each row is all zeros, and the two rows have no nonzero column in common. Three problems:
- Blind: no shared context ⇒ cosine =0, even for near-synonyms
- Isolated: every context is its own axis, so car and automobile never meet
- Huge: ∣C∣≈50,000 dimensions, but models want ≈300
Rotating can’t change angles
Goal: a new representation where espresso and cappuccino have a smaller angle.
Cheapest try: keep all ∣C∣ dimensions and just switch to a new orthonormal basis Q (a rotation):
(Q⊤a)⋅(Q⊤b)=a⊤QQ⊤b=a⋅b
- Every dot product and every length stays the same ⇒ every cosine stays the same. Still 0.
- So keeping every dimension can’t help. To change angles, we have to throw some directions away.
- Two questions: does throwing away really bring vectors closer, and which directions should go?
Minimize the loss ⇒ land perpendicular
Expand the loss, using ∥u∥=1:
lossw(t)=(vw−tu)⋅(vw−tu)=∥vw∥2−2t(vw⋅u)+t2
- Set the derivative to 0: −2(vw⋅u)+2t=0⇒t∗=vw⋅u
- At t∗, the drop is perpendicular: (vw−t∗u)⋅u=vw⋅u−t∗=0. Not a coincidence.
- Espresso: t∗=(1,0)⋅2(1,1)=21, landing at t∗u=(0.5,0.5) ✓
- Plug t∗ back in: the smallest loss is ∥vw∥2−(vw⋅u)2, so ∥vw∥2=kept(vw⋅u)2+lostlossw(t∗)
Which line? Lose the least
Landing perpendicularly splits each word’s squared length into kept (shadow) and lost (dashed):
∥vw∥2=kept(vw⋅u)2+lost(dashed length)2
The total is fixed (2+1+1=4), so keep the most = lose the least.
| brew axis |
1 |
1 |
0 |
2 |
2 |
| diagonal |
2 |
1/2 |
1/2 |
3 |
1 |
- Each cell is a word’s kept part, (vw⋅u)2.
- The rule never looks at espresso vs. cappuccino. The diagonal wins because coffee, the longest point (2), lies on it.
Project and compare
Project every row onto the diagonal u=(1,1)/2:
coffee↦2,espresso↦21,cappuccino↦21
- Same sign on a 1-D line ⇒ cos(espresso,cappuccino) goes from 0 to 1.
- The similarity is borrowed from coffee: one word that uses both contexts (exactly lie on the diagonal)
What is rank?
A matrix has rank 1 when every row is a multiple of one row: one pattern, rescaled.
360120rows=1⋅(3,1),2⋅(3,1),0⋅(3,1)
- To store it, you only need the pattern (3,1) and the scales (1,2,0)
- Rank k = the matrix is a sum of k such patterns, and no fewer
The outer product builds patterns
uv⊤,(uv⊤)ij=uivj120(31)=360120
- v = the pattern: what every row looks like
- u = the weights: how much of the pattern each row gets
- Every rank-1 matrix is an outer product, and every outer product has rank 1
- Compare with the inner product u⋅v: it sums the index away and returns a scalar
Practice
- What is the rank of A=12−136−3?
- Write it as uv⊤.
- Rank 1. Every row is a multiple of (1,3).
- u=(1,2,−1)⊤ and v⊤=(1,3)
- Scaling u by c and v by 1/c gives the same matrix, so the factorization is not unique.
SVD: the theorem
Every real matrix M∈Rm×n factors as
M=UΣV⊤
- U, V: orthogonal (their columns are unit length and mutually perpendicular)
- Σ: diagonal, with σ1≥σ2≥⋯≥0 (the singular values)
Read Mx=U(Σ(V⊤x)) from right to left:
| V⊤ |
rotate the input space to a new basis |
| Σ |
stretch each new axis by σi |
| U |
rotate into the output space |
SVD, geometrically
Apply A=(3405) to the unit circle, one factor at a time:
- V⊤: rotate
- Σ: stretch
- U: rotate
Red/green: v1,v2 end up as the ellipse’s axes σ1u1,σ2u2, with σ1=35≈6.71 and σ2=5≈2.24.
SVD as a sum of rank-1 pieces
Read UΣV⊤ one column at a time:
M=σ1u1v1⊤+σ2u2v2⊤+⋯+σrurvr⊤
| vi |
the i-th pattern over contexts |
| ui |
how much each word uses that pattern |
| σi |
how important the pattern is (sorted) |
Running SVD on our PPMI matrix
U, s, Vt = np.linalg.svd(M, full_matrices=False)
print("sigma =", s)
print("U =\n", U)
print("Vt =\n", Vt)
sigma = [2.127 2.016 0.292 0.083]
U =
[[ 0. 0.697 -0.717 0. ]
[ 0. 0.717 0.697 0. ]
[ 0.681 0. 0. -0.733]
[ 0.733 0. 0. 0.681]]
Vt =
[[ 0.003 0. -0. 0.696 0.718 0. ]
[ 0. 0.604 0.645 0. 0. 0.468]
[ 0. 0.414 0.247 0. 0. -0.876]
[-0.07 0. -0. -0.716 0.694 0. ]]
Pattern 1: “device-ness”
Term i adds σi⋅ui[w]⋅vi[c] to cell (word w, context c). Highlighted: σ1, column 1 of U, row 1 of V⊤.
U (words)
|
|
1
|
2
|
3
|
4
|
|
coffee
|
0.00
|
0.70
|
−0.72
|
0.00
|
|
tea
|
0.00
|
0.72
|
0.70
|
0.00
|
|
laptop
|
0.68
|
0.00
|
0.00
|
−0.73
|
|
tablet
|
0.73
|
0.00
|
0.00
|
0.68
|
σ
|
|
|
|
1
|
2.13
|
|
2
|
2.02
|
|
3
|
0.29
|
|
4
|
0.08
|
Vᵀ (contexts)
|
|
the
|
cup
|
brew
|
screen
|
battery
|
morning
|
|
1
|
0.00
|
0.00
|
0.00
|
0.70
|
0.72
|
0.00
|
|
2
|
0.00
|
0.60
|
0.65
|
0.00
|
0.00
|
0.47
|
|
3
|
0.00
|
0.41
|
0.25
|
0.00
|
0.00
|
−0.88
|
|
4
|
−0.07
|
0.00
|
0.00
|
−0.72
|
0.69
|
0.00
|
- Large in u1: laptop, tablet. Large in v1: screen, battery ⇒ term 1 fills the device block: a topic.
- e.g. (laptop, screen): 2.13×0.68×0.70≈1.01 (observed 1.05)
Pattern 2: “beverage-ness”
Term i adds σi⋅ui[w]⋅vi[c] to cell (word w, context c). Highlighted: σ2, column 2 of U, row 2 of V⊤.
U (words)
|
|
1
|
2
|
3
|
4
|
|
coffee
|
0.00
|
0.70
|
−0.72
|
0.00
|
|
tea
|
0.00
|
0.72
|
0.70
|
0.00
|
|
laptop
|
0.68
|
0.00
|
0.00
|
−0.73
|
|
tablet
|
0.73
|
0.00
|
0.00
|
0.68
|
σ
|
|
|
|
1
|
2.13
|
|
2
|
2.02
|
|
3
|
0.29
|
|
4
|
0.08
|
Vᵀ (contexts)
|
|
the
|
cup
|
brew
|
screen
|
battery
|
morning
|
|
1
|
0.00
|
0.00
|
0.00
|
0.70
|
0.72
|
0.00
|
|
2
|
0.00
|
0.60
|
0.65
|
0.00
|
0.00
|
0.47
|
|
3
|
0.00
|
0.41
|
0.25
|
0.00
|
0.00
|
−0.88
|
|
4
|
−0.07
|
0.00
|
0.00
|
−0.72
|
0.69
|
0.00
|
- Large in u2: coffee, tea. Large in v2: cup, brew, morning ⇒ the beverage block: another topic.
- e.g. (tea, brew): 2.02×0.72×0.65≈0.93 (observed 0.98)
Pattern 3: coffee vs. tea
Term i adds σi⋅ui[w]⋅vi[c] to cell (word w, context c). Highlighted: σ3, column 3 of U, row 3 of V⊤.
U (words)
|
|
1
|
2
|
3
|
4
|
|
coffee
|
0.00
|
0.70
|
−0.72
|
0.00
|
|
tea
|
0.00
|
0.72
|
0.70
|
0.00
|
|
laptop
|
0.68
|
0.00
|
0.00
|
−0.73
|
|
tablet
|
0.73
|
0.00
|
0.00
|
0.68
|
σ
|
|
|
|
1
|
2.13
|
|
2
|
2.02
|
|
3
|
0.29
|
|
4
|
0.08
|
Vᵀ (contexts)
|
|
the
|
cup
|
brew
|
screen
|
battery
|
morning
|
|
1
|
0.00
|
0.00
|
0.00
|
0.70
|
0.72
|
0.00
|
|
2
|
0.00
|
0.60
|
0.65
|
0.00
|
0.00
|
0.47
|
|
3
|
0.00
|
0.41
|
0.25
|
0.00
|
0.00
|
−0.88
|
|
4
|
−0.07
|
0.00
|
0.00
|
−0.72
|
0.69
|
0.00
|
- Mixed signs: coffee −, tea + in u3; cup +, morning − in v3. The signs multiply:
- (coffee, morning): (−)(−)⇒+0.18. (tea, morning): (+)(−)⇒−0.18
- Not a new topic: a difference inside the beverage topic (coffee goes with morning more than tea does).
Pattern 4: laptop vs. tablet
Term i adds σi⋅ui[w]⋅vi[c] to cell (word w, context c). Highlighted: σ4, column 4 of U, row 4 of V⊤.
U (words)
|
|
1
|
2
|
3
|
4
|
|
coffee
|
0.00
|
0.70
|
−0.72
|
0.00
|
|
tea
|
0.00
|
0.72
|
0.70
|
0.00
|
|
laptop
|
0.68
|
0.00
|
0.00
|
−0.73
|
|
tablet
|
0.73
|
0.00
|
0.00
|
0.68
|
σ
|
|
|
|
1
|
2.13
|
|
2
|
2.02
|
|
3
|
0.29
|
|
4
|
0.08
|
Vᵀ (contexts)
|
|
the
|
cup
|
brew
|
screen
|
battery
|
morning
|
|
1
|
0.00
|
0.00
|
0.00
|
0.70
|
0.72
|
0.00
|
|
2
|
0.00
|
0.60
|
0.65
|
0.00
|
0.00
|
0.47
|
|
3
|
0.00
|
0.41
|
0.25
|
0.00
|
0.00
|
−0.88
|
|
4
|
−0.07
|
0.00
|
0.00
|
−0.72
|
0.69
|
0.00
|
- Mixed signs again: laptop −, tablet +; screen −, battery + ⇒ a difference inside the device topic.
- Tiny σ4, tiny effect: (tablet, battery) gets only +0.04.
- Large σ: topics. Small σ: distinctions within a topic. Nobody supplied these categories; the factorization found them.
Question
- u3=(−0.72,+0.70,0,0) has opposite signs on coffee and tea.
- What would it mean if u3 were all non-negative instead?
- It would be a third topic: another group of words that share a pattern.
- With opposite signs, it is a difference inside an existing topic.
- The sign structure of u tells you which of the two you are looking at.
How big is a matrix? The Frobenius norm
Flatten the matrix into one long vector and take its length:
∥A∥F=∑i,jaij2
- Each piece has size exactly its singular value: ∥σiuivi⊤∥F=σi
- The pieces are mutually orthogonal, so squared sizes add.
∥M∥F2=∑iσi2
print(np.linalg.norm(M), np.sqrt(np.sum(s**2)))
2.946356663184046 2.9463566631840457
Practice
A matrix M has singular values σ=(6,3,2,1).
- What is ∥M∥F?
- What is the relative error ∥M−Mk∥F/∥M∥F at k=1 and k=2?
- What is the smallest k that keeps at least 90% of ∑σi2?
- 36+9+4+1=50≈7.07
- k=1: 14/50≈0.53. k=2: 5/50≈0.32
- k=1 keeps 36/50=72%; k=2 keeps 45/50=90%. So k=2.
The recipe
- Build the PPMI matrix M, of size ∣V∣×∣C∣
- Compute M=UΣV⊤
- Choose k
- Word vectors = rows of W=UkΣkα, with α∈[0,1]
print("variance kept by k:", np.cumsum(s**2) / np.sum(s**2))
W = U[:, :2] * s[:2] # k = 2, alpha = 1
print("word vectors (coffee, tea, laptop, tablet):\n", W)
variance kept by k: [0.521 0.989 0.999 1. ]
word vectors (coffee, tea, laptop, tablet):
[[0. 1.405]
[0. 1.446]
[1.448 0. ]
[1.558 0. ]]
- A big gap between σ2 and σ3 (about 7x): this matrix is basically two-dimensional
- In practice, α=0 (throwing the σ’s away) often works best. It’s a hack that works.
Toy example ↔ SVD: the pieces
The toy matrix (rows coffee, espresso, cappuccino; columns brew, cup) has this SVD:
M110101=U6261610−2121Σ(3001)V⊤21(1−111)
| kept line u1=(1,1)/2 |
v1, row 1 of V⊤ |
| dropped direction u2=(−1,1)/2 |
v2, row 2 of V⊤ |
| best line keeps 3, loses 1 |
σ12=3, σ22=1 |
| total ∑w∥vw∥2=4 |
∥M∥F2=σ12+σ22=4 |
Careful with names: the toy example’s u1,u2 live in context space, so SVD calls them v1,v2. SVD’s ui are the word-side weights.
Toy example ↔ SVD: the steps
| Rotate into (u1,u2): coffee (2,0), espresso (21,−21), cappuccino (21,21) |
rows of UΣ=MV |
| Drop u2: landing points t∗=2,21,21 |
truncate to k=1: word vectors U1Σ1 |
| One number per word ⇒ rank 1: rows (1,1),(21,21),(21,21) |
M1=σ1u1v1⊤ |
| Which line? Lose the least: loss 1 |
Eckart–Young: ∥M−M1∥F2=σ22=1 |
- Trying lines by hand found the top singular vector. SVD finds it directly, and Eckart–Young says no line does better.
- The PPMI example is the same procedure with 6 contexts and k=2.