算法三:朴素贝叶斯算法前两个算法都被要求做出一个艰难的决定,给出数据所属分类的明确答案,但往往因为分类特征统计不足,或者分类特征选择有误导致了错误的分类结果,哪怕是训练集也有可能出现不能正确分类的情形。这时,前两种方法都如同现实生活一样是用“少数服从多数”的办法来做出决策。正如帕斯卡指出的:“少数服从多数未必是因为多数人更正义,而是多数人更加强力”,所以为了保证“少数人的权利”,我们要求分类器给出一个最优的猜测结果,同时给出猜测的概率估计值。贝叶斯统计基础在说朴素贝叶斯算法之前,还是要说说贝叶斯统计,关于贝叶斯统计,这里只给出最最最基本的简述,其余的还请参阅furtherreading中的《数学之美番外篇:平凡而又神奇的贝叶斯方法》先说贝叶斯公式:定义:设A、B是两个事件,且P(A)0,称P(B|A)=P(AB)/P(A)为在事件A发生的条件下事件B发生的条件概率。相关公式:乘法公式P(XYZ)=P(Z|XY)P(Y|X)P(X)全概率公式P(X)=P(X|Y1)+P(X|Y2)+…+P(X|Yn)贝叶斯公式:如上所示,其中P(A|B)是在B发生的情况下A发生的可能性。在贝叶斯定理中,每个名词都有约定俗成的名称:P(A)是A的先验概率或边缘概率。之所以称为先验是因為它不考虑任何B方面的因素。P(A|B)是已知B发生后A的条件概率(直白来讲,就是先有B而后=才有A),也由于得自B的取值而被称作A的后验概率。P(B|A)是已知A发生后B的条件概率(直白来讲,就是先有A而后=才有B),也由于得自A的取值而被称作B的后验概率。P(B)是B的先验概率或边缘概率,也作标准化常量。按这些术语,Bayes定理可表述为:后验概率=(相似度*先验概率)/标准化常量,也就是說,后验概率与先验概率和相似度的乘积成正比。另外,比例P(B|A)/P(B)也有时被称作标准相似度,Bayes定理可表述为:后验概率=标准相似度*先验概率。朴素贝叶斯再说说朴素贝叶斯,朴素贝叶斯在英文中叫做naiveBayes,是不是这个贝叶斯方法toosimple,sometimesnaive呢?我们一起来看看他的基本假设:条件独立性。给定类标号A,朴素贝叶斯分类器在估计类条件概率时假设属性之间条件独立。条件独立假设可以形式化的表达如下:P(B|A)=P(b1|A)*P(b2|A)*…*P(bn|A)其中每个训练样本可用一个属性向量B=(b1,b2,b3,…,bn)表示,各个属性之间条件独立。比如,对于一篇文章,“Goodgoodstudy,Daydayup.”可以用一个文本特征向量来表示,x=(Good,good,study,Day,day,up)。一般各个词语之间肯定不是相互独立的,有一定的上下文联系。但在朴素贝叶斯文本分类时,我们假设个单词之间没有联系,可以用一个文本特征向量来表示这篇文章,这就是“朴素”的来历。有了条件独立假设,就不必计算X和Y的每一种组合的类条件概率,只需对给定的Y,计算每个xi的条件概率。后一种方法更实用,因为它不需要很大的训练集就能获得较好的概率估计。其实这种条件独立也不是在日常中看不到,比如Markov过程,再比如我们前面说的脊椎动物数据集的各个指标都可以看作条件独立的(前者是严格的,后者是近似的)我们为了说明这个问题,使用TomMitchell的《机器学习》一书的playingtennis数据集(点击这里下载本文所有代码及用到数据集)来说明这个问题。R代码如下:data-read.csv(D:/R/data/playingtennis.csv)data-data[,-1]#去掉了日期这一个没有可作为分类变量价值的变量prior.yes-sum(data[,5]==Yes)/length(data[,5]);prior.no-sum(data[,5]==No)/length(data[,5]);bayespre-function(condition){post.yes-sum((data[,1]==condition[1])&(data[,5]==Yes))/sum(data[,5]==Yes)*sum((data[,2]==condition[2])&(data[,5]==Yes))/sum(data[,5]==Yes)*sum((data[,3]==condition[3])&(data[,5]==Yes))/sum(data[,5]==Yes)*sum((data[,4]==condition[4])&(data[,5]==Yes))/sum(data[,5]==Yes)*prior.yes;post.no-sum((data[,1]==condition[1])&(data[,5]==No))/sum(data[,5]==No)*sum((data[,2]==condition[2])&(data[,5]==No))/sum(data[,5]==No)*sum((data[,3]==condition[3])&(data[,5]==No))/sum(data[,5]==No)*sum((data[,4]==condition[4])&(data[,5]==No))/sum(data[,5]==No)*prior.no;return(list(prob.yes=post.yes,prob.no=post.no,prediction=ifelse(post.yes=post.no,Yes,No)));}测试:bayespre(c(Rain,Hot,High,Strong))bayespre(c(Sunny,Mild,Normal,Weak))bayespre(c(Overcast,Mild,Normal,Weak))上面三个测试集输出结果为:bayespre(c(Rain,Hot,High,Strong))$prob.yes[1]0.005291005$prob.no[1]0.02742857$prediction[1]Nobayespre(c(Sunny,Mild,Normal,Weak))$prob.yes[1]0.02821869$prob.no[1]0.006857143$prediction[1]Yesbayespre(c(Overcast,Mild,Normal,Weak))$prob.yes[1]0.05643739$prob.no[1]0$prediction[1]Yes我们同样可以来训练一下我们之前提到的脊椎动物数据集(略去代码),来看看分类效果:bayespre(animals,c(no,yes,no,sometimes,yes))$prob.mammals[1]0$prob.amphibians[1]0.1$prob.fishes[1]0$prob.reptiles[1]0.0375$prediction[1]amphibiansLevels:amphibiansbirdsfishesmammalsreptiles这里我们仍然没有区分出是两栖动物还是爬行动物,但是至少它告诉我们选择时要考虑到爬行动物这种可能,而不是像决策树那样告诉你他是两栖动物。bayespre(animals,c(no,yes,no,yes,no))$prob.mammals[1]0.0004997918$prob.amphibians[1]0$prob.fishes[1]0.06666667$prob.reptiles[1]0$prediction[1]fishesLevels:amphibiansbirdsfishesmammalsreptiles这个是第三条数据作为测试数据的,也就是得到了正确分类,他告诉我们有极小的可能他是哺乳动物,我们可以忽略它,毕竟两个概率相差太大了bayespre(animals,c(yes,no,no,yes,no))$prob.mammals[1]0.0179925$prob.amphibians[1]0$prob.fishes[1]0.01666667$prob.reptiles[1]0$prediction[1]mammalsLevels:amphibiansbirdsfishesmammalsreptiles这个分类相当不好,两个分类的概率也相差无几,我们确实需要考虑。这至少告诉了我们两个事实:这个学习器的分类效果不太好;这个数据集的生物特征统计信息不够。除此以外,我们还发现这个学习器处理不了他没见过的情况,以playingtennis数据为例:假设有来了一个新样本x1=(Outlook=Foggy,Temprature=Hot,Humidity=High,Wind=Strong),要求对其分类。我们来开始计算:bayespre(c(foggy,Hot,High,Strong))$prob.yes[1]0$prob.no[1]0$prediction[1]Yes计算到这里,大家就会意识到,这里出现了一个新的属性值,在训练样本中所没有的。如果有一个属性的类条件概率为0,则整个类的后验概率就等于0,我们可以直接得到后验概率P(Yes|x1)=P(No|x1)=0,这时二者相等,无法分类。(虽说程序设定时我遵从疑罪从无的思想偏向了正例)当训练样本不能覆盖那么多的属性值时,都会出现上述的窘境。简单的使用样本比例来估计类条件概率的方法太脆弱了,尤其是当训练样本少而属性数目又很大时。如何解决?引入m估计(m-estimate)方法来估计条件概率:P(xi|yj)=(nc+mp)/(n+m)n是类yj中的样本总数,nc是类yj中取值xi的样本数,m是称为等价样本大小的参数,而p是用户指定的参数。如果没有训练集(即n=0),则P(xi|yj)=p,因此p可以看作是在类yj的样本中观察属性值xi的先验概率。等价样本大小决定先验概率和观测概率nc/n之间的平衡,提高了估计的稳健性。朴素贝叶斯方法是一个很特别的方法,所以值得介绍一下。在众多的分类模型中,应用最为广泛的两种分类模型是决策树模型(DecisionTreeModel)和朴素贝叶斯模型(NaiveBayesModel,NBC)。朴素贝叶斯模型发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率。同时,NBC模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。理论上,NBC模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为NBC模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,这给NBC模型的正确分类带来了一定影响。在属性个数比较多或者属性之间相关性较大时,NBC模型的分类效率比不上决策树模型。而在属性相关性较小时,NBC模型的性能最为良好。(所以在文本分类时能够用关键词就更好了)R语言中NaiveBayes的实现函数R的e1071包的naiveBayes函数提供了naivebayes的具体实现,其用法如下:##S3methodforclass'formula'naiveBayes(formula,data,laplace=0,...,subset,na.action=na.pass)##DefaultS3method:naiveBayes(x,y,laplace=0,...)我们以titanic数据集为例,看看titanic上的符合怎样条件的人更容易得救:data(Titanic)m-naiveBayes(Survived~.,data=Titanic)mR中的文本处理工具在介绍贝叶斯文本挖掘之前,我想我们先得把R处理文本的一些工具简单的介绍一下,比如处理文本的tm包,R语言处理正则表达式之类的,关于tm包你可以参阅的是tm的帮助文档《IntroductiontothetmPackageTextMininginR》,关于正则表达式可以参阅furtherreading的《文本(字符串)处理与正则表达式》先说tm包,在tm中导入数据需要函数Corpus(或VCorpus),函数