朴素贝叶斯算法实现分类以及Matlab实现

  • 开始

其实在学习机器学习的一些算法,最近也一直在看这方面的东西,并且尝试着使用Matlab进行一些算法的实现。这几天一直在看得就是贝叶斯算法实现一个分类问题。大概经过了一下这个过程:

看书 算法公式推演 网上查询资料 进一步理解 搜集数据集开始尝试 写代码 调试 代码整理与优化 自编代码结果与Matlab自带函数fitcnb结果对比验证 朴素贝叶斯算法优缺点总结

经过这几天的努力,总算是把这个算法彻底弄明白了,也发现,很多算法只有自己去亲自写一写才会发现自己的不足,还是需要多努力。

  • 贝叶斯分类

    • 分类问题理解
      贝叶斯分类是一类分类算法的总称,这类算法以贝叶斯定理为基础,因此称之为贝叶斯分类。
      而对于分类问题,其实谁都不会陌生,说我们每个人每天都在执行分类操作一点都不夸张,只是我们没有意识到罢了。例如,当你看到一个陌生人,你的脑子下意识判断TA是男是女;你可能经常会走在路上对身旁的朋友说“这个人一看就很有钱、那边有个非主流”之类的话,其实这就是一种分类操作。
      从数学角度来说,分类问题可做如下定义:
      已知集合, C={y1,y2,,yn} I={x1,x2,,xm,} 确定映射规则 y=f(x) ,使得任意的 xI 有且仅有一个 yiC 使得 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)} ,则 xyk
      因此,从上面我们可以看出来,该算法的关键步骤就是第(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程序实现

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:sampleNumber
    if 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:3
    for j=1:trainSampleNumber
        for ii=1:attributeNumber
            for k=1:attributeValueNumber
                if jj==1
                    if trainLabel(j,1)==1&&trainData(j,ii)==k
                        count_1(ii,k)=count_1(ii,k)+1;
                    end
                elseif jj==2
                    if trainLabel(j,1)==2&&trainData(j,ii)==k
                        count_2(ii,k)=count_2(ii,k)+1;
                    end
                else
                    if trainLabel(j,1)==3&&trainData(j,ii)==k
                        count_3(ii,k)=count_3(ii,k)+1;
                    end
                end
            end
        end
    end
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:attributeNumber
        Pxy1=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>PxyPy3
        predictLabel(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>PxyPy3
        predictLabel(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>PxyPy1
        predictLabel(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=

8300000014

可以发现其结果是完全一样的。
为了进一步验证程序的可靠性,我改变了交叉验证中训练集和测试集的比例,设置为0.2,
此时采用自编程序得到的混淆矩阵为:
C_Bayes=
63400000552

而使用自带函数fitcnb得到的结果为:
C_nb=
63400000552

可以发现再次得到了一致的结果。

  • 总结

    1. 从上面的整个程序的实现,可以发现,整个朴素贝叶斯分类分为三个阶段。
      ①. 准备阶段。包括数据搜集,数据导入以及数据清洗阶段。获得可以进行分析的质量比较好的结果。然后将数据划分为训练集和测试集。
      ②. 构建分类器,进行数据训练。要工作是计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率估计,并将结果记录。其输入是特征属性和训练样本,输出是分类器。这一阶段是机械性阶段,根据前面讨论的公式可以由程序自动计算完成。
      ③. 第三阶段——应用阶段。这个阶段的任务是使用分类器对待分类项进行分类,其输入是分类器和待分类项,输出是待分类项与类别的映射关系。这一阶段也是机械性阶段,由程序完成。

    2. 朴素贝叶斯算法成立的前提是各个属性之间是相互独立的。当数据集满足这个独立性假设的时候,分类的准确率较高,否则就可能不是很好。

    3. 朴素贝叶斯分类算法的特点
      ①. 这个算法比较的就简单,但是却很高效,在朴素贝叶斯的假设前提之下,分类结果准确率很高。
      ②. 同样,如果属性之间存在一个相关性联系的话,这个分类的精度就会大大降低。因为此时的独立性假设就会不成立,导致计算得到的条件概率均出现不同程度的偏差。

艾勇-上海交通大学
2017/7/19

朴素贝叶斯算法是一种基于贝叶斯定理和特征条件独立假设的分类算法,可以用于文本分类、垃圾邮件过滤、情感分析等领域。下面是Matlab程序实现朴素贝叶斯算法的示例: 1. 数据准备 假设有一个包含n个样本的训练数据集,每个样本包含m个特征。其,每个样本有一个类别标签,共有k个类别。我们需要将数据集按照一定的比例划分为训练集和测试集。 2. 训练模型 首先,需要计算每个类别的先验概率P(Ci),即在所有样本,属于类别Ci的样本占总样本数的比例。假设训练集属于类别Ci的样本数为ni,总样本数为n,则P(Ci)=ni/n。 然后,需要计算每个特征在每个类别下的条件概率P(Xj|Ci),即在属于类别Ci的样本,特征Xj取某个值的样本数占所有属于类别Ci的样本数的比例。假设训练集属于类别Ci且特征Xj取值为vj的样本数为nij,属于类别Ci的样本数为ni,则P(Xj=vi|Ci)=nij/ni。 3. 测试模型 对于测试集的每个样本,需要计算它属于每个类别的后验概率P(Ci|X),并将其归为概率最大的类别。根据贝叶斯定理,P(Ci|X)=P(X|Ci)P(Ci)/P(X),其P(X|Ci)表示在类别Ci下,特征X的联合概率密度函数,通常假设各个特征之间相互独立,即P(X|Ci)=P(X1|Ci)P(X2|Ci)...P(Xm|Ci)。 4. 代码实现 下面是一个简单的Matlab实现示例: ```matlab % 数据准备 data = load('data.txt'); train_ratio = 0.7; idx = randperm(size(data, 1)); train_idx = idx(1:round(size(data, 1)*train_ratio)); test_idx = idx(round(size(data, 1)*train_ratio)+1:end); train_data = data(train_idx, :); test_data = data(test_idx, :); k = length(unique(data(:, end))); % 训练模型 prior = zeros(k, 1); cond_prob = zeros(size(data, 2)-1, k); for i = 1:k prior(i) = sum(train_data(:, end)==i) / size(train_data, 1); for j = 1:size(data, 2)-1 for v = unique(data(:, j))' cond_prob(j, i, v) = sum(train_data(train_data(:, j)==v, end)==i) / sum(train_data(:, end)==i); end end end % 测试模型 pred = zeros(size(test_data, 1), 1); for i = 1:size(test_data, 1) posterior = zeros(k, 1); for j = 1:k likelihood = 1; for v = unique(data(:, end))' likelihood = likelihood * cond_prob(:, j, test_data(i, :)'); end posterior(j) = prior(j) * likelihood; end [~, pred(i)] = max(posterior); end % 计算准确率 accuracy = sum(pred==test_data(:, end)) / size(test_data, 1); disp(['Accuracy: ', num2str(accuracy)]); ``` 其,data.txt是数据集文件,每行表示一个样本,最后一个数为类别标签。prior和cond_prob分别表示先验概率和条件概率,各自的维度为k和(m,k,2),分别对应类别数和特征数及其取值范围。pred为预测结果,accuracy为准确率。
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值