Python機器學習 — 樸素貝葉斯演算法(Naive Bayes)

ls秦發表於2018-07-10

一、樸素貝葉斯演算法 -- 簡介

       樸素貝葉斯法是基於貝葉斯定理與特徵條件獨立假設的分類方法。最為廣泛的兩種分類模型是決策樹模型(Decision Tree Model)和樸素貝葉斯模型(Naive Bayesian Model,NBM)。
       和決策樹模型相比,樸素貝葉斯分類器(Naive Bayes Classifier,或 NBC)發源於古典數學理論,有著堅實的數學基礎,以及穩定的分類效率。同時,NBC模型所需估計的引數很少,對缺失資料不太敏感,演算法也比較簡單。
       理論上,NBC模型與其他分類方法相比具有最小的誤差率。但是實際上並非總是如此,這是因為NBC模型假設屬性之間相互獨立,這個假設在實際應用中往往是不成立的,這給NBC模型的正確分類帶來了一定影響。

       演算法步驟:
           1)收集資料;
           2)準備資料:需要數值型或布林型資料。如果是文字檔案,要解析成詞條向量bai;
           3)分析資料:有大量特徵時,用直方圖分析效果更好;
           4)訓練演算法:計算不同的獨立特徵的條件概率;
           5)測試演算法:計算錯誤率;
           6)使用演算法:一個常見的樸素貝葉斯應用是文件分類。

二、貝葉斯定理

       條件概率就是事件 A 在另外一個事件 B 已經發生條件下的發生概率。條件概率表示為P(A|B),讀作“在 B 發生的條件下 A 發生的概率”。

       聯合概率表示兩個事件共同發生(數學概念上的交集)的概率。A 與 B 的聯合概率表示為聯合概率

       推導:

       我們可以從條件概率的定義推匯出貝葉斯定理。
       根據條件概率的定義,在事件 B 發生的條件下事件 A 發生的概率為:

       同樣地,在事件 A 發生的條件下事件 B 發生的概率為:

       結合這兩個方程式,我們可以得到:

       這個引理有時稱作概率乘法規則。上式兩邊同除以 P(A),若P(A)是非零的,我們可以得到貝葉斯定理:

三、高斯樸素貝葉斯(GaussianNB)

       在高斯樸素貝葉斯中,每個特徵都是連續的,並且都呈高斯分佈。高斯分佈又稱為正態分佈。圖畫出來以後像一個倒掛的鐘,以均值為軸對稱,如下圖所示:

       GaussianNB 實現了運用於分類的高斯樸素貝葉斯演算法。特徵的可能性(即概率)假設為高斯分佈:

       引數\large \sigma _{y}\large \mu _{y}使用最大似然法估計。

from sklearn import datasets
iris = datasets.load_iris()

from sklearn.naive_bayes import GaussianNB
clf = GaussianNB()
clf = clf.fit(iris.data, iris.target)
y_pred=clf.predict(iris.data)
print("高斯樸素貝葉斯,樣本總數: %d 錯誤樣本數 : %d" % (iris.data.shape[0],(iris.target != y_pred).sum()))

四、多項分佈樸素貝葉斯(MultinomialNB)

       MultinomialNB實現服從多項分佈資料(multinomially)的貝葉斯演算法,是一個經典的樸素貝葉斯文字分類中使用的變種(其中的資料是通常表示為詞向量的數量,雖然TF-IDF向量在實際專案中表現得很好),對於每一個y來說,分佈通過向量引數化,n是類別的數目(在文字分類中,表示詞彙量的長度) 表示標籤i出現的樣本屬於類別y的概率
       該引數   是一個平滑的最大似然估計,即相對頻率計數:

          表示標籤i在樣本集T中屬於類別y的 數目
         表示在所有標籤中類別y出現的數目

       先驗平滑先驗 α >=0表示學習樣本中不存在的特徵並防止在計算中概率為0,設定 α = 1被稱為拉普拉斯平滑(Lapalce smoothing),當α<1稱為利德斯通平滑(Lidstone smoothing)

from sklearn import datasets
iris = datasets.load_iris()

from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB()
clf = clf.fit(iris.data, iris.target)
y_pred=clf.predict(iris.data)
print("多項分佈樸素貝葉斯,樣本總數: %d 錯誤樣本數 : %d" % (iris.data.shape[0],(iris.target != y_pred).sum()))

引數說明如下:

        alpha:浮點型可選引數,預設為1.0,其實就是新增拉普拉斯平滑,即為上述公式中的λ ,如果這個引數設定為0,就是不新增平滑;
        fit_prior:布林型可選引數,預設為True。布林引數fit_prior表示是否要考慮先驗概率,如果是false,則所有的樣本類別輸出都有相同的類別先驗概率。否則可以自己用第三個引數class_prior輸入先驗概率,或者不輸入第三個引數class_prior讓MultinomialNB自己從訓練集樣本來計算先驗概率,此時的先驗概率為P(Y=Ck)=mk/m。其中m為訓練集樣本總數量,mk為輸出為第k類別的訓練集樣本數。
        class_prior:可選引數,預設為None。

總結如下:

fit_prior   class_prior         最終先驗概率
False       填或不填沒有意義        P(Y = Ck) = 1 / k
True        不填                  P(Y = Ck) = mk / m
True        填                   P(Y = Ck) = class_prior

五、伯努利分佈樸素貝葉斯(BernoulliNB

       BernoulliNB 實現了用於多重伯努利分佈資料的樸素貝葉斯訓練和分類演算法,即有多個特徵,但每個特徵 都假設是一個二元 (Bernoulli, boolean) 變數。 因此,這類演算法要求樣本以二元值特徵向量表示;如果樣本含有其他型別的資料, 一個 BernoulliNB 例項會將其二值化(取決於 binarize 引數)。

       伯努利樸素貝葉斯的決策規則基於:

        與多項分佈樸素貝葉斯的規則不同 伯努利樸素貝葉斯明確地懲罰類 y 中沒有出現作為預測因子的特徵 i ,而多項分佈分佈樸素貝葉斯只是簡單地忽略沒出現的特徵。

        在文字分類的例子中,詞頻向量(word occurrence vectors)(而非詞數向量(word count vectors))可能用於訓練和用於這個分類器。 BernoulliNB 可能在一些資料集上可能表現得更好,特別是那些更短的文件。 如果時間允許,建議對兩個模型都進行評估。

from sklearn import datasets
iris = datasets.load_iris()

from sklearn.naive_bayes import BernoulliNB
clf = BernoulliNB()
clf = clf.fit(iris.data, iris.target)
y_pred=clf.predict(iris.data)
print("伯努利樸素貝葉斯,樣本總數: %d 錯誤樣本數 : %d" % (iris.data.shape[0],(iris.target != y_pred).sum()))

六、樸素貝葉斯 -- 程式碼實現

       對於新聞分類,屬於多分類問題。我們可以使用MultinamialNB()完成我們的新聞分類問題。

import numpy as np

"""
    這個指南的目的是在一個實際任務上探索scikit-learn的主要工具,在二十個不同的主題上分析一個文字集合。
    在這一節中,可以看到:
        1、載入文字檔案和類別
        2、適合機器學習的特徵向量提取
        3、訓練線性模型進行分類
        4、使用網格搜尋策略,找到一個很好的配置的特徵提取元件和分類器
"""

"""
    1、Loading the 20 newsgroups dataset 載入20個新聞組資料集
    為了獲得更快的執行時間為第一個例子,我們將工作在部分資料集只有4個類別的資料集中:
"""
categories = ['alt.atheism', 'soc.religion.christian', 'comp.graphics', 'sci.med']
from sklearn.datasets import fetch_20newsgroups

twenty_train = fetch_20newsgroups(subset='train', categories=categories, shuffle=True, random_state=42)
print(twenty_train.target)
print(twenty_train.target_names)  # 訓練集中類別的名字,這裡只有四個類別
print(len(twenty_train.data))  # 訓練集中資料的長度
print(len(twenty_train.filenames))  # 訓練集檔名長度
print('-----')
print("\n".join(twenty_train.data[0].split("\n")[:3]))
print('-----')
print(twenty_train.target_names[twenty_train.target[0]])
print('-----')
print(twenty_train.target[:10])  # 前十個的類別
print('-----')
for t in twenty_train.target[:10]:
    print(twenty_train.target_names[t])  # 類別的名字
print('-----')
"""
    2、Extracting features from text files 從文字檔案中提取特徵
    為了在文字檔案中使用機器學習演算法,首先需要將文字內容轉換為數值特徵向量
"""

"""
    Bags of words 詞袋
    最直接的方式就是詞袋錶示法
        1、為訓練集的任何文件中的每個單詞分配一個固定的整數ID(例如通過從字典到整型索引建立字典)
        2、對於每個文件,計算每個詞出現的次數,並儲存到X[i,j]中。

    詞袋錶示:n_features 是語料中不同單詞的數量,這個數量通常大於100000.
    如果 n_samples == 10000,儲存X的陣列就需要10000*10000*4byte=4GB,這麼大的儲存在今天的計算機上是不可能實現的。
    幸運的是,X中的大多數值都是0,基於這種原因,我們說詞袋是典型的高維稀疏資料集,我們可以只儲存那些非0的特徵向量。
    scipy.sparse 矩陣就是這種資料結構,而scikit-learn內建了這種資料結構。
"""

"""
    Tokenizing text with scikit-learn 使用scikit-learn標記文字
    文字處理、分詞、過濾停用詞都在這些高階元件中,能夠建立特徵字典並將文件轉換成特徵向量。
"""
from sklearn.feature_extraction.text import CountVectorizer  # sklearn中的文字特徵提取元件中,匯入特徵向量計數函式

count_vect = CountVectorizer()  # 特徵向量計數函式
X_train_counts = count_vect.fit_transform(twenty_train.data)  # 對文字進行特徵向量處理
print(X_train_counts)  # 特徵向量和特徵標籤
print(X_train_counts.shape)  # 形狀
print('-----')

"""
    CountVectorizer支援計算單詞或序列的N-grams,一旦合適,這個向量化就可以建立特徵詞典。
    在整個訓練預料中,詞彙中的詞彙索引值與其頻率有關。
"""
print(count_vect.vocabulary_.get(u'algorithm'))
print('-----')

"""
    From occurrences to frequencies 從事件到頻率
    計數是一個好的開始,但是也存在一個問題:較長的文字將會比較短的文字有很高的平均計數值,即使他們所表示的話題是一樣的。
    為了避免潛在的差異,它可以將文件中的每個單詞出現的次數在文件的總字數的比例:這個新的特徵叫做詞頻:tf
    tf-idf:詞頻-逆文件頻率
"""
from sklearn.feature_extraction.text import TfidfTransformer  # sklearn中的文字特徵提取元件中,匯入詞頻統計函式

tf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts)  # 建立詞頻統計函式,注意這裡idf=False
print(tf_transformer)  # 輸出函式屬性 TfidfTransformer(norm=u'l2', smooth_idf=True, sublinear_tf=False, use_idf=False)
print('-----')
X_train_tf = tf_transformer.transform(X_train_counts)  # 使用函式對文字文件進行tf-idf頻率計算
print(X_train_tf)
print('-----')
print(X_train_tf.shape)
print('-----')
"""
    在上面的例子中,使用fit()方法來構建基於資料的預測器,然後使用transform()方法來將計數矩陣用tf-idf表示。
    這兩個步驟可以通過跳過冗餘處理,來更快的達到相同的最終結果。
    這些可以通過使用fit_transform()方法來實現:
"""
tfidf_transformer = TfidfTransformer()  # 這裡使用的是tf-idf
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
print(X_train_tfidf)
print(X_train_tfidf.shape)
print('-----')
"""
    Training a classifier 訓練一個分類器
    既然已經有了特徵,就可以訓練分類器來試圖預測一個帖子的類別,先使用貝葉斯分類器,貝葉斯分類器提供了一個良好的基線來完成這個任務。
    scikit-learn中包括這個分類器的許多變數,最適合進行單詞計數的是多項式變數。
"""
from sklearn.naive_bayes import MultinomialNB  # 使用sklearn中的貝葉斯分類器,並且載入貝葉斯分類器

# 中的MultinomialNB多項式函式
clf = MultinomialNB()  # 載入多項式函式
x_clf = clf.fit(X_train_tfidf, twenty_train.target)  # 構造基於資料的分類器
print(x_clf)  # 分類器屬性:MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True)
print('-----')
"""
    為了預測輸入的新的文件,我們需要使用與前面相同的特徵提取鏈進行提取特徵。
    不同的是,在轉換中,使用transform來代替fit_transform,因為訓練集已經構造了分類器
"""
docs_new = ['God is love', 'OpenGL on the GPU is fast']  # 文件
X_new_counts = count_vect.transform(docs_new)  # 構建文件計數
X_new_tfidf = tfidf_transformer.transform(X_new_counts)  # 構建文件tfidf
predicted = clf.predict(X_new_tfidf)  # 預測文件
print(predicted)  # 預測類別 [3 1],一個屬於3類,一個屬於1類
for doc, category in zip(docs_new, predicted):
    print('%r => %s' % (doc, twenty_train.target_names[category]))  # 將文件和類別名字對應起來
print('-----')
"""
    Building a pipeline 建立管道
    為了使向量轉換更加簡單(vectorizer => transformer => classifier),scikit-learn提供了pipeline類來表示為一個複合分類器
"""
from sklearn.pipeline import Pipeline

text_clf = Pipeline([('vect', CountVectorizer()), ('tfidf', TfidfTransformer()), ('clf', MultinomialNB())])
text_clf = text_clf.fit(twenty_train.data, twenty_train.target)
print(text_clf)  # 構造分類器,分類器的屬性
predicted = text_clf.predict(docs_new)  # 預測新文件
print(predicted)  # 獲取預測值
print('-----')

"""
    分析總結:
        1、載入資料集,主要是載入訓練集,用於對資料進行訓練
        2、文字特徵提取:
                對文字進行計數統計 CountVectorizer
                詞頻統計  TfidfTransformer  (先計算tf,再計算tfidf)
        3、訓練分類器:
                貝葉斯多項式訓練器 MultinomialNB
        4、預測文件:
                通過構造的訓練器進行構造分類器,來進行文件的預測
        5、最簡單的方式:
                通過使用pipeline管道形式,來講上述所有功能通過管道來一步實現,更加簡單的就可以進行預測
"""

"""
    Evaluation of the performance on the test set 測試集效能評價
    評估模型的預測精度同樣容易:
"""
import numpy as np

twenty_test = fetch_20newsgroups(subset='test', categories=categories, shuffle=True, random_state=42)
docs_test = twenty_test.data
predicted = text_clf.predict(docs_test)
print(np.mean(predicted == twenty_test.target))  # 預測的值和測試值的比例,mean就是比例函式
print('-----')  # 精度已經為0.834886817577

"""
    精度已經實現了83.4%,那麼使用支援向量機(SVM)是否能夠做的更好呢,支援向量機(SVM)被廣泛認為是最好的文字分類演算法之一。
    儘管,SVM經常比貝葉斯要慢一些。
    我們可以改變學習方式,使用管道來實現分類:
"""
from sklearn.linear_model import SGDClassifier

text_clf = Pipeline(
    [('vect', CountVectorizer()), ('tfidf', TfidfTransformer()),
     ('clf', SGDClassifier(loss='hinge', penalty='l2', alpha=1e-3, n_iter=5, random_state=42))])
# _ = text_clf.fit(twenty_train.data, twenty_train.target)  # 和下面一句的意思一樣,一個槓,表示本身
text_clf = text_clf.fit(twenty_train.data, twenty_train.target)
predicted = text_clf.predict(docs_test)
print(np.mean(predicted == twenty_test.target))  # 精度 0.912782956059
print('-----')
"""
    sklearn進一步提供了結果的更詳細的效能分析工具:
"""
from sklearn import metrics
print(metrics.classification_report(twenty_test.target, predicted, target_names=twenty_test.target_names))
print(metrics.confusion_matrix(twenty_test.target, predicted))

 

相關文章