朴素贝叶斯
介绍
朴素贝叶斯是监督学习分类算法
分类方法:比如已知一个数据集由两类数据(类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()