朴素贝叶斯算法
Posted 数据挖掘与R语言
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了朴素贝叶斯算法相关的知识,希望对你有一定的参考价值。
朴素贝叶斯(Naive Bayes,以下简称NB)算法是基于概率学习的一种分类方法,朴素贝叶斯利用先验信息来预测将来事件发生的概率。举个例子,就好比古时大夫给病人看病,他需要“望闻问切”,才能对病人的病情做出诊断。这里“望闻问切”是为了获取病人的病情即特征,然后他会对比该病情特征和以往他所见过的病情特征做比较,推断具体病症然后对症下药,这里的推断从某种程度上就用到了贝叶斯算法。好了,下面进入主题,介绍什么是NB算法。
NB算法的基础是贝叶斯定理,我们首先来理解一下贝叶斯定理的定义。
贝叶斯定理
我们直接抛出贝叶斯定理公式:
这里对其中的一些记号做一些说明,表示条件概率,即事件B发生的前提下A发生的概率。
接下来说说这个公式的妙用了,我们在实际问题中经常遇到很难直接得到,而很容易求得的情形,这时便可采用贝叶斯定理了。
为了便于理解上面的贝叶斯定理,我们引用一个典型的垃圾邮件分类问题。
把问题做简化,假设只有一个分类特征,获得如下频数表信息,行spam
表示垃圾邮件,ham
表示正常邮件,yes
表示邮件中出现free
单词,no
表示邮件中未出现free
单词。接下来,我们看如何用朴素贝叶斯算法来判断新来邮件是否为垃圾邮件。
freq | yes | no | Total |
---|---|---|---|
spam | 15 | 5 | 20 |
ham | 10 | 70 | 80 |
Total | 25 | 75 | 100 |
假如新来一封邮件,我们在未获取任何特征信息的时,认为这封邮件是垃圾邮件的概率为20%(历史样本中垃圾邮件的比例)。这里的被称之为先验概率。
如果我们获取了特征信息,知道这封邮件中是否含有free
单词,那么我们可以计算出或者,这个条件概率称之为似然,与之对应的
有了以上这些概率值,我们便可很容易的求得后验概率,公式如下,
同理,我们可以计算出,然后比较其与的大小,哪一项概率值更大,那么邮件则更可能属于哪一类。
这时,新来一封邮件,经统计发现,该邮件内容中出现了free单词,我们首先按步骤计算似然、边缘似然以及先验概率:
然后计算后验概率:
于是,我们判定这封邮件为垃圾邮件。下面对NB算法的一般步骤做个总结。
NB算法
NB分类计算步骤
1 获取特征属性值
,设表示类别 2 计算条件概率
,先验概率 ,以及边缘似然 3 由贝叶斯公式计算
4 计算
,则
这里计算的关键是计算条件概率。
当特征属性为离散情形时,
非常容易计算,只要统计各个离散的特征属性值在每种分类中出现的频率即可; 当特征为连续特征时,可以把连续特征离散化,离散化后计算规则同上;更一般地,我们假定其值服从正态分布。即
这里的和
正态分布的好处是分布参数容易估计,大多数特征属性在大样本情形下用正态分布不会有太大的问题。实际上,这里的分布形式并不局限于正态分布,确切地说,为提高分类的准确度,我们应当采用更适合的特征属性的分布形式。
Laplace校准
当特征属性为离散情形时,当其中一个类别下某个特征划分没有出现即
这时假定特征之间相互独立,那有
这可能会导致严重的预测错误。我们来看个例子:
这时,判定属于C1类的概率为
判定属于C2类的概率为
这种判定合理吗,很可能不是的。
这便是Laplace估计量设计的初衷,引入它就是用来防止这类问题的发生。Laplace校准的思想非常简单,就是对每个类别下所有划分的统计频数加一个小的常数,这个常数通常设为1,就是为了保证在每一种分类情况下,其特征的统计频数不为0。回过头来,在上述例子中加入Laplace=1,重新计算分类概率:
我们看到,经Laplace校正,判定属于C1类的概率为
判定属于C2类的概率为
可以看到,判定结论来了个大翻转,而这个判定才极有可能是最终正确的。
编写一个NB算法程序
# 设随机数种子
set.seed(123)
# 拆分训练和测试集
as.ind <- which(asdat$player %in% allStarnames[-c(3,6,9,12,15,18,21,24)])
no.ind <- which(!asdat$player %in% allStarnames)
train.index <- c(as.ind,sample(no.ind, length(no.ind)%/%4*3))
asdat.train <- asdat[train.index, ]asdat.test <- asdat[-train.index, ]
# 计算概率密度函数的参数
density.M <- ddply(.data = asdat.train,
.variables = "as",
.fun = function(dat)
data.frame(feature=c("pts","per","wr"),
m=c(mean(dat$pts),mean(dat$per),mean(dat$wr)),
sd=c(sd(dat$pts),sd(dat$per),sd(dat$wr)))
)
# 先验概率
prior.p <- sum(asdat.train$as==1)/nrow(asdat.train)
# 朴素贝叶斯分类预测
predict.nb <- function(newdata,density.M,prior.p){
# 提取不同类别概率密度函数的参数
density.pts0 <- filter(density.M,feature=="pts" & as==0)
density.per0 <- filter(density.M,feature=="per" & as==0)
density.wr0 <- filter(density.M,feature=="wr" & as==0)
density.pts1 <- filter(density.M,feature=="pts" & as==1)
density.per1 <- filter(density.M,feature=="per" & as==1)
density.wr1 <- filter(density.M,feature=="wr" & as==1)
n <- nrow(newdata)
pred <- rep(NA,n)
for(i in 1:n){
temp <- newdata[i,]
# 计算负类概率
f1_C0 <- dnorm(temp$pts,mean = density.pts0$m,sd = density.pts0$sd)
f2_C0 <- dnorm(temp$per,mean = density.per0$m,sd = density.per0$sd)
f3_C0 <- dnorm(temp$wr,mean = density.wr0$m,sd = density.wr0$sd)
p0 <- f1_C0*f2_C0*f3_C0*(1-prior.p)
# 计算正类概率
f1_C0 <- dnorm(temp$pts,mean = density.pts1$m,sd = density.pts1$sd)
f2_C0 <- dnorm(temp$per,mean = density.per1$m,sd = density.per1$sd)
f3_C0 <- dnorm(temp$wr,mean = density.wr1$m,sd = density.wr1$sd)
p1 <- f1_C0*f2_C0*f3_C0*prior.p
# 判定
pred[i] <- ifelse(p1>p0,1,0)
}
return(pred)
}
# 检验预测结果
pred <- predict.nb(asdat.test,density.M,prior.p)
table(pred,asdat.test$as)
## pred 0 1
## 0 23 3
## 1 1 5
# 预测错误项
asdat.test$pred <- pred
select(asdat.test,player,as,pred) %>% filter(as!=pred)
## player as pred
## 1 达米安-利拉德 0 1
## 2 德维恩-韦德 1 0
## 3 科比-布莱恩特 1 0
## 4 艾尔-霍福德 1 0
可以看到,预测的准确率还可以。有意思的是,NB算法预测结果与(←点此链接可跳转)的预测结果那完全一样,预测效果可接受,分析参见前篇。
上述案例可以帮你深入理解贝叶斯分类算法的计算流程,但是程序代码写得比较粗糙,仅适用于这个特定案例。其实,当你十分熟悉NB算法后,也可以直接调用e1071
包中的naiveBayes
函数,其调用形式相当简单,留给大家自行尝试,有任何问题欢迎交流,谢谢!
长按识别图中二维码
以上是关于朴素贝叶斯算法的主要内容,如果未能解决你的问题,请参考以下文章