不同于K-近邻算法和决策树,在使用这两种分类器时,会要求分类器给出一个明确的决策,但分类器有时会给出错误的决策。此时,可以要求分类器给出一个最优的类别猜测作为决策,同时给出这个决策的正确概率。


贝叶斯决策理论

用p1(x,y)和p2(x,y)分别表示点(x,y)属于类别1和类别2的概率。
若p1 > p2,则做出将点(x,y)确定为类别1的决策。
若p2 > p1,则做出将点(x,y)确定为类别2的决策。
即选择高概率对应的类别。

条件概率

P(A | B):事件A在事件B已发生的概率下的发生概率
P(A | B) = P(AB) / P(B) = P(B | A)*P(A) / P(B)
image.png

使用条件概率来分类

P(Ci | x,y):给定某个由x,y表示的数据点,该数据点来自类别Ci的概率。
P(Ci | x,y) = P(x,y | Ci)*P(Ci) / P(x,y)
P(Ci | x,y)越大,则点(x,y)属于类别Ci的概率越大。故可以基于该概率对数据进行分类。

应用朴素贝叶斯分类器的数据基础

假设各个元素之间没有联系,即数据间相互独立。
假设每个特征权重相同。


从文本中构建词向量

_#程序4-1:从文本中构建词向量<br />_def loadDataSet():<br /> postingList = [['本人', '二战', '考研'],<br /> ['考研', '二战', '自习'],<br /> ['五一', '放假', '开心'],<br /> ['朋友', '相聚', '开心']<br /> ]<br /> _#print("type(postingList=",type(postingList))<br /> _classVec=[1,1,0,0] _# 1代表与考研相关,0代表与考研无关<br /> _return postingList,classVec<br />_# 基于训练样本中的所有词汇创建词列表<br />_def createVocabList(dataSet): _# 构建词汇表:将训练样本中的所有词汇放在一个列表中<br /> _vocabSet = set([]) _# 定义一个词汇表变量,类型是集合(值唯一),初值为空<br /> _for document in dataSet:<br /> vocabSet = vocabSet | set(document) _# 求两个集合的并集<br /> _return list(vocabSet) _# 最后将集合转换为列表<br /># 基于词汇表vocabList构建某条言论inputSet的词向量<br />_def setOfWords2Vec(vocabList, inputSet):<br /> returnVec = [0]*len(vocabList) _# 定义一个词向量变量,类型是列表,列表长度为词的个数,初值为0<br /> _for word in inputSet:_# 遍历该条言论中的每个词语<br /> _if word in vocabList: _# 如果该词语出现在词汇表的某个位置<br /> _returnVec[vocabList.index(word)] = 1 _# 则词汇表的该位置赋值为1,index() 函数用于从列表中找出某个值第一个匹配项的索引位置。<br /> _else: print("词语",word,"不在词汇表中。" )<br /> return returnVec _# 返回词向量_

部分函数解释:

set():集合;集合中值唯一,即不存在重复值。

A | B :或,返回集合A,B的并集。

list.index(value):返回列表中指定值的索引。

运行及结果:

postingList,classVec = loadDataSet()<br />print("postingList = ",postingList)<br />print("classVec = ",classVec)<br />vocabSet = createVocabList(postingList)<br />print("词列表 vocabVec = ",vocabSet)<br />trainMat = []<br />for postinDoc in postingList:<br /> trainMat.append(setOfWords2Vec(vocabSet, postinDoc))<br />print("基于词列表的词向量矩阵:",trainMat)
image.png


训练算法:从词向量计算概率

image.png
image.png
image.png

朴素贝叶斯分类器的训练函数

_# 朴素贝叶斯分类器训练函数<br /># 词向量矩阵, 类别列表<br />_def trainNB0(trainMatrix, trainCategory):<br /> numTrainDocs = len(trainMatrix)_# 计算词向量的行数(数据总条数),每行表示一个文档<br /> _numWords = len(trainMatrix[0]) _# 计算词向量长度,即每个文档的单词个数<br /> _pAbusive = sum(trainCategory)/float(numTrainDocs) _# pAbusive=0.5,表示文档中发言与考研相关的概率<br /> # sum(trainCategory):所有类别为1的数据条数。(因为在类别列表中类别为1的取值就是1,故可以这样得到。实际上统计的是类别为1的数据个数)<br /> # numTrainDocs:词向量矩阵中数据的条数<br /> _p0Num = np.zeros(numWords) _# 创建两个长度为词向量长度的全为0的一维矩阵<br /> _p1Num = np.zeros(numWords) _# 分别存放特征值为0、1的那安条数据的词向量<br /> _p0Denom = 0.0 _# 初始化两个用于存放类别为0、1的那条数据的词向量中特征值为1的个数<br /> _p1Denom = 0.0<br /> _# 遍历每一条数据<br /> _for i in range(numTrainDocs):<br /> if trainCategory[i] == 1: _# 如果这条数据类别为1<br /> _p1Num += trainMatrix[i] _# 将这条词向量各个特征位置的取值累加到p1Num<br /> _p1Denom += sum(trainMatrix[i]) _# 将这条词向量中特征值为1的数目累加到p1Denom<br /> _else:<br /> p0Num += trainMatrix[i] _# 将这条词向量各个特征位置的取值累加到p0Num<br /> _p0Denom += sum(trainMatrix[i]) _# 将这条词向量中特征值为1的数目累加到p0Denom<br /> _p1Vect = p1Num/p1Denom _# 类别为1的语句中各词出现的概率<br /> _p0Vect = p0Num/p0Denom _# 类别为0的语句中各词出现的概率<br /> _return p0Vect, p1Vect, pAbusive

运行及结果:

trainMat = [] _#初始化训练数据集的词向量矩阵<br />_for postinDoc in postingList:<br /> trainMat.append(setOfWords2Vec(vocabSet, postinDoc))<br />print("基于词列表的词向量矩阵:",trainMat)<br />p0Vect, p1Vect, pAbusive = trainNB0(trainMat,classVec)<br />print("类别为1(与考研相关)的语句中各词出现的概率:",p1Vect)<br />print("类别为0(与考研无关)的语句中各词出现的概率:", p0Vect)<br />print("类别为1(与考研相关)的语句占全部语句的比例为:",pAbusive)
image.png


测试算法:根据现实情况修改分类器

优化1:

利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得该文档属于某个类别的概率,即计算p(w0,1)*p(w1,1)…。如果其中一个概率为0,则最后的乘积也为0。为了降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化2。
对trainNB0()函数进行修改:
**p0Num = ones(numWords)**
**p1Num = ones(numWords)**
**p0Denom = 2.0**
**p1Denom = 2.0**

优化2:

当特征值很多时,每个特征的概率就会很小,很多很小的数相乘就会导致出现下溢出问题。在计算p(w0,1)*p(w1,1)…时,由于大部分因子很小,会导致程序因为下溢出问题得不到正确的答案。
其中一种解决方法是对乘积取自然对数。在代数中有 ln(a*b) = ln(a) + ln(b)。
对trainNB0()函数进行修改:
**p1Vect = log(p1Num / p1Denom)**
**p0Vect = log(p0Num / p0Denom)**

构建分类器:

`# 朴素贝叶斯分类函数
#(待检测语句,0类别句子中各词出现的频率,1类别句子中各词出现的频率,训练数据集中类别为1的句子比例)
_def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1 = sum(vec2Classify*p1Vec) + np.log(pClass1)
# 该语句为类1的概率
p0 = sum(vec2Classify*p0Vec) + np.log(1.0 - pClass1) # 该语句为类0的概率
# 判断决定返回待检测语句属于哪类
_if p1 > p0:
return “该语句与考研相关”
else:
return “该语句与考研无关”

def testingNB():
# 训练分类器
_listOPosts,listClasses = loadDataSet()
# 获取训练数据,类别
myVocabList = createVocabList(listOPosts) # 获取基于训练数据创建的词列表
trainMat = [] #初始化训练数据集的词向量矩阵
for postinDoc in listOPosts: # 取出每一条数据
trainMat.append(setOfWords2Vec(myVocabList,postinDoc)) # 构建词向量矩阵
p0Vect, p1Vect, pAbusive = trainNB0(trainMat, listClasses)
print(“类别为1(与考研相关)的语句中各词出现的概率:”, p1Vect)
print(“类别为0(与考研无关)的语句中各词出现的概率:”, p0Vect)
print(“类别为1(与考研相关)的语句占全部语句的比例为:”, pAbusive)
print(“分类器已构建完成!”)
inputStr = input(“请输入待预测的句子:”)
testEntry = jieba.cut(inputStr)
print(“分词结果:”,testEntry)
thisDoc = np.array(setOfWords2Vec(myVocabList,testEntry))
# 获取待预测语句的词向量
print(inputStr,”的预测类别是:”,classifyNB(thisDoc,p0Vect,p1Vect,pAbusive)) # 获取分类结果_`

运行及结果:

image.png