机器学习—朴素贝叶斯算法

一,概念

 朴素贝叶斯是经典的机器学习算法之一,也是为数不多的基于概率论的分类算法。对于大多数的分类算法,在所有的机器学习分类算法中,朴素贝叶斯和其他绝大多数的分类算法都不同。比如决策树,KNN,逻辑回归,支持向量机等,他们都是判别方法,也就是直接学习出特征输出Y和特征X之间的关系,要么是决策函数,要么是条件分布。但是朴素贝叶斯却是生成方法,该算法原理简单,也易于实现。
 

1.先验概率

P(cj)代表还没有训练模型之前,根据历史数据/经验估算cj拥有的初始概率。P(cj)常被称为cj的先验概率(prior probability) ,它反映了cj的概率分布,该分布独立于样本。

 通常可以用样例中属于cj的样例数|cj|比上总样例数|D|来近似,即:

2.后验概率

给定数据样本x时cj成立的概率P(cj  | x )被称为后验概率(posterior probability),因为它反映了在看到数据样本 x后 cj 成立的置信度。

大部分机器学习模型尝试得到后验概率。

贝叶斯定理

已知两个独立事件A和B,事件B发生的前提下,事件A发生的概率可以表示为P(A|B),即上图中橙色部分占红色部分的比例,即:

公式:

3.朴素贝叶斯分类器

MAP 分类准则 MAP: Maximum A Posterior  x 属于类别 c* 的概率:

利用 MAP 准则 利用贝叶斯准则转化为:

朴素贝叶斯分类器(Naïve Bayes Classifier)采用了“属性条件独立性假设” ,即每个属性独立地对分类结果发生影响。为方便公式标记,不妨记P(C=c|X=x)为P(c|x),基于属性条件独立性假设,贝叶斯公式可重写为:

   朴素贝叶斯分类器的训练器的训练过程就是基于训练集D估计类先验概率 P(c) ,并为每个属性估计条件概率  P(x_i|c) 。

        令  D_c  表示训练集D中第c类样本组合的集合,则类先验概率:   

4.拉普拉斯修正

在概率估计中,当对某个事件的概率进行估计时,如果该事件在样本中没有出现过,传统的频率估计方法会将其概率估计为零。然而,这可能导致在实际应用中遇到问题,在贝叶斯分类器中,如果一个特征在训练数据中没有出现过,那么该特征对分类结果的影响就会被忽略掉。

 为了避免其他属性携带的信息,被训练集中未出现的属性值“抹去”,在估计概率值时通常要进行“拉普拉斯修正”:        令 N 表示训练集 D 中可能的类别数,N_i表示第i个属性可能的取值数,则贝叶斯公式可修正为:

5.防溢出策略

条件概率乘法计算过程中,因子一般较小(均是小于1的实数)。当属性数量增多时候,会导致累乘结果下溢出的现象。 在代数中有ln(a*b) = ln(a)+ln(b),因此可以把条件概率累乘转化成对数累加。分类结果仅需对比概率的对数累加法运算后的数值,以确定划分的类别。

二,朴素贝叶斯的简单实现

垃圾邮件的分类

import numpy as np

# 创建不重复词的列表 ———— 词汇表
def createVocabList(dataSet):
    vocabSet = set([])                       # 创建一个空集
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 创建两个集合的并集
    return list(vocabSet)                    # 返回不重复的词条列表

# 输出文档向量
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("单词 %s 不在词汇表中!" % word)
    return returnVec

# 朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)                      # 获得训练的文档总数
    numWords = len(trainMatrix[0])                       # 获得每篇文档的词总数
    pAbusive = sum(trainCategory) / float(numTrainDocs)  # 计算文档是侮辱类的概率
    p0Num = np.ones(numWords)                            # 创建numpy.ones数组,初始化概率
    p1Num = np.ones(numWords)                            # 创建numpy.ones数组,初始化概率
    p0Denom = 2.0                                        # 初始化为2.0
    p1Denom = 2.0                                        # 初始化为2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]                      # 向量相加,统计侮辱类的条件概率的数据,即P(w0|1),P(w1|1),P(w2|1)···
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]                      # 向量相加,统计非侮辱类的条件概率的数据,即P(w0|0),P(w1|0),P(w2|0)···
            p0Denom += sum(trainMatrix[i])
    p1Vect = np.log(p1Num / p1Denom)                    # 侮辱类,每个元素除以该类别中的总词数
    p0Vect = np.log(p0Num / p0Denom)                    # 非侮辱类,每个元素除以该类别中的总词数
    return p0Vect, p1Vect, pAbusive                      # p0Vect非侮辱类的条件概率数组、p1Vect侮辱类的条件概率数组、pAbusive文档属于侮辱类的概率

# 朴素贝叶斯分类器分类函数
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 bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

# 文件解析
def textParse(bigString):  # 输入字符串, 输出单词列表
    import re
    listOfTokens = re.split(r'\W+', bigString)                  # 字符串切分,去掉除单词、数字外的任意字符串
    return [str(tok).lower() for tok in listOfTokens if len(str(tok)) > 2]  # 除了单个字母外,其他字符串全部转换成小写

# 完整的垃圾邮件测试函数
def spamTest():
    docList = []                 # 文档列表
    classList = []               # 文档标签
    fullText = []                # 全部文档内容集合
    for i in range(1, 26):                                           # 遍历垃圾邮件和非垃圾邮件各25个
        wordList = textParse(open('email/spam/%d.txt' % i).read())   # 读取垃圾邮件,将大字符串并将其解析为字符串列表
        docList.append(wordList)                                     # 垃圾邮件加入文档列表
        fullText.extend(wordList)                                    # 把当前垃圾邮件加入文档内容集合
        classList.append(1)                                          # 1表示垃圾邮件,标记垃圾邮件
        wordList = textParse(open('email/ham/%d.txt' % i).read())    # 读非垃圾邮件,将大字符串并将其解析为字符串列表
        docList.append(wordList)                                     # 非垃邮件加入文档列表
        fullText.extend(wordList)                                    # 把当前非垃圾邮件加入文档内容集合
        classList.append(0)                                          # 0表示非垃圾邮件,标记非垃圾邮件
 
    vocabList = createVocabList(docList)                             # 创建不重复的词汇表
    trainingSet = list(range(50))                                    # 为训练集添加索引
    testSet = []                                                     # 创建测试集
    for i in range(10):                                              # 目的为了从50个邮件中,随机挑选出40个作为训练集,10个做测试集
        randIndex = int(np.random.uniform(0, len(trainingSet)))      # 随机产生索引
        testSet.append(trainingSet[randIndex])                       # 添加测试集的索引值
        del (trainingSet[randIndex])                                 # 在训练集中,把加入测试集的索引删除
 
    trainMat = []                                                    # 创建训练集矩阵训练集类别标签系向量
    trainClasses = []                                                # 训练集类别标签
    for docIndex in trainingSet:                                     # for循环使用词向量来填充trainMat列表t
        trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))  # 把词集模型添加到训练矩阵中
        trainClasses.append(classList[docIndex])                     # 把类别添加到训练集类别标签中
    p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses)) # 朴素贝叶斯分类器训练函数
 
    print('词表:\n', vocabList)
    print('p0V:\n', p0V)
    print('p1V:\n', p1V)
    print('pSpam:\n', pSpam)
 
    errorCount = 0                                                   # 用于计数错误分类
    for docIndex in testSet:                                         # 循环遍历训练集
        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])  # 获得测试集的词集模型
        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1                                          # 预测值和真值不一致,则错误分类计数加1
            print("分类错误集", docList[docIndex])
    print('错误率: ', float(errorCount) / len(testSet))


if __name__ == '__main__':
    spamTest()

这段代码实现了一个基于朴素贝叶斯算法的垃圾邮件分类器。具体来说,代码中包括以下几个函数:

  • createVocabList(dataSet):创建不重复词的列表,即词汇表。
  • setOfWords2Vec(vocabList, inputSet):将输入的文本转换成词向量。
  • trainNB0(trainMatrix, trainCategory):训练朴素贝叶斯分类器。
  • classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):使用训练好的模型对新文本进行分类。
  • bagOfWords2VecMN(vocabList, inputSet):朴素贝叶斯词袋模型。
  • textParse(bigString):对原始文本进行解析,返回单词列表。
  • spamTest():完整的垃圾邮件测试函数。

代码中的主要流程如下:

  • 遍历垃圾邮件和非垃圾邮件各25个,将其解析为单词列表,并将垃圾邮件标记为1,非垃邮件标记为0。
  • 创建词汇表,用于将文本转换成词向量。
  • 随机选择10封邮件作为测试集,将剩下的40封邮件作为训练集。
  • 将训练集转换成词向量,并使用训练集训练朴素贝叶斯分类器。
  • 对测试集中的邮件进行分类,并计算分类错误率。

测试结果:

p0V给出了每个词汇在非垃圾邮件中出现的概率,而p1V给出了每个词汇在垃圾邮件中出现的概率。pSpam是指在整个数据集中垃圾邮件的比例。错误率为0.1,即10%,这表示有10%的测试集中的样本被错误地分类。

参考自【精选】机器学习 —— 朴素贝叶斯_机器学习贝叶斯_DreamWendy的博客-CSDN博客

三,小结

  1. 调整分类器参数:针对单词"scifinance"的错误分类,可以尝试调整分类器的参数,以提高分类的准确性。
  2. 数据集分析:在对数据集中具体单词的分布情况可以进行更深入的分析,特别是那些容易导致错误分类的单词。
  3. 考虑其他特征:除了单词出现的概率外,还可以考虑其他特征,像邮件的长度、特殊字符的使用频率等,来提高分类器的性能。

在对垃圾邮件分类的代码实现中,运用了垃圾邮件的代码简单修改后进行操作,理解了大部分内容,也学习到了朴素贝叶斯的简单实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值