樸素貝葉斯分類(下):如何對文檔進行分類?
樸素貝葉斯分類最适合的場景是文本分類、情感分析和垃圾郵件識别,其中情感分析和垃圾郵件識别都是通過文本來進行判斷,三個場景基本都是文本分類,是以樸素貝葉斯常用于自然語言處理NLP的工具
使用樸素貝葉斯做文檔分類:
sklearn機器學習包
sklearn提供了3個樸素貝葉斯分類算法,分别是高斯樸素貝葉斯、多項式樸素貝葉斯、伯努利樸素貝葉斯
根據特征變量的不同選擇不同的算法:
- 高斯樸素貝葉斯:特征變量是連續變量,符合高斯分布,比如人的身高、物體長度
- 多項式樸素貝葉斯:特征變量是離散變量,符合多項分布,在文檔分類中特征變量展現在一個單詞出現的次數或者是單詞的TF-IDF值等
- 伯努利樸素貝葉斯:特征變量是布爾變量,符合0/1分布,在文檔分類中特征是單詞是否出現
伯努利樸素貝葉斯是以檔案為粒度,如果該單詞在某檔案中出現了即為1,否則為0,而多項式樸素貝葉斯是以單詞為粒度,會計算在某個檔案中的具體次數,而高斯樸素貝葉斯适合處理特征變量是連續變量,且符合正态分布(高斯分布)的情況,比如身高、體重這種自然界的現象就比較适合用高斯樸素貝葉斯來處理,而文本分類是使用多項式樸素貝葉斯或者伯努利樸素貝葉斯。
TF-IDF值
TF-IDF是一個統計方法,用來評估某個詞語對于一個檔案集或文檔庫中的其中一份檔案的重要程度
詞頻TF計算了一個單詞在文檔中出現的次數,認為一個單詞的重要性和它在文檔中出現的次數呈正比
逆向文檔頻率IDF,指一個單詞在文檔中的區分度,認為一個單詞出現在的文檔數越少,就越能通過這個單詞把該文檔和其他文檔區分開,IDF越大就代表該單詞的區分度越大
TF-IDF就是詞頻TF和逆向文檔頻率IDF的乘積,這樣這個單詞在一個文檔中出現的次數多且很少出現在其他文檔中,這樣的單詞适合用于分類

IDF中可能有些單詞不會存在文檔中,為了避免分母為0 是以統一給單詞出現的文檔數都+1
TF-IDF=
TF*IDF
更準确的對文檔進行分類,比如“我”這種高頻單詞,是以TF詞頻高,但是IDF很低,整體的TF-IDF不高
例子:假設一個檔案夾中一共有10篇文檔,其中一篇文檔有1000個單詞,“this”單詞出現了20次,“bayes”出現了5次,“this”在所有文檔中均出現過,而“bayes”隻在2篇文檔中出現過,是以針對“this”,詞頻TF值為:20/1000=0.02,逆向文檔頻率IDF=log(10/10+1)=-0.0414,是以TF-IDF=-8.28e-4,針對“bayes”的詞頻TF=5/1000=0.005,IDF=log(10/2+1)=0.5229,TF-IDF=2.61e-3,是以“bayes”比“this”做區分要好
如何求TF-IDF?
在sklearn中直接使用TfidfVectorizer類,幫助計算單詞TF-IDF向量的值,類中取sklearn計算的對數log時,底數為e,不是10
建立TfidfVectorizer類:
TfidfVectorizer(stop_words=stop_words, token_pattern=token_pattern)
建立的時候有兩個構造參數,可以自定義停用詞stop_words,和過濾規則token_pattern,注意傳遞的資料結構,停用詞stop_words是一個清單List類型,而過濾規則token_pattern是正規表達式
停用詞就是在分類中沒有用的詞,這些詞的詞頻TF高,但是IDF很低,沒有分類的作用,為了節省空間和計算空間,把這些詞作為停用詞stop_words,告訴機器這些詞不需要計算
建立好TF-IDF向量類型的時候,用fit_transform計算,傳回文本矩陣,該矩陣表示了每個單詞在每個文檔中的TF-IDF值
進行fit_transform拟合模型後,得到更多的TF-IDF向量屬性,比如我們可以得到詞彙的對應關系(字典類型)和向量的IDF值,也可以擷取設定的停用詞stop_words
例子:假如有4個文檔:
文檔1:this is the bayes document;
文檔2:this is the second second document;
文檔3:and the third one;
文檔4: is this the document;
計算文檔中都有哪些單詞,這些單詞在不同文檔中的TF-IDF值都為多少
- 首先建立TfidfVectorizer類:
from sklearn.feature_extraction.text import TfidfVectorizer tfidf_vec = TfidfVectorizer()
- 建立4個文檔的清單documents,并讓建立好的tfidf_vec 對documents進行拟合,得到TF-IDF矩陣
輸出文檔中所有不重複的詞: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())
運作結果:
不重複的詞: [‘and’, ‘bayes’, ‘document’, ‘is’, ‘one’, ‘second’, ‘the’, ‘third’, ‘this’]
輸出每個單詞對應的ID值:
print('每個單詞的ID:', tfidf_vec.vocabulary_)
運作結果:
每個單詞的ID: {‘this’: 8, ‘is’: 3, ‘the’: 6, ‘bayes’: 1, ‘document’: 2, ‘second’: 5, ‘and’: 0, ‘third’: 7, ‘one’: 4}
輸出每個單詞在每個文檔中的TF-IDF值,向量裡的順序是按照詞語的ID順序來的:
print('每個單詞的tfidf值:', tfidf_matrix.toarray())
如何對文檔進行分類
對文檔分類有兩個重要階段:
- 基于分詞的資料準備:包括分詞、單詞權重計算、去掉停用詞
- 應用樸素貝葉斯分類進行分類,首先通過訓練集得到樸素貝葉斯分類器,然後将分類器應用于測試集,并與實際結果作對比,最終得到測試集的分類準确率
子產品1:對文檔進行分詞
在準備階段最重要的就是分詞,英文文檔和中文文檔所使用的分詞工具不同,英文文檔中,最常用的是NTLK包,NTLK包中包含了英文的停用詞stop_words、分詞和标注方法
import nltk
word_list = nltk.word_tokenize(text) #分詞
nltk.pos_tag(word_list) #标注單詞的詞性
中文文檔中,最常用的是jieba包,jieba包中包含了中文的停用詞和分詞方法
import jieba
word_list = jieba.cut (text) #中文分詞
子產品2:加載停用詞表
需要自己讀取停用詞表檔案,從網上找到中文常用的 停用詞儲存在stop_words.txt,利用Python的檔案讀取函數讀取檔案,儲存在stop_words數組中
stop_words = [line.strip().decode('utf-8') for line in io.open('stop_words.txt').readlines()]
子產品3:計算單詞的權重
直接建立TfidfVectorizer類,然後使用fit_transform方法進行拟合,得到TF-IDF特征空間features,了解為選出來的分詞就是特征,計算這些特征在文檔上的特征向量,得到特征空間features
tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
features = tf.fit_transform(train_contents)
max_df參數用來描述單詞在文檔中的最高出現率,假設max_df=0.5,代表一個單詞在50%的文檔中都出現過,那麼它隻攜帶了非常少的資訊是以不作為分詞統計
不設定min_df,因為min_df很小
子產品4:生成樸素貝葉斯分類器
将特征訓練集的特征空間train_features,以及訓練集對應的分類train_labels傳遞給貝葉斯分類器clf,可以自動生成一個符合特征空間和對應分類的分類器,采用多項式貝葉斯分類器,其中alpha為平滑參數。當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:使用生成的分類器做預測
首先需要得到測試集的特征矩陣,方法是用訓練集的分詞建立一個TfidfVectorizer類,使用同樣的stop_words和max_df,然後用這個TfidfVectorizer類對測試集的内容進行fit_transform拟合,得到測試集的特征矩陣test_features
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)
資料挖掘的流程包括擷取資料、資料清洗、模型訓練、模型評估和模型部署幾個過程
練習題:
對中文文檔分類的練習題< https://github.com/cystanford/text_classification >
- 文檔中有4中類型:女性、體育、文學、校園;
- 訓練集放到train檔案夾裡,測試集放到test檔案夾裡,停用詞放到stop檔案夾裡
請使用樸素貝葉斯分類對訓練集進行訓練,并對測試集進行驗證,并給出測試集的準确率
假設我們要判斷一個人的性别,是通過身高、體重、鞋碼、外貌等屬性進行判斷的,如果我們用樸素貝葉斯做分類,适合使用哪種樸素貝葉斯分類器?停用詞的作用是什麼?
import os
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics
LABEL_MAP = {'體育': 0, '女性': 1, '文學': 2, '校園': 3}
# 加載停用詞
with open('./text classification/stop/stopword.txt', 'rb') as f:
STOP_WORDS = [line.strip() for line in f.readlines()]
def load_data(base_path):
"""
:param base_path: 基礎路徑
:return: 分詞清單,标簽清單
"""
documents = []
labels = []
for root, dirs, files in os.walk(base_path): # 循環所有檔案并進行分詞打标
for file in files:
label = root.split('\\')[-1] # 因為windows上路徑符号自動轉成\了,是以要轉義下
labels.append(label)
filename = os.path.join(root, file)
with open(filename, 'rb') as f: # 因為字元集問題是以直接用二進制方式讀取
content = f.read()
word_list = list(jieba.cut(content))
words = [wl for wl in word_list]
documents.append(' '.join(words))
return documents, labels
def train_fun(td, tl, testd, testl):
"""
構造模型并計算測試集準确率,字數限制變量名簡寫
:param td: 訓練集資料
:param tl: 訓練集标簽
:param testd: 測試集資料
:param testl: 測試集标簽
:return: 測試集準确率
"""
# 計算矩陣
tt = TfidfVectorizer(stop_words=STOP_WORDS, max_df=0.5)
tf = tt.fit_transform(td)
# 訓練模型
clf = MultinomialNB(alpha=0.001).fit(tf, tl)
# 模型預測
test_tf = TfidfVectorizer(stop_words=STOP_WORDS, max_df=0.5, vocabulary=tt.vocabulary_)
test_features = test_tf.fit_transform(testd)
predicted_labels = clf.predict(test_features)
# 擷取結果
x = metrics.accuracy_score(testl, predicted_labels)
return x
# text classification與代碼同目錄下
train_documents, train_labels = load_data('./text classification/train')
test_documents, test_labels = load_data('./text classification/test')
x = train_fun(train_documents, train_labels, test_documents, test_labels)
print(x)
eturn x
text classification與代碼同目錄下
train_documents, train_labels = load_data(’./text classification/train’)
test_documents, test_labels = load_data(’./text classification/test’)
x = train_fun(train_documents, train_labels, test_documents, test_labels)
print(x)