樸素貝葉斯模型

【前言】

在《從零開始學Python【37】--樸素貝葉斯模型(理論部分)》中我們詳細介紹了樸素貝葉斯算法的基本概念和理論知識,在這一期我們繼續介紹該算法的實戰案例。將會對高斯貝葉斯、多項式貝葉斯和伯努利貝葉斯三種分類器案例的做實戰講解。希望通過這部分內容的講解,能夠使讀者對貝葉斯算法有一個較深的理解(文末有數據和源代碼的下載鏈接 )。

【高斯貝葉斯分類器】

面部皮膚區分數據集來自于UCI網站,該數據集含有兩個部分,一部分為人類面部皮膚數據,該部分數據是由不同種族、年齡和性別人群的圖片轉換而成的;另一部分為非人類面部皮膚數據。兩個部分的數據集一共包含245?057條樣本和4個變量,其中用于識別樣本是否為人類面部皮膚的因素是圖片中的三原色R、G、B,它們的值均落在0~255;因變量為二分類變量,表示樣本在對應的R、G、B值下是否為人類面部皮膚,其中1表示人類面部皮膚,2表示非人類面部皮膚。

通常情況下,研究人員會對樣本是否為人類面部皮膚更加感興趣,所以需要將原始數據集中因變量為1的值設置為正例、因變量為2的值設置為負例,代碼如下:

#?導入第三方包  
import?pandas?as?pd  

#?讀入數據  
skin?=?pd.read_excel(r'C:\Users\Administrator\Desktop\Skin_Segment.xlsx')  
#?設置正例和負例  
skin.y?=?skin.y.map({2:0,1:1})  
skin.y.value_counts()  

out:  
0????194198  
1?????50859

如上結果所示,因變量0表示負例,說明樣本為非人類面部皮膚,一共包含194?198個觀測;因變量1表示正例,說明樣本為人類面部皮膚,一共包含50?859個觀測;因變量值為0和1之間的比例為5:1。接下來將該數據集拆分為訓練集和測試集,分別用于模型的構建和模型的評估,代碼如下:

#?導入第三方模塊  
from?sklearn?import?model_selection  

#?樣本拆分  
X_train,X_test,y_train,y_test?=?model_selection.train_test_split(skin.iloc[:,:3],?skin.y,?  
????????????????????????????????????????????????????test_size?=?0.25,?random_state=1234)  
#?調用高斯樸素貝葉斯分類器的“類”  
gnb?=?naive_bayes.GaussianNB()  
#?模型擬合  
gnb.fit(X_train,?y_train)  
#?模型在測試數據集上的預測  
gnb_pred?=?gnb.predict(X_test)  

#?各類別的預測數量  
pd.Series(gnb_pred).value_counts()  

out:  
0????50630  
1????10635

如上結果所示,通過構建高斯樸素貝葉斯分類器,實現測試數據集上的預測,經統計,預測為負例的一共有50?630條樣本、預測為正例的一共有10?635條樣本。為檢驗模型在測試數據集上的預測效果,需要構建混淆矩陣和繪制ROC曲線,其中混淆矩陣用于模型準確率、覆蓋率、精準率指標的計算;ROC曲線用于計算AUC值,并將AUC值與0.8相比,判斷模型的擬合效果,代碼如下:

#?導入第三方包  
from?sklearn?import?metrics  
import?matplotlib.pyplot?as?plt  
import?seaborn?as?sns  

#?構建混淆矩陣  
cm?=?pd.crosstab(gnb_pred,y_test)  
#?繪制混淆矩陣圖  
sns.heatmap(cm,?annot?=?True,?cmap?=?'GnBu',?fmt?=?'d')  
#?去除x軸和y軸標簽  
plt.xlabel('Real')  
plt.ylabel('Predict')  
#?顯示圖形  
plt.show()  

如上圖所示,將混淆矩陣做了可視化處理,其中主對角線的數值表示正確預測的樣本量,剩余的4?720條樣本為錯誤預測的樣本。經過對混淆矩陣的計算,可以得到模型的整體預測準確率為92.30%。

print('模型的準確率為:\n',metrics.accuracy_score(y_test,?gnb_pred))  
print('模型的評估報告:\n',metrics.classification_report(y_test,?gnb_pred))  

模型的準確率為:  
?0.922957643026  

模型的評估報告:  
??????????????precision?????recall????f1-score???support  
??????????0???????0.93??????0.97??????0.95?????48522  
??????????1???????0.88??????0.73??????0.80?????12743  
avg?/?total???????0.92??????0.92??????0.92?????61265

進一步可以得到每個類別的預測精準率(precision=正確預測某類別的樣本量/該類別的預測樣本個數)和覆蓋率(recall=正確預測某類別的樣本量/該類別的實際樣本個數),通過準確率、精準率和覆蓋率的對比,模型的預測效果還是非常理想的。接下來繪制ROC曲線,用于進一步驗證得到的結論,代碼如下:

#?計算正例的預測概率,用于生成ROC曲線的數據  
y_score?=?gnb.predict_proba(X_test)[:,1]  
fpr,tpr,threshold?=?metrics.roc_curve(y_test,?y_score)  
#?計算AUC的值  
roc_auc?=?metrics.auc(fpr,tpr)  

#?繪制面積圖  
plt.stackplot(fpr,?tpr,?color='steelblue',?alpha?=?0.5,?edgecolor?=?'black')  
#?添加邊際線  
plt.plot(fpr,?tpr,?color='black',?lw?=?1)  
#?添加對角線  
plt.plot([0,1],[0,1],?color?=?'red',?linestyle?=?'--')  
#?添加文本信息  
plt.text(0.5,0.3,'ROC?curve?(area?=?%0.2f)'?%?roc_auc)  
#?添加x軸與y軸標簽  
plt.xlabel('1-Specificity')  
plt.ylabel('Sensitivity')  
#?顯示圖形  
plt.show()  

如上圖所示的ROC曲線,計算得到的AUC值為0.94,超過用于評判模型好壞的閾值0.8,故可以認為構建的貝葉斯分類器是非常理想的,進而驗證了前文所得的結論。最后需要強調的是,利用高斯貝葉斯分類器對數據集進行分類時要求輸入的數據集X為連續的數值型變量。

【多項式貝葉斯分類器】

蘑菇數據集來自于UCI網站,一共包含8?124條觀測和22個變量,其中因變量為type,表示蘑菇是否有毒,剩余的自變量是關于蘑菇的形狀、表面光滑度、顏色、生長環境等。首先將該數據集讀入Python,并預覽前5行數據,代碼如下:

#?讀取數據  
mushrooms?=?pd.read_csv(r'C:\Users\Administrator\Desktop\mushrooms.csv')  
#?數據的前5行,見表12-3  
mushrooms.head()

如上表所示,表中的所有變量均為字符型的離散值,由于Python建模過程中必須要求自變量為數值類型,因此需要對這些變量做因子化處理,即把字符值轉換為對應的數值。接下來利用pandas模塊中的factorize函數對離散的自變量進行數值轉換,代碼如下:

#?將字符型數據做因子化處理,將其轉換為整數型數據  
columns?=?mushrooms.columns[1:]  
for?column?in?columns:  
????mushrooms[column]?=?pd.factorize(mushrooms[column])[0]  
mushrooms.head()  

如上表所示,所有的字符型變量全部轉換成了數值,而且每一列中的數值都代表了各自不同的字符值。需要注意的是,factorize函數返回的是兩個元素的元組,第一個元素為轉換成的數值,第二個元素為數值對應的字符水平,所以在類型轉換時,需要通過索引方式返回因子化的值。接著就可以使用多項式貝葉斯分類器對如上數據集進行類別的預測,為了實現模型的驗證,需要將該數據集拆分為訓練集和測試集,代碼如下:

#?將數據集拆分為訓練集合測試集  
Predictors?=?mushrooms.columns[1:]  
X_train,X_test,y_train,y_test?=?model_selection.train_test_split(mushrooms[Predictors],  
?????????????????????????????????mushrooms['type'],?test_size?=?0.25,  
?????????????????????????????????random_state?=?10)  
#?構建多項式貝葉斯分類器的“類”  
mnb?=?naive_bayes.MultinomialNB()  
#?基于訓練數據集的擬合  
mnb.fit(X_train,?y_train)  
#?基于測試數據集的預測  
mnb_pred?=?mnb.predict(X_test)  

#?構建混淆矩陣  
cm?=?pd.crosstab(mnb_pred,y_test)  
#?繪制混淆矩陣圖  
sns.heatmap(cm,?annot?=?True,?cmap?=?'GnBu',?fmt?=?'d')  
#?去除x軸和y軸標簽  
plt.xlabel('')  
plt.ylabel('')  
#?顯示圖形  
plt.show()  

在如上的混淆矩陣圖中,橫坐標代表測試數據集中的實際類別值,縱坐標為預測類別值,正確預測無毒的有981個樣本,正確預測有毒的有786個樣本。

#?模型的預測準確率  
print('模型的準確率為:\n',metrics.accuracy_score(y_test,?mnb_pred))  
print('模型的評估報告:\n',metrics.classification_report(y_test,?mnb_pred))  

模型的準確率為:  
0.870014771049  

模型的評估報告:  
??????????????precision????recall?????f1-score????support  
edible??????????0.85???????0.92??????0.88???????1072  
poisonous???????0.90???????0.82??????0.86???????959  
avg?/?total?????0.87???????0.87??????0.87???????2031

基于混淆矩陣的進一步運算,可以得到如上所示的兩部分結果,并從中發現,模型在測試數據集上的整體預測準確率為87%,而且從各類別值來看,無毒蘑菇的預測覆蓋率為92%、有毒蘑菇的預測覆蓋率為82%。總體來說,模型的預測效果還是非常理想的,接下來繼續繪制ROC曲線,查看對應的AUC值的大小,代碼如下:

#?計算正例的預測概率,用于生成ROC曲線的數據  
y_score?=?mnb.predict_proba(X_test)[:,1]  
fpr,tpr,threshold?=?metrics.roc_curve(y_test.map({'edible':0,'poisonous':1}),?y_score)  
#?計算AUC的值  
roc_auc?=?metrics.auc(fpr,tpr)  

#?繪制面積圖  
plt.stackplot(fpr,?tpr,?color='steelblue',?alpha?=?0.5,?edgecolor?=?'black')  
#?添加邊際線  
plt.plot(fpr,?tpr,?color='black',?lw?=?1)  
#?添加對角線  
plt.plot([0,1],[0,1],?color?=?'red',?linestyle?=?'--')  
#?添加文本信息  
plt.text(0.5,0.3,'ROC?curve?(area?=?%0.2f)'?%?roc_auc)  
#?添加x軸與y軸標簽  
plt.xlabel('1-Specificity')  
plt.ylabel('Sensitivity')  
#?顯示圖形  
plt.show()  

如上圖所示,ROC曲線下的面積為0.94,超過閾值0.8,可以認為模型的效果是可以接受的。需要注意的是,當因變量為字符型的值時,子模塊metrics中的函數roc_curve必須傳入數值型的因變量(如代碼所示,將字符值和數值做了映射),否則會報錯誤信息。

對于離散型自變量的數據集而言,在分類問題上并非都可以使用多項式貝葉斯分類器,如果自變量在特定y值下的概率不服從多項式分布的話,分類器的預測效果就不會很理想。通常情況下,會利用多項式貝葉斯分類器作文本分類,如一份郵件是否垃圾郵件、用戶評論是否為正面等。

【伯努利貝葉斯分類器】

用戶對其購買的蚊帳進行評論,該數據集是通過爬蟲的方式獲得,一共包含10?644條評論,數據集中的Type變量為評論所對應的情緒。首先將爬蟲獲得的數據集讀入Python中,并預覽前幾行數據,代碼如下:

#?讀入評論數據  
evaluation?=?pd.read_excel(r'C:\Users\Administrator\Desktop\Contents.xlsx',sheetname=0)  
#?查看數據前10行,見表12-6  
evaluation.head(10)  

如上表所示,數據集包含4個字段,分別是用戶昵稱、評價時間、評價內容和對應的評價情緒。從評價內容來看,會有一些“臟”文本在內,如數字、英文等,所以需要將這些“臟”文本刪除,代碼如下:

#?運用正則表達式,將評論中的數字和英文去除  
evaluation.Content?=?evaluation.Content.str.replace('[0-9a-zA-Z]','')  
evaluation.head()

經過數據的初步清洗后,下一步要做的就是對文本進行切詞,但在切詞前,通常需要引入用戶自定義的詞庫和停止詞。利用詞典的目的是將無法正常切割的詞實現正確切割(如“沙瑞金書記”會被切詞為“沙”“瑞金”“書記”,為了避免這種情況,就需要將類似“沙瑞金”這樣的詞組合為詞庫),使用停止詞的目的是將句子中無意義的詞語刪除(如“的”“啊”“我們”等)。

#?導入第三方包  
import?jieba  

#?加載自定義詞庫  
jieba.load_userdict(r'C:\Users\Administrator\Desktop\all_words.txt')  
#?讀入停止詞  
with?open(r'C:\Users\Administrator\Desktop\mystopwords.txt',?encoding='UTF-8')?as?words:  
????stop_words?=?[i.strip()?for?i?in?words.readlines()]  
#?構造切詞的自定義函數,并在切詞過程中刪除停止詞  
def?cut_word(sentence):  
????words?=?[i?for?i?in?jieba.lcut(sentence)?if?i?not?in?stop_words]  
????#?切完的詞用空格隔開  
????result?=?'?'.join(words)  
????return(result)  

#?調用自定義函數,并對評論內容進行批量切詞  
words?=?evaluation.Content.apply(cut_word)  
#?前5行內容的切詞效果  
words[:5]  

out:  
0????包裝?破損?拉鏈?處有?線頭?阻礙?拉鏈?拉動?沒有?安裝?第一次?安裝?費勁  
1????上長?三根?空桿?裝  
2????沒有?送貨上門?失望  
3????挺不錯?破?口子?不知道?啥時候?弄?不錯?老婆?懷孕?蚊香?不錯  
4????不錯?效果

如上結果所示,通過調入第三方包jieba實現中文的切詞,并在切詞過程中加入自定義詞庫和刪除停止詞。接下來利用如上的切詞結果,構造文檔詞條矩陣,矩陣的每一行代表一個評論內容,矩陣的每一列代表切詞后的詞語,矩陣的元素為詞語在文檔中出現的頻次。代碼如下:

#?導入第三方包  
from?sklearn.feature_extraction.text?import?CountVectorizer  

#?計算每個詞在各評論內容中的次數,并將稀疏度為99%以上的詞刪除  
counts?=?CountVectorizer(min_df?=?0.01)  
#?文檔詞條矩陣  
dtm_counts?=?counts.fit_transform(words).toarray()  
#?矩陣的列名稱  
columns?=?counts.get_feature_names()  
#?將矩陣轉換為數據框,即X變量  
X?=?pd.DataFrame(dtm_counts,?columns=columns)  
#?情感標簽變量  
y?=?evaluation.Type  
X.head()  

如上表所示,將文檔詞條矩陣轉換為數據框后得到一個龐大的稀疏矩陣,即數據框中的大部分值為0。為了避免數據框的列數過多,在構造文檔詞條矩陣時做了相應的限制條件,即代碼中的CountVectorizer(min_df = 0.01),表示詞語所對應的文檔數目必須在所有文檔中至少占1%的比例,最終得到上表中所呈現的99個變量。有了如上的數據框,接下來要做的就是將數據集拆分為訓練集和測試集,并利用訓練集構建伯努利貝葉斯分類器,利用測試集對分類器的預測效果進行評估,具體代碼如下:

#?將數據集拆分為訓練集和測試集  
X_train,X_test,y_train,y_test?=?model_selection.train_test_split(X,y,test_size?=?0.25,  
?????????????????????????????????????????????????????????random_state=1)  
#?構建伯努利貝葉斯分類器  
bnb?=?naive_bayes.BernoulliNB()  
#?模型在訓練數據集上的擬合  
bnb.fit(X_train,y_train)  
#?模型在測試數據集上的預測  
bnb_pred?=?bnb.predict(X_test)  
#?構建混淆矩陣  
cm?=?pd.crosstab(bnb_pred,y_test)  
#?繪制混淆矩陣圖  
sns.heatmap(cm,?annot?=?True,?cmap?=?'GnBu',?fmt?=?'d')  
#?去除x軸和y軸標簽  
plt.xlabel('Real')  
plt.ylabel('Predict')  
#?顯示圖形  
plt.show()  

如上結果所示,從混淆矩陣圖形來看,伯努利貝葉斯分類器在預測數據集上的效果還是非常棒的,絕大多數的樣本都被預測正確(因為主對角線上的數據非常大),而且總的預測準確率接近85%。

#?模型的預測準確率  
print('模型的準確率為:\n',metrics.accuracy_score(y_test,?bnb_pred))  
print('模型的評估報告:\n',metrics.classification_report(y_test,?bnb_pred))  

模型的準確率為:  
?0.84704998121  

模型的評估報告:  
??????????????precision????recall?????f1-score???support  
???Negative??????0.82????????0.90??????0.86??????1341  
???Positive??????0.88????????0.80??????0.84??????1320  
???avg?/?total???0.85????????0.85??????0.85??????2661

從模型的評估報告來看,預測為消極情緒的覆蓋率0.9相比于積極情緒的覆蓋率0.8要更高一些,但總體來說模型的預測效果還是不錯的。同理,再繪制一下關于模型在測試數據集上的ROC曲線,代碼如下:

#?計算正例Positive所對應的概率,用于生成ROC曲線的數據  
y_score?=?bnb.predict_proba(X_test)[:,1]  
fpr,tpr,threshold?=?metrics.roc_curve(y_test.map({'Negative':0,'Positive':1}),?y_score)  
#?計算AUC的值  
roc_auc?=?metrics.auc(fpr,tpr)  

#?繪制面積圖  
plt.stackplot(fpr,?tpr,?color='steelblue',?alpha?=?0.5,?edgecolor?=?'black')  
#?添加邊際線  
plt.plot(fpr,?tpr,?color='black',?lw?=?1)  
#?添加對角線  
plt.plot([0,1],[0,1],?color?=?'red',?linestyle?=?'--')  
#?添加文本信息  
plt.text(0.5,0.3,'ROC?curve?(area?=?%0.2f)'?%?roc_auc)  
#?添加x軸與y軸標簽  
plt.xlabel('1-Specificity')  
plt.ylabel('Sensitivity')  
#?顯示圖形  
plt.show()  

如上圖所示,繪制的ROC曲線所對應的AUC值為0.93,同樣是一個非常高的數值,再結合模型準確率、覆蓋率等指標,可以認為該模型在測試數據集上的預測效果是非常理想的。需要說明的是,如果訓練數據集是關于詞語在各文檔中出現的頻次,直接調用BernoulliNB類是沒有問題的,因為該“類”中參數binarize默認值為0,即如果詞的頻次大于0,則對應的變量值在模型運算時會轉換成1,否則轉換為0。

【結語】

OK,關于貝葉斯算法的實戰我們就分享到這里,如果你有任何問題,歡迎在公眾號的留言區域表達你的疑問。同時,也歡迎各位朋友繼續轉發與分享文中的內容,讓更多的人學習和進步。

關注公眾號,回復【貝葉斯】便可獲得文內的數據和代碼~



?------------------------------------------------

原文地址:https://mp.weixin.qq.com/s/wQIC_F84cTMTsdp8FM9wfw

轉載請標明來之:阿貓學編程
更多教程:阿貓學編程-python基礎教程

所有評論

如果對文章有異議,請加qq:1752338621