文章本身是一種非結構化的資料,無法直接被計算。word representation 就是將這種訊息轉化為結構化的資訊,這樣就可以針對 word representation 計算,完成文章分類、情緒判斷等工作。
word representation 的方法很多,例如:
one-hot representation
例如: 貓、狗、牛、羊 用向量中一個欄位來表示
貓:[1,0,0,0] 狗:[0,1,0,0] 牛:[0,0,1,0] 羊:[0,0,0,1]
缺點是沒有辦法表示出詞語之間的關係,另外因為向量中大部分都是 0,稀疏的向量,導致計算及儲存的效率都很低
integer representation
都以一個整數來表示每一個詞,將詞語的整數連接成 list,就是一句話
貓:1 狗:2 牛:3 羊:4
缺點是沒有辦法表示出詞語之間的關係
word embedding
可用較低維度的向量表示詞語,不像one-hot 那麼長。詞意相近的詞,在向量空間中的距離比較接近
有兩種主流的 word embedding 方法
word2vec
2013 年由 google 的 Mikolov 提出,該演算法有兩種模式:利用前後文來預測目前的詞語,或是利用目前的詞語預測前後文
GloVe (Global Vector for Word Representation)
延伸了 word2vec
word2vec
CBOW (Continuous Bag-of-Words Model)
利用前後文來預測目前的詞語,相當於一句話中扣掉一個詞,猜這個詞是什麼。
Skip-gram (Continuous Skip-gram Model)
利用目前的詞語預測前後文,相當於給一個詞,猜前面和後面可能出現什麼詞。
ref: Word2Vec 的兩種模型:CBOW 與 Skip-gram
優點:
- 通用性佳,適合用在多種 NLP 問題上
- 比舊的 word embedding 方法的向量維度小,計算速度比較快
缺點:
- 由於詞和向量是一對一的關係,所以無法處理多義詞的問題
- word2vec 是一種靜態的表示方式,通用性強,但無法針對特定任務做動態優化
window
以 「孔乙己 一到 店 所有 喝酒 的 人 便都 看著 他 笑」 這一句話為例,去掉停用字後,會得到
孔乙己 一到 店 所有 喝酒 人 看著 笑
以 「人」 這個單詞為例,window =1 時,就是該單詞前後 1 格的另一個單詞,這樣會得到這樣的結果
喝酒 人 看著
電腦就能知道「人」跟「喝酒」「看著」有關係。
window 用來定義 word2vec 文章分析時,單詞前後關係的距離。
gensim
gensim 是使用 google 釋出的 word2vec 模型的套件,可找到字的向量、相似字,計算向量之間的相似度,WMDistance 可計算兩個句子之間的相似度。
取得 wiki 文章資料
以下以 維基百科 wiki zh data 下載的 20200301 中文版資料 zhwiki-20200301-pages-articles.xml.bz2 1.8 GB 做測試,注意我們要的是以 pages-articles.xml.bz2
結尾的備份。
先安裝 gensim
pip3 install gensim
gensim 已經有提供了 WikiCorpus,可以快速取得 wiki 文章的標題及內容。執行以下程式,會產生一個 wiki_texts.txt 文字檔,裡面是所有 wiki_corpus.get_texts()
取得的文章內容。
# -*- coding: utf-8 -*-
## wiki_to_txt.py
import logging
import sys
from gensim.corpora import WikiCorpus
def main():
if len(sys.argv) != 2:
print("Usage: python3 " + sys.argv[0] + " wiki_data_path")
exit()
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
wiki_corpus = WikiCorpus(sys.argv[1], dictionary={})
texts_num = 0
with open("wiki_texts.txt",'w',encoding='utf-8') as output:
for text in wiki_corpus.get_texts():
output.write(' '.join(text) + '\n')
texts_num += 1
if texts_num % 10000 == 0:
logging.info("已處理 %d 篇文章" % texts_num)
if __name__ == "__main__":
main()
執行
python3 wiki_to_txt.py zhwiki-20200301-pages-articles.xml.bz2
執行結果
2020-03-30 15:27:59,410 : INFO : finished iterating over Wikipedia corpus of 356901 documents with 82295378 positions (total 3436353 articles, 97089149 positions before pruning articles shorter than 50 words)
斷詞
因為wiki 文章中,把簡體字跟繁體字混在一起了,先透過 OpenCC 進行簡體字轉繁體的處理
安裝 OpenCC
wget https://github.com/BYVoid/OpenCC/archive/ver.1.0.5.tar.gz -O opencc.1.0.5.tgz
tar -zxvf opencc.1.0.5.tgz
cd OpenCC-ver.1.0.5/
# 產生 Makefile
mkdir build
cd build
## CENTOS 執行以下命令
cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release LE_GETTEXT:BOOL=ON ..
## MAC 執行以下命令
# cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -D ENABLE_GETTEXT:BOOL=OFF -DCMAKE_OSX_ARCHITECTURES=x86_64 ..
make
make install
sudo ln -s /usr/lib/libopencc.so.2 /usr/lib64/libopencc.so.2
## 測試
opencc --help
opencc --version
利用 opencc 將簡體字轉為繁體
opencc -i wiki_texts.txt -o wiki_zh_tw.txt -c s2tw.json
安裝 jieba
pip3 install jieba
測試
import jieba
seg_list = jieba.cut("我来到清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))
從 ithomeironman/day16NLP_Chinese/ 可下載一個繁體中文的字典 dict.txt.big,以及 停用字 stops.txt。將 jieba 改為使用繁體字典
# -*- coding: utf-8 -*-
## segment.py
import jieba
import logging
def main():
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
# jieba custom setting.
jieba.set_dictionary('jieba_dict/dict.txt.big')
# load stopwords set
stopword_set = set()
with open('jieba_dict/stops.txt','r', encoding='utf-8') as stopwords:
for stopword in stopwords:
stopword_set.add(stopword.strip('\n'))
output = open('wiki_seg.txt', 'w', encoding='utf-8')
with open('wiki_zh_tw.txt', 'r', encoding='utf-8') as content :
for texts_num, line in enumerate(content):
line = line.strip('\n')
words = jieba.cut(line, cut_all=False)
for word in words:
if word not in stopword_set:
output.write(word + ' ')
output.write('\n')
if (texts_num + 1) % 10000 == 0:
logging.info("已完成前 %d 行的斷詞" % (texts_num + 1))
output.close()
if __name__ == '__main__':
main()
執行要花 30 分鐘
# python3 segment.py
2020-03-30 15:50:14,355 : DEBUG : Prefix dict has been built successfully.
......
2020-03-30 16:17:47,295 : INFO : 已完成前 350000 行的斷詞
訓練單詞向量
Word2Vec 有許多參數
gensim.models.word2vec.Word2Vec(sentences=None, size=100, alpha=0.025, window=5, min_count=5, max_vocab_size=None, sample=0.001, seed=1, workers=3, min_alpha=0.0001, sg=0, hs=0, negative=5, cbow_mean=1, hashfxn=<built-in function hash>, iter=5, null_word=0, trim_rule=None, sorted_vocab=1, batch_words=10000)
比較常用的是
- sentences:這是要訓練的句子集合
- size:這是訓練出的詞向量會有幾維
- alpha:機器學習中的學習率,這東西會逐漸收斂到
min_alpha
- sg:sg=1表示採用skip-gram,sg=0 表示採用cbow
- window:能往左往右看幾個字的意思
- workers:執行緒數目
- min_count:若這個詞出現的次數小於
min_count
,那他就不會被視為訓練對象
# -*- coding: utf-8 -*-
import logging
from gensim.models import word2vec
def main():
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentences = word2vec.LineSentence("wiki_seg.txt")
model = word2vec.Word2Vec(sentences, size=250)
#保存模型,供日後使用
model.save("word2vec.model")
#模型讀取方式
# model = word2vec.Word2Vec.load("your_model_name")
if __name__ == "__main__":
main()
模型測試
# -*- coding: utf-8 -*-
from gensim.models import word2vec
from gensim import models
import logging
def main():
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
model = models.Word2Vec.load('word2vec.model')
print("提供 3 種測試模式\n")
print("輸入一個詞,則去尋找前一百個該詞的相似詞")
print("輸入兩個詞,則去計算兩個詞的餘弦相似度")
print("輸入三個詞,進行類比推理")
while True:
try:
query = input()
q_list = query.split()
if len(q_list) == 1:
print("相似詞前 100 排序")
res = model.most_similar(q_list[0],topn = 100)
for item in res:
print(item[0]+","+str(item[1]))
elif len(q_list) == 2:
print("計算 Cosine 相似度")
res = model.similarity(q_list[0],q_list[1])
print(res)
else:
print("%s之於%s,如%s之於" % (q_list[0],q_list[2],q_list[1]))
res = model.most_similar([q_list[0],q_list[1]], [q_list[2]], topn= 100)
for item in res:
print(item[0]+","+str(item[1]))
print("----------------------------")
except Exception as e:
print(repr(e))
if __name__ == "__main__":
main()
測試
籃球
相似詞前 100 排序
美式足球,0.6760541796684265
排球,0.6475502848625183
橄欖球,0.6430544257164001
男子籃球,0.6427032351493835
冰球,0.6138877272605896
棒球,0.6081532835960388
籃球隊,0.6004550457000732
足球,0.5992617607116699
.....
----------------------------
電腦 程式
計算 Cosine 相似度
0.5263175
----------------------------
衛生紙 啤酒
計算 Cosine 相似度
0.3263663
----------------------------
衛生紙 面紙
計算 Cosine 相似度
0.70471543
----------------------------
電腦 程式 電視
電腦之於電視,如程式之於
電腦系統,0.6098563075065613
程式碼,0.6063085198402405
軟體,0.5896543264389038
電腦程式,0.5740373730659485
終端機,0.5652117133140564
計算機程序,0.5597981810569763
除錯,0.554024875164032
計算機,0.549680769443512
作業系統,0.543748140335083
直譯器,0.5432565212249756
介面,0.5425338745117188
......
自然語言處理的應用
簡單
- 拼寫檢查 ( Spell Checking )
- 關鍵字搜索 ( Keyword Search )
尋找同義詞 ( Finding Synonyms )
從網頁和文檔解析信息 ( Parsing information from websites, documents, etc. )
複雜
- 機器翻譯 ( Machine Translation )
- 語義分析 ( Semantic Analysis )
- 指代詞分析 ( Coreference ), 例如,”he” 和”it” 在文檔中指誰或什麼?
- 問答系統 ( Question Answering )
沒有留言:
張貼留言