如何使用 scikit-learn 為機器學習準備文字資料
文字資料需要特殊處理,然後才能開始將其用於預測建模。
我們需要解析文字,以刪除被稱為標記化的單詞。然後,這些詞還需要被編碼為整型或浮點型,以用作機器學習演算法的輸入,這一過程稱為特徵提取(或向量化)。
scikit-learn 庫提供易於使用的工具來對文字資料進行標記和特徵提取。
在本教程中,您可以學到如何使用 scikit-learn 為 Python 中的預測建模準備文字資料。
完成本教程後,您可以學到:
- 如何使用 CountVectorizer 將文字轉換為文字計數向量。
- 如何使用 TfidfVectorizer 將文字轉換為詞頻向量。
- 如何使用 HashingVectorizer 將文字轉換為唯一的整數。
讓我們開始吧。
詞袋模型( Bag-of-Words Model )
使用機器學習演算法時,我們不能直接使用文字。
相反,我們需要將文字轉換為數字。
我們可能想對文件進行分類,每一類文件都是“輸入”,而類別標籤是我們預測演算法的“輸出”。演算法將數字向量作為輸入,因此我們需要將文件轉換為固定長度的數字向量。
在機器學習中,Bag-of-Words 模型(BoW)是一種簡單而有效的讓計算機“理解”文字文件的模型。
這個模型非常簡單,它移除了單詞的諸如詞序、語法等順序資訊,只關注文件中該單詞的出現情況。
上面這一步可以通過為每個單詞分配一個唯一的編碼來完成。我們所看到的任何文件都可以被編碼為一個固定長度的向量,其長度為文件中全部已知單詞的詞彙量。向量中每個位置的值可以用編碼文件中每個單詞的出現個數或頻率填充。
在詞袋模型中,我們只關心編碼方案,而編碼方案描述了文件中出現了什麼單詞,以及這些單詞在編碼文件中出現的頻率,而沒有任何關於順序的資訊。
有很多方法來擴充套件這個簡單的方法,例如,我們可以想辦法更好地解釋一個單詞的含義,或是更好地規定向量中每個單詞的編碼方式。
scikit-learn 庫提供了3種不同的方案供我們使用,下面簡要地介紹一下。
使用 CountVectorizer 計算字數
CountVectorizer 提供了一個簡單的方法,既可以標記文字文件的集合, 也可以生成每個已知單詞的索引, 還可以使用這一套索引對新文件進行編碼。
下面是一種使用方法:
- 例項化一個 CountVectorizer
- 呼叫 fit() 函式以從一個或多個文件中建立索引。
- 根據需要在一個或多個文件中呼叫 transform() 函式,將每個文件編碼為一個向量。
最終會返回一個已編碼的向量, 其長度為索引的個數,該向量還攜帶有文件中每個單詞出現的次數資訊。
包含很多零的向量被稱為稀疏向量。Python 的 scipy.sparse 包中提供了一種處理稀疏向量的有效方法。
呼叫 transform() 返回的向量是稀疏向量,這裡可以通過呼叫 toarray() 函式將它們轉換回 numpy 陣列以便檢視並更好地理解這個過程。
下面是使用 CountVectorizer 標記,構建索引,然後編碼文件的示例。
from sklearn.feature_extraction.text import CountVectorizer
# 下面是一個文字文件的列表
text = ["The quick brown fox jumped over the lazy dog."]
# 例項化 CountVectorizer 類
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。
從接下來輸出的型別中可以看出,編碼向量是一個稀疏向量。而最後的輸出是編碼向量的陣列版本,其表達的含義是,索引值為 7 的單詞出現次數為 2,其餘單詞出現次數為 1。
{'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())
執行這個例子,會輸出編碼的稀疏向量的陣列版本,從這個輸出中可以看出,在詞彙中出現的單詞的沒有被忽略,而另一個不在詞彙中的單詞被忽略了。
[[0 0 0 0 0 0 0 1]]
這之後,編碼向量就可以直接使用到機器學習演算法中了。
使用 TfidfVectorizer 統計詞頻
單詞計數是一個非常好、非常簡單的起點。
不過,簡單計數也存在不足。例如,簡單計數中像“ the ” 這樣的詞會出現很多次,在編碼的向量中,這樣的單詞計數會很大,卻沒有太大意義。
除了統計個數外的另一種方法是計算詞頻,到目前為止,最流行的方法是TF-IDF。TF-IDF 是 Term Frequency - Inverse Document Frequency 的首字母縮寫詞,它是分配給每個單詞的結果分數的組成部分。
- 詞頻(Term Frequency):該值表示給定單詞在這份文件中出現的頻率。
- 逆向檔案頻率(Inverse Document Frequency):該值用於降低其他文件中普遍出現的單詞的最終評分。
沒有進入數學,TF-IDF是詞頻分數,可以突出個性化的單詞,例如在只在這份文件中頻繁出現,但其他文件中較少出現的單詞。
TfidfVectorizer 將標記檔案、建立索引、求出逆文件頻率權重,並允許您編碼新的檔案。或者,如果您已經有了一個已經訓練過的 CountVectorizer,您可以將其與 TfidfTransformer 一起使用,以計算逆文件頻率並開始編碼文件。
TfidfVectorizer 的例項化、擬合和轉換方法和 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(text0)
# 檢視編碼後的向量
print(vector.shape)
print(vector.toarray())
從文件中學習 8 個單詞的得到索引,並且每個單詞在輸出向量中被分配唯一的整數索引值。
計算每個單詞的逆文件頻率,將最低分數 1.0 分配給最常見的詞:索引值為 7 的“the”。
最後,第一個文件被編碼為一個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 這樣的小值可能導致散列表衝突。回想電腦科學課裡相關的知識,這裡可以使用試探法,根據估計的詞彙量的大小和碰撞概率來挑選雜湊長度。
請注意,這個向量化器不需要呼叫 fit() 函式來訓練資料文件。例項化之後,它可以直接用於編碼文件。
from sklearn.feature_extraction.text import HashingVectorizer
# 下面是一個文字文件的列表
text = ["The quick brown fox jumped over the lazy dog."]
# 例項化 HashingVectorizer
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. ]]
進一步探究
如果您想進行深入研究,下面提供更多有關該主題的資源。
自然語言處理
sciki-learn
- 4.2節 特徵提取,scikit-learn使用者指南
- sckit-learn特徵提取API
- 使用文字資料,scikit學習教程
API
- CountVectorizer scikit-learn API
- TfidfVectorizer scikit學習API
- TfidfTransformer scikit-learn API
- HashingVectorizer scikit學習API
概要
在本教程中,你可以瞭解如何使用scikit-learn為機器學習準備文字文件。
這些例子只是抓住了表面,但是,這些類有很多配置細節影響著文件的標記,這些都是值得深究的。