中文word2vector 小實作 pyladies Taiwan

MMChiou/0623/2018

此實作程式檔在[4]


word2vec 是 Google 的一個開源工具,能夠根據輸入的「詞的集合」計算出詞與詞之間的距離。

word2vec將「字詞」轉換成「向量」形式,所以文本內容的處理簡化為向量空間中的向量運算。

向量空間上的相似度,則類比到文本語義上的相似度。

向量空間上的相似度類比到文本語義上的相似度

word2vec 計算的是餘弦值(cosine),距離範圍為 0–1 之間,值越大代表兩個詞關聯度越高。

wordvector, 詞向量,用 Distributed Representation 表示詞,通常也被稱為「Word Representation」或「Word Embedding」。

使用新聞文本做為資料集

搜狗實驗室的新聞文本 兩個版本(簡體 vs 繁體)(完整版 648MB、tar.gz 格式);記得下載 tar.gz 格式的資料集 [1],因示範程式碼是以此格式做清理。

本次示範以python2 進行,以繁體文本為主。

實作後的結果: jieba 套件在簡體文本分詞表現較好,完整程式碼也會附上簡繁體結果。若有自己的文本資料集可以跳至 PART2 斷詞開始。

另外提供 PTT 的中文的語料庫,是去年 PyLadies 辦 Data Mining Workshop 時,其中有兩組使用到的資料文本 [2]。

實作流程

1 在 terminal 進行套件安裝以及資料格式檔案轉換

2 在 jupyter notebook 透過 jieba 套件斷詞,再經由 word2vec 萃取文章特徵,轉換成以向量空間表示的詞向量

3 視覺化呈現「相近詞」向量之結果

PART 1 :套件+資料格式轉換

a 所需安裝套件:jieba、word2vec、sklearn、hanziconv(繁簡轉換)、matplotlib。

下載way-microhei.ttc (中文的顯示需做特殊處理);可依個人習習慣安裝套件 (pip直接安裝or安裝在虛擬環境當中)

b 解壓縮新聞文本資料集

  • 與文本資料同一目錄底下,執行解壓縮指令
# 同一目錄底下
youngmihuang in ~/downloads
# 解壓縮
$ tar -zvxf news_sohusite_xml.full.tar.gz
# 會產生 news_sohusite_xml.dat檔案

取出 <content> </content> 之間的內容,並存成corpus.txt文件,執行:

$ cat news_sohusite_xml.full.dat | iconv -f gbk -t utf-8 -c | grep "<content>"  > corpus.txt

PART 2:實現斷詞與word2vec

完成資料格式轉換後,便開啟 jupyter notebook 。

餵給word2vec 的文件是需要斷詞的,採用 jieba 套件實現可以斷詞,將前面已經安裝好的套件 import 進來,並將剛剛存好的 corpus.txt 的新聞內容讀進來:

# coding=utf-8
import jieba 
from hanziconv import HanziConv
# 讀檔:一條一條讀進來
fileTrainRead = []
with open('/Users/youngmihuang/Downloads/corpus.txt') as      fileTrainRaw:
  for line in fileTrainRaw:
      fileTrainRead.append(HanziConv.toTraditional(line)) # 簡轉繁

a 斷詞

接下來就要將每一條讀進來的新聞文章使用 jieba 斷詞,每一條讀進來的文章分詞後,併成一條更長的 list:

# 斷詞
fileTrainSeg=[]
for i in range(len(fileTrainRead)):
    fileTrainSeg.append([' '.join(list(jieba.cut(fileTrainRead[i][9:-11],cut_all=False)))])
# 因為會跑很久,檢核是否資料有持續在跑
    if i % 50000 == 0 :
        print i

上述共有 1,411,996 筆文章資料,如果是 MacBook Air 大約需要 1hr 。

jieba 支持的三種分詞模式:

  • 精確模式,試圖將句子最精確地切開,適合文本分析。
  • 全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義。
  • 搜索引擎模式,在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於搜索引擎分詞。
# 精確模式、同時也是預設模式
seg_list1 = jieba.cut("一是嬰兒哭啼二是學遊戲三是青春物語四是碰巧遇見你", cut_all=False)
print "Default Mode: " + "/ ".join(seg_list1)  
# Result
 一是/ 嬰兒/ 哭啼/ 二是/ 學遊戲/ 三/ 是/ 青春/ 物語/ 四/ 是/ 碰/ 巧遇/ 見/ 你

# 全模式
seg_list2 = jieba.cut("一是嬰兒哭啼二是學遊戲三是青春物語四是碰巧遇見你", cut_all=True)
print "All Mode: " + "/ ".join(seg_list2)
# Result
 一/ 是/ 嬰/ 兒/ 哭啼/ 二/ 是/ 學/ 遊/ 戲/ 三/ 是/ 青春/ 物/ 語/ 四/ 是/ 碰巧/ 巧遇/ 見/ 你

# 搜尋引擎模式
seg_list3 = jieba.cut_for_search("一是嬰兒哭啼二是學遊戲三是青春物語四是碰巧遇見你")
print "Search Mode: " + "/ ".join(seg_list3)
# Result
 一是/ 嬰兒/ 哭啼/ 二是/ 學遊戲/ 三/ 是/ 青春/ 物語/ 四/ 是/ 碰/ 巧遇/ 見/ 你

精確模式 vs 全模式之間有明顯差異,精確模式整體相對分得較好,像在「嬰兒/ 學遊戲/ 物語」都分出來了。但全模式將「碰巧/ 巧遇」兩個字詞都保留下來了,「碰巧」是分得比精確模式的「巧遇」來得好的部分。

試了很多次不同的 sample 丟進精確模式 vs 搜尋引擎模式,分詞結果都一樣。

接著,我們如果好奇文章斷成哪些字詞的話可以下面的方法檢視;最後需將jieba 的斷詞結果存成 corpusSegDone.txt:

# 將jieba的斷詞產出存檔
fileSegWordDonePath ='corpusSegDone.txt'
with open(fileSegWordDonePath,'wb') as fW:
    for i in range(len(fileTrainSeg)):
        fW.write(fileTrainSeg[i][0].encode('utf-8'))
        fW.write('\n')
# 檢視斷詞jieba的結果
def PrintListChinese(list):
    for i in range(len(list)):
        print list[i],
PrintListChinese(fileTrainSeg[10])

b. word2vec 轉成高維空間向量

將上述 jieba 斷詞後的 corpusSegDone.txt 轉成向量後存成 corpusWord2Vec.bin 檔

import word2vec
# jieba分詞轉word2vec向量
word2vec.word2vec('corpusSegDone.txt', 'corpusWord2Vec.bin', size=300,verbose=True)

這裡將 size 設定為 300 維,透過調整 size 參數的大小決定「詞向量」的維度數,也可以是 400、200、100 維;verbose = True 是會印出 word2vec 執行的詳細狀況:

# 執行的詳細結果(繁體)
Starting training using file corpusSegDone.txt
Vocab size: 842956
Words in train file: 407852192

若要檢視文字轉為向量的樣子:

model = word2vec.load('corpusWord2Vec.bin')
print model.vectors #文字以向量形式呈現
# Result
[[ 0.08015626  0.08850129 -0.07670335 ..., -0.02626957 -0.03316621
   0.0614953 ]
 [-0.02138876 -0.06264357 -0.09334067 ...,  0.00015479 -0.05851945
   0.04908165]
 [-0.00995629 -0.01481206 -0.09394402 ..., -0.01619852 -0.06687199
   0.02453057]
 ..., 
 [ 0.04989554  0.08304838 -0.05676759 ..., -0.00947851  0.07963122
  -0.03377864]
 [-0.01265288  0.07221718  0.10141564 ...,  0.01648477  0.1009445
  -0.07451316]
 [ 0.03596683 -0.08654522  0.03320961 ..., -0.07038685  0.09750576
  -0.03816517]]

若要檢視字詞:

# 可檢視第996~1000個字詞是什麼
for i in range(995,1000):
    print model.vocab[i]
# Result
達到
屏
行為
觀眾
日訊

如何評價所建「詞向量」的好壞?

依照 word2vec 的原理,詞意相近的詞在向量空間當中的距離是接近的,但會因為「文本內容性質的差異」而有所不同,例如在新聞類的文本中,「台灣」字詞會常與地名或時事事件等字詞距離接近;然而若是跟 PTT 論壇相關的文本,「台灣」字詞可能會更常與「鄉民/島民/魯蛇/溫拿」這類聊天用語的字詞距離靠近;總之,可以透過所訓練之詞向量距離,觀察他們語意是否相近去做衡量:

# 顯示空間距離相近的詞
model = word2vec.load('corpusWord2Vec.bin')
indexes = model.cosine(u'畢業') # 此字詞有出現在corpusWord2Vec.bin當中
for index in indexes[0]:
    print model.vocab[index]
# Result
畢業生
離校
考入
剛畢業
大四
學畢業
考上
開學
結業
放暑假

若是使用迷你版 ( 110KB ) 這一版的檔案,因為出現的字詞較少,建議可以嘗試「u'經濟'」、「u'企業'」等字詞;若是使用其他的資料集,這裡可以輸入上一步驟檢視有出現的字詞替換,才不會出現 Key error 的情形。

PART3:將距離相近的詞以視覺化呈現

選擇要呈現在二維平面上的字詞們(建議可以先選擇種類不同的字詞,在二維平面做視覺化的分佈會分得較開):

# 選擇你想丟進去的字詞
# 顯示空間距離相近的詞
# 放入字詞: '寶寶'
indexes = model.cosine(u'寶寶')
for index in indexes[0]:
    print model.vocab[index]
# Result
寶寶的
小寶寶
孩子
準媽媽
寶寶在
胎兒
媽媽們
小孩
小孩子
媽媽
# 放入字詞: '打車'
indexes = model.cosine(u'打車')
for index in indexes[0]:
    print model.vocab[index]
# Result
坐車
開車
坐火車
買票
下車
買菜
上車
搭車
乘車
騎車
# 放入字詞: '楊冪'
indexes = model.cosine(u'楊冪')
for index in indexes[0]:
    print model.vocab[index]
# Result
姚晨
林心如
穎兒
張智霖
劉愷威
馮紹峰
鄧超
周迅
柳岩
李晨
# 放入字詞: '騰訊'
indexes = model.cosine(u'騰訊')
for index in indexes[0]:
    print model.vocab[index]
# Result
網易
新浪
百度
優酷
搜狗
凡客
窩窩團
電商
愛奇藝
百視通

將要視覺化的五組字詞,找出其相近詞後 append 起來成 array 形式:

# 在高維向量空間(k=300)找出與所選字詞距離最接近的前10名
index1,metrics1 = model.cosine(u'畢業')
index2,metrics2 = model.cosine(u'寶寶')
index3,metrics3 = model.cosine(u'打車')
index4,metrics4 = model.cosine(u'楊冪')
index5,metrics5 = model.cosine(u'騰訊')

# 所選字詞
index01 = np.where(model.vocab == u'畢業')
index02 = np.where(model.vocab == u'寶寶')
index03 = np.where(model.vocab == u'打車')
index04 = np.where(model.vocab == u'楊冪')
index05 = np.where(model.vocab == u'騰訊')
# 將所選字詞與其最接近之前10名合併 
index1 = np.append(index1,index01)
index2 = np.append(index2,index02)
index3 = np.append(index3,index03)
index4 = np.append(index4,index04)
index5 = np.append(index5,index05)

接下來,將上述高維空間的向量,變成二維平面:

import numpy as np
# 視覺化套件
import matplotlib
import matplotlib.pyplot as plt
# 主成分因子
from sklearn.decomposition import PCA

PCA Principal Component Analysis

( Principal Component Analysis ) 是一種常用的數據分析方法。PCA 通過線性變換將原始數據變換為一組各維度線性無關的表示,可用於提取數據的主要特徵分量,常用於高維數據的降維。若想了解數學原理及更詳細的介紹可以看

PCA 降維示意圖 PCA using truncated SVD。

# 引入上述將文章斷詞後轉為300維向量的資料
rawWordVec = model.vectors
# 將原本300維向量空間降為2維
X_reduced = PCA(n_components=2).fit_transform(rawWordVec)

視覺化呈現步驟:

# 須先下載wqy-microhei.ttc,因中文顯示需做特殊處理
zhfont = matplotlib.font_manager.FontProperties(fname='/Users/youngmihuang/Downloads/wqy-microhei.ttc')
# 畫圖
fig = plt.figure()
ax = fig.add_subplot(111)

for i in index1:
    ax.text(X_reduced[i][0],X_reduced[i][1],model.vocab[i], fontproperties=zhfont,color='C3')
for i in index2:
    ax.text(X_reduced[i][0],X_reduced[i][1],model.vocab[i], fontproperties = zhfont,color= 'C1')
for i in index3:
    ax.text(X_reduced[i][0],X_reduced[i][1],model.vocab[i], fontproperties=zhfont,color='C7')
for i in index4:
    ax.text(X_reduced[i][0],X_reduced[i][1],model.vocab[i], fontproperties=zhfont,color='C0')
for i in index5:
    ax.text(X_reduced[i][0],X_reduced[i][1],model.vocab[i], fontproperties=zhfont,color='C4')
ax.axis([0,0.5,-0.2,0.6])
plt.figure(figsize=(60,60))
plt.show()

結果如下

透過各個相近群的字詞以不同顏色標記之後,可以看到各字詞群組之間相對都蠻聚合的,但因為他們是從 300 維空間當中投射至 2 維空間,「打車」和「騰訊」這兩組相近字(灰色、紫色)在視覺上看起來跟其他重疊性很高,若把它們移除結果如下:

Conclusion

1 詞向量相近詞結果表現:會根據資料量大小( full / small 資料集)、以及繁/簡中文與分詞套件( jieba 擅長簡體分詞)而有所差異:

# jieba斷詞轉為word2vec結果 (繁體)
Starting training using file corpusSegDone.txt
Vocab size: 842956
Words in train file: 407852192
# jieba斷詞轉為word2vec結果 (簡體)
Starting training using file corpusSegDone.txt
Vocab size: 507882
Words in train file: 391829209

以分詞效果看來,簡體斷的較好;繁體相對來講在斷詞時,有些字詞無法斷在一起,導致字詞數較多,e.g. “晚自習” 在簡體會被分成 “晚自習”;但在繁體會被分成 “晚” 和 “自習” 兩個單詞。

  1. word2vec 將文字轉為詞向量,僅為自然語言處理中表示單詞的最基本問題。

以下舉幾個 NLP 的任務:

簡單任務

  • 拼寫檢查 ( Spell Checking )
  • 關鍵字搜索 ( Keyword Search )
  • 尋找同義詞 ( Finding Synonyms )

中級任務

  • 從網頁和文檔解析信息 ( Parsing information from websites, documents, etc. )

複雜任務

  • 機器翻譯 ( Machine Translation )
  • 語義分析 ( Semantic Analysis )
  • 指代詞分析 ( Coreference ), 例如,”he” 和”it” 在文檔中指誰或什麼?
  • 問答系統 ( Question Answering )

[0]

https://medium.com/pyladies-taiwan/自然語言處理入門-word2vec小實作-f8832d9677c8

[1]

http://www.sogou.com/labs/resource/cs.php

[2]

http://tw.pyladies.com/~marsw/dmworkshop.slides.html#/2

[3]

https://zhuanlan.zhihu.com/p/21580949

[4]

https://github.com/youngmihuang/word2vec

results matching ""

    No results matching ""