通過(guò)前面幾個(gè)小節(jié)的學(xué)習(xí),我們現(xiàn)在已經(jīng)學(xué)會(huì)了如何獲取文本預(yù)料,然后分詞,在分詞之后的結(jié)果上,我們可以提取文本的關(guān)鍵詞查看文本核心思想,進(jìn)而可以通過(guò)可視化技術(shù)把文檔從視覺(jué)的角度表達(dá)出來(lái)。
下面,我們來(lái)看看,文本數(shù)據(jù)如何轉(zhuǎn)換成計(jì)算機(jī)能夠計(jì)算的數(shù)據(jù)。這里介紹兩種常用的模型:詞袋和詞向量模型。
詞袋模型(Bag of Words Model)
詞袋模型的概念
先來(lái)看張圖,從視覺(jué)上感受一下詞袋模型的樣子。
詞袋模型看起來(lái)好像一個(gè)口袋把所有詞都裝進(jìn)去,但卻不完全如此。在自然語(yǔ)言處理和信息檢索中作為一種簡(jiǎn)單假設(shè),詞袋模型把文本(段落或者文檔)被看作是無(wú)序的詞匯集合,忽略語(yǔ)法甚至是單詞的順序,把每一個(gè)單詞都進(jìn)行統(tǒng)計(jì),同時(shí)計(jì)算每個(gè)單詞出現(xiàn)的次數(shù),常常被用在文本分類中,如貝葉斯算法、LDA 和 LSA 等。
動(dòng)手實(shí)戰(zhàn)詞袋模型
(1)詞袋模型
本例中,我們自己動(dòng)手寫代碼看看詞袋模型是如何操作的。
首先,引入 jieba 分詞器、語(yǔ)料和停用詞(標(biāo)點(diǎn)符號(hào)集合,自己可以手動(dòng)添加或者用一個(gè)文本字典代替)。
import jieba
#定義停用詞、標(biāo)點(diǎn)符號(hào)
punctuation = [",","。", ":", ";", "?"]
#定義語(yǔ)料
content = ["機(jī)器學(xué)習(xí)帶動(dòng)人工智能飛速的發(fā)展。",
"深度學(xué)習(xí)帶動(dòng)人工智能飛速的發(fā)展。",
"機(jī)器學(xué)習(xí)和深度學(xué)習(xí)帶動(dòng)人工智能飛速的發(fā)展。"
]
接下來(lái),我們先對(duì)語(yǔ)料進(jìn)行分詞操作,這里用到 lcut() 方法:
#分詞
segs_1 = [jieba.lcut(con) for con in content]
print(segs_1)
得到分詞后的結(jié)果如下:
[['機(jī)器', '學(xué)習(xí)', '帶動(dòng)', '人工智能', '飛速', '的', '發(fā)展', '。'], ['深度', '學(xué)習(xí)', '帶動(dòng)', '人工智能', '飛速', '的', '發(fā)展', '。'], ['機(jī)器', '學(xué)習(xí)', '和', '深度', '學(xué)習(xí)', '帶動(dòng)', '人工智能', '飛速', '的', '發(fā)展', '。']]
因?yàn)橹形恼Z(yǔ)料帶有停用詞和標(biāo)點(diǎn)符號(hào),所以需要去停用詞和標(biāo)點(diǎn)符號(hào),這里語(yǔ)料很小,我們直接去標(biāo)點(diǎn)符號(hào):
tokenized = []
for sentence in segs_1:
words = []
for word in sentence:
if word not in punctuation:
words.append(word)
tokenized.append(words)
print(tokenized)
去標(biāo)點(diǎn)符號(hào)后,我們得到結(jié)果如下:
[['機(jī)器', '學(xué)習(xí)', '帶動(dòng)', '人工智能', '飛速', '的', '發(fā)展'], ['深度', '學(xué)習(xí)', '帶動(dòng)', '人工智能', '飛速', '的', '發(fā)展'], ['機(jī)器', '學(xué)習(xí)', '和', '深度', '學(xué)習(xí)', '帶動(dòng)', '人工智能', '飛速', '的', '發(fā)展']]
下面操作就是把所有的分詞結(jié)果放到一個(gè)袋子(List)里面,也就是取并集,再去重,獲取對(duì)應(yīng)的特征詞。
#求并集
bag_of_words = [ x for item in segs_1 for x in item if x not in punctuation]
#去重
bag_of_words = list(set(bag_of_words))
print(bag_of_words)
得到的特征詞結(jié)果如下:
['飛速', '的', '深度', '人工智能', '發(fā)展', '和', '機(jī)器', '學(xué)習(xí)', '帶動(dòng)']
我們以上面特征詞的順序,完成詞袋化:
bag_of_word2vec = []
for sentence in tokenized:
tokens = [1 if token in sentence else 0 for token in bag_of_words ]
bag_of_word2vec.append(tokens)
最后得到詞袋向量:
[[1, 1, 0, 1, 1, 0, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1]]
上面的例子在編碼時(shí),對(duì)于 for 循環(huán)多次直接用到列表推導(dǎo)式。在 Python 中,列表推導(dǎo)式的效率比 for 快很多,尤其在數(shù)據(jù)量大的時(shí)候效果更明顯,建議多使用列表推導(dǎo)式。
(2)Gensim 構(gòu)建詞袋模型
下面我們介紹 Gensim 庫(kù)的使用,繼續(xù)沿用上面的例子:
from gensim import corpora
import gensim
#tokenized是去標(biāo)點(diǎn)之后的
dictionary = corpora.Dictionary(tokenized)
#保存詞典
dictionary.save('deerwester.dict')
print(dictionary)
這時(shí)我們得到的結(jié)果不全,但通過(guò)提示信息可知道共9個(gè)獨(dú)立的詞:
Dictionary(9 unique tokens: ['人工智能', '發(fā)展', '學(xué)習(xí)', '帶動(dòng)', '機(jī)器']...)
那我們?nèi)绾尾榭此性~呢?通過(guò)下面方法,可以查看到所有詞和對(duì)應(yīng)的下標(biāo):
#查看詞典和下標(biāo) id 的映射
print(dictionary.token2id)
最后結(jié)果如下:
{'人工智能': 0, '發(fā)展': 1, '學(xué)習(xí)': 2, '帶動(dòng)': 3, '機(jī)器': 4, '的': 5, '飛速': 6, '深度': 7, '和': 8}
根據(jù)得到的結(jié)果,我們同樣可以得到詞袋模型的特征向量。這里順帶提一下函數(shù) doc2bow(),作用只是計(jì)算每個(gè)不同單詞的出現(xiàn)次數(shù),將單詞轉(zhuǎn)換為其整數(shù)單詞 id 并將結(jié)果作為稀疏向量返回。
corpus = [dictionary.doc2bow(sentence) for sentence in segs_1]
print(corpus )
得到的稀疏向量結(jié)果如下:
[[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1)], [(0, 1), (1, 1), (2, 1), (3, 1), (5, 1), (6, 1), (7, 1)], [(0, 1), (1, 1), (2, 2), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1)]]
詞向量 (Word Embedding)
深度學(xué)習(xí)帶給自然語(yǔ)言處理最令人興奮的突破是詞向量(Word Embedding)技術(shù)。詞向量技術(shù)是將詞語(yǔ)轉(zhuǎn)化成為稠密向量。在自然語(yǔ)言處理應(yīng)用中,詞向量作為機(jī)器學(xué)習(xí)、深度學(xué)習(xí)模型的特征進(jìn)行輸入。因此,最終模型的效果很大程度上取決于詞向量的效果。
詞向量的概念
在 Word2Vec 出現(xiàn)之前,自然語(yǔ)言處理經(jīng)常把字詞進(jìn)行獨(dú)熱編碼,也就是 One-Hot Encoder。
大數(shù)據(jù) [0,0,0,0,0,0,0,1,0,……,0,0,0,0,0,0,0]
云計(jì)算[0,0,0,0,1,0,0,0,0,……,0,0,0,0,0,0,0]
機(jī)器學(xué)習(xí)[0,0,0,1,0,0,0,0,0,……,0,0,0,0,0,0,0]
人工智能[0,0,0,0,0,0,0,0,0,……,1,0,0,0,0,0,0]
比如上面的例子中,大數(shù)據(jù) 、云計(jì)算、機(jī)器學(xué)習(xí)和人工智能各對(duì)應(yīng)一個(gè)向量,向量中只有一個(gè)值為1,其余都為0。所以使用 One-Hot Encoder有以下問(wèn)題:
- 第一,詞語(yǔ)編碼是隨機(jī)的,向量之間相互獨(dú)立,看不出詞語(yǔ)之間可能存在的關(guān)聯(lián)關(guān)系。
- 第二,向量維度的大小取決于語(yǔ)料庫(kù)中詞語(yǔ)的多少,如果語(yǔ)料包含的所有詞語(yǔ)對(duì)應(yīng)的向量合為一個(gè)矩陣的話,那這個(gè)矩陣過(guò)于稀疏,并且會(huì)造成維度災(zāi)難。
而解決這個(gè)問(wèn)題的手段,就是使用向量表示(Vector Representations)。比如 Word2Vec 可以將 One-Hot Encoder 轉(zhuǎn)化為低維度的連續(xù)值,也就是稠密向量,并且其中意思相近的詞也將被映射到向量空間中相近的位置。經(jīng)過(guò)降維,在二維空間中,相似的單詞在空間中的距離也很接近。
這里簡(jiǎn)單給詞向量一個(gè)定義,詞向量就是要用某個(gè)固定維度的向量去表示單詞。也就是說(shuō)要把單詞變成固定維度的向量,作為機(jī)器學(xué)習(xí)(Machine Learning)或深度學(xué)習(xí)模型的特征向量輸入。
動(dòng)手實(shí)戰(zhàn)詞向量
(1)Word2Vec
Word2Vec 是 Google 團(tuán)隊(duì)2013年推出的,自提出后被廣泛應(yīng)用在自然語(yǔ)言處理任務(wù)中,并且受到它的啟發(fā),后續(xù)出現(xiàn)了更多形式的詞向量模型。Word2Vec 主要包含兩種模型:Skip-Gram 和 CBOW,值得一提的是,Word2Vec 詞向量可以較好地表達(dá)不同詞之間的相似和類比關(guān)系。
下面我們通過(guò)代碼實(shí)戰(zhàn)來(lái)體驗(yàn)一下 Word2Vec。通過(guò) pip install gensim 安裝好庫(kù)后,即可導(dǎo)入使用。
先導(dǎo)入 Gensim 中的 Word2Vec 和 jieba 分詞器,再引入從百度百科抓取的黃河和長(zhǎng)江的語(yǔ)料:
from gensim.models import Word2Vec
import jieba
#定義停用詞、標(biāo)點(diǎn)符號(hào)
punctuation = [",","。", ":", ";", ".", "'", '"', "’", "?", "/", "-", "+", "&", "(", ")"]
sentences = [
"長(zhǎng)江是中國(guó)第一大河,干流全長(zhǎng)6397公里(以沱沱河為源),一般稱6300公里。流域總面積一百八十余萬(wàn)平方公里,年平均入海水量約九千六百余億立方米。以干流長(zhǎng)度和入海水量論,長(zhǎng)江均居世界第三位。",
"黃河,中國(guó)古代也稱河,發(fā)源于中華人民共和國(guó)青海省巴顏喀拉山脈,流經(jīng)青海、四川、甘肅、寧夏、內(nèi)蒙古、陜西、山西、河南、山東9個(gè)省區(qū),最后于山東省東營(yíng)墾利縣注入渤海。干流河道全長(zhǎng)5464千米,僅次于長(zhǎng)江,為中國(guó)第二長(zhǎng)河。黃河還是世界第五長(zhǎng)河。",
"黃河,是中華民族的母親河。作為中華文明的發(fā)祥地,維系炎黃子孫的血脈.是中華民族民族精神與民族情感的象征。",
"黃河被稱為中華文明的母親河。公元前2000多年華夏族在黃河領(lǐng)域的中原地區(qū)形成、繁衍。",
"在蘭州的“黃河第一橋”內(nèi)蒙古托克托縣河口鎮(zhèn)以上的黃河河段為黃河上游。",
"黃河上游根據(jù)河道特性的不同,又可分為河源段、峽谷段和沖積平原三部分。 ",
"黃河,是中華民族的母親河。"
]
上面定義好語(yǔ)料,接下來(lái)進(jìn)行分詞,去標(biāo)點(diǎn)符號(hào)操作 :
sentences = [jieba.lcut(sen) for sen in sentences]
tokenized = []
for sentence in sentences:
words = []
for word in sentence:
if word not in punctuation:
words.append(word)
tokenized.append(words)
這樣我們獲取的語(yǔ)料在分詞之后,去掉了標(biāo)點(diǎn)符號(hào),如果做得更嚴(yán)謹(jǐn),大家可以去停用詞,然后進(jìn)行模型訓(xùn)練:
model = Word2Vec(tokenized, sg=1, size=100, window=5, min_count=2, negative=1, sample=0.001, hs=1, workers=4)
參數(shù)解釋如下:
- sg=1 是
skip-gram算法,對(duì)低頻詞敏感;默認(rèn) sg=0 為 CBOW 算法。 - size 是輸出詞向量的維數(shù),值太小會(huì)導(dǎo)致詞映射因?yàn)闆_突而影響結(jié)果,值太大則會(huì)耗內(nèi)存并使算法計(jì)算變慢,一般值取為100到200之間。
- window 是句子中當(dāng)前詞與目標(biāo)詞之間的最大距離,3表示在目標(biāo)詞前看3-b 個(gè)詞,后面看 b 個(gè)詞(b 在0-3之間隨機(jī))。
-
min_count是對(duì)詞進(jìn)行過(guò)濾,頻率小于min-count的單詞則會(huì)被忽視,默認(rèn)值為5。 - negative 和 sample 可根據(jù)訓(xùn)練結(jié)果進(jìn)行微調(diào),sample 表示更高頻率的詞被隨機(jī)下采樣到所設(shè)置的閾值,默認(rèn)值為 1e-3。
- hs=1 表示層級(jí) softmax 將會(huì)被使用,默認(rèn) hs=0 且 negative 不為0,則負(fù)采樣將會(huì)被選擇使用。
- 詳細(xì)參數(shù)說(shuō)明可查看 Word2Vec 源代碼。
訓(xùn)練后的模型可以保存與加載,如下代碼所示:
model.save('model') #保存模型
model = Word2Vec.load('model') #加載模型
模型訓(xùn)練好之后,接下來(lái)就可以使用模型,可以用來(lái)計(jì)算句子或者詞的相似性、最大匹配程度等。
例如,我們判斷一下黃河和黃河自己的相似度:
print(model.similarity('黃河', '黃河'))
結(jié)果輸出為:
1.0000000000000002
例如,當(dāng)輸入黃河和長(zhǎng)江來(lái)計(jì)算相似度的時(shí)候,結(jié)果就比較小,因?yàn)槲覀兊恼Z(yǔ)料實(shí)在太小了。
print(model.similarity('黃河', '長(zhǎng)江'))
結(jié)果輸出為:
-0.036808977457324699
下面我們預(yù)測(cè)最接近的詞,預(yù)測(cè)與黃河和母親河最接近,而與長(zhǎng)江不接近的詞:
print(model.most_similar(positive=['黃河', '母親河'], negative=['長(zhǎng)江']))
得到結(jié)果如下,可以根據(jù)相似度大小找到與黃河和母親河最接近的詞(實(shí)際處理建議增大數(shù)據(jù)量和去停用詞)。
[('是', 0.14632007479667664), ('以', 0.14630728960037231), ('長(zhǎng)河', 0.13878652453422546), ('河道', 0.13716217875480652), ('在', 0.11577725410461426), ('全長(zhǎng)', 0.10969121754169464), ('內(nèi)蒙古', 0.07590540498495102), ('入海', 0.06970417499542236), ('民族', 0.06064444035291672), ('中華文明', 0.057667165994644165)]
上面通過(guò)小數(shù)據(jù)量的語(yǔ)料實(shí)戰(zhàn),加強(qiáng)了對(duì) Word2Vec 的理解,總之 Word2Vec 是一種將詞變成詞向量的工具。通俗點(diǎn)說(shuō),只有這樣文本預(yù)料才轉(zhuǎn)化為計(jì)算機(jī)能夠計(jì)算的矩陣向量。
(2)Doc2Vec
Doc2Vec 是 Mikolov 在 Word2Vec 基礎(chǔ)上提出的另一個(gè)用于計(jì)算長(zhǎng)文本向量的工具。在 Gensim 庫(kù)中,Doc2Vec 與 Word2Vec 都極為相似。但兩者在對(duì)輸入數(shù)據(jù)的預(yù)處理上稍有不同,Doc2vec 接收一個(gè)由 LabeledSentence 對(duì)象組成的迭代器作為其構(gòu)造函數(shù)的輸入?yún)?shù)。其中,LabeledSentence 是 Gensim 內(nèi)建的一個(gè)類,它接收兩個(gè) List 作為其初始化的參數(shù):word list 和 label list。
Doc2Vec 也包括兩種實(shí)現(xiàn)方式:DBOW(Distributed Bag of Words)和 DM (Distributed Memory)。DBOW 和 DM 的實(shí)現(xiàn),二者在 gensim 庫(kù)中的實(shí)現(xiàn)用的是同一個(gè)方法,該方法中參數(shù) dm = 0 或者 dm=1 決定調(diào)用 DBOW 還是 DM。Doc2Vec 將文檔語(yǔ)料通過(guò)一個(gè)固定長(zhǎng)度的向量表達(dá)。
下面是 Gensim 中 Doc2Vec 模型的實(shí)戰(zhàn),我們把上述語(yǔ)料每一句話當(dāng)做一個(gè)文本,添加上對(duì)應(yīng)的標(biāo)簽。接下來(lái),定義數(shù)據(jù)預(yù)處理類,作用是給每個(gè)文章添加對(duì)應(yīng)的標(biāo)簽:
#定義數(shù)據(jù)預(yù)處理類,作用是給每個(gè)文章添加對(duì)應(yīng)的標(biāo)簽
from gensim.models.doc2vec import Doc2Vec,LabeledSentence
doc_labels = ["長(zhǎng)江","黃河","黃河","黃河","黃河","黃河","黃河"]
class LabeledLineSentence(object):
def __init__(self, doc_list, labels_list):
self.labels_list = labels_list
self.doc_list = doc_list
def __iter__(self):
for idx, doc in enumerate(self.doc_list):
yield LabeledSentence(words=doc,tags=[self.labels_list[idx]])
model = Doc2Vec(documents,dm=1, size=100, window=8, min_count=5, workers=4)
model.save('model')
model = Doc2Vec.load('model')
上面定義好了數(shù)據(jù)預(yù)處理函數(shù),我們將 Word2Vec 中分詞去標(biāo)點(diǎn)后的數(shù)據(jù),進(jìn)行轉(zhuǎn)換:
iter_data = LabeledLineSentence(tokenized, doc_labels)
得到一個(gè)數(shù)據(jù)集,我開(kāi)始定義模型參數(shù),這里 dm=1,采用了 Gensim 中的 DM 實(shí)現(xiàn)。
model = Doc2Vec(dm=1, size=100, window=8, min_count=5, workers=4)
model.build_vocab(iter_data)
接下來(lái)訓(xùn)練模型, 設(shè)置迭代次數(shù)1000次,start_alpha 為開(kāi)始學(xué)習(xí)率,end_alpha 與 start_alpha 線性遞減。
model.train(iter_data,total_examples=model.corpus_count,epochs=1000,start_alpha=0.01,end_alpha =0.001)
最后我們對(duì)模型進(jìn)行一些預(yù)測(cè):
#根據(jù)標(biāo)簽找最相似的,這里只有黃河和長(zhǎng)江,所以結(jié)果為長(zhǎng)江,并計(jì)算出了相似度
print(model.docvecs.most_similar('黃河'))
得到的結(jié)果:
[('長(zhǎng)江', 0.25543850660324097)]
然后對(duì)黃河和長(zhǎng)江標(biāo)簽做相似性計(jì)算:
print(model.docvecs.similarity('黃河','長(zhǎng)江'))
得到的結(jié)果:
0.25543848271351405
上面只是在小數(shù)據(jù)量進(jìn)行的小練習(xí),而最終影響模型準(zhǔn)確率的因素有:文檔的數(shù)量越多,文檔的相似性越好,也就是基于大數(shù)據(jù)量的模型訓(xùn)練。在工業(yè)界,Word2Vec 和 Doc2Vec 常見(jiàn)的應(yīng)用有:做相似詞計(jì)算;相關(guān)詞挖掘,在推薦系統(tǒng)中用在品牌、用戶、商品挖掘中;上下文預(yù)測(cè)句子;機(jī)器翻譯;作為特征輸入其他模型等。
總結(jié),本文只是簡(jiǎn)單的介紹了詞袋和詞向量模型的典型應(yīng)用,對(duì)于兩者的理論和其他詞向量模型,比如 TextRank 、FastText 和 GloVe 等,閱讀文末給出參考文獻(xiàn)將了解更多。
參考文獻(xiàn):
- https://radimrehurek.com/gensim/tut1.html
- https://radimrehurek.com/gensim/models/word2vec.html
- https://radimrehurek.com/gensim/summarization/summariser.html
- https://radimrehurek.com/gensim/models/fasttext.html
- https://nlp.stanford.edu/projects/glove/
如有侵權(quán)請(qǐng)聯(lián)系QQ:758230255刪除