5 Part III 準備教材:下載維基百科、抽 20%(TODO4,配分 5%)

結論:維基百科有 ⟦R:n_articles⟧ 篇文章,太多了,所以隨機抽 20% 來當教材。重點是一篇一篇讀、讀到就決定要不要留,不要整份讀進記憶體。

助教給的資料長什麼樣

助教已經把維基百科「洗乾淨」了:拿掉網頁語法、標點,全部轉小寫,字和字之間用空白隔開。每一行就是一整篇文章。

⟦R:wiki_head⟧

分成 11 個壓縮檔,加起來 6.93 GB(筆記本 In [18] 那一格印的)。前 10 個檔各 562,365 篇(模板註解寫的),最後一個只有 5 篇(推算:5,623,655 − 10 × 562,365 = 5)。

注意上面第 5 行(abraham lincoln):「february april was」「the th president」中間的日期和數字被拿掉了;第 0 行「anarchism is political philosophy」、第 3 行「alabama is state」少了單一字母的 a。原因是助教用 gensim WikiCorpus 的預設參數清資料:模板在 TODO4 前面寫「words with one character were discarded」(只有一個字母的字被丟掉);筆記本 In [27] 的註解也寫了這個預設「只留 2~15 個字母的英文字」,所以數字(不是字母)也會被拿掉。

抽樣的程式(逐段講解)

SAMPLE_RATIO = 0.20
random.seed(42)        # ① 固定亂數種子
...
for part in wiki_parts:                          # ② 11 個壓縮檔依序讀
    with gzip.open(part, "rt", encoding="utf-8") as f:
        for line in f:                           # ③ 一次只讀一行(一篇文章)
            n_total += 1
            r = random.random()                  # ④ 擲一個 0~1 的亂數
            if r < SAMPLE_RATIO:                 #    小於 0.2 就留下
                output_file.write(line)
            for ratio, fh in extra_outputs.items():   # ⑤ 同一個亂數,順便做 5%、10%
                if r < ratio:
                    fh.write(line)
#在做什麼為什麼要這樣
①固定亂數種子亂數其實是「照公式算出來的」,起點(種子)固定,每次跑抽到的文章就一樣。結果才能重現,報告的數字才不會每次跑都不同。
②直接讀壓縮檔不用先解壓縮、合併,省下大量硬碟空間。解壓後約 3 倍大(推算:20% 樣本解壓後 4.16 GB,全部約 4.16 ÷ 0.2 ≈ 20.8 GB,是壓縮檔 6.93 GB 的約 3 倍)。
③一行一行讀老師在課堂上特別提醒過:以前有同學說 Colab 記憶體不夠,通常是程式寫法有問題——「你不要把百分之百的 wikidump 都載入,然後自己再選」(W3 01:55:49–01:56:07)。一行一行讀,記憶體裡永遠只有一篇文章。
④擲亂數決定留不留每篇文章有 20% 的機會被留下,所以最後大約留下 20%。是「以篇為單位」抽(助教影片 00:13:26「每一行代表一篇 Wikipedia 的文章,因此我們是以文章為單位進行取樣」)。
⑤同一個亂數順便做 5%、10%亂數小於 0.05 的文章,一定也小於 0.10、0.20。所以 5% 的文章一定包含在 10% 裡、10% 一定包含在 20% 裡。這樣三份教材只差在「多少」,內容種類一樣,報告第 2 題的比較才公平。而且讀一趟 11 個壓縮檔要幾分鐘,這樣省掉兩趟。

第 ⑤ 步畫成圖:同一個亂數決定三份樣本

每篇文章只擲一次亂數 r。下圖四顆星是四篇文章的例子(r 是我編的):

⟦IMG:sampling_line⟧

所以「5% 裡的每一篇,一定也在 10% 裡」,不可能有例外。

跑出來的結果

⟦R:todo4_out⟧

花了 ⟦R:todo4_time⟧。

考試可能問
「抽樣時為什麼不能先把整份資料讀進來再隨機挑?」→ 資料比記憶體大,會爆掉;要用串流(一行一行讀)的方式。
「為什麼要設定 random seed?」→ 讓結果可以重現。
所以對你的影響是:考試問「怎麼避免記憶體爆掉」,答「一行一行讀、讀到就決定留不留」;問「5%、10%、20% 怎麼抽才公平」,答「同一個亂數,5% 一定包含在 10% 裡」。

6 Part III 自己編字典:前處理+訓練 Word2Vec(TODO5,配分 10%)

結論:前處理只做一件事(只留純英文字母的字),而且刻意不做「移除停用詞」和「詞形還原」,因為它們會把考卷的答案弄不見。訓練用 Skip-gram,參數盡量跟 GloVe 一樣,比較才公平。

前處理:做了什麼、沒做什麼

作業規格建議的做法(PDF p28)我有沒有做為什麼
移除非英文字有(只留 a~z 組成的字)不過效果很小:筆記本 TODO5 的輸出顯示只刪掉 ⟦R:nonen_pct⟧ 的字,因為助教已經清過了。
移除停用詞(the、of、he、she…)刻意不做考卷裡有 ⟦R:n_stop_q⟧ 題含有停用詞,例如家人/性別小類(筆記本上叫 family)的 he she his her,刪掉之後這些題目必錯。老師上課也說停用詞「現在一點都不重要也不會去做」(W2 01:39:50)(老師是在講搜尋引擎建索引時說的;本作業的停用詞實驗也支持同樣的結論)。第 12 節有實驗。
詞形還原(rocks → rock)刻意不做語法類的考題就是在考字形變化(bigger、biggest、cars、danced)。還原成原形,答案就不見了。
更好的斷詞不需要助教的資料已經斷好字了。
只保留常見字有(最多 30 萬字)見下面參數表的 max_final_vocab。
所以對你的影響是(報告第 1 題的重點)
作業規定寫「不是每個技巧都會更好,你要自己試」(作業規格 PDF p28「Recommended data pre-processing steps」那頁,原文:Note that not every tricks yield better performance. You should try on your own.)。所以報告不要只列「我做了 A、B、C」,要寫「我沒做 D,因為它會讓考卷的哪些題目答不出來」。能說出「為什麼不做」,比做了一堆更有說服力。

訓練參數:每一個在調什麼

W2V_PARAMS = dict(vector_size=100, window=5, sg=1, negative=5, sample=1e-3,
                  min_count=5, max_final_vocab=300_000, epochs=5, workers=15, seed=42)
參數我設多少白話為什麼這樣設
vector_size100每個字用幾個數字表示(詞向量有幾個數字)跟 GloVe-100 一樣,比較才公平。第 12 節有試 300。
window5「前後文的字」算多遠:最多前後各 5 個字(每次實際在 1~5 之間隨機取,所以近的字被看到的次數比較多;外部知識:gensim 的預設做法)常見預設。第 12 節有試 10。
sg11 = Skip-gram,0 = CBOW老師投影片的重點是 Skip-gram。第 12 節有比較 CBOW。
negative5每一組真的(中心字, 前後文的字)配 5 個隨機抽的假字,讓電腦練習分辨「這一對是不是真的前後文」這叫 negative sampling(負取樣):取代「對 30 萬字算機率」,所以快很多。預設值。手算例子見課堂補充第 5 點。
sample0.001太常見的字(the、of)隨機跳過一些加速,也讓少見字學得比較好(外部知識:Mikolov 2013 原論文)。預設值。注意:它的效果有點像「部分刪掉停用詞」,第 12 節停用詞實驗要考慮這點。
min_count5(實際被調成 ⟦R:eff_min_count⟧)收錄門檻:出現次數低於門檻的字不收進字典出現太少,詞向量學不準。實際被自動調高,見下面例外 1。
max_final_vocab300,000字典最多收 30 萬個字作業規格 PDF p28 建議的「只保留最高頻的字」(Retain the most frequent words)。字典太大會讓答題變很慢、也比較容易猜到冷門字。
epochs5整份教材讀幾遍預設值。讀越多遍越準,但越久。
workers15同時用幾個 CPU 執行緒你的電腦有 16 個,留 1 個給系統。Colab 要改成 2。
seed42亂數種子固定初始值。但結果仍會小幅變動,見下面例外 2。

例外 1:收錄門檻被自動調高(設 5,實際 ⟦R:eff_min_count⟧)

因為 max_final_vocab 規定字典最多收 30 萬字,出現 5 次以上的字卻超過 30 萬個。gensim 就自動把門檻往上調,直到字數壓到 30 萬以內。

結果:20% 維基實際是出現 ⟦R:eff_min_count⟧ 次以上才收(TODO5 輸出有印);10% 是 16 次、5% 是 9 次(In [31])。教材越大,門檻被調得越高。

例外 2:種子固定了,結果還是會小幅變動

用 15 個執行緒同時訓練時,大家更新詞向量的先後順序每次不同,所以就算種子一樣,結果也會差一點點。差多少,第 12 節實驗 ① 有量(整體約 1 個百分點)。

抽樣用的 random.seed(42) 只有一條執行緒,可以完全重現。

跑出來的結果

⟦R:todo5_out⟧
考試可能問
「Skip-gram 和 CBOW 差在哪?」→ Skip-gram 用中心字猜前後文;CBOW 用前後文猜中心字。
「window 調大會怎樣?」→ 一般的說法是:看比較遠的前後文,比較偏向「主題」相關,而不是「用法」相關(這是文獻上的說法,本作業只做了一次實驗,見第 12 節)。
「為什麼你沒有移除停用詞?」→ 考卷答案裡有停用詞(he、she、his、her),刪了就答不到。