通過前面幾個小節(jié)的學(xué)習(xí)董栽,我們現(xiàn)在已經(jīng)學(xué)會了如何獲取文本預(yù)料,然后分詞瞭稼,在分詞之后的結(jié)果上,我們可以提取文本的關(guān)鍵詞查看文本核心思想腻惠,進(jìn)而可以通過可視化技術(shù)把文檔從視覺的角度表達(dá)出來环肘。
下面,我們來看看集灌,文本數(shù)據(jù)如何轉(zhuǎn)換成計算機(jī)能夠計算的數(shù)據(jù)悔雹。這里介紹兩種常用的模型:詞袋和詞向量模型。
詞袋模型(Bag of Words Model)
詞袋模型的概念
先來看張圖绝页,從視覺上感受一下詞袋模型的樣子荠商。
詞袋模型看起來好像一個口袋把所有詞都裝進(jìn)去寂恬,但卻不完全如此续誉。在自然語言處理和信息檢索中作為一種簡單假設(shè),詞袋模型把文本(段落或者文檔)被看作是無序的詞匯集合初肉,忽略語法甚至是單詞的順序酷鸦,把每一個單詞都進(jìn)行統(tǒng)計,同時計算每個單詞出現(xiàn)的次數(shù)牙咏,常常被用在文本分類中臼隔,如貝葉斯算法、LDA 和 LSA 等妄壶。
動手實戰(zhàn)詞袋模型
(1)詞袋模型
本例中摔握,我們自己動手寫代碼看看詞袋模型是如何操作的。
首先丁寄,引入 jieba 分詞器氨淌、語料和停用詞(標(biāo)點符號集合,自己可以手動添加或者用一個文本字典代替)伊磺。
import jieba
#定義停用詞盛正、標(biāo)點符號
punctuation = [",","屑埋。", ":", "豪筝;", "?"]
#定義語料
content = ["機(jī)器學(xué)習(xí)帶動人工智能飛速的發(fā)展摘能。",
"深度學(xué)習(xí)帶動人工智能飛速的發(fā)展续崖。",
"機(jī)器學(xué)習(xí)和深度學(xué)習(xí)帶動人工智能飛速的發(fā)展。"
]
接下來团搞,我們先對語料進(jìn)行分詞操作严望,這里用到 lcut() 方法:
#分詞
segs_1 = [jieba.lcut(con) for con in content]
print(segs_1)
得到分詞后的結(jié)果如下:
[['機(jī)器', '學(xué)習(xí)', '帶動', '人工智能', '飛速', '的', '發(fā)展', '。'], ['深度', '學(xué)習(xí)', '帶動', '人工智能', '飛速', '的', '發(fā)展', '莺丑。'], ['機(jī)器', '學(xué)習(xí)', '和', '深度', '學(xué)習(xí)', '帶動', '人工智能', '飛速', '的', '發(fā)展', '著蟹。']]
因為中文語料帶有停用詞和標(biāo)點符號墩蔓,所以需要去停用詞和標(biāo)點符號,這里語料很小萧豆,我們直接去標(biā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)點符號后奸披,我們得到結(jié)果如下:
[['機(jī)器', '學(xué)習(xí)', '帶動', '人工智能', '飛速', '的', '發(fā)展'], ['深度', '學(xué)習(xí)', '帶動', '人工智能', '飛速', '的', '發(fā)展'], ['機(jī)器', '學(xué)習(xí)', '和', '深度', '學(xué)習(xí)', '帶動', '人工智能', '飛速', '的', '發(fā)展']]
下面操作就是把所有的分詞結(jié)果放到一個袋子(List)里面,也就是取并集涮雷,再去重阵面,獲取對應(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í)', '帶動']
我們以上面特征詞的順序洪鸭,完成詞袋化:
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]]
上面的例子在編碼時样刷,對于 for 循環(huán)多次直接用到列表推導(dǎo)式。在 Python 中览爵,列表推導(dǎo)式的效率比 for 快很多置鼻,尤其在數(shù)據(jù)量大的時候效果更明顯,建議多使用列表推導(dǎo)式蜓竹。
(2)Gensim 構(gòu)建詞袋模型
下面我們介紹 Gensim 庫的使用箕母,繼續(xù)沿用上面的例子:
from gensim import corpora
import gensim
#tokenized是去標(biāo)點之后的
dictionary = corpora.Dictionary(tokenized)
#保存詞典
dictionary.save('deerwester.dict')
print(dictionary)
這時我們得到的結(jié)果不全,但通過提示信息可知道共9個獨立的詞:
Dictionary(9 unique tokens: ['人工智能', '發(fā)展', '學(xué)習(xí)', '帶動', '機(jī)器']...)
那我們?nèi)绾尾榭此性~呢俱济?通過下面方法嘶是,可以查看到所有詞和對應(yīng)的下標(biāo):
#查看詞典和下標(biāo) id 的映射
print(dictionary.token2id)
最后結(jié)果如下:
{'人工智能': 0, '發(fā)展': 1, '學(xué)習(xí)': 2, '帶動': 3, '機(jī)器': 4, '的': 5, '飛速': 6, '深度': 7, '和': 8}
根據(jù)得到的結(jié)果,我們同樣可以得到詞袋模型的特征向量蛛碌。這里順帶提一下函數(shù) doc2bow()聂喇,作用只是計算每個不同單詞的出現(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í)帶給自然語言處理最令人興奮的突破是詞向量(Word Embedding)技術(shù)希太。詞向量技術(shù)是將詞語轉(zhuǎn)化成為稠密向量。在自然語言處理應(yīng)用中浮梢,詞向量作為機(jī)器學(xué)習(xí)跛十、深度學(xué)習(xí)模型的特征進(jìn)行輸入。因此秕硝,最終模型的效果很大程度上取決于詞向量的效果芥映。
詞向量的概念
在 Word2Vec 出現(xiàn)之前,自然語言處理經(jīng)常把字詞進(jìn)行獨熱編碼远豺,也就是 One-Hot Encoder奈偏。
大數(shù)據(jù) [0,0,0,0,0,0,0,1,0,……,0,0,0,0,0,0,0]
云計算[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ī)器學(xué)習(xí)和人工智能各對應(yīng)一個向量,向量中只有一個值為1枉证,其余都為0矮男。所以使用 One-Hot Encoder有以下問題:
- 第一,詞語編碼是隨機(jī)的室谚,向量之間相互獨立毡鉴,看不出詞語之間可能存在的關(guān)聯(lián)關(guān)系。
- 第二秒赤,向量維度的大小取決于語料庫中詞語的多少猪瞬,如果語料包含的所有詞語對應(yīng)的向量合為一個矩陣的話,那這個矩陣過于稀疏入篮,并且會造成維度災(zāi)難陈瘦。
而解決這個問題的手段,就是使用向量表示(Vector Representations)崎弃。比如 Word2Vec 可以將 One-Hot Encoder 轉(zhuǎn)化為低維度的連續(xù)值甘晤,也就是稠密向量,并且其中意思相近的詞也將被映射到向量空間中相近的位置饲做。經(jīng)過降維,在二維空間中遏弱,相似的單詞在空間中的距離也很接近盆均。
這里簡單給詞向量一個定義,詞向量就是要用某個固定維度的向量去表示單詞漱逸。也就是說要把單詞變成固定維度的向量泪姨,作為機(jī)器學(xué)習(xí)(Machine Learning)或深度學(xué)習(xí)模型的特征向量輸入。
動手實戰(zhàn)詞向量
(1)Word2Vec
Word2Vec 是 Google 團(tuán)隊2013年推出的饰抒,自提出后被廣泛應(yīng)用在自然語言處理任務(wù)中肮砾,并且受到它的啟發(fā),后續(xù)出現(xiàn)了更多形式的詞向量模型袋坑。Word2Vec 主要包含兩種模型:Skip-Gram 和 CBOW仗处,值得一提的是,Word2Vec 詞向量可以較好地表達(dá)不同詞之間的相似和類比關(guān)系枣宫。
下面我們通過代碼實戰(zhàn)來體驗一下 Word2Vec婆誓。通過 pip install gensim
安裝好庫后,即可導(dǎo)入使用也颤。
先導(dǎo)入 Gensim 中的 Word2Vec 和 jieba 分詞器洋幻,再引入從百度百科抓取的黃河和長江的語料:
from gensim.models import Word2Vec
import jieba
#定義停用詞、標(biāo)點符號
punctuation = [",","翅娶。", ":", ";", ".", "'", '"', "’", "?", "/", "-", "+", "&", "(", ")"]
sentences = [
"長江是中國第一大河文留,干流全長6397公里(以沱沱河為源)好唯,一般稱6300公里。流域總面積一百八十余萬平方公里燥翅,年平均入海水量約九千六百余億立方米渠啊。以干流長度和入海水量論,長江均居世界第三位权旷。",
"黃河替蛉,中國古代也稱河,發(fā)源于中華人民共和國青海省巴顏喀拉山脈拄氯,流經(jīng)青海躲查、四川、甘肅译柏、寧夏镣煮、內(nèi)蒙古、陜西鄙麦、山西典唇、河南、山東9個省區(qū)胯府,最后于山東省東營墾利縣注入渤海介衔。干流河道全長5464千米,僅次于長江骂因,為中國第二長河炎咖。黃河還是世界第五長河。",
"黃河,是中華民族的母親河寒波。作為中華文明的發(fā)祥地,維系炎黃子孫的血脈.是中華民族民族精神與民族情感的象征乘盼。",
"黃河被稱為中華文明的母親河。公元前2000多年華夏族在黃河領(lǐng)域的中原地區(qū)形成俄烁、繁衍绸栅。",
"在蘭州的“黃河第一橋”內(nèi)蒙古托克托縣河口鎮(zhèn)以上的黃河河段為黃河上游。",
"黃河上游根據(jù)河道特性的不同页屠,又可分為河源段粹胯、峽谷段和沖積平原三部分。 ",
"黃河,是中華民族的母親河卷中。"
]
上面定義好語料矛双,接下來進(jìn)行分詞,去標(biā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)
這樣我們獲取的語料在分詞之后蟆豫,去掉了標(biā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
算法栈幸,對低頻詞敏感愤估;默認(rèn) sg=0 為 CBOW 算法。 - size 是輸出詞向量的維數(shù)速址,值太小會導(dǎo)致詞映射因為沖突而影響結(jié)果玩焰,值太大則會耗內(nèi)存并使算法計算變慢,一般值取為100到200之間芍锚。
- window 是句子中當(dāng)前詞與目標(biāo)詞之間的最大距離昔园,3表示在目標(biāo)詞前看3-b 個詞,后面看 b 個詞(b 在0-3之間隨機(jī))并炮。
-
min_count
是對詞進(jìn)行過濾默刚,頻率小于min-count
的單詞則會被忽視,默認(rèn)值為5逃魄。 - negative 和 sample 可根據(jù)訓(xùn)練結(jié)果進(jìn)行微調(diào)荤西,sample 表示更高頻率的詞被隨機(jī)下采樣到所設(shè)置的閾值,默認(rèn)值為 1e-3伍俘。
- hs=1 表示層級 softmax 將會被使用邪锌,默認(rèn) hs=0 且 negative 不為0,則負(fù)采樣將會被選擇使用癌瘾。
- 詳細(xì)參數(shù)說明可查看 Word2Vec 源代碼觅丰。
訓(xùn)練后的模型可以保存與加載,如下代碼所示:
model.save('model') #保存模型
model = Word2Vec.load('model') #加載模型
模型訓(xùn)練好之后柳弄,接下來就可以使用模型舶胀,可以用來計算句子或者詞的相似性、最大匹配程度等碧注。
例如,我們判斷一下黃河和黃河自己的相似度:
print(model.similarity('黃河', '黃河'))
結(jié)果輸出為:
1.0000000000000002
例如糖赔,當(dāng)輸入黃河和長江來計算相似度的時候萍丐,結(jié)果就比較小,因為我們的語料實在太小了放典。
print(model.similarity('黃河', '長江'))
結(jié)果輸出為:
-0.036808977457324699
下面我們預(yù)測最接近的詞逝变,預(yù)測與黃河和母親河最接近,而與長江不接近的詞:
print(model.most_similar(positive=['黃河', '母親河'], negative=['長江']))
得到結(jié)果如下奋构,可以根據(jù)相似度大小找到與黃河和母親河最接近的詞(實際處理建議增大數(shù)據(jù)量和去停用詞)壳影。
[('是', 0.14632007479667664), ('以', 0.14630728960037231), ('長河', 0.13878652453422546), ('河道', 0.13716217875480652), ('在', 0.11577725410461426), ('全長', 0.10969121754169464), ('內(nèi)蒙古', 0.07590540498495102), ('入海', 0.06970417499542236), ('民族', 0.06064444035291672), ('中華文明', 0.057667165994644165)]
上面通過小數(shù)據(jù)量的語料實戰(zhàn),加強了對 Word2Vec 的理解弥臼,總之 Word2Vec 是一種將詞變成詞向量的工具宴咧。通俗點說,只有這樣文本預(yù)料才轉(zhuǎn)化為計算機(jī)能夠計算的矩陣向量径缅。
(2)Doc2Vec
Doc2Vec 是 Mikolov 在 Word2Vec 基礎(chǔ)上提出的另一個用于計算長文本向量的工具掺栅。在 Gensim 庫中烙肺,Doc2Vec 與 Word2Vec 都極為相似。但兩者在對輸入數(shù)據(jù)的預(yù)處理上稍有不同氧卧,Doc2vec 接收一個由 LabeledSentence 對象組成的迭代器作為其構(gòu)造函數(shù)的輸入?yún)?shù)桃笙。其中,LabeledSentence 是 Gensim 內(nèi)建的一個類沙绝,它接收兩個 List 作為其初始化的參數(shù):word list 和 label list搏明。
Doc2Vec 也包括兩種實現(xiàn)方式:DBOW(Distributed Bag of Words)和 DM (Distributed Memory)。DBOW 和 DM 的實現(xiàn)闪檬,二者在 gensim 庫中的實現(xiàn)用的是同一個方法星著,該方法中參數(shù) dm = 0 或者 dm=1 決定調(diào)用 DBOW 還是 DM。Doc2Vec 將文檔語料通過一個固定長度的向量表達(dá)谬以。
下面是 Gensim 中 Doc2Vec 模型的實戰(zhàn)强饮,我們把上述語料每一句話當(dāng)做一個文本,添加上對應(yīng)的標(biāo)簽为黎。接下來邮丰,定義數(shù)據(jù)預(yù)處理類,作用是給每個文章添加對應(yīng)的標(biāo)簽:
#定義數(shù)據(jù)預(yù)處理類铭乾,作用是給每個文章添加對應(yīng)的標(biāo)簽
from gensim.models.doc2vec import Doc2Vec,LabeledSentence
doc_labels = ["長江","黃河","黃河","黃河","黃河","黃河","黃河"]
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)點后的數(shù)據(jù),進(jìn)行轉(zhuǎn)換:
iter_data = LabeledLineSentence(tokenized, doc_labels)
得到一個數(shù)據(jù)集炕檩,我開始定義模型參數(shù)斗蒋,這里 dm=1,采用了 Gensim 中的 DM 實現(xiàn)笛质。
model = Doc2Vec(dm=1, size=100, window=8, min_count=5, workers=4)
model.build_vocab(iter_data)
接下來訓(xùn)練模型泉沾, 設(shè)置迭代次數(shù)1000次,start_alpha
為開始學(xué)習(xí)率妇押,end_alpha
與 start_alpha
線性遞減跷究。
最后我們對模型進(jìn)行一些預(yù)測:
#根據(jù)標(biāo)簽找最相似的,這里只有黃河和長江敲霍,所以結(jié)果為長江俊马,并計算出了相似度
print(model.docvecs.most_similar('黃河'))
得到的結(jié)果:
[('長江', 0.25543850660324097)]
然后對黃河和長江標(biāo)簽做相似性計算:
print(model.docvecs.similarity('黃河','長江'))
得到的結(jié)果:
0.25543848271351405
上面只是在小數(shù)據(jù)量進(jìn)行的小練習(xí),而最終影響模型準(zhǔn)確率的因素有:文檔的數(shù)量越多肩杈,文檔的相似性越好柴我,也就是基于大數(shù)據(jù)量的模型訓(xùn)練。在工業(yè)界扩然,Word2Vec 和 Doc2Vec 常見的應(yīng)用有:做相似詞計算气筋;相關(guān)詞挖掘怪得,在推薦系統(tǒng)中用在品牌摆舟、用戶、商品挖掘中嘉抓;上下文預(yù)測句子;機(jī)器翻譯晕窑;作為特征輸入其他模型等抑片。
總結(jié),本文只是簡單的介紹了詞袋和詞向量模型的典型應(yīng)用杨赤,對于兩者的理論和其他詞向量模型敞斋,比如 TextRank 、FastText 和 GloVe 等疾牲,閱讀文末給出參考文獻(xiàn)將了解更多植捎。
參考文獻(xiàn):