樸素貝葉斯分類實戰:對文檔進行分類

樸素貝葉斯分類最適合的場景就是文本分類、情感分析和垃圾郵件識別。其中情感分析和垃圾郵件識別都是通過文本來進行判斷。所以樸素貝葉斯也常用於自然語言處理 NLP 的工具。

sklearn 機器學習包

sklearn 的全稱叫 Scikit-learn,它給我們提供了 3 個樸素貝葉斯分類算法,分別是高斯樸素貝葉斯(GaussianNB)、多項式樸素貝葉斯MultinomialNB)和伯努利樸素貝葉斯(BernoulliNB)。

這三種算法適合應用在不同的場景下,我們應該根據特徵變量的不同選擇不同的算法:

高斯樸素貝葉斯:特徵變量是連續變量,符合高斯分佈,比如說人的身高,物體的長度。
多項式樸素貝葉斯:特徵變量是離散變量,符合多項分佈,在文檔分類中特徵變量體現在一個單詞出現的次數,或者是單詞的 TF-IDF 值等。
伯努利樸素貝葉斯:特徵變量是布爾變量,符合 0/1 分佈,在文檔分類中特徵是單詞是否出現。

伯努利樸素貝葉斯是以文件爲粒度,如果該單詞在某文件中出現了即爲 1,否則爲 0。而多項式樸素貝葉斯是以單詞爲粒度,會計算在某個文件中的具體次數。

如身高、體重這種自然界的現象就比較適合用高斯樸素貝葉斯來處理。而文本分類是使用多項式樸素貝葉斯或者伯努利樸素貝葉斯。

什麼是 TF-IDF 值呢?

TF-IDF 是一個統計方法,用來評估某個詞語對於一個文件集或文檔庫中的其中一份文件的重要程度。

詞頻 TF計算了一個單詞在文檔中出現的次數,它認爲一個單詞的重要性和它在文檔中出現的次數呈正比。
逆向文檔頻率 IDF,是指一個單詞在文檔中的區分度。它認爲一個單詞出現在的文檔數越少,就越能通過這個單詞把該文檔和其他文檔區分開。IDF 越大就代表該單詞的區分度越大。
所以 TF-IDF 實際上是詞頻 TF 和逆向文檔頻率 IDF 的乘積。這樣我們傾向於找到 TF 和 IDF 取值都高的單詞作爲區分,即這個單詞在一個文檔中出現的次數多,同時又很少出現在其他文檔中。這樣的單詞適合用於分類。

TF-IDF 如何計算

在這裏插入圖片描述
在這裏插入圖片描述

些單詞可能不會存在文檔中,爲了避免分母爲 0,統一給單詞出現的文檔數都加 1。

舉個例子
假設一個文件夾裏一共有 10 篇文檔,其中一篇文檔有 1000 個單詞,“this”這個單詞出現 20 次,“bayes”出現了 5 次。“this”在所有文檔中均出現過,而“bayes”只在 2 篇文檔中出現過。

針對“this”,計算 TF-IDF 值:
在這裏插入圖片描述
所以 TF-IDF=0.02*(-0.0414)=-8.28e-4。
針對“bayes”,計算 TF-IDF 值:
在這裏插入圖片描述
TF-IDF=0.005*0.5229=2.61e-3。

很明顯“bayes”的 TF-IDF 值要大於“this”的 TF-IDF 值。這就說明用“bayes”這個單詞做區分比單詞“this”要好。

如何求 TF-IDF
在 sklearn 中我們直接使用 TfidfVectorizer 類,它可以幫我們計算單詞 TF-IDF 向量的值。

在這個類中,取 sklearn 計算的對數 log 時,底數是 e,不是 10。
創建 TfidfVectorizer 的方法是:

TfidfVectorizer(stop_words=stop_words, token_pattern=token_pattern)

在這裏插入圖片描述
在這裏插入圖片描述
當我們創建好 TF-IDF 向量類型時,可以用 fit_transform 幫我們計算,返回給我們文本矩陣,該矩陣表示了每個單詞在每個文檔中的 TF-IDF 值。
在這裏插入圖片描述
在我們進行 fit_transform 擬合模型後,我們可以得到更多的 TF-IDF 向量屬性,比如,我們可以得到詞彙的對應關係(字典類型)和向量的 IDF 值,當然也可以獲取設置的停用詞 stop_words。

在這裏插入圖片描述
在這裏插入圖片描述
現在想要計算文檔裏都有哪些單詞,這些單詞在不同文檔中的 TF-IDF 值是多少呢?
首先我們創建 TfidfVectorizer 類:

from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vec = TfidfVectorizer()
documents = [
    'this is the bayes document',
    'this is the second second document',
    'and the third one',
    'is this the document'
]
tfidf_matrix = tfidf_vec.fit_transform(documents)
print('不重複的詞:', tfidf_vec.get_feature_names())
print('每個單詞的 ID:', tfidf_vec.vocabulary_)
#輸出每個單詞在每個文檔中的 TF-IDF 值,向量裏的順序是按照詞語的 id 順序來的:
print('每個單詞的 tfidf 值:', tfidf_matrix.toarray())


輸出
不重複的詞: ['and', 'bayes', 'document', 'is', 'one', 'second', 'the', 'third', 'this']
每個單詞的 ID: {'this': 8, 'is': 3, 'the': 6, 'bayes': 1, 'document': 2, 'second': 5, 'and': 0, 'third': 7, 'one': 4}
每個單詞的 tfidf 值: [[0.         0.63314609 0.40412895 0.40412895 0.         0.
  0.33040189 0.         0.40412895]
 [0.         0.         0.27230147 0.27230147 0.         0.85322574
  0.22262429 0.         0.27230147]
 [0.55280532 0.         0.         0.         0.55280532 0.
  0.28847675 0.55280532 0.        ]
 [0.         0.         0.52210862 0.52210862 0.         0.
  0.42685801 0.         0.52210862]]

如何對文檔進行分類(以下六個模塊只作示意)

在這裏插入圖片描述

1、基於分詞的數據準備,包括分詞、單詞權重計算、去掉停用詞; 2、應用樸素貝葉斯分類進行分類,首先通過訓練集得到樸素貝葉斯分類器,然後將分類器應用於測試集,並與實際結果做對比,最終得到測試集的分類準確率。

模塊 1:對文檔進行分詞
在準備階段裏,最重要的就是分詞。那麼如果給文檔進行分詞呢?英文文檔和中文文檔所使用的分詞工具不同。

在英文文檔中,最常用的是 NTLK 包。NTLK 包中包含了英文的停用詞 stop words、分詞和標註方法。

import nltk
word_list = nltk.word_tokenize(text) # 分詞
nltk.pos_tag(word_list) # 標註單詞的詞性

在中文文檔中,最常用的是 jieba 包。jieba 包中包含了中文的停用詞 stop words 和分詞方法。

import jieba
word_list = jieba.cut (text) # 中文分詞

模塊 2:加載停用詞表
我們需要自己讀取停用詞表文件,從網上可以找到中文常用的停用詞保存在 stop_words.txt,然後利用 Python 的文件讀取函數讀取文件保存在 stop_words 數組中。

#加r可以幫助解析轉意
with open(r'C:/Users/baihua/Desktop/stopword.txt', 'rb') as f:
    STOP_WORDS = [line.strip() for line in f.readlines()]

模塊 3:計算單詞的權重

直接創建 TfidfVectorizer 類,然後使用 fit_transform 方法進行擬合,得到 TF-IDF 特徵空間 features,你可以理解爲選出來的分詞就是特徵。我們計算這些特徵在文檔上的特徵向量,得到特徵空間 features。

test_tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5, vocabulary=train_vocabulary)
test_features=test_tf.fit_transform(test_contents)

這裏 max_df 參數用來描述單詞在文檔中的最高出現率。假設 max_df=0.5,代表一個單詞在 50% 的文檔中都出現過了,那麼它只攜帶了非常少的信息,因此就不作爲分詞統計。

模塊 4:生成樸素貝葉斯分類器
我們將特徵訓練集的特徵空間 train_features,以及訓練集對應的分類 train_labels 傳遞給貝葉斯分類器 clf,它會自動生成一個符合特徵空間和對應分類的分類器。
這裏我們採用的是多項式貝葉斯分類器,其中 alpha 爲平滑參數。爲什麼要使用平滑呢?因爲如果一個單詞在訓練樣本中沒有出現,這個單詞的概率就會被計算爲 0。爲了解決這個問題,我們需要做平滑處理。

當 alpha=1 時,使用的是 Laplace 平滑。Laplace 平滑就是採用加 1 的方式,來統計沒有出現過的單詞的概率。

當 0<alpha<1 時,使用的是 Lidstone 平滑。對於 Lidstone 平滑來說,alpha 越小,迭代次數越多,精度越高。我們可以設置 alpha 爲 0.001。

# 多項式貝葉斯分類器
from sklearn.naive_bayes import MultinomialNB  
clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels)

模塊 5:使用生成的分類器做預測
首先我們需要得到測試集的特徵矩陣。

test_tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5, vocabulary=train_vocabulary)
test_features=test_tf.fit_transform(test_contents)

然後我們用訓練好的分類器對新數據做預測。
方法是使用 predict 函數,傳入測試集的特徵矩陣 test_features,得到分類結果 predicted_labels。predict 函數做的工作就是求解所有後驗概率並找出最大的那個。

predicted_labels=clf.predict(test_features)

模塊 6:計算準確率
我們可以調用 sklearn 中的 metrics 包,在 metrics 中提供了 accuracy_score 函數,方便我們對實際結果和預測的結果做對比,給出模型的準確率。使用方法如下:

from sklearn import metrics
print metrics.accuracy_score(test_labels, predicted_labels)

總結
一般來說 NTLK 包適用於英文文檔,而 jieba 適用於中文文檔。我們可以根據文檔選擇不同的包,對文檔提取分詞。這些分詞就是貝葉斯分類中最重要的特徵屬性。基於這些分詞,我們得到分詞的權重,即特徵矩陣。

練習

在這個鏈接下下載數據集:https://github.com/cystanford/text_classification
在這裏插入圖片描述

train_contents=[]
train_labels=[]
test_contents=[]
test_labels=[]
#  導入文件
import os
import io
start=os.listdir(r'C:/Users/baihua/Desktop/text classification/train')
for item in start:
    test_path='C:/Users/baihua/Desktop/text classification/test/'+item+'/'
    train_path='C:/Users/baihua/Desktop/text classification/train/'+item+'/'
    for file in os.listdir(test_path):
        with open(test_path+file,encoding="GBK") as f:
            test_contents.append(f.readline())
            #print(test_contents)
            test_labels.append(item)
    for file in os.listdir(train_path):
        with open(train_path+file,encoding='gb18030', errors='ignore') as f:
            train_contents.append(f.readline())
            train_labels.append(item)
print(len(train_contents),len(test_contents))
 
# 導入stop word
import jieba
from sklearn import metrics
from sklearn.naive_bayes import MultinomialNB  
#stop_words = [line.strip() for line in io.open(r'C:/Users/baihua/Desktop/stopword.txt').readlines()]
with open(r'C:/Users/baihua/Desktop/stopword.txt', 'rb') as f:
    stop_words = [line.strip() for line in f.readlines()]


# 分詞方式使用jieba,計算單詞的權重
tf = TfidfVectorizer(tokenizer=jieba.cut,stop_words=stop_words, max_df=0.5)
train_features = tf.fit_transform(train_contents)
print(train_features.shape)

#模塊 4:生成樸素貝葉斯分類器
# 多項式貝葉斯分類器
clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels)
 
#模塊 5:使用生成的分類器做預測
test_tf = TfidfVectorizer(tokenizer=jieba.cut,stop_words=stop_words, max_df=0.5, vocabulary=tf.vocabulary_)
test_features=test_tf.fit_transform(test_contents)
predicted_labels=clf.predict(test_features)
#模塊六,計算準確性
from sklearn import metrics
print (metrics.accuracy_score(test_labels, predicted_labels))
#print(test_features.shape)
#print(metrics.accuracy_score(test_labels, predicted_labels))

在這裏插入圖片描述

參考文獻
數據分析實戰45講
在線獲取文本進行文本處理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章