朴素贝叶斯
作者:互联网
一、贝叶斯定理
1、简介
贝叶斯理论是以18世纪的一位神学家托马斯贝叶斯(Thomas Bayes)命名。通常,事件A在事件B(发生)的条件下的概率,与事件B在事件A(发生)的条件下的概率是不一样的;然而,这两者是有确定的关系的,贝叶斯定理就是这种关系的陈述。
2、优缺点
优点:在数据较少时仍有效,可处理多类别问题
缺点:对输入数据准备方式较敏感。
假设现在我们有一个数据集,它由两类数据组成,数据分布如下图所示:
我们现在用p1(x,y)表示数据点(x,y)属于类别1(图中圆点表示的类别)的概率,用p2(x,y)表示数据点(x,y)属于类别2(图中三角形表示的类别)的概率,那么对于一个新数据点(x,y),可以用下面的规则来判断它的类别:
如果p1(x,y) > p2(x,y),那么类别为1
如果p1(x,y) < p2(x,y),那么类别为2
也就是说,我们会选择高概率对应的类别。这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。
适用决策树不会非常成功,和简单的概率计算相比,KNN计算量太大,因此对于上述问题,最佳选择是概率比较方法。
接下来,为了学习如何计算p1和p2概率,我们有必要讨论一下条件概率。
二、概率公式
1、条件概率
条件概率是指事件A在事件B发生的条件下发生的概率。条件概率表示为:P(A|B),读作“A在B发生的条件下发生的概率”。若只有两个事件A,B,那么,
2、全概率公式
若事件A1,A2,…构成一个完备事件组且都有正概率,则对任意一个事件B,有如下公式成立:
P(B)=P(BA1)+P(BA2)+...+P(BAn)=P(B|A1)P(A1) + P(B|A2)P(A2) + ... + P(B|An)P(An).
此公式即为全概率公式。
特别地,对于任意两随机事件A和B,有如下成立:
三、朴素贝叶斯
1、公式推导
通过条件概率公式变形得出公式:
我们把P(A)称为”先验概率”,即在B事件发生之前,我们对A事件概率的一个判断。
P(A|B)称为”后验概率”,即在B事件发生之后,我们对A事件概率的重新评估。
P(B|A)/P(B)称为”可能性函数”,这是一个调整因子,使得预估概率更接近真实概率。
先验概率是指根据以往经验和分析得到的概率,例如全概率公式 ,它往往作为“由因求果”问题中的“因”出现。后验概率是指在得到“结果”的信息后重新修正的概率,是“执果寻因”问题中的“因” 。后验概率是基于新的信息,来修正原来的先验概率后所获得的更接近实际情况的概率估计
所以,条件概率可以理解成下面的式子:
后验概率=先验概率∗调整因子后验概率=先验概率∗调整因子
这就是贝叶斯推断的含义:我们先预估一个”先验概率”,然后加入实验结果,看这个实验到底是增强还是削弱了”先验概率”,由此得到更接近事实的”后验概率”。
如果”可能性函数”P(B|A)/P(B)>1,意味着”先验概率”被增强,事件A的发生的可能性变大;
如果”可能性函数”=1,意味着B事件无助于判断事件A的可能性;
如果”可能性函数”<1,意味着”先验概率”被削弱,事件A的可能性变小。
四、代码实现(垃圾邮件过滤)
1 流程说明
朴素贝叶斯分类器模型的训练:首先提供两组已经识别好的邮件,一组是正常邮件,另一组是垃圾邮件,然后用这两组邮件对过滤器进行“训练”。首先解析所有邮件,提取每一个词,然后计算每个词语在正常邮件和垃圾邮件中出现的概率。例如,我们假定“cute”这个词在词数(不重复)为4000的垃圾邮件中出现200次,那么该词在垃圾邮件中出现的频率就是5%;而在词数(不重复)为4000的正常邮件中出现1000次,那么该词在正常邮件中出现的频率就是25%。此时就已经初步建立了关于“cute”这个词的过滤模型。
使用朴素贝叶斯分类器模型进行过滤测试:读入一封未进行分类的新邮件,然后该邮件进行解析,发现其中包含了“cute”这个词,用P(W|S)和P(W|H)分别表示这个词在垃圾邮件和正常邮件中出现的概率。随后将邮件中解析出来的每个词(已建立对应的训练模型)属于正常邮件和垃圾邮件的概率分别进行累乘,最后比较该邮件属于正常邮件和垃圾邮件的概率大小,可将该邮件归类为概率较大的那一类。
2、构建词向量
我们把文本看成单词向量或者词条向量,也就是说将句子转换为向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。
# 创建实验样本
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]
return postingList,classVec
def createVocabList(dataset):
vocabSet = set([])
for document in dataset:
# 取并集
vocabSet = vocabSet | set(document)
return list(vocabSet)
# 将数据集转为词向量
def setOfWords2Vec(vocabList, inputSet):
# 创建一个其中所含元素都为0的列表
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print('the word: %s is not in my Vocabulary!' % word)
return returnVec
if __name__ == '__main__':
postingList, classVec = loadDataSet()
myVocabList = createVocabList(postingList)
print('myVocabList:\n', myVocabList)
print(setOfWords2Vec(myVocabList, postingList[0]))
3、词向量计算概率
记w表示一个向量(可理解为经过向量化后的一封邮件),它由多个数值组成。在该例中数值个数与词汇表中的词条个数相同,则贝叶斯公式可表示为:
def trainNBO(trainMatrix,trainCategory):
#文档数目(6)
numTrainDocs = len(trainMatrix)
#词数
numWords = len(trainMatrix[0])
##初始化概率
pAbusive = sum(trainCategory) / float(numTrainDocs)
#使用zeros()初始化后的数组如果其中一个概率值为0则最后的结果也为0,因此使用ones()
# numpy中的ndarray对象用于存放同类型元素的多维数组。
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
if trainCategory[i] == 1:
#向量相加
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
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)
trainMat = []
for postinDoc in postingList:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0Vect, p1Vect, pAbusive = trainNBO(trainMat, classVec)
print('p0Vect:\n', p0Vect)
print('p1Vect:\n', p1Vect)
print('文档属于侮辱性文档的概率:', pAbusive)
4、朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
if __name__ == '__main__':
postingList, classVec = loadDataSet()
myVocabList = createVocabList(postingList)
trainMat = []
for postinDoc in postingList:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V,p1V,pAb = trainNBO(np.array(trainMat),np.array(classVec))
testEntry = ['love', 'my', 'dalmation']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类')
else:
print(testEntry,'属于非侮辱类')
testEntry = ['stupid', 'love', 'cute']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类')
else:
print(testEntry,'属于非侮辱类')
5、使用朴素贝叶斯进行交叉验证
def bagOfWords2Vec(vocabList,inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
def textParse(bigString):
#将非数字、单词外的任意字符串作为切分标志
listOfTokens = re.split('\\W*',bigString)
#为了使所有词的形式统一,除了单个字母,例如大写的I,其余的单词变成小写
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest():
docList = [];classList = [];fullTest = []
#导入并解析50个文件,并记录对应的标签值,垃圾邮件标记为1
for i in range(1,26):
wordList = textParse(open('C:/Users/cool/Desktop/mach/email/spam/%d.txt'% i,'r').read())
docList.append(wordList)
fullTest.append(wordList)
classList.append(1)
wordList = textParse(open('C:/Users/cool/Desktop/mach/email/ham/%d.txt'% i,'r').read())
docList.append(wordList)
fullTest.append(wordList)
classList.append(0)
#获得50个文件构成的词列表
vocabList = createVocabList(docList)
trainingSet = list(range(50))
testSet = []
#随机选取测试集合,并从训练集中去除,留存交叉验证
for i in range(10):
randIndex = int(random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = [];trainClass = []
#构建训练集词向量列表,训练集标签
for docIndex in trainingSet:
trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
trainClass.append(classList[docIndex])
#训练算法,计算分类所需的概率
p0V,p1V,pSpam = trainNBO(np.array(trainMat),np.array(trainClass))
#分类错误个数初始化为0
errorCount = 0
#,遍历测试集,验证算法错误率
for docIndex in testSet:
wordVector = setOfWords2Vec(vocabList,docList[docIndex])
if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print('错误率:%.2f%%'%(float(errorCount)/len(testSet)*100))
if __name__ == '__main__':
spamTest()
标签:__,概率,append,贝叶斯,朴素,np,邮件 来源: https://blog.csdn.net/shirakami00/article/details/121461270