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

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

助教給的資料長什麼樣

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

⟦R:wiki_head⟧

分成 11 個壓縮檔,加起來 6.93 GB(筆記本 In [18] 那一格印的)。前 10 個檔各 562,365 篇,最後一個只有 5 篇(剩下的零頭)。

注意上面第 0 行的「he led the united states through the ...」:數字被拿掉了(例如年份),單一字母的字也被拿掉了(例如 a)。這是助教清資料時用的工具預設會做的事。

抽樣的程式(逐段講解)

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 倍大)。
③一行一行讀老師在課堂上特別警告過:以前有同學把整份維基百科讀進記憶體再挑,Colab 直接爆掉。一行一行讀,記憶體裡永遠只有一篇文章。
④擲亂數決定留不留每篇文章有 20% 的機會被留下,所以最後大約留下 20%。是「以篇為單位」抽,助教影片有特別講。
⑤同一個亂數順便做 5%、10%亂數小於 0.05 的文章,一定也小於 0.10、0.20。所以 5% 的文章一定包含在 10% 裡、10% 一定包含在 20% 裡。這樣三份教材只差在「多少」,內容種類一樣,報告第 2 題的比較才公平。而且讀一趟 11 個壓縮檔要幾分鐘,這樣省掉兩趟。

跑出來的結果

⟦R:todo4_out⟧

花了 ⟦R:todo4_time⟧。

考試可能問
「抽樣時為什麼不能先把整份資料讀進來再隨機挑?」→ 資料比記憶體大,會爆掉;要用串流(一行一行讀)的方式。
「為什麼要設定 random seed?」→ 讓結果可以重現。

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

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

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

老師建議的做法我有沒有做為什麼
移除非英文字有(只留 a~z 組成的字)不過效果很小:筆記本 TODO5 的輸出顯示只刪掉 ⟦R:nonen_pct⟧ 的字,因為助教已經清過了。
移除停用詞(the、a、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 題的重點
老師說「不是每個技巧都會更好,你要自己試」。所以報告不要只列「我做了 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 之間隨機取,所以近的字被看到的次數比較多)常見預設。第 12 節有試 10。
sg11 = Skip-gram,0 = CBOW老師投影片的重點是 Skip-gram。第 12 節有比較 CBOW。
negative5每一組真的(中心字, 鄰居)配 5 個照字頻隨機抽的字,訓練一個「是不是真鄰居」的二元分類器這叫 negative sampling(負取樣):取代「對 30 萬字算機率」,所以快很多。預設值。見課堂補充第 5 點。
sample0.001太常見的字(the、of)隨機跳過一些加速,也讓少見字學得比較好(原論文的發現)。預設值。注意:它的效果有點像「部分刪掉停用詞」,第 12 節停用詞實驗要考慮這點。
min_count5(實際被調成 ⟦R:eff_min_count⟧)出現次數低於門檻的字不收進字典出現太少,座標學不準。注意:因為下一列設了「最多 30 萬字」,gensim 會自動把門檻往上調,20% 維基實際是出現 ⟦R:eff_min_count⟧ 次以上才收(TODO5 輸出有印)。
max_final_vocab300,000字典最多收 30 萬個字老師建議的「只保留高頻字」。字典太大會讓答題變很慢、也比較容易猜到冷門字。
epochs5整份教材讀幾遍預設值。讀越多遍越準,但越久。
workers15同時用幾個 CPU 執行緒你的電腦有 16 個,留 1 個給系統。Colab 要改成 2。
seed42亂數種子固定初始值。但用 15 個執行緒同時訓練時,計算順序每次不同,結果仍會小幅變動(第 12 節實驗 ① 有量)。抽樣用的 random.seed(42) 則可以完全重現。

跑出來的結果

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