朴素贝叶斯分类器

目录

1.朴素贝叶斯和Logistic回归

2.朴素贝叶斯模型分类实例

(1)二分类

(2)文本分类


1.朴素贝叶斯和Logistic回归

贝叶斯分类器作为一种数据分类的预测方法,最终会给出一个或多个分类边界。

以下代码,随机生成两组各50个服从标准正态分布的随机数,分别作为输入变量X1和X2。然后,指定相应样本观测点(X1,X2)对应的输出变量Y取值为0或1,0和1的各占50%,也就是左图所示。需要找到可将两类点分开的分类线,实现分类预测。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
import warnings
warnings.filterwarnings(action = 'ignore')
from scipy.stats import beta
from sklearn.naive_bayes import GaussianNB
import sklearn.linear_model as LM
from sklearn.model_selection import cross_val_score,cross_validate,train_test_split
from sklearn.metrics import classification_report
from sklearn.metrics import roc_curve, auc,accuracy_score,precision_recall_curve

np.random.seed(123)
N=50
n=int(0.5*N)
X=np.random.normal(0,1,size=100).reshape(N,2)
Y=[0]*n+[1]*n
X[0:n]=X[0:n]+1.5

fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(15,6))
axes[0].scatter(X[:n,0],X[:n,1],color='black',marker='o')
axes[0].scatter(X[(n+1):N,0],X[(n+1):N,1],edgecolors='magenta',marker='o',c='')
axes[0].set_title("样本观测点的分布情况")
axes[0].set_xlabel("X1")
axes[0].set_ylabel("X2")

modelNB = GaussianNB()
modelNB.fit(X, Y)
modelLR=LM.LogisticRegression()
modelLR.fit(X,Y)
Data=np.hstack((X,np.array(Y).reshape(N,1)))
Yhat=modelNB.predict(X)
Data=np.hstack((Data,Yhat.reshape(N,1)))
Data=pd.DataFrame(Data)

X1,X2 = np.meshgrid(np.linspace(X[:,0].min(),X[:,0].max(),100), np.linspace(X[:,1].min(),X[:,1].max(),100))
New=np.hstack((X1.reshape(10000,1),X2.reshape(10000,1)))
YnewHat1=modelNB.predict(New)
DataNew=np.hstack((New,YnewHat1.reshape(10000,1)))
YnewHat2=modelLR.predict(New)
DataNew=np.hstack((DataNew,YnewHat2.reshape(10000,1)))
DataNew=pd.DataFrame(DataNew)

for k,c in [(0,'silver'),(1,'red')]:
    axes[1].scatter(DataNew.loc[DataNew[2]==k,0],DataNew.loc[DataNew[2]==k,1],color=c,marker='o',s=1)
for k,c in [(0,'silver'),(1,'mistyrose')]:
    axes[1].scatter(DataNew.loc[DataNew[3]==k,0],DataNew.loc[DataNew[3]==k,1],color=c,marker='o',s=1)

axes[1].scatter(X[:n,0],X[:n,1],color='black',marker='+')
axes[1].scatter(X[(n+1):N,0],X[(n+1):N,1],color='magenta',marker='+')
for k,c in [(0,'black'),(1,'magenta')]:
    axes[1].scatter(Data.loc[(Data[2]==k) & (Data[3]==k),0],Data.loc[(Data[2]==k) & (Data[3]==k),1],color=c,marker='o')
axes[1].set_title("朴素贝叶斯分类器(误差%.2f)和Logistic回归模型(误差%.2f)的分类边界"%(1-modelNB.score(X,Y),1-modelLR.score(X,Y)))
axes[1].set_xlabel("X1")
axes[1].set_ylabel("X2")

np.random.seed(123)
k=10
CVscore=cross_validate(modelNB,X,Y,cv=k,scoring='accuracy',return_train_score=True) 
axes[1].text(-2,3.5,'贝叶斯测试误差:%.4f' %(1-CVscore['test_score'].mean()),fontsize=12,color='r')
CVscore=cross_validate(modelLR,X,Y,cv=k,scoring='accuracy',return_train_score=True) 
axes[1].text(-2,3,"Logistic回归测试误差:%.2f" %(1-CVscore['test_score'].mean()),fontsize=12,color='r')
plt.show()

输出:

右图中,底层是贝叶斯的分类区域,也就是银色和红色部分之间呈弧形的边界线,也叫贝叶斯决策边界。中间层是Logistic分类,中间的直线就是回归的分类线,隔开了浅红色和银色部分区域。再上一层用(+)标记了原始样本点,黑色加号为0的样本点,洋红色加号为1的样本点。顶层用(o)标记预测正确的样本,这样,没有被覆盖的加号就是没有预测正确的样本。所以贝叶斯决策预测错误率:

同理,对于Logistic回归模型,落在直线下方和上方的点分别为0类和1类(图中未显示),总预测错误率:

结论:

朴素贝叶斯分类器能够解决非线性分类问题(朴素贝叶斯决策边界是一条曲线,Logistic 决策边界为一条直线),且模型的复杂度高于 Logistic 回归。

np.random.seed(123)
k=10
CVscore=cross_validate(modelNB,X,Y,cv=k,scoring='accuracy',return_train_score=True) 
print('朴素贝叶斯分类器10折交叉验证:训练错误率:%.4f 测试错误率:%.4f' %(1-CVscore['train_score'].mean(),1-CVscore['test_score'].mean()))
CVscore=cross_validate(modelLR,X,Y,cv=k,scoring='accuracy',return_train_score=True) 
print("Logistic回归10折交叉验证:训练错误率:%.4f 测试错误率:%.4f" %(1-CVscore['train_score'].mean(),1-CVscore['test_score'].mean()))

输出:

结论:朴素贝叶斯的泛化能力比Logistic更好(测试错误率较低)。同时,朴素贝叶斯相比于Logistic回归模型更稳健,过拟合的风险更低(训练集到测试集的错误率增长更小)。

2.朴素贝叶斯模型分类实例
(1)二分类
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
import warnings
warnings.filterwarnings(action = 'ignore')
from scipy.stats import beta
from sklearn.naive_bayes import GaussianNB
import sklearn.linear_model as LM
from sklearn.model_selection import cross_val_score,cross_validate,train_test_split
from sklearn.metrics import classification_report
from sklearn.metrics import roc_curve, auc,accuracy_score,precision_recall_curve

data=pd.read_excel('北京市空气质量数据.xlsx')
data=data.replace(0,np.NaN)
data=data.dropna()
data['有无污染']=data['质量等级'].map({'优':0,'良':0,'轻度污染':1,'中度污染':1,'重度污染':1,'严重污染':1})
data['有无污染'].value_counts()
X=data.loc[:,['PM2.5','PM10','SO2','CO','NO2','O3']]
Y=data.loc[:,'有无污染']

#构建贝叶斯分类器和Logistic回归模型
modelNB = GaussianNB()
modelNB.fit(X, Y)
modelLR=LM.LogisticRegression()
modelLR.fit(X,Y)
print('评价模型结果:\n',classification_report(Y,modelNB.predict(X)))

输出:

利用 ROC和P-R曲线对比朴素贝叶斯分类器和 Logistic 回归模型的预测性能:

fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,4))
fpr,tpr,thresholds = roc_curve(Y,modelNB.predict_proba(X)[:,1],pos_label=1) 
fpr1,tpr1,thresholds1 = roc_curve(Y,modelLR.predict_proba(X)[:,1],pos_label=1) 
axes[0].plot(fpr, tpr, color='r',label='贝叶斯ROC(AUC = %0.5f)' % auc(fpr,tpr)) 
axes[0].plot(fpr1, tpr1, color='blue',linestyle='-.',label='Logistic回归ROC(AUC = %0.5f)' % auc(fpr1,tpr1)) 
axes[0].plot([0, 1], [0, 1], color='navy', linewidth=2, linestyle='--')
axes[0].set_xlim([-0.01, 1.01])
axes[0].set_ylim([-0.01, 1.01])
axes[0].set_xlabel('FPR')
axes[0].set_ylabel('TPR')
axes[0].set_title('两个分类模型的ROC曲线')
axes[0].legend(loc="lower right")

pre, rec, thresholds = precision_recall_curve(Y,modelNB.predict_proba(X)[:,1],pos_label=1)
pre1, rec1, thresholds1 = precision_recall_curve(Y,modelLR.predict_proba(X)[:,1],pos_label=1)
axes[1].plot(rec, pre, color='r',label='贝叶斯总正确率 = %0.3f)' % accuracy_score(Y,modelNB.predict(X))) 
axes[1].plot(rec1, pre1, color='blue',linestyle='-.',label='Logistic回归总正确率 = %0.3f)' % accuracy_score(Y,modelLR.predict(X))) 
axes[1].plot([0,1],[1,pre.min()],color='navy', linewidth=2, linestyle='--')
axes[1].set_xlim([-0.01, 1.01])
axes[1].set_ylim([pre.min()-0.01, 1.01])
axes[1].set_xlabel('查全率R')
axes[1].set_ylabel('查准率P')
axes[1].set_title('两个分类模型的P-R曲线')
axes[1].legend(loc='lower left')
plt.show()

输出:

结论:

朴素贝叶斯分类器和Logistic回归模型曲线下的面积分别约等于0.96和0.98,表明朴素贝叶斯模型虽然较好地实现了二分类预测,但整体性能略低于Logistic回归模型。在P-R曲线中,随着査全率R的增加,Logistic回归模型的查准率P并没有快速下降,优于朴素贝叶斯分类器,且前者的预测总正确率(0.92)高于后者(0.88)。可见,对该问题Logistic回归模型有更好的表现。

(2)文本分类

先介绍TF-IDF,TF是词频,其可以衡量一个词再单篇文本中的出现频率,他的计算方式为该词在文本中出现的次数与文本中总词数的比。IDF是逆文本频率,衡量一个词在整个文档集合中“普遍度”,其计算公式为:

包含某词的文本数越多,IDF越小。IDF越大,说明该词越能区分文档。TF-IDF的定义为:

TF-IDF越大,词 i 对文本 j 越重要。例如,“游戏”这个词在文本 j 中多次出现,其TF较大,但如果“游戏”也在其他多个文本中多次出现,其IDF就较小。此时TF-DF也会较小,即不能认为“游戏”是文本 j的典型代表词。文本量化时通常选择若干TF-IDF较大的词作为文本的典型代表词,并将相应的TF-IDF值作为输入变量参与文本的分类建模。
如果分词结果中包含了很多对文本预测没有意义的词,将会影响TF-IDF的计算结果并对后续的分类建模产生负面作用。通常将意义模糊、语气助词、标点符号等对文本分类没有意义的词称为停用词。可事先准备停用词表并在指定过滤掉文本中的停用词后再计算 TF-IDF。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
import warnings
warnings.filterwarnings(action = 'ignore')
from scipy.stats import beta
from sklearn.naive_bayes import GaussianNB
import sklearn.linear_model as LM
from sklearn.model_selection import cross_val_score,cross_validate,train_test_split
from sklearn.metrics import classification_report
from sklearn.metrics import roc_curve, auc,accuracy_score,precision_recall_curve

import jieba #用于文本分词和分析
import jieba.analyse #文本量化计算
from sklearn.feature_extraction.text import  TfidfVectorizer
import json #用于处理JSON格式文件
from sklearn.naive_bayes import MultinomialNB #多分类的朴素贝叶斯分类器

documents = ["中国的发展是开放的发展",
    "中国经济发展的质量在稳步提升,人民生活在持续改善",
    "从集市、超市到网购,线上年货成为中国老百姓最便捷的硬核年货",
    "支付体验的优化以及物流配送效率的提升,线上购物变得越来越便利"]
documents = [" ".join(jieba.cut(item)) for item in documents] #通过jieba分词
print("文本分词结果:\n",documents)
vectorizer = TfidfVectorizer()  #定义TF-IDF对象
X = vectorizer.fit_transform(documents) #生成特征词表,通常以词为行,文本为列

words=vectorizer.get_feature_names() #获取到这个特征词表,将其赋值给变量words
print("特征词表:\n",words)  
print("idf:\n",vectorizer.idf_)  #idf
X=X.toarray() #print(X.toarray())   #文本-词的tf-idf矩阵
for i in range(len(X)): #打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重 
    for j in range(len(words)):
        print(words[j],X[i][j])

 输出:

文本可用JSON格式存储,在Python中用键-值对访问:

alltext=[]
label=[]
fenceText=[]
fn = open('离婚诉讼文本.json', 'r',encoding='utf-8')
line = fn.readline()
while line:
    data = json.loads(line)
    for sent in data:
        if len(sent['labels']) ==1:
            label.append(sent['labels'])
            alltext.append(sent['sentence'])
    line=fn.readline()
fn.close()

#将文本数据随机分成训练集和测试集,比例为6:4。
X_train, X_test, Y_train, Y_test = train_test_split(alltext,label,train_size=0.60, random_state=123) 
fenceText=[" ".join(jieba.cut(item)) for item in X_train]
with open("停用词表.txt", "r" ,encoding='utf-8') as fn:
    stpwrdlst = fn.read().splitlines()
fn.close()     

# 指定以"停用词表.txt"文件中的词作为停用词。仅提取前400个最重要(权重最大)的特征词。   
vectorizer = TfidfVectorizer(stop_words=stpwrdlst,max_features=400)
X_train = vectorizer.fit_transform(fenceText)
X_train=X_train.toarray()
modelNB = MultinomialNB() ##MultinomialNB虽然理论上适合离散计数数据,但TF-IDF本质是 “加权后的词频”(源于计数,非负,保留计数特征),因此可以被当作 “特殊的小数型计数” 输入,实际应用中能有效工作。这也是文本分类中常用MultinomialNB处理TF-IDF特征的原因。
modelNB.fit(X_train, Y_train)
print("朴素贝叶斯分类器的训练误差:%.3f"%(1-modelNB.score(X_train,Y_train)))

fenceText=[" ".join(jieba.cut(item)) for item in X_test]

#这里不能用vectorizer.fit_transform(fenceText),因为这样测试集的独特词汇会被加入词汇表,模型在训练时本应只看到训练集,也就是会导致测试集信息泄露。
X_test = vectorizer.transform(fenceText)
X_test=X_test.toarray()
print("朴素贝叶斯分类器的测试误差:%.3f"%(1-modelNB.score(X_test,Y_test)))

输出:

上面贝叶斯分类器的训练误差和测试误差均较高。一方面可优化分词和TF-IDF计算。例如补充法律专业用词,完善停用词表等。或者尝试其他分类模型。另一方面也说明采用tf-idf量化文本并非最佳方案。目前另一种较为流行的文本量化方式是采用基于word2vec的词向量。


资料百度网盘自取:

链接: https://pan.baidu.com/s/1R-G7EODwX6-BFVnyn8Bhlg?pwd=u8sw 提取码: u8sw

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值