很久没写博客了,最近忙着中期考核。补写一下朴素贝叶斯的代码部分,重点和难点在于计算条件概率。计算条件概率和类概率时,代码部分都使用了拉普拉斯平滑,主要是为了避免其它属性携带的信息被训练集中未出现的属性值“抹去”的现象。并且这里使用了对数的表示法代替直接计算若干个条件概率的乘积值,是因为如果连续的概率乘积会造成数值过小,python可能无法显示出来这样的数值。也叫做防止数值溢出。之所以对数表示法可行,因为一个数在加对数为底数以后,它的单调性并未改变,而这样的得数不会过小(python能够显示)
1.词表到向量的转换函数
"""
第一个函数loadDataSet()创建了一些实验样本。该函数返回的第一个变量是进行词条切分后的文档集合,
这些文档来自斑点犬爱好者留言板。这些留言文本被切分成一系列的词条集合,标点符号从文本中去掉,后面会
探讨文本处理的细节。loadDataSet()函数返回的第二个变量是一个类别标签的集合。这里有两类,侮辱性和非
侮辱性。这些文本的类别由人工标注,这些标注信息用于训练程序以便自动检测侮辱性留言
"""
def loadDataSet():
postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], #切分的词条
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0,1,0,1,0,1] #类别标签向量,1代表侮辱性词汇,0代表不是
return postingList,classVec
postingList, classVec = loadDataSet()
for each in postingList:
print(each)
print(classVec)
print(postingList)
['my', 'dog', 'has', 'flea', 'problems', 'help', 'please']
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid']
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him']
['stop', 'posting', 'stupid', 'worthless', 'garbage']
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him']
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']
[0, 1, 0, 1, 0, 1]
[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'], ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'], ['stop', 'posting', 'stupid', 'worthless', 'garbage'], ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
"""
下一个函数 createVocabList()会创建一个包含在所有文档中出现的不重复词的列表,为此使用了python的set数据类型。
将词条列表输给set构造函数,set就会返回一个不重复词表。首先,创建一个空集合, 然后将每篇文档返回的新词集合添
加到该集合中.操作符丨用于求两个集合的并集
"""
#dataSet--整理的样本数据集; vocabSet--返回不重复的词条列表,也就是词汇表
def createVocabList(dataSet):
vocabSet = set([]) #创建一个空的不重复列表
for document in dataSet:
vocabSet = vocabSet | set(document) #取并集
return list(vocabSet)
import numpy as np
#vocabList--createVocabList返回的列表; inputSet--切分的词条列表; returnVec--文档向量,词集模型
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList) #创建一个其中所含元素都为0的向量
for word in inputSet: #遍历每个词条
if word in vocabList: #如果词条存在于词汇表中,则置1
returnVec[vocabList.index(word)] = 1
else: print("the word: %s is not in my Vocabulary!" % word)
return returnVec #返回文档向量
myVocabList=createVocabList(postingList)
myVocabList
if __name__ == '__main__':
postingList, classVec = loadDataSet()
print('postingList:\n',postingList)
myVocabList = createVocabList(postingList)
print('myVocabList:\n',myVocabList)
trainMat = []
for postinDoc in postingList:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
print('trainMat:\n', trainMat)
postingList:
[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'], ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'], ['stop', 'posting', 'stupid', 'worthless', 'garbage'], ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
myVocabList:
['food', 'licks', 'dalmation', 'help', 'cute', 'is', 'stupid', 'ate', 'mr', 'not', 'park', 'buying', 'I', 'love', 'to', 'problems', 'flea', 'worthless', 'my', 'please', 'take', 'him', 'stop', 'so', 'dog', 'how', 'posting', 'garbage', 'maybe', 'quit', 'has', 'steak']
trainMat:
[[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0]]
2.建立朴素贝叶斯分类器,并进行测试
"""
函数说明:朴素贝叶斯分类器训练函数
trainMatrix--训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵;trainCategory--训练类别标签向量,即loadDataSet返回的classVec
p0Vect--侮辱类的条件概率数组;p1Vect--非侮辱类的条件概率数组;pAbusive--文档属于侮辱类的概率
"""
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix) #计算训练的文档数目
numWords = len(trainMatrix[0]) #计算每篇文档的词条数
pAbusive = sum(trainCategory)/float(numTrainDocs) #文档属于侮辱类的概率
p0Num = np.ones(numWords); p1Num = np.ones(numWords) #创建numpy.zeros数组,词条出现数初始化为0
p0Denom = 2.0; p1Denom = 2.0 #分母初始化为0
for i in range(numTrainDocs):
if trainCategory[i] == 1: #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else: #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = np.log(p1Num/p1Denom)
p0Vect = np.log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率
if __name__ == '__main__':
postingList, classVec = loadDataSet()
myVocabList = createVocabList(postingList)
print('myVocabList:\n', myVocabList)
trainMat = []
for postinDoc in postingList:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(trainMat, classVec)
print('p0V:\n', p0V)
print('p1V:\n', p1V)
print('classVec:\n', classVec)
print('pAb:\n', pAb)
myVocabList:
['food', 'licks', 'dalmation', 'help', 'cute', 'is', 'stupid', 'ate', 'mr', 'not', 'park', 'buying', 'I', 'love', 'to', 'problems', 'flea', 'worthless', 'my', 'please', 'take', 'him', 'stop', 'so', 'dog', 'how', 'posting', 'garbage', 'maybe', 'quit', 'has', 'steak']
p0V:
[-3.25809654 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -2.56494936
-3.25809654 -2.56494936 -2.56494936 -3.25809654 -3.25809654 -3.25809654
-2.56494936 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -3.25809654
-1.87180218 -2.56494936 -3.25809654 -2.15948425 -2.56494936 -2.56494936
-2.56494936 -2.56494936 -3.25809654 -3.25809654 -3.25809654 -3.25809654
-2.56494936 -2.56494936]
p1V:
[-2.35137526 -3.04452244 -3.04452244 -3.04452244 -3.04452244 -3.04452244
-1.65822808 -3.04452244 -3.04452244 -2.35137526 -2.35137526 -2.35137526
-3.04452244 -3.04452244 -2.35137526 -3.04452244 -3.04452244 -1.94591015
-3.04452244 -3.04452244 -2.35137526 -2.35137526 -2.35137526 -3.04452244
-1.94591015 -3.04452244 -2.35137526 -2.35137526 -2.35137526 -2.35137526
-3.04452244 -3.04452244]
classVec:
[0, 1, 0, 1, 0, 1]
pAb:
0.5
#函数说明:朴素贝叶斯分类器分类函数
#vec2Classify--待分类的词条数组; p0Vec--侮辱类的条件概率数组; p1Vec--非侮辱类的条件概率数组; pClass1--文档属于侮辱类的概率
#0--属于非侮辱类; 1--属于侮辱类
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) #对应元素相乘。logA * B = logA + logB,所以这里加上log(pClass1)
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
#测试朴素贝叶斯
def testingNB():
listOPosts,listClasses = loadDataSet() #创建实验样本
myVocabList = createVocabList(listOPosts) #创建词汇表
trainMat=[]
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) #将实验样本向量化
p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses)) #训练朴素贝叶斯分类器
testEntry = ['love', 'my', 'dalmation'] #测试样本1
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) #测试样本向量化
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类') #执行分类并打印分类结果
else:
print(testEntry,'属于非侮辱类') #执行分类并打印分类结果
testEntry = ['stupid', 'garbage'] #测试样本2
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) #测试样本向量化
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类') #执行分类并打印分类结果
else:
print(testEntry,'属于非侮辱类') #执行分类并打印分类结果
if __name__ == '__main__':
testingNB()
['love', 'my', 'dalmation'] 属于非侮辱类
['stupid', 'garbage'] 属于侮辱类
参考文档:
1.《西瓜书》朴素贝叶斯理论部分
2.《机器学习实战》代码部分