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