主要使用sklearn這個機器學習包完成對文檔的分類
sklearn機器學習包sklearn提供了三個樸素貝葉斯分類算法:
高斯樸素貝葉斯:特徵變量是連續變量,符合高斯分布,比如人的身高和體重等多項式樸素貝葉斯:特徵變量是離線變量,符合多項分布,在文檔分類中特徵向量體現在一個單詞出現的次數或者是單詞的TF-IDF值等
伯努利樸素貝葉斯:特徵變量是布爾變量,符合0/1分布,在文檔分類中特徵是單詞是否出現
TF-IDF多項式樸素貝葉斯中提到了TF-IDF(Term Frequency-Inverse Document Frequence 的縮寫,其中TF代表詞頻,IDF代表逆向文檔頻率),它是一個統計方法,用以評估某一個詞語對於一個文檔集或一個語料庫中的其中一份文檔的重要程度。詞語的重要性隨著它在文檔中出現的次數成正比增加,但同時會隨著它在語料庫中出現的頻率成反比下降
詞頻TF:指的是某一個給定的詞語在該文檔中出現的頻率,詞語的重要性和它在文檔中出現的次數正相關
逆向文檔頻率IDF:指的是某一個給定的詞語在文檔中的區分度,詞語出現在的文檔數越少,這個詞語的區分度就越高,越容易用這個詞語把該文檔和其他文檔區分開。IDF越大,這個詞語的區分度就越高所以,我們傾向於找到TF和IDF都高的單詞作為區分,即這個單詞在一個文檔中出現的次數多,但在其他文檔中出現的次數少,所以很適合用於分類
TF-IDF=TF*IDF
其中計算IDF公式的分母中,單詞出現的次數+1是為了避免分子為0
介紹完了sklearn機器學習包中的三個樸素貝葉斯算法,下面就開始用這個包進行對文檔的分類
文檔分類的步驟對文檔分類,分兩個階段,看下面的圖:
基於分詞的準備:分詞、單詞權重計算、去掉停用詞
應用樸素貝葉斯進行分類:先通過訓練集得到樸素貝葉斯分類器,然後將分類器用於測試集,將預測結果和實際結果做對比並計算準確率
整個過程分為六個步驟
1.對文檔進行分詞
對文檔分詞的時候,英文文檔和中文文檔使用的分詞工具不同。英文文檔常用NTLK包,其中包含了英文的停用詞表、分詞和標註方法中文文檔常用jieba包,其中包含了中文的停用詞表和分詞方法
def get_data(base_path, labels): # 獲取數據集
contents = []
# 數據在文件夾下,需要遍歷各個文檔類別下的文件
for label in labels:
files = {fileName for fileName in os.listdir(base_path + label)}
try:
for fileName in files:
file = open(base_path + label + '/' + fileName, encoding='gb18030')
word = jieba.cut(file.read()) # 對文檔進行分詞 中文文檔多用 jieba 包
contents.append(' '.join(word)) # 因為切詞,所以用 ' ' 分隔
except Exception:
print(fileName + '文件讀取錯誤')
return contents
# 獲取訓練集
train_contents = get_data(train_base_path, train_labels)
# 獲取測試集
test_contents = get_data(test_base_path, test_labels)
2.加載停用詞表
需要我們先下載常用的停用詞表,然後放到stop_word.txt中,讀取裡面的數據放到數組中
stop_words = [line.strip() for line in open(stop_words_path, encoding='utf-8-sig').readlines()]
3.計算單詞權重
用sklearn裡的TfidfVectorizer類,使用fit_transform方法進行擬合,得到TF-IDF特徵空間features,可以理解為選出來的分詞就是特徵,計算這些特徵在文檔上的特徵向量,得到特徵空間features
tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
train_features = tf.fit_transform(train_contents) # 擬合
這裡max_df參數表示單詞在文檔中最高出現頻率。這裡設置0.5代表一個單詞在50%的文檔中都出現過了,攜帶的信息比較少,就不作為分詞統計,這個參數可以調。min_df很少設置
4.生成樸素貝葉斯分類器
把訓練集訓練出來的特徵空間features和訓練集對應的分類labels傳給貝葉斯分類器,會自動生成一個符合特徵空間和對應分類的分類器上面介紹了sklearn包提供的三個樸素貝葉斯分類器,根據特性這裡採用多項式樸素貝葉斯分類器即MultinomialNB,其中alpha為平滑參數。當一個單詞在訓練集中樣本中沒有出現的時候,該單詞的概率被置為0,但是訓練集一般是抽樣的,所以單詞在樣本中不存在就置為0不太合理,需要平滑處理
alpha平滑類別特點0~1Laplace平滑採用+1的方式,當樣本很大的時候,+1得到的概率的影響可以忽略不計,同時也避免了分母為0的情況1Lidstone平滑alpha越小,迭代次數越多,精度越高代碼
train_labels = ['體育'] * 1337 + ['女性'] * 954 + ['文學'] * 766 + ['校園'] * 249 # 數字是自己算的,也可以用程序判斷
clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels) # MultinomialNB.fit(x,y) 其中 y的數量要等於訓練x的數量
5.使用生成的分類器做預測
主要是得到測試集的特徵矩陣,先創建FfidfVectorizer,使是和訓練集一樣的stop_words和max_df,然後用FfidfVectorizer類對測試集內容進行擬合,得到測試集特徵矩陣,再用訓練好的分類器對測試特徵矩陣進行預測,predict這個函數求解所有的後驗概率並找到最大的那個
test_tf = TfidfVectorizer(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)
6.計算準確率
計算正確率主要是對模型預測的結果和實際的結果進行對比,給出模型的準確率,用到sklearn中的metrics包中的accuracy_score函數
test_labels = ['體育'] * 115 + ['女性'] * 38 + ['文學'] * 31 + ['校園'] * 16
print('準確率', metrics.accuracy_score(test_labels, predicted_labels))
實戰實戰用到的數據從專欄老師的github上下載,點擊這裡下載數據介紹:提供了三份數據,一個訓練集文檔(train文件夾下)、一個測試集文檔(test文件夾下)和一個停用詞表,其中訓練集和測試集的文檔分為四種類型:女性、體育、文學、校園用樸素貝葉斯分類對訓練集訓練,並對測試集進行驗證,並給出真確率
我的完整代碼import os
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics
# 停用詞表地址
stop_words_path = 'D:/workspace/study/python/test_data/text_classification/stop/stop_word.txt'
# 訓練集根目錄
train_base_path = 'D:/workspace/study/python/test_data/text_classification/train/'
# 測試集根目錄
test_base_path = 'D:/workspace/study/python/test_data/text_classification/test/'
# 文檔類別
train_labels = ['女性', '體育', '文學', '校園']
test_labels = ['女性', '體育', '文學', '校園']
def get_data(base_path, labels): # 獲取數據集
contents = []
# 數據在文件夾下,需要遍歷各個文檔類別下的文件
for label in labels:
files = {fileName for fileName in os.listdir(base_path + label)}
try:
for fileName in files:
file = open(base_path + label + '/' + fileName, encoding='gb18030')
word = jieba.cut(file.read()) # 對文檔進行分詞 中文文檔多用 jieba 包
contents.append(' '.join(word)) # 因為切詞,所以用 ' ' 分隔
except Exception:
print(fileName + '文件讀取錯誤')
return contents
# 1.對文檔進行分詞
# 獲取訓練集
train_contents = get_data(train_base_path, train_labels)
# print(train_contents)
# print('訓練集的長度:', len(train_contents))
# 獲取測試集
test_contents = get_data(test_base_path, test_labels)
# print(test_contents)
# print('測試集的長度:', len(test_contents))
# 2.加載停用詞表
stop_words = [line.strip() for line in open(stop_words_path, encoding='utf-8-sig').readlines()]
# print(stop_words)
# 3.計算單詞權重
tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
train_features = tf.fit_transform(train_contents) # 擬合
# 4.生成樸素貝葉斯分類器 這裡使用多項式貝葉斯分類器
train_labels = ['體育'] * 1337 + ['女性'] * 954 + ['文學'] * 766 + ['校園'] * 249
clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels) # MultinomialNB.fit(x,y) 其中 y的數量要等於訓練x的數量
# 5.使用生成的分類器做預測
test_tf = TfidfVectorizer(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)
# 6.計算準確率
test_labels = ['體育'] * 115 + ['女性'] * 38 + ['文學'] * 31 + ['校園'] * 16
print('準確率', metrics.accuracy_score(test_labels, predicted_labels))
結果0.62這個結果的準確率我覺得還是挺低的,剛過60%,需要優化
聲明:文中的圖片和知識點內容主要來自極客時間的專欄《數據分析實戰45講》,這是專欄的一道實戰作業題