机器学习-朴素贝叶斯分类

来源:互联网 发布:淘宝二手购买 编辑:程序博客网 时间:2024/04/28 05:51

今天在图书馆学习了一天,把朴素贝叶斯的基本原理给弄明白了。故写篇文章加深印象。
当然还是要推荐一发博客的:ACdreamers-朴素贝叶斯分类

一、贝叶斯定理

条件概率:

P(c|x⃗ )=P(x⃗ c)P(x⃗ )

贝叶斯定理:
P(c|x⃗ )=P(c)P(x⃗ |c)P(x⃗ )

证明如下:
P(c|x⃗ )=P(x⃗ c)P(x⃗ )

P(x⃗ |c)=P(x⃗ c)P(c)

联立两式,消去 P(x⃗ c) 得到贝叶斯定理。

二、朴素贝叶斯分类器

不难发现,基于贝叶斯定理来估计概率 P(c|x⃗ ) 的主要困难在于,类条件概率 P(x⃗ |c) 是所有属性上的联合概率,难以从有限的训练样本直接估计而得。为了避开这个障碍,朴素贝叶斯分类器采用了“属性条件独立性假设”:对已知类别,假设所有属性相互独立。

基于属性条件独立性假设,贝叶斯定理可重写为:

P(c|x⃗ )=P(c)P(x⃗ |c)P(x⃗ )=P(c)P(x⃗ )i=1dP(xi|c)

其中 d 是属性数目,xix 在第 i 个属性上的取值。

朴素贝叶斯分类原理

对于给定待分类项,求解在此项出现的条件下,各个类别出现的概率,待分类项属于概率最大的那一类

三、使用朴素贝叶斯过滤垃圾邮件

定义事件:
- c0 : 不是垃圾邮件
- c1 : 是垃圾邮件
- w⃗  : 词向量,表示一篇邮件中出现的词汇
- P(w⃗ |ci) : 在出现事件 ci 的情况下,出现的是 w⃗  的概率
- P(ci|w⃗ ) : 在出现的是词向量 w⃗  的情况下,出现的是事件 ci 的概率

由上可知,我们需要求得所有的 P(ci|w⃗ ) 并找出最大的,其事件 ci 即是分类结果

训练算法

根据朴素贝叶斯分类器:

P(ci|w⃗ )=P(ci)P(w⃗ |ci)P(w⃗ )=P(ci)P(w⃗ )j=1dP(wj|ci)

我们可以对于每个待分类的数据,已知 P(ci)P(w⃗ ) 。我们需通过训练算法得到得是 dj=1P(wj|ci)

测试算法

计算 P(ci|w⃗ ) 的值,取其中最大的,得到分类结果。与测试样本集中的结果进行比对。最后求出错误率

代码

import numpy as npimport re, os, random# 将inputset转换为一个长度为len(vocabList)的列表def setOfWord2Vec(vocabList, inputset):    returnVec = [0] * len(vocabList)    for word in inputset:        if word in vocabList:            returnVec[vocabList.index(word)] = 1    return returnVec# 读取文件filename中的所有单词,并以全部小写返回def file2vec(filename):    returnVec = []    fr = open(filename, 'r')    regEx = re.compile("\W+")    text = fr.read()    returnVec.extend(regEx.split(text))    fr.close()    return [word.lower() for word in returnVec if len(word) > 2]# 创建词汇表def createVocabList(dataSet):    returnVec = set([])    for word in dataSet:        returnVec |= set(word)    return list(returnVec)# 训练数据def trainNB0(trainMatrix, trainCategory):    numTrainMatrix = len(trainMatrix)    numTrainWord = len(trainMatrix[0])    pAbvious = sum(trainCategory) / float(numTrainMatrix)    p0Num, p1Num = np.ones(numTrainWord), np.ones(numTrainWord)    p0Denom, p1Denom = 2.0, 2.0    for i in range(numTrainMatrix):        # 如果是侮辱性文档,则增加p1Num        if trainCategory[i] == 1:            p1Num += trainMatrix[i]            p1Denom += sum(trainMatrix[i])        else:            p0Num += trainMatrix[i]            p0Denom += sum(trainMatrix[i])    p0Vect, p1Vect = np.log(p0Num / p0Denom), np.log(p1Num / p1Denom)    return p0Vect, p1Vect, pAbvious# 验证def classifyNB(testSet, p0Vec, p1Vec, pAbvious):    p0 = sum(testSet * p0Vec) + np.log(1.0 - pAbvious)    p1 = sum(testSet * p1Vec) + np.log(pAbvious)    if p0 > p1:        return 0    else:        return 1def spamTest():    # 所有词汇集合,结果集合,    docList, classList = [], []    for i in range(1, 26):        wordList = file2vec("spam/%d.txt" % i)        docList.append(wordList)        classList.append(1)        wordList = file2vec("ham/%d.txt" % i)        docList.append(wordList)        classList.append(0)    vocabList = createVocabList(docList)    trainingSet, testSet = list(range(50)), []    # 随机构造测试集    for i in range(10):        randIndex = int(random.uniform(0, len(trainingSet)))        testSet.append(trainingSet[randIndex])        del(trainingSet[randIndex])    trainMat, trainClasses = [], []    for docIndex in trainingSet:        trainMat.append(setOfWord2Vec(vocabList, docList[docIndex]))        trainClasses.append(classList[docIndex])    p0V, p1V, pSpam = trainNB0(np.array(trainMat), trainClasses)    errorCount = 0    for index in testSet:        word = setOfWord2Vec(vocabList, docList[index])        if classifyNB(word, p0V, p1V, pSpam) != classList[index]:            errorCount += 1    print ("the error rate is:", float(errorCount) / len(docList))if __name__ == '__main__':    spamTest()

计算的时候考虑上溢出和下溢出的问题,分别做了相应的处理。具体参加《机器学习实战》4.5.3节

根据这个算法,进行了多次测试(相同数据集),因为数据集划分的是随机的,随机结果最大错误率是4%(不一定准确)

总结

优点: 在数据较少的情况下仍然有效,可以处理多类别问题
缺点: 对于输入数据的准备方式较为敏感
使用数据类型: 标称型数据

说句题外话

为什么CSDN的markdown代码块在预览的时候感觉就挺好看的…为什么发表出来之后就感觉挺难看的了….加载的样式表不一样么….

0 0
原创粉丝点击