如何使用Scikit-learn實(shí)現(xiàn)用于機(jī)器學(xué)習(xí)的文本數(shù)據(jù)準(zhǔn)備
在使用文本數(shù)據(jù)來(lái)搭建預(yù)測(cè)模型前,都需要特殊的準(zhǔn)備工作。
文本首先要通過(guò)解析來(lái)提取單詞,這一過(guò)程稱為詞條化。然后單詞需要編碼為整數(shù)或浮點(diǎn)值,作為機(jī)器學(xué)習(xí)算法的輸入,稱為特征提取(或量化)。
scikit-learn提供了簡(jiǎn)單的工具幫助我們對(duì)你的文本數(shù)據(jù)進(jìn)行詞條化和特征提取。
在這篇文章中,你會(huì)學(xué)到在Python中如何使用scikit-learn實(shí)現(xiàn)用于機(jī)器學(xué)習(xí)的文本數(shù)據(jù)準(zhǔn)備。
在讀完這篇文章后,你會(huì)了解到:
- 如何使用CountVectorizer將文本的轉(zhuǎn)化成單詞頻數(shù)向量。
- 如何使用TfidfVectorizer提取文本的單詞權(quán)重向量。
- 如何使用HashingVectorizer將文本映射到特征索引。
讓我們開(kāi)始吧。
“詞袋(Bag-of-words)”模型
在使用機(jī)器學(xué)習(xí)算法時(shí),我們不能直接用文本進(jìn)行運(yùn)算。相反,我們需要將文本轉(zhuǎn)換成數(shù)字。
我們想對(duì)文檔進(jìn)行分類時(shí),每個(gè)文檔作為“輸入”,文檔的類別標(biāo)簽是我們預(yù)測(cè)算法的“輸出”。算法只能接受數(shù)字向量作為輸入,所以需要將文檔轉(zhuǎn)換成固定長(zhǎng)度的數(shù)字向量。
機(jī)器學(xué)習(xí)領(lǐng)域有一個(gè)簡(jiǎn)單且有效的模型,適用于文本文檔,叫做“詞袋”(Bag-of-Words)模型,簡(jiǎn)稱為BOW。
該模型的簡(jiǎn)單之處在于,它舍棄了單詞中的所有順序信息,并主要關(guān)注文檔中單詞的出現(xiàn)頻率。
這一點(diǎn)可以通過(guò)分配給每個(gè)單詞一個(gè)唯一的數(shù)字來(lái)實(shí)現(xiàn)。這樣一來(lái),我們看到的任何文檔都可以編碼成一個(gè)固定長(zhǎng)度的向量,長(zhǎng)度為已知單詞所構(gòu)成的詞匯表的長(zhǎng)度。該向量中每個(gè)位置的值是編碼文檔中的每個(gè)單詞出現(xiàn)的次數(shù)或頻率。
這就是“詞袋”模型,我們只關(guān)心編碼方法,能表示哪些詞語(yǔ)在文檔中出現(xiàn)了,或者他們?cè)诰幋a文檔中出現(xiàn)的頻率,而不考慮任何關(guān)于順序的信息。
這個(gè)簡(jiǎn)單的方法有很多種擴(kuò)展,既可以更好地解釋“單詞”的含義,也可以定義向量中每個(gè)單詞的編碼方式。
scikit-learn提供了3種可供我們使用的不同方法,我們將簡(jiǎn)要地看一下每種方法。
CountVectorizer——量化單詞數(shù)量
CountVectorizer提供了一種簡(jiǎn)單的方法,不僅可以將文本文檔的數(shù)據(jù)集轉(zhuǎn)化成詞條并建立一個(gè)已知單詞的詞匯表,而且還可以用該詞匯表對(duì)新文本進(jìn)行編碼。
使用方法如下:
- 創(chuàng)建CountVectorizer類的一個(gè)實(shí)例。
- 調(diào)用fit()函數(shù),通過(guò)學(xué)習(xí)從一個(gè)或多個(gè)文檔中得出一個(gè)詞匯表。
- 對(duì)一或多個(gè)文檔應(yīng)用transform()函數(shù),將每個(gè)文檔編碼成一個(gè)向量。
編碼得到的向量能夠返回整個(gè)詞匯表的長(zhǎng)度,以及每個(gè)單詞在該文檔中出現(xiàn)的次數(shù)。
由于這些向量含有許多零值,所以我們稱之為稀疏的。Python在scipy.sparse庫(kù)中提供了一種處理這類稀疏向量的有效方法。
調(diào)用transform()所返回的向量是稀疏向量,你可以將它們轉(zhuǎn)換為numpy數(shù)組,看起來(lái)更直觀也更好理解,這一步可以通過(guò)調(diào)用toarray()函數(shù)完成。
下面是一個(gè)使用CountVectorizer來(lái)詞條化、構(gòu)造詞匯表,以及編碼文檔的示例。
- from sklearn.feature_extraction.text import CountVectorizer
- # 文本文檔列表
- text = ["The quick brown fox jumped over the lazy dog."]
- # 構(gòu)造變換函數(shù)
- vectorizer = CountVectorizer()
- # 詞條化以及建立詞匯表
- vectorizer.fit(text)
- # 總結(jié)
- print(vectorizer.vocabulary_)
- # 編碼文檔
- vector = vectorizer.transform(text)
- # 總結(jié)編碼文檔
- print(vector.shape)
- print(type(vector))
- print(vector.toarray())
從上例中可以看到,我們通過(guò)詞匯表來(lái)查看到底是什么被詞條化了:
- print(vectorizer.vocabulary_)
可以看到,所有單詞默認(rèn)情況下是小寫(xiě),并且忽略掉標(biāo)點(diǎn)符號(hào)。詞條化的這些參數(shù)以及其他方面是可配置的,我建議你在API文檔中查看所有選項(xiàng)。
運(yùn)行這個(gè)示例,首先會(huì)顯示出詞匯表,然后顯示出編碼文檔的形狀。我們可以看到,詞匯表中有8個(gè)單詞,于是編碼向量的長(zhǎng)度為8。
可以看出,編碼向量是一個(gè)稀疏矩陣。***,我們可以看到以數(shù)組形式出現(xiàn)的編碼向量,顯示出每個(gè)單詞的出現(xiàn)次數(shù)為1,除了索引號(hào)為7的單詞出現(xiàn)次數(shù)為2。
- {'dog': 1, 'fox': 2, 'over': 5, 'brown': 0, 'quick': 6, 'the': 7, 'lazy': 4, 'jumped': 3}
- (1, 8)
- <class 'scipy.sparse.csr.csr_matrix'>
- [[1 1 1 1 1 1 1 2]]
重要的是,該量化方法可以用于含有詞匯表中沒(méi)有出現(xiàn)的單詞的文檔。這些單詞會(huì)被忽略掉,然后在得到的向量結(jié)果中不會(huì)給出出現(xiàn)次數(shù)。
下面是一個(gè)使用上述的詞條化工具對(duì)文檔進(jìn)行編碼的示例,該文檔中含有一個(gè)詞匯表中的詞,以及一個(gè)不在詞匯表中的詞。
- # 編碼其他文檔
- text2 = ["the puppy"]
- vector = vectorizer.transform(text2)
- print(vector.toarray())
運(yùn)行示例,顯示出編碼稀疏向量的矩陣形式,可以看出詞匯表中的單詞出現(xiàn)了1次,而沒(méi)在詞匯表中的單詞完全被忽略了。
- [[0 0 0 0 0 0 0 1]]
編碼的向量可以直接用于機(jī)器學(xué)習(xí)算法。
TfidfVectorizer——計(jì)算單詞權(quán)重
統(tǒng)計(jì)單詞出現(xiàn)次數(shù)是一個(gè)很好的切入點(diǎn),但也是很基礎(chǔ)的特征。
簡(jiǎn)單的次數(shù)統(tǒng)計(jì)的一個(gè)問(wèn)題在于,有些單詞,例如“the”會(huì)出現(xiàn)很多次,它們的統(tǒng)計(jì)數(shù)量對(duì)于編碼向量沒(méi)有太大意義。
一個(gè)替代方法是統(tǒng)計(jì)單詞權(quán)重,目前***的方法是TF-IDF。這是一個(gè)縮寫(xiě)詞,代表“詞頻-逆文檔頻率”(Term Frequency–Inverse Document Frequency),代表一個(gè)詞對(duì)于一個(gè)文檔的重要程度。
詞頻(Term Frequency):指的是某一個(gè)給定的詞語(yǔ)在一篇文檔中出現(xiàn)的次數(shù)。
逆文檔頻率(Inverse Document Frequency):?jiǎn)卧~在文檔中出現(xiàn)的頻率越高,IDF值越低。
撇開(kāi)數(shù)學(xué)不說(shuō),TF-IDF給出的是單詞權(quán)重,會(huì)把更有意思的單詞標(biāo)注出來(lái),例如僅在某篇文檔中頻率很高但不會(huì)在所有文檔中都頻繁出現(xiàn)的詞。
TfidfVectorizer可以詞條化文檔,學(xué)習(xí)詞匯表以及逆文檔頻率權(quán)重,并且可以編碼新文檔?;蛘?,如果你已經(jīng)用CountVectorizer學(xué)習(xí)得到了向量,你可以對(duì)它使用Tfidftransformer函數(shù),計(jì)算逆文檔頻率并且開(kāi)始編碼文件。
同樣的,創(chuàng)建(create)、擬合(fit)以及變換(transform)函數(shù)的調(diào)用都與CountVectorizer相同。
下面是一個(gè)使用TfidfVectorizer來(lái)學(xué)習(xí)詞匯表和3篇小文檔的逆文檔頻率的示例,并對(duì)其中一篇文檔進(jìn)行編碼。
- from sklearn.feature_extraction.text import TfidfVectorizer
- # 文本文檔列表
- text = ["The quick brown fox jumped over the lazy dog.",
- "The dog.",
- "The fox"]
- # 創(chuàng)建變換函數(shù)
- vectorizer = TfidfVectorizer()
- # 詞條化以及創(chuàng)建詞匯表
- vectorizer.fit(text)
- # 總結(jié)
- print(vectorizer.vocabulary_)
- print(vectorizer.idf_)
- # 編碼文檔
- vector = vectorizer.transform([text[0]])
- # 總結(jié)編碼文檔
- print(vector.shape)
- print(vector.toarray())
上例中,我們從文檔中學(xué)到了含有8個(gè)單詞的詞匯表,在輸出向量中,每個(gè)單詞都分配了一個(gè)唯一的整數(shù)索引。
我們計(jì)算了詞匯表中每個(gè)單詞的逆文檔頻率,給觀測(cè)到的最常出現(xiàn)的單詞“the”(索引號(hào)為7)分配了***的分?jǐn)?shù)1.0。
最終,***個(gè)文檔被編碼成一個(gè)8個(gè)元素的稀疏矩陣,我們可以查看每個(gè)單詞的最終權(quán)重分?jǐn)?shù),可以看到“the”、“fox”,以及“dog”的值與詞匯表中其他單詞的值不同。
- {'fox': 2, 'lazy': 4, 'dog': 1, 'quick': 6, 'the': 7, 'over': 5, 'brown': 0, 'jumped': 3}
- [ 1.69314718 1.28768207 1.28768207 1.69314718 1.69314718 1.69314718
- 1.69314718 1. ]
- (1, 8)
- [[ 0.36388646 0.27674503 0.27674503 0.36388646 0.36388646 0.36388646
- 0.36388646 0.42983441]]
這些分?jǐn)?shù)被歸一化為0到1之間的值,編碼的文檔向量可以直接用于大多數(shù)機(jī)器學(xué)習(xí)算法。
HashingVectorizer——哈希量化文本
單詞頻率和權(quán)重是很有用的,但是當(dāng)詞匯表變得很大時(shí),以上兩種方法就會(huì)出現(xiàn)局限性。
反過(guò)來(lái),這將需要巨大的向量來(lái)編碼文檔,并對(duì)內(nèi)存要求很高,而且會(huì)減慢算法的速度。
一種很好的方法是使用單向哈希方法來(lái)將單詞轉(zhuǎn)化成整數(shù)。好處是該方法不需要詞匯表,可以選擇任意長(zhǎng)的固定長(zhǎng)度向量。缺點(diǎn)是哈希量化是單向的,因此無(wú)法將編碼轉(zhuǎn)換回單詞(對(duì)與許多有監(jiān)督的學(xué)習(xí)任務(wù)來(lái)說(shuō)或許并不重要)。
HashingVectorizer類實(shí)現(xiàn)了這一方法,所以可以使用它對(duì)單詞進(jìn)行連續(xù)哈希量化,然后按需求詞條化和編碼文檔。
下面是對(duì)單一文檔使用HashingVectorizer進(jìn)行編碼的示例。
我們選擇了一個(gè)固定長(zhǎng)度為20的任意向量。這個(gè)值對(duì)應(yīng)哈希函數(shù)的范圍,小的值(例如20)可能會(huì)導(dǎo)致哈希碰撞。在之前的計(jì)算機(jī)科學(xué)課程中,我們介紹過(guò)一些啟發(fā)式算法,可以根據(jù)估計(jì)的詞匯量來(lái)選擇哈希長(zhǎng)度和碰撞概率。
要注意這種量化方法不要求調(diào)用函數(shù)來(lái)對(duì)訓(xùn)練數(shù)據(jù)文件進(jìn)行擬合。相反,在實(shí)例化之后,它可以直接用于編碼文檔。
- from sklearn.feature_extraction.text import HashingVectorizer
- # 文本文檔列表
- text = ["The quick brown fox jumped over the lazy dog."]
- # 創(chuàng)建變換函數(shù)
- vectorizer = HashingVectorizer(n_features=20)
- # 編碼文檔
- vector = vectorizer.transform(text)
- # 總結(jié)編碼文檔
- print(vector.shape)
- print(vector.toarray())
運(yùn)行該示例代碼可以把樣例文檔編碼成一個(gè)含有20個(gè)元素的稀疏矩陣。
編碼文檔的值對(duì)應(yīng)于正則化的單詞計(jì)數(shù),默認(rèn)值在-1到1之間,但是可以修改默認(rèn)設(shè)置,然后設(shè)置成整數(shù)計(jì)數(shù)值。
- (1, 20)
- [[ 0. 0. 0. 0. 0. 0.33333333
- 0. -0.33333333 0.33333333 0. 0. 0.33333333
- 0. 0. 0. -0.33333333 0. 0.
- -0.66666667 0. ]]
深度閱讀
這一節(jié)我們?yōu)榇蠹姨峁┝艘恍╆P(guān)于這篇文章的深度閱讀材料。
自然語(yǔ)言處理
scikit-learn
- scikit-learn使用手冊(cè)4.2節(jié),特征提取。
- sckit-learn特征提取API。
- scikit-learn教程:文本數(shù)據(jù)處理。
類API
- CountVectorizer scikit-learn API
- TfidfVectorizer scikit-learn API
- TfidfTransformer scikit-learn API
- HashingVectorizer scikit-learn API
總結(jié)
在這篇教程中,你會(huì)學(xué)習(xí)到如何用scikit-learn來(lái)準(zhǔn)備用于機(jī)器學(xué)習(xí)的文本數(shù)據(jù)。
我們只是在這些例子中接觸了皮毛,我想強(qiáng)調(diào)的是這些類有許多設(shè)置細(xì)節(jié)會(huì)影響文檔詞條化的結(jié)果,值得我們繼續(xù)探究。