基于概率论的分类方法:朴素贝叶斯

本文代码摘自书籍《机器学习实战》,我仅稍加改正和整理,详细代码和数据集见GitHub

朴素贝叶斯概述

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

具体贝叶斯的数学原理自行百度,下面代码实战:

示例:过滤垃圾邮件

机器学习的一个重要应用就是文档的自动分类,依据文本的内容,如出现的单词进行分类。

文本预处理

书籍为我们提供了50封邮件,其中有25封垃圾邮件,每封邮件保存在一个txt文件中,大体内容如图所示。
在这里插入图片描述
我们先编写textParse函数来切分文本

# bigString -- 文本字符串
def textParse(bigString):
    import re
    # 切分字符串,分隔符是除单词、数字外的任何字符串
    reg=re.compile('\W')
    listOfTokens = reg.split(bigString)
    
    #当切分出来的单词过短如a,an,则认为该单词意义不大,舍弃 
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]

我们还需要创建一个不含重复的词的词汇表

#dataSet -- 所有用于训练的文本
def createVocabList(dataSet):
	#创建一个空集合
    vocabSet = set([])
    for document in dataSet:
    	#创建两个集合的并集
        vocabSet = vocabSet | set(document)
    return list(vocabSet)

获得词汇表后,便可以编写函数bagOfWords2VecMN,该函数的输入参数为词汇表及某个文档,输出的是文档向量,向量的每个元素为对应词汇出现的次数。

# vocabList -- 词汇表
def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
    	# 如果单词在词汇表中出现
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

朴素贝叶斯分类函数

接下来我们就可以计算贝叶斯公式的参数了。

# trainMatrix -- 文档矩阵,trainCategory -- 它们对应的标签向量
def trainNB0(trainMatrx, trainCategory):
    numTrainDocs = len(trainMatrx)
    numWords = len(trainMatrx[0])
    # 计算垃圾邮件的概率
    pAbusive = sum(trainCategory) / float(numTrainDocs)

	# 为防止计算多个概率的乘积为0,将所有词的出现数初始化为1,将分母初始化为2
    p0Num = np.ones(numWords)
    p1Num = np.ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0
    
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrx[i]
            p1Denom += len(trainMatrx[i])
        else:
            p0Num += trainMatrx[i]
            p0Denom += len(trainMatrx[i])
    
    p1Vect = np.log(p1Num / p1Denom)
    p0Vect = np.log(p0Num / p0Denom)
    
    return p0Vect, p1Vect, pAbusive

函数返回 单词在正常邮件,垃圾邮件出现的概率,以及垃圾邮件的概率。

接下来编写判断函数classifyNB。当邮件是垃圾邮件的概率更大时,我们就认为该邮件时垃圾邮件,反之我们认为是正常邮件。

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

测试算法

def spamTest():
    docList = []
    classList = []
    fullText = []

	#得到50封邮件的文档矩阵和标签向量
    for i in range(1, 26):
        wordList = textParse(open('email/spam/%d.txt' % i).read())
        docList.append(wordList)
        classList.append(1)
        wordList = textParse(open('email/ham/%d.txt' % i).read())
        docList.append(wordList)
        classList.append(0)
        
    vocabList = createVocabList(docList)
    trainingSet = list(range(50))

	# 随机抽取10封邮件作为测试邮件
    testSet = [] 
    for i in range(10):
        randIndex = int(np.random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del (trainingSet[randIndex])
    
    #训练    
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet: 
        trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses))
    
    # 测试算法性能
    errorCount = 0
    for docIndex in testSet: 
        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
            print("classification error", docList[docIndex])
    print('the error rate is: ', float(errorCount) / len(testSet))

运算结果如下:

classification error ['ryan', 'whybrew', 'commented', 'your', 'status', 'ryan', 'wrote', 'turd', 'ferguson', 'butt', 'horn']
the error rate is:  0.1

因为测试邮件都是随机抽取的,所以每次测试的结果都会有所不同,我这次测试的训练算法错误率为10%,也就是错了一封。

总结

对于分类而言,使用概率有时要比使用硬规则更为有效。贝叶斯概率及贝叶斯准则提供了一种利用已知值来估计未知概率的有效方法。

**朴素贝叶斯容易受到数据不平衡影响,倾向于选择训练样本较少的类别。**在实际应用中,应当做到数据平衡,可以在较少的类中多生成一些点或多采集一些点

本文没有考虑模型错误代价,比如当正常邮件被判断为垃圾邮件时,肯定严重的多。解决的方法有很多种,我会在以后的博客介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值