- 开始
其实在学习机器学习的一些算法,最近也一直在看这方面的东西,并且尝试着使用Matlab进行一些算法的实现。这几天一直在看得就是贝叶斯算法实现一个分类问题。大概经过了一下这个过程:
看书→算法公式推演→网上查询资料→进一步理解→搜集数据集开始尝试→写代码→调试→代码整理与优化→自编代码结果与Matlab自带函数fitcnb结果对比验证→朴素贝叶斯算法优缺点总结
经过这几天的努力,总算是把这个算法彻底弄明白了,也发现,很多算法只有自己去亲自写一写才会发现自己的不足,还是需要多努力。
贝叶斯分类
分类问题理解
贝叶斯分类是一类分类算法的总称,这类算法以贝叶斯定理为基础,因此称之为贝叶斯分类。
而对于分类问题,其实谁都不会陌生,说我们每个人每天都在执行分类操作一点都不夸张,只是我们没有意识到罢了。例如,当你看到一个陌生人,你的脑子下意识判断TA是男是女;你可能经常会走在路上对身旁的朋友说“这个人一看就很有钱、那边有个非主流”之类的话,其实这就是一种分类操作。
从数学角度来说,分类问题可做如下定义:
已知集合,C={y1,y2,…,yn}和I={x1,x2,…,xm,…}确定映射规则y=f(x),使得任意的x∈I有且仅有一个yi∈C使得yi=f(xi)成立。
其中C叫做类别集合,其中每一个元素是一个类别,而I叫做项集合,其中每一个元素是一个待分类项,f叫做分类器。分类算法的任务就是构造分类器f。
这里要着重强调,分类问题往往采用经验性方法构造映射规则,即一般情况下的分类问题缺少足够的信息来构造100%正确的映射规则,而是通过对经验数据的学习从而实现一定概率意义上正确的分类,因此所训练出的分类器并不是一定能将每个待分类项准确映射到其分类,分类器的质量与分类器构造方法、待分类数据的特性以及训练样本数量等诸多因素有关。贝叶斯定理
该定理最早Thomas Bayes发明,显然这个定理可能就是为了纪念他而已他的名字命名的吧。这个定理是概率论中的一个结论,大学学习概率论的时候学习过的。它是跟随机变量的条件概率以及边缘概率分布有关的。该定理可以让人们知道如何用新的信息进行以后看法或者结论的修改。一般来说,事件A在事件B已经发生的条件下发生的概率与事件B在事件A已经发生的条件下发生的概率是不一样的。但是这个两者是有一定的联系的。下面就开始搬出这个伟大的定理:P(X,Y)=P(Y|X)P(X)=P(X|Y)P(Y)
那么对这个公式进行变形有:
P(Y|X)=P(X|Y)P(Y)P(X)
由上面可以看出来,这个定理允许使用先验概率P(Y)、条件概率P(X|Y)和证据P(X)来表示后验概率。
朴素贝叶斯分类原理
朴素贝叶斯分类是一种十分简单的分类算法,叫它朴素贝叶斯分类是因为这种方法的思想真的很朴素。
朴素贝叶斯的思想基础是这样的:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。
通俗来说,就好比这么个道理,你在街上看到一个黑人,我问你你猜这哥们哪里来的,你十有八九猜非洲。为什么呢?因为黑人中非洲人的比率最高,当然人家也可能是美洲人或亚洲人,但在没有其它可用信息下,我们会选择条件概率最大的类别,这就是朴素贝叶斯的思想基础。
朴素贝叶斯分类器建立在一个类条件独立性假设(朴素假设)的基础之上,给定类变量后,各个变量之间相互独立。根据朴素贝叶斯独立性假设,有:P(X|Ci)=∏mk=1P(XK|Ci)
其中,当给定训练数据集的时候,条件概率P(X1|Ci)P(X2|Ci),P(X3|Ci),...,P(Xn|Ci)可以算出来,因此,根据这个方法,对一个未知类别的样本X,可以先分别计算X属于每个类别Ci的概率P(X|Ci)P(Ci),然后选择其中概率最大的类别作为其类别。
- 算法流程
朴素贝叶斯分类的一个基本算法流程为:
(1)设x={x1,x2,…,xm}为一个待分类的项,而每一个xi为x的一个属性,每一个属性有k个取值xi={a1,a2,…,ak}
(2)有一个类别集合C={y1,y2,…,yn}
(3)计算P(y1|x),P(y2|x),...,P(yn|x)
(4)如果P(yk|x)=max{P(y1|x),P(y2|x),...,P(yn|x)},则x∈yk
因此,从上面我们可以看出来,该算法的关键步骤就是第(3)中的各个条件概率的计算,基于独立性假设,可以这样计算:
1、找到一个已知分类的待分类项集合,这个集合叫做训练样本集。
2、统计得到在各个类别下各个特征属性的条件概率估计值,即:
P(a1|y1),P(a2|y1),...,P(am|y1)
P(a1|y2),P(a2|y2),...,P(am|y2)
⋮
P(a1|y1),P(a2|y1),...,P(am|y1)
3、如果各个特征属性是条件独立的,则根据贝叶斯定理有如下推导:
P(yi|x)=P(x|yi)P(yi)P(x)
因为分母对于所有类别来说都是常数,因此,该算法就是求解分子最大化问题。因为各个特征属性之间是条件独立的,所以有:
依据上面的分析,可以得到朴素贝叶斯分类的一个基本流程图如下:
Matlab程序实现
- 程序
使用Matlab实现朴素贝叶斯算法的数据来源:http://archive.ics.uci.edu/ml/machine-learning-databases/balance-scale/balance-scale.data。
不多说先开始上程序吧
- 程序
clc
clear
close all
data=importdata('data.txt');
wholeData=data.data;
%交叉验证选取训练集和测试集
cv=cvpartition(size(wholeData,1),'holdout',0.04);%0.04表明测试数据集占总数据集的比例
trainData=wholeData(training(cv),:);
testData=wholeData(test(cv),:);
label=data.textdata;
attributeNumber=size(trainData,2);
attributeValueNumber=5;
%%
%将分类标签转化为数据
sampleNumber=size(label,1);
labelData=zeros(sampleNumber,1);
for i=1:sampleNumberif label{i,1}=='R'labelData(i,1)=1;elseif label{i,1}=='B'labelData(i,1)=2;else labelData(i,1)=3;end
end
trainLabel=labelData(training(cv),:);
trainSampleNumber=size(trainLabel,1);
testLabel=labelData(test(cv),:);
%计算每个分类的样本的概率
labelProbability=tabulate(trainLabel);
%P_yi,计算P(yi)
P_y1=labelProbability(1,3)/100;
P_y2=labelProbability(2,3)/100;
P_y3=labelProbability(3,3)/100;
%%
%
count_1=zeros(attributeNumber,attributeValueNumber);%count_1(i,j):y=1情况下,第i个属性取j值的数量统计
count_2=zeros(attributeNumber,attributeValueNumber);%count_1(i,j):y=2情况下,第i个属性取j值的数量统计
count_3=zeros(attributeNumber,attributeValueNumber);%count_1(i,j):y=3情况下,第i个属性取j值的数量统计
%统计每一个特征的每个取值的数量
for jj=1:3for j=1:trainSampleNumberfor ii=1:attributeNumberfor k=1:attributeValueNumberif jj==1if trainLabel(j,1)==1&&trainData(j,ii)==kcount_1(ii,k)=count_1(ii,k)+1;endelseif jj==2if trainLabel(j,1)==2&&trainData(j,ii)==kcount_2(ii,k)=count_2(ii,k)+1;endelseif trainLabel(j,1)==3&&trainData(j,ii)==kcount_3(ii,k)=count_3(ii,k)+1;endendendendend
end
%计算第i个属性取j值的概率,P_a_y1是分类为y=1前提下取值,其他依次类推。
P_a_y1=count_1./labelProbability(1,2);
P_a_y2=count_2./labelProbability(2,2);
P_a_y3=count_3./labelProbability(3,2);
%%
%使用测试集进行数据测试
labelPredictNumber=zeros(3,1);
predictLabel=zeros(size(testData,1),1);
for kk=1:size(testData,1)testDataTemp=testData(kk,:);Pxy1=1;Pxy2=1;Pxy3=1;%计算P(x|yi)for iii=1:attributeNumberPxy1=Pxy1*P_a_y1(iii,testDataTemp(iii));Pxy2=Pxy2*P_a_y2(iii,testDataTemp(iii));Pxy3=Pxy3*P_a_y3(iii,testDataTemp(iii));end%计算P(x|yi)*P(yi)PxyPy1=P_y1*Pxy1;PxyPy2=P_y2*Pxy2;PxyPy3=P_y3*Pxy3;if PxyPy1>PxyPy2&&PxyPy1>PxyPy3predictLabel(kk,1)=1;disp(['this item belongs to No.',num2str(1),' label or the R label'])labelPredictNumber(1,1)=labelPredictNumber(1,1)+1;elseif PxyPy2>PxyPy1&&PxyPy2>PxyPy3predictLabel(kk,1)=2;labelPredictNumber(2,1)=labelPredictNumber(2,1)+1;disp(['this item belongs to No.',num2str(2),' label or the B label'])elseif PxyPy3>PxyPy2&&PxyPy3>PxyPy1predictLabel(kk,1)=3;labelPredictNumber(3,1)=labelPredictNumber(3,1)+1;disp(['this item belongs to No.',num2str(3),' label or the L label'])end
end
testLabelCount=tabulate(testLabel);
% 计算混淆矩阵
disp('the confusion matrix is : ')
C_Bayes=confusionmat(testLabel,predictLabel)
以上部分就是针对于这个已有的数据集进行的算法的实现。
- 结果与分析
C_Bayes是计算出来的混淆矩阵。
其结果为:
C_Bayes=8300000014
为了验证该自编程序是否可靠,我再使用了Matlab自带的贝叶斯算法的函数fitcnb进行该数据的分类测试
Nb=fitcnb(trainData,trainLabel);
y_nb=Nb.predict(testData);
C_nb=confusionmat(testLabel,y_nb)
其中C_nb是采用自带函数得到的结果的混淆矩阵
其结果为:
C_nb=
可以发现其结果是完全一样的。
为了进一步验证程序的可靠性,我改变了交叉验证中训练集和测试集的比例,设置为0.2,
此时采用自编程序得到的混淆矩阵为:
C_Bayes=
而使用自带函数fitcnb得到的结果为:
C_nb=
可以发现再次得到了一致的结果。
总结
从上面的整个程序的实现,可以发现,整个朴素贝叶斯分类分为三个阶段。
①. 准备阶段。包括数据搜集,数据导入以及数据清洗阶段。获得可以进行分析的质量比较好的结果。然后将数据划分为训练集和测试集。
②. 构建分类器,进行数据训练。要工作是计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率估计,并将结果记录。其输入是特征属性和训练样本,输出是分类器。这一阶段是机械性阶段,根据前面讨论的公式可以由程序自动计算完成。
③. 第三阶段——应用阶段。这个阶段的任务是使用分类器对待分类项进行分类,其输入是分类器和待分类项,输出是待分类项与类别的映射关系。这一阶段也是机械性阶段,由程序完成。朴素贝叶斯算法成立的前提是各个属性之间是相互独立的。当数据集满足这个独立性假设的时候,分类的准确率较高,否则就可能不是很好。
朴素贝叶斯分类算法的特点
①. 这个算法比较的就简单,但是却很高效,在朴素贝叶斯的假设前提之下,分类结果准确率很高。
②. 同样,如果属性之间存在一个相关性联系的话,这个分类的精度就会大大降低。因为此时的独立性假设就会不成立,导致计算得到的条件概率均出现不同程度的偏差。
艾勇-上海交通大学
2017/7/19