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


朴素貝葉斯算法 -- 簡介

 朴素貝葉斯法是基於貝葉斯定理與特征條件獨立假設的分類方法。最為廣泛的兩種分類模型是決策樹模型(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 實現了運用於分類的高斯朴素貝葉斯算法。特征的可能性(即概率)假設為高斯分布:


        參數使用最大似然法估計。
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))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM