過年大掃除的時候發現,我的草稿夾內塞滿之前寫一半的草稿,想說欠都欠過年了,還是讓它繼續欠下去吧(誤)…
這篇是記錄前一陣子(其實也快一年前了…)為了弄出一份符合我們應用的詞向量,開始嘗試著自給自足的故事 XD
為了方便操作,這邊採用的是使用 python 的函式庫 — gensim 來實作,文章裡所有的程式碼都會傳上 github…如果找不到那肯定是我拖延症又發作了XD
Word Embedding
先來看看 Word Embedding,又名詞向量,是指將文字的語意用一組向量來表示,其概念是出自於 Bengio 的 《A Neural Probabilistic Language Model》(論文:原文、筆記)。
在詞向量的訓練過程中,它會儘可能提取特徵,使著具有相同上下文語意的詞,在高緯度空間中盡可能相近;反之,使含義並不相似的詞距盡可能遠離。此外,也是我們能夠用類別的方式推導詞,最著名的例子:
\[\overrightarrow{King} - \overrightarrow{Man} + \overrightarrow{Woman} \approx \overrightarrow{Queen}\]word vector(圖片來源: 闪念基因)
而 word2vec 則是 Google 所提出用來實現 word embedding 的一種方法,主要採用了 Skip-Gram 與 CBOW 兩種模型。
在 Skip-Gram 中,一次只輸入一個字,輸出為其前後一定距離內的文字,所以同一個輸入會有多個輸出;與 Skip-Gram 不同的是,CBOW 是將一段句子的中間字當作輸出為,其左右文字為輸入,所以是多個輸入一個輸出輸入。
簡單來說,Skip-Gram 是給定 input word 來預測上下文。而 CBOW 則是給定上下文,反過來預測 input word。
不過,我們這邊不討論 word2vec 的數學公式,也不討論 word2vec 的訓練模型,這邊就只想辦法訓練一包詞向量而已 XD
-
若是對數學公式有興趣的,往這走 ↓
– word2vec 中的数学原理详解(一)目录和前言_word2vec,CBOW,Skip-gram|peghoty-CSDN博客 -
若是對神經網路架構有興趣的,看這邊 ↓
– 類神經網路 – word2vec (part 1 : Overview)|MARK CHANG’S BLOG -
只想訓練一份詞向量的,就往下看吧
函式庫安裝
在開始之前,先把 gensim 安裝起來,只要一條指令:
1 |
|
另外,需要準備一套斷詞工具,顧名思義就是將整篇的語料拆成一個一個詞,才能交給 word2vec 進行訓練。這邊可以依照自己的需求挑選斷詞工具,例如: jieba,而我所挑選的是 pyhanlp,在自定義的字典檔添加上它還滿方便的。
1 |
|
HanLP 最近似乎有釋出 HanLP 2.0 ,但還在 Alpha 階段就是了。不過,不管是 jieba 或是 pyhanlp/HanLP 都是以簡體中文為核心,若想使用繁體中文為核心的,可以考慮在去年(2019)中研院釋出 CKIP的 python API 。
最後記錄一下我所使用的 pyhanlp 版號,我有一陣子沒更新了說,1.x branch 最後的版號應該是 v1.7.6 吧
data 1.6.8: /py3.6/lib/python3.6/site-packages/pyhanlp/static/data
config : /py3.6/lib/python3.6/site-packages/pyhanlp/static/hanlp.properties
取得語料
下載語料
在機器學習中,萬事起頭的第一步就是備齊資料,當然訓練詞向量也是。為了訓練出針對特定 domain 但又不失一般性的詞向量,因此必須分別收集 general 與 specific domain 的資料。
這邊 general 的資料是此用中文的維基百科,因為它資料夠大,也較為全面。維基百科有最新中文資料可供下載,也可以前往維基百科的資料庫下載所需要的版本。需要特別注意的是,請選擇 *-pages-articles.xml.bz2
形式的語料。
下載完成後,先別急著將下載檔案解壓縮,因為這是一份 1.9G 的 xml 文件,如果不想自己寫 parser 來解析這份文件,請先別衝動!…說的就是我自己 XD
解析語料
在 gensim 中有提供 API 可以直接調用,以取出文章的標題和內容。
使用的方法很簡單,先初始化 wikiCorpus
,指定 wikipedia 的路徑與 dictionary…等選項。再使用 get_texts()
迭代每一篇文章,它會回傳一個 tokens list,將這些 tokens 用空白符號串接成字串寫出到另一份文件中,就完成解析了。
1 |
|
在解析的過程中,我發現這份檔案有繁簡交雜的現象,導致我最終的結果不如預期,因此我在寫出檔案前多做了些處理:
- 每個 token,依序做一次繁簡轉換。
- 檢查轉換的結果是否符合編碼,若結果符合則留下,反之則保留轉換前的輸入。
訓練前處理
斷詞
因為 pyhanlp 的預設斷詞是使用簡體中文斷詞,如果想用簡體的斷詞器對繁體的語句進行斷詞,效果不彰,因此必須將語句換成簡體,或改用繁體的斷詞器,這邊是選擇使用繁體的斷詞器。
但,想使用繁體的斷詞器必須去你的 pyevn 中的 site-packages 中更改 pyhanlp 的 init 檔案,讓它可以引入繁體的斷詞器:
1 |
|
接著修改 __init__.py
,在最下方的 API 列表中加入:
1 |
|
如此一來就可以使用繁體的斷詞器了:
1 |
|
若是不想修改 init 檔案,也可以在使用繁體斷詞器前才引入:
1 |
|
完成繁體斷詞器配置之後,就可以針對上一個步驟所解析出來的語料進行斷詞:
1 |
|
其他前處理
如果有注意到,在上段程式碼中我留了三個其他處理的註解,這邊可以取決你的應用是否希望將它換成特定的標籤,例如將對話中的 e-mail 或 url 換成 #EMAIL#
、#URL#
等標籤,使斷詞結果更符合我們預期,或是可以依照詞性取代掉姓名、機關名稱,使最後訓練出來詞向量可以集中這些詞性的詞,但這樣的修改有好有壞就是了。
最後一個註解是移除 stop word,中文翻作停用詞,這些詞極其普遍,但與其他詞相比它並沒有什麼實際含義,如:阿、呀。這些詞的移除可以加強單詞的上下文關係,理論上有助於詞向量的訓練。
除了上述的前處理外,在程式碼看不到的部分,我還對 Hanlp 的字典進行調整,引入了內政資料開放平臺的資料,並使用 Hanlp 內建的新詞挖掘的功能,使它的斷詞結果更符合臺灣的對話情境。
Out-of-Vocabulary Words,即 OOV
斷詞處理的最後一段,我們設置一個詞頻的門檻值,針對低詞頻的詞將使用 #UNK#
這個標籤來取代。這是因為這些低頻詞由於缺乏足夠數量的語料,訓練出來的詞向量往往效果不佳,也可同時訓練出一組詞向量用以表示詞彙表中不存在的詞。
這邊就不分享替換 OOV 的程式碼了,我這邊暫時使用暴力法來實做,先將斷詞結果讀入,並計算每個詞出現次數,最後將低於門檻值的詞換成 UNK 標籤,再將結果寫出。只是這樣實做流程耗時又耗空間,還有待改進 orz…
訓練詞向量
最後就是訓練詞向量啦!程式碼很簡單就幾行而已,但麻煩的是超參數的調整只能按你的應用,慢慢實驗來調整。
1 |
|
回頭看看程式碼的部分:
-
sentences
指的就是我們用來訓練詞向量的語料,因為我這邊無論是維基百科或是 specific domain 的資料,都是以一行為一篇文章或是對話。因此在讀入語料時,使用LineSentence
的模式。 -
vector_size
詞向量的維度。 -
window
指訓練窗格大小,白話來說,一個詞在看上下文關係時,上下應該各看幾個字的意思。 -
workers
執行緒數目。 -
min_count
指一個詞出現的次數若小於 min_count,則拋棄不參與訓練。
訓練詞結果
訓練完成了,那就來試試下面的結果!
1 |
|
參考資料
- word2vec和word embedding有什么区别?|知乎
- DeepNLP的表示学习·词嵌入来龙去脉·深度学习(Deep Learning)·自然语言处理(NLP)·表示(Representation)|Mr.Scofield-CSDN博客
- A Neural Probabilistic Language Model
- A Neural Probabilistic Language Model 論文筆記|程式工作紡 - Medium
- zonghan程式筆記: Word2Vec model Introduction (skip-gram & CBOW)
- word2vec 中的数学原理详解(一)目录和前言_word2vec,CBOW,Skip-gram|peghoty-CSDN博客
- 類神經網路 – word2vec (part 1 : Overview)|MARK CHANG’S BLOG
- word2vec实战:获取和预处理中文维基百科(Wikipedia)语料库,并训练成word2vec模型|欢迎来到Jimmy的博客-CSDN博客
- 中文维基百科语料库词向量的训练|吴良超的学习笔记
- 以 gensim 訓練中文詞向量|雷德麥的藏書閣
更新紀錄
最後更新日期:2022-04-28
- 2022-04-28 fix 'NameError: name 'self' is not defined'
- 2020-01-17 發布