2020/08/10

詞向量 Word Embedding

文章本身是一種非結構化的資料,無法直接被計算。word representation 就是將這種訊息轉化為結構化的資訊,這樣就可以針對 word representation 計算,完成文章分類、情緒判斷等工作。

word representation 的方法很多,例如:

  1. one-hot representation

    例如: 貓、狗、牛、羊 用向量中一個欄位來表示

    貓:[1,0,0,0]
    狗:[0,1,0,0]
    牛:[0,0,1,0]
    羊:[0,0,0,1]

    缺點是沒有辦法表示出詞語之間的關係,另外因為向量中大部分都是 0,稀疏的向量,導致計算及儲存的效率都很低

  2. integer representation

    都以一個整數來表示每一個詞,將詞語的整數連接成 list,就是一句話

    貓:1
    狗:2
    牛:3
    羊:4

    缺點是沒有辦法表示出詞語之間的關係

  3. 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

優點:

  1. 通用性佳,適合用在多種 NLP 問題上
  2. 比舊的 word embedding 方法的向量維度小,計算速度比較快

缺點:

  1. 由於詞和向量是一對一的關係,所以無法處理多義詞的問題
  2. 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 進行簡體字轉繁體的處理

ref:

安裝 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 )

References

詞向量詳解:從word2vec、glove、ELMo到BERT

Word2vec

詞嵌入 | Word embedding

自然語言處理入門- Word2vec小實作

讓電腦聽懂人話: 直觀理解 Word2Vec 模型

Gensim Word2Vec 簡易教學

產品標籤分群實作-Word2Vec

以 gensim 訓練中文詞向量

實作Tensorflow (5):Word2Vec

沒有留言:

張貼留言