![b45914a0863dd74a9f30c4adf59381d3.png](https://i-blog.csdnimg.cn/blog_migrate/7e45be84acfa035e09b5760e468d611c.jpeg)
【机器学习】算法原理详细推导与实现(三):朴素贝叶斯
在上一篇算法中,逻辑回归作为一种二分类的分类器,一般的回归模型也是是判别模型,也就根据特征值来求结果概率。形式化表示为
贝叶斯定理
定理推导
朴素贝叶斯是基于贝叶斯原理得到的。假设A和B为两个不相互独立的事件:
![574a4a278a479f3b425a625b17a8e8cd.png](https://i-blog.csdnimg.cn/blog_migrate/a03e33f6fbf8a0a8ac78a3aa28882c20.png)
由上图可以看出,在事件B已经发生的情况下,事件A发生的概率为事件A和事件B的交集除以事件B:
同理,在事件A已经发生的情况下,事件B发生的概率为事件A和事件B的交集除以事件A:
结合这两个方程式,我们可以得到:
转换后贝叶斯定理的公式定义为:
总的来说,贝叶斯定理可以总结为:
- 贝叶斯定理是将先验概率做一次更新,得到后验概率
- 朴素贝叶斯是输入先验概率,找到后验概率,最后找到最大后验概率,作为最终的分类结果,以及分类概率
实际问题
假设我们有两个装满了糖果的碗,第一个碗里有10个巧克力糖果和30个普通糖果,第二个碗里两种糖果都有20个。我们随机挑一个碗,再在碗里随机挑糖果。那么我们挑到的普通糖果来自一号碗的概率有多少?
解决方案
我们用 x1 代表一号碗,x2 代表二号碗。在x1中取到普通糖果的概率是
根据 全概率公式 可知,其中拿到普通糖果的概率为:
计算为:
朴素贝叶斯
例如如果想实现一个垃圾邮件分类器,用邮件作为输入,确定邮件是否为垃圾邮件作为输出,即
电子邮件仅仅是一段文本,就像是一个词列表,因此利用词来构建特征向量
![748a0ef0bfe9dc5e496ea7adada6929f.png](https://i-blog.csdnimg.cn/blog_migrate/a4bfa97ce70dd9b9a04722e409b4eb95.png)
假设邮件中存在字典中的词,那么特征向量
则
算法推导
在朴素贝叶斯算法中,我们会对
为了拟合出模型的参数,符号假设为,其中
假设存在
假设训练样本为
上述公式中,分子的含义是从 1到
同理,正常邮件
垃圾邮件
假设
所以当垃圾邮件分类器开始训练时,假设训练垃圾邮件中包含某些词
上式中分母是由 全概率 计算出词
这就意味着如果
为了修正这个方法,这里最好是在分子分母加上一个极小数,防止数学上的无效计算和实际中的绝对不可能发生。
拉普拉斯平滑(Laplace smoothing)
继续上面投篮球的例子,假设没投中的概率记为
如果给每一项都平滑一个极小数1,代表投中篮球和没投中篮球在事先都已经发生过一次了,那么上述式子变成:
那么同理可以知道,
正常邮件
因为
总结
总的来说,朴素贝叶斯训练阶段为,给定一组已知的训练样本
而在 预测阶段 ,给定一封邮件的单词向量
上述中,
实例
朴素贝叶斯是一个非常优秀的文本分类器,现在大部分垃圾邮件过滤的底层也是基于贝叶斯思想。作者收集了 25
封垃圾邮件, 25
封正常邮件,取 40
封邮件做训练,10
封邮件做测试。
加载数据:
# 打开数据集,获取邮件内容,
# spam为垃圾邮件,ham为正常邮件
def loadData():
# 选取一部分邮件作为测试集
testIndex = random.sample(range(1, 25), 5)
dict_word_temp = []
testList = []
trainList = []
testLabel = []
trainLabel = []
for i in range(1, 26):
wordListSpam = textParse(open('./email/spam/%d.txt' % i, 'r').read())
wordListHam = textParse(open('./email/ham/%d.txt' % i, 'r').read())
dict_word_temp = dict_word_temp + wordListSpam + wordListHam
if i in testIndex:
testList.append(wordListSpam)
# 用1表示垃圾邮件
testLabel.append(1)
testList.append(wordListHam)
# 用0表示正常邮件
testLabel.append(0)
else:
trainList.append(wordListSpam)
# 用1表示垃圾邮件
trainLabel.append(1)
trainList.append(wordListHam)
# 用0表示正常邮件
trainLabel.append(0)
# 去重得到词字典
dict_word = list(set(dict_word_temp))
trainData = tranWordVec(dict_word, trainList)
testData = tranWordVec(dict_word, testList)
return trainData, trainLabel, testData, testLabel
训练函数为:
# 训练函数
def train(trainData, trainLabel):
trainMatrix = np.array(trainData)
# 计算训练的文档数目
trainNum = len(trainMatrix)
# 计算每篇文档的词条数
wordNum = len(trainMatrix[0])
# 文档属于垃圾邮件类的概率
ori_auc = sum(trainLabel) / float(trainNum)
# 拉普拉斯平滑
# 分子+1
HamNum = np.ones(wordNum)
SpamNum = np.ones(wordNum)
# 分母+2
HamDenom = 2.0
SpamDenom = 2.0
for i in range(trainNum):
# 统计属于垃圾邮件的条件概率所需的数据,即P(x0|y=1),P(x1|y=1),P(x2|y=1)···
if trainLabel[i] == 1:
SpamNum += trainMatrix[i]
SpamDenom += sum(trainMatrix[i])
else:
# 统计属于正常邮件的条件概率所需的数据,即P(x0|y=0),P(x1|y=0),P(x2|y=0)···
HamNum += trainMatrix[i]
HamDenom += sum(trainMatrix[i])
# 取对数,防止下溢出
SpamVec = np.log(SpamNum / SpamDenom)
HamVec = np.log(HamNum / HamDenom)
# 返回属于正常邮件类的条件概率数组,属于垃圾邮件类的条件概率数组,文档属于垃圾邮件类的概率
return HamVec, SpamVec, ori_auc
预测函数:
# 预测函数
def predict(testDataVec, HamVec, SpamVec, ori_auc):
predictToSpam = sum(testDataVec * SpamVec) + np.log(ori_auc)
predictToHam = sum(testDataVec * HamVec) + np.log(1.0 - ori_auc)
if predictToSpam > predictToHam:
return 1
else:
return 0
预测错误一个,错误率 10%
,正确率 90%
:
![6578ca9f6cb49b7b8a05c33f408028cf.png](https://i-blog.csdnimg.cn/blog_migrate/af9443b97bb46ef93b3ea74dff0f6ad5.png)
数据和代码下载请即可获取