朴素贝叶斯

介绍

朴素贝叶斯是监督学习分类算法
分类方法:比如已知一个数据集由两类数据(类A,类B)组成,对于一个数据x,如果x属于A的概率大于x属于B的概率,那么x属于A类。
如何计算相关概率:使用条件概率进行分类
条件概率
在事件B发生的条件下事件A发生的概率\(p(A|B)\)
\(p(A|B)=\frac{p(A \cap B)}{p(B)}\)
\(p(A \cap B)=p(A|B){p(B)}\)
\(p(A \cap B)=p(B|A){p(A)}\)
所以可得贝叶斯公式
\(P(A | B)=\frac{P(A) P(B | A)}{P(B)}\)
其中先验概率p(A)后验概率p(A|B)


所以通过贝叶斯公式求得\(p(x|A)\)\(p(a|B)\)的值进行比较,因为公式中p(x)都是相同的,所以实际只需要分别计算\(P(A) P(x | A)\)\(P(B) P(x | B)\)比较即可
朴素的含义
在此算法中,是假定每一个属性是独立的,所以对于\(p(w_{i}|B)\)可由\(p(w_{1}|B)p(w_{2}|B)···p(w_{n}|B)\)得到
朴素贝叶斯实现方式
一种是贝努利模型(只考虑出不出现),一种是多项式模型(考虑属性出现的次数)
一篇贝叶斯算法的推导文章https://www.cnblogs.com/liuwu265/p/4685361.html

示例

采用多项式模型
比如有已知3篇文章
第一篇文章包含love,it,love是A类
第二篇文章包含it,dirty,ugly是B类
第三篇文章包含not,ugly是B类
现在第四篇文章包含love未知类,要判断它的类
1、统计词集
即将所有文章的词都加在这个集合中为{love,it,dirty,not,ugly}
2、转化为词向量
如第一篇文章有2个love,1个it
[[2 1 0 0 0 ]
[0 1 1 0 1]
[0 0 0 1 1]]
类别向量为[A,B,B]
3、计算类别概率
p(A)=\(\frac{1}{3}\)p(B)=\(\frac{2}{3}\)
每一个类中各词在该类所占的比例如第一行为\(p(w_{i}|A)\)
[[\(\frac{2}{3}\) \(\frac{1}{3}\) 0 0 0]
[0 \(\frac{1}{5}\) \(\frac{1}{5}\) \(\frac{1}{5}\) \(\frac{2}{5}\)]]
4、进行判断
对于第四篇也转换为向量为
[1 0 0 0 0 ]
计算\(P(A) P(x | A)\)\(\frac{1}{3}(\frac{2}{3}\times 1+0+0+0+0)=\frac{2}{9}\)
\(P(B) P(x | B)\)\(\frac{2}{3}(0\times1 +0+0+0+0)=0\)
比较大小所以是A类

对垃圾邮件过滤程序

以下是对垃圾邮件分类的程序实例,数据来自《机器学习项目实战》第四章所附资源

import random
import numpy as np
import math
import re


# 用于统计所有的单词,输入从所有文档中提取的单词,输出这些文档的词集
def wordList(dataSet):
    # 字典的值不可重复,所以采用字典
    wordSet = set([])
    for i in dataSet:
        wordSet = wordSet | set(i)
    return list(wordSet)


# 对每一篇文档的词,用于统计其是否在词集中,若在词集中次数加1,输入每一篇文档的词,输出词次数的向量
def signWord(wordVec, dataSet):
    signVec = np.zeros(len(wordVec))
    for word in dataSet:
        if word in wordVec:
            signVec[wordVec.index(word)] += 1
    return signVec


# 训练函数,对于每一类,计算该类文档在所有文档中的比例(即每一类的概率),计算每一类的所有词(即属性)所占该类词集的比例(即已知这一类的情况下,该词的概率)
# 输入包含文档的一个数组,每一列代表一个文档,输入每一个文档所对应的的属性的数组,返回每一类的所有词所占该类词集的比例(以数组形式输出,每一行代表一类)和每一类的概率(以字典形式输出,键为类名,值为比列)
def train(trainData, trainCategory):
    numDoc, numWord = np.shape(trainData)  # 得到数组的行数和列数,其实分别代表文档的个数,和词集所包含词的个数
    # 用于统计每个类的个数
    classCount = {}
    for key in trainCategory:
        classCount[key] = classCount.get(key, 0) + 1
    # 用于计算每个类的概率
    p_class = {}
    for key in classCount:
        p_class[key] = classCount[key] / len(trainCategory)
    p_num = np.ones((len(p_class), numWord))  # 防止下溢出初始化时不用0
    p_sum = np.ones(len(p_class)) + 1
    # 生成一个包含类名的列表
    keys = list(p_class)
    # 遍历每一个文档
    for i in range(numDoc):
        # 对每一个文档,查看是属于哪一个类
        for j in range(len(p_class)):
            if trainCategory[i] == keys[j]:
                # 用于统计,该类中每个词的个数,对应的词上加上对应的词的个数
                p_num[j] += trainData[i]
                # 用于统计该类所有的词数
                p_sum[j] += sum(trainData[i])
                break
    p_sum = p_sum.repeat(numWord).reshape(np.shape(p_num))  # 将其扩展为和p_num一样的形状,便于数组间计算
    return np.log(p_num / p_sum), p_class  # 防止下溢出,采用log


# 分类函数,输出测试数据和train()输出的p和p_class,计算测试数据在每一类的概率,选择概率最大的,返回对应的类名
def classfy(testData, p, p_class):
    keys = list(p_class)
    classArrary = np.sum(testData * p, axis=1)
    classList = []
    for i in range(len(p_class)):
        classList.append(classArrary[i] + math.log(p_class[keys[i]]))
    return keys[classList.index(max(classList))]


# 用于提取文档的内容
def getData(str):
    # 去除任何非单词字符
    data = re.split('\W+', str)
    return [word.lower() for word in data if len(word) > 0]


# 对50封邮件的分类测试
def trainData():
    wordsList = []  # 用于存放词集
    docList = []  # 用于存放每篇文档的每个词,每一行代表一篇文档
    classList = []  # 用于,存放每篇文档所对应的类别
    for i in range(1, 26):
        str = getData(
            open(r'C:\Users\Desktop\我的文件\机器学习实战\machinelearninginaction\Ch04\email\ham\%d.txt' % i,
                 encoding='utf8', errors='ignore').read())  # 对于一些识别不了的词,用errors='ignore'忽略
        docList.append(str)
        wordsList.extend(str)
        classList.append(1)
        str = getData(
            open(r'C:\Users\Desktop\我的文件\机器学习实战\machinelearninginaction\Ch04\email\spam\%d.txt' % i,
                 encoding='utf8', errors='ignore').read())
        docList.append(str)
        wordsList.extend(str)
        classList.append(0)
    wordVec = wordList(wordsList)
    trainSet = list(range(50))  # 存放用于训练的文档的在docList中的序号
    testSet = []  # 存放用于测试的文档的在docList中的序号
    # 随机选取测试文档序号
    for i in range(10):
        randIndex = int(random.uniform(0, len(trainSet)))
        testSet.append(trainSet[randIndex])
        del (trainSet[randIndex])
    # 进行训练
    trainData = []
    trainClass = []
    for doc in trainSet:
        trainData.append(signWord(wordVec, docList[doc]))
        trainClass.append(classList[doc])
    p, p_class = train(np.array(trainData), np.array(trainClass))
    # 进行测试
    errorCount = 0
    for doc in testSet:
        testWordVec = signWord(wordVec, docList[doc])
        if classfy(np.array(testWordVec), p, p_class) != classList[doc]:
            errorCount += 1
    # 输出识别错误的概率
    print(errorCount / len(testSet))


trainData()
posted @ 2020-02-08 17:15  启林O_o  阅读(261)  评论(0编辑  收藏  举报