結論:整份作業只建立在 4 個觀念上。這 4 個懂了,後面每一步都只是「把觀念寫成程式」。考試也最可能從這裡出題。
為什麼要先學這 4 個:後面每一節都會用到「詞向量」「相似度」「b − a + c」「前後文」這四個詞。先用小例子手算一次,後面看到 19,544 題的正確率才知道它是怎麼來的。
電腦不知道 cat 是什麼。它只會做數學。
所以我們幫每個英文字準備一串數字,例如 100 個數字。這串數字叫 詞向量(word embedding)。
可以把這 100 個數字想成這個字在「意思地圖」上的座標。平常的地圖只有 2 個數字(經度、緯度),這張地圖有 100 個數字,所以人畫不出來,但電腦算得出來。(「座標」只是比喻,後面一律叫詞向量。)
把每個字的詞向量收集起來,就是一本字典:查一個字,得到它的詞向量。
一份好的字典,會把 cat(貓)和 dog(狗)放得很近,把 cat 和 truck(卡車)放得很遠。
「多近」用相似度(cosine similarity,餘弦相似度)來量。它看的是兩個詞向量的「方向」有多一致。
| 相似度 | 意思 |
|---|---|
| 接近 1 | 方向幾乎一樣 → 很像 |
| 接近 0 | 方向垂直 → 沒什麼關係 |
| 負的 | 方向相反(注意:方向相反 ≠ 意思相反。反義詞的相似度可能很高,例如 good/bad 在 Wiki 20% 字典裡是 0.70,In [33],第 11 節) |
前提:真的詞向量有 100 個數字,算不動。所以這裡假設每個字只有 2 個數字(我自己編的):cat=(3, 4)、dog=(4, 3)、truck=(4, −3)。步驟跟 100 個數字時完全一樣,只是數字少。
⟦IMG:toy_cosine⟧
第 1 小步:乘起來再加總(叫「內積」)。兩個字的第 1 個數字相乘,加上第 2 個數字相乘。
cat 和 dog: 3×4 + 4×3 = 12 + 12 = 24
cat 和 truck:3×4 + 4×(−3) = 12 − 12 = 0
第 2 小步:算每個字的「長度」。兩個數字各自平方、加起來、開根號。
cat: √(3×3 + 4×4) = √25 = 5
dog: √(4×4 + 3×3) = √25 = 5
truck:√(4×4 + 3×3) = √25 = 5
第 3 小步:內積 ÷(兩個長度相乘)。
相似度(cat, dog) = 24 ÷ (5×5) = 0.96 → 很像
相似度(cat, truck)= 0 ÷ (5×5) = 0 → 沒關係
為什麼要除以長度:只想比「方向」,不想讓「數字比較大的字」佔便宜。除掉長度之後,結果一定在 −1~1 之間。
在相似度、鄰居、b − a + c 這些地方,本文說的「近」「最像」都是指方向(夾角小),不是兩點之間的距離。(例外:看 t-SNE/PCA 圖時——第 4、7 節、第 12 節實驗 ⑦、總結論第 1 條、第 14 節 Q17——說的「近」「黏在一起」「貼得很緊」,是圖上兩點的距離。)所以下面手算時,★ 跟 woman 位置不完全一樣,但方向幾乎一樣,相似度就接近 1。
這幾個數字由 make_toy_figures.py 算出來核對過。老師上課講的是同一個公式(W2 02:00:40「兩個向量之間算 cos 就是這個公式」)。
import numpy as np
cat, dog = np.array([3, 4]), np.array([4, 3])
print(cat @ dog / (np.linalg.norm(cat) * np.linalg.norm(dog))) # 0.96
# gensim 裡直接寫:model.similarity("cat", "dog")
報告第 4 題「找最像的 5 個字」,就是把一個字跟字典裡每個字都算一次相似度,取最高的 5 個。這 5 個字本文叫「鄰居」(只用在這個意思)。
這是整份作業最核心的一點。看這張圖:
從 man 走到 woman 的那一步(紅色箭頭),跟從 king 走到 queen 的那一步,方向和長度大致一樣(老師原話:「不是這麼 exactly 是一樣的 pattern,但是趨勢非常的 close」,W3 00:32:03)。四個點大約排成一個平行四邊形。
所以只要知道三個點,就能推出第四個點:
queen − king + man ≈ woman
(b) (a) (c) (d)
白話:「把 king 變成 queen 的那一步」,套到 man 身上,就會走到 woman。這個公式本文叫 b − a + c(論文叫 3CosAdd)。
注意:作業規格 PDF 第 2 頁寫成「King + Queen − Man ≈ Woman」,那是筆誤。正確是 b − a + c(程式模板的提示寫的才對)。
作業的考卷 19,544 題,每一題都是這樣:給 a、b、c,叫電腦算出 d。
前提:
⟦IMG:toy_analogy⟧
第 1 小步:做加減,算出一個新的點 ★。公式是 b − a + c,也就是 queen − king + man。兩個數字各自算:
第 1 個數字:3.2 − 1 + 1 = 3.2
第 2 個數字:4.1 − 4 + 1 = 1.1
所以 ★ = (3.2, 1.1)
圖上兩條紅色箭頭幾乎一樣長、一樣方向:「king 變 queen」和「man 變 woman」是同一步。從 man 走同一步,就走到 ★。
第 2 小步:算 ★ 跟每個候選字的相似度。照上面「手算相似度」的三小步做:
| 候選字 | ① 內積 | ② 長度 | ③ 相似度 |
|---|---|---|---|
| woman (3, 1) | 3.2×3 + 1.1×1 = 10.70 | ★ 3.384;woman 3.162 | 10.70 ÷ (3.384×3.162) = 1.000 |
| girl (3, 0.3) | 3.2×3 + 1.1×0.3 = 9.93 | ★ 3.384;girl 3.015 | 9.93 ÷ (3.384×3.015) = 0.973 |
| apple (−2, 3) | 3.2×(−2) + 1.1×3 = −3.10 | ★ 3.384;apple 3.606 | −3.10 ÷ (3.384×3.606) = −0.254 |
數字由 make_toy_figures.py 算出,四捨五入到小數點後 2~3 位。woman 的 1.000 是四捨五入後的值,實際比 1 小一點點。
第 3 小步:相似度最高的就是答案。woman 1.000 最高,所以電腦答 woman,答對。
king、queen、man 是題目給的字,規定不能拿來當答案,所以第 2 小步只算剩下的字。gensim 的 most_similar 會自動排除它們。
為什麼要排除:★ 是從 man 出發走一小步得到的,所以 ★ 有可能離 man 自己很近。這個玩具例子裡 man 的相似度是 0.899(比 woman 低,沒有搶走第一名);但在真的 100 維字典裡,★ 不會剛好落在 woman 上,題目自己的字就可能排到前面。
如果 a、b、c 有一個字典裡查不到(叫 OOV,查不到的字),第 1 小步就算不出來。這種題目在作業裡直接算答錯。
真的作業就是把這 3 小步,對 19,544 題每一題做一遍(候選字是字典收的所有字,Wiki 20% 字典約 30 萬個)。girl 0.973 也很接近 woman——在真的字典裡,「很像但不是答案」的字很多,所以不少題的正確答案排在第 2、3 名(第 12 節實驗 ④:前 3 名正確率比第 1 名多 13.14 個百分點)。
沒有人一個一個去設定詞向量。電腦是讀了大量文章(教材)之後,自己算出來的。
它根據一句很有名的話:「看一個字的朋友,就知道它是什麼字。」常常出現在同樣前後文的字,意思通常很像。
例如「我養了一隻__,牠很可愛」,空格可以填貓、狗、兔子。這些字前後文很像,所以詞向量會很近。
這個「讀教材、看前後文、算詞向量」的過程叫 訓練。作業用的方法叫 Word2Vec,裡面又分兩種:
| 方法 | 它在練習什麼 | 例子 |
|---|---|---|
| Skip-gram(老師投影片的重點) | 給中心字,猜前後文會出現哪些字 | 看到「貓」,猜前後可能有「養」「可愛」 |
| CBOW | 給前後文的字,猜中心字是什麼 | 看到「養了一隻__很可愛」,猜中間是「貓」 |
練習的過程中,電腦會一直微調每個字的詞向量,讓猜測越來越準。練完之後,這些數字就是詞向量。
結論:你有兩個選擇:Google Colab(老師推薦)或自己的電腦(我這次用的)。你的筆電比 Colab 免費版強很多,我建議在自己電腦跑,但交作業時要多附 requirements.txt、報告要寫電腦規格。
| Google Colab(免費版) | 自己的電腦(你的筆電) | |
|---|---|---|
| 要不要安裝 | 不用,開瀏覽器就能用 | 要裝 Python 和套件(下面有指令) |
| CPU(訓練詞向量只用 CPU) | 通常只有 2 核心 | i7-11800H,8 核心 16 執行緒 |
| 記憶體 | 約 12 GB(外部知識,依 Colab 免費版一般配置) | 16 GB(系統顯示 15.7 GB) |
| 斷線風險 | 閒置太久或跑太久會被中斷,畫面上的結果會不見(外部知識) | 不會 |
| 模板指令能不能直接用 | 可以(!wget、!gdown、!cat 都是 Linux 指令) | 不行,要改成 Python(我已經改好,見第 5 節) |
| 交作業要多做什麼 | 報告寫「Colab」+Python 版本 | 報告寫作業系統、CPU、Python 版本;一定要附 requirements.txt |
「Colab 通常只有 2 核心、訓練會慢很多」是我根據 Colab 免費版一般配置推的,這次沒有實際在 Colab 上計時。
用不到。這份作業用的 Gensim 只用 CPU 算。你的 RTX 3060、Colab 的免費 GPU,對這份作業都沒有幫助。
在作業資料夾打開終端機,一行一行貼:
py -3.12 -m venv .venv
.venv\Scripts\python.exe -m pip install gensim pandas scikit-learn matplotlib tqdm gdown nltk jupyter
.venv\Scripts\python.exe -m jupyter notebook
這三行在做什麼:
.venv 資料夾)。好處是作業用的套件跟電腦裡其他東西分開,不會互相打架。| 東西 | 大小 |
|---|---|
| 維基百科 11 個壓縮檔 | 6.93 GB(Windows 檔案總管會顯示約 6.45 GB,因為它用 1024 進位) |
| 抽樣出來的 5%、10%、20% 文字檔,加上整理過的版本 | ⟦R:sample_disk⟧ |
| GloVe 字典+論壇教材 | 約 0.4 GB(放在 C:\Users\你\gensim-data) |
requirements.txt、報告寫電腦規格。這兩件第 13 節都已經做好了。