当前位置:   article > 正文

朴素贝叶斯算法(Naive Bayes)_采用鸢尾花数据集实现朴素贝叶斯算法

采用鸢尾花数据集实现朴素贝叶斯算法

朴素贝叶斯算法(Naive Bayes)

时间:2022/5/11

0.数据集分析

数值属性数据集依旧是采用鸢尾花iris数据集这里就不过多介绍,标称属性数据集采用weather.arff数据集。这个数据集如下,有五个属性组成,决策属性为最后一个是否出去玩。这个数据集全部由标称属性组成,且数据集大小较小,非常适合入门学习。

@relation weather.symbolic

@attribute outlook {sunny, overcast, rainy}
@attribute temperature {hot, mild, cool}
@attribute humidity {high, normal}
@attribute windy {TRUE, FALSE}
@attribute play {yes, no}

@data
sunny,hot,high,FALSE,no
sunny,hot,high,TRUE,no
overcast,hot,high,FALSE,yes
rainy,mild,high,FALSE,yes
rainy,cool,normal,FALSE,yes
rainy,cool,normal,TRUE,no
overcast,cool,normal,TRUE,yes
sunny,mild,high,FALSE,no
sunny,cool,normal,FALSE,yes
rainy,mild,normal,FALSE,yes
sunny,mild,normal,TRUE,yes
overcast,mild,high,TRUE,yes
overcast,hot,normal,FALSE,yes
rainy,mild,high,TRUE,no
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

1.算法介绍

1.1朴素贝叶斯

朴素贝叶斯算法是基于贝叶斯定理进行预测分类的一种算法,贝叶斯定理如下:
P ( H ∣ X ) = P ( X ∣ H ) P ( H ) P ( X ) (1) P(H|X)={P(X|H)P(H)\over P(X)} \tag{1} P(HX)=P(X)P(XH)P(H)(1)
其中X是n个属性集的测量值,而H为某种假设。P(H|X)是后验概率(posterior probability),或者说条件X下,H的后验概率,以weather数据集举例,后验概率为晴天出去玩的概率P(yes | sunny)。而P(H)为先验概率(prior probability),比如出去玩的概率P(yes)。先验概率考虑的因素要更少,或者说后验概率比先验概率基于更多的信息。先验概率P(H)独立于X。
那朴素贝叶斯算法如何利用贝叶斯定理进行预测分类呢?
从贝叶斯定理可得,令X=x1∧x2∧⋯∧xm 表示一个条件的组合,outlook=sunny ∧ temperature=hot ∧ humidity=high^windy=false,对应一条数据。则Di为是否出去玩的假设,i有两种即yes/no。则由(1)式可得:
P ( D i ∣ X ) = P ( X ∣ D i ) P ( D i ) P ( X ) (2) P(D_i|X)={P(X|D_i)P(D_i) \over P(X)} \tag{2} P(DiX)=P(X)P(XDi)P(Di)(2)
从已知数据X可计算两种假设的概率,概率最大的发生的可能性就越大,我们则认为该假设为事件的最大后验假设。由于P(X)对于所有的假设而言都是个常数,所以只需要计算 P ( X ∣ D i ) P ( D i ) P(X|D_i)P(D_i) P(XDi)P(Di)最大值即可。如果先验概率 P ( D i ) P(D_i) P(Di)是未知的,则假定所有的假设都是等概率的,以此最大化 P ( X ∣ D i ) P(X|D_i) PXDi
为了减少 P ( X ∣ D i ) P(X|D_i) P(XDi)的计算开销,朴素贝叶斯算法做出类条件独立性的朴素假定。认为所有属性之间均相互独立。因此:
P ( X ∣ D i ) = ∏ j = 1 m P ( X k ∣ D i ) = P ( X 1 ∣ D i ) P ( X 2 ∣ D i ) … P ( X k ∣ D i ) (3) P(X|D_i)=\prod_{j=1}^m {P(X_k|D_i)}=P(X_1|D_i)P(X_2|D_i)\ldots P(X_k|D_i) \tag{3} P(XDi)=j=1mP(XkDi)=P(X1Di)P(X2Di)P(XkDi)(3)
则(2)式变为:
P ( D i ∣ X ) = P ( D i ) ∏ j = 1 m P ( X k ∣ D i ) P ( X ) (4) P(D_i|X)={P(D_i)\prod_{j=1}^m {P(X_k|D_i)} \over P(X)}\tag{4} P(DiX)=P(X)P(Di)j=1mP(XkDi)(4)
使用对数将连乘转化成连加,得到最后的预测函数:
D ( X ) = a r g   max ⁡ 1 ≤ i ≤ k P ( D i ∣ X ) = a r g   max ⁡ 1 ≤ i ≤ k P ( D i ) ∏ j = 1 m P ( X k ∣ D i ) = a r g   max ⁡ 1 ≤ i ≤ k ( l o g P ( D i ) + ∑ j = 1 m l o g P ( X i ∣ D i ) ) (5) D(X)=arg\,\max_{1\le i \le k}P(D_i|X)=arg\,\max_{1\le i \le k}P(D_i)\prod_{j=1}^m {P(X_k|D_i)}=arg\,\max_{1\le i \le k}(logP(D_i)+\sum_{j=1}^m{logP(X_i|D_i)})\tag{5} D(X)=arg1ikmaxP(DiX)=arg1ikmaxP(Di)j=1mP(XkDi)=arg1ikmax(logP(Di)+j=1mlogP(XiDi))(5)

1.2拉普拉斯平滑( Laplacian smooth)

在计算(3)式时如果出现一个属性的概率为0时则整个结果都为0了,这样计算便出问题了。为了避免这种0概率情况,法国数学家拉普拉斯便提出了一种平滑方法——拉普拉斯平滑。具体公式如下
P L ( x j ∣ D i ) = n P ( X j D i ) + 1 n P ( D i ) + v j (6) P^L(x_j|D_i)={{nP(X_jD_i)+1}\over {nP(D_i)+v_j}}\tag{6} PL(xjDi)=nP(Di)+vjnP(XjDi)+1(6)
其中n是对象的数量, v j v_j vj是属性的可能取值数。这样通过分子的加一,便解决的数据的0概率情况。

1.3数值属性的计算

对于数值型数据,不能使用P(humidity=87),因为湿度恰好等于87的概率过小,小概率事件在大量随机测试中发生概率趋近于0。但P(80<humidity<=87)的概率却不为0;这样便可实现数值属性的离散化。但如果不使用离散化方法,还可以使用数值的概率密度函数。
首先根据数据及分布假设,求得概率密度函数p(humidity=87)。然后直接使用p代替(5)式中的P。通常概率分布可以选择高斯分布,其概率密度函数如下:
p ( x ) = 1 2 π σ e x p ( − ( s − μ ) 2 2 σ 2 ) (7) p(x)={1\over \sqrt{2\pi}\sigma}exp(-{(s-\mu)^2\over 2\sigma^2})\tag{7} p(x)=2π σ1exp(2σ2(sμ)2)(7)
代入(5)式得
D ( X ) = a r g   max ⁡ 1 ≤ i ≤ k ( l o g P ( D i ) + ∑ j = 1 m − l o g σ i j − ( x j − μ i j 2 ) 2 σ i j 2 ) (8) D(X)=arg\,\max_{1\le i \le k}(logP(D_i)+\sum_{j=1}^m{-log\sigma_{ij}-{(x_j-\mu _{ij}^2)\over 2\sigma_{ij}^2}})\tag{8} D(X)=arg1ikmax(logP(Di)+j=1mlogσij2σij2(xjμij2))(8)

2.算法流程

模型构建

  1. 读入数据
  2. 计算决策属性的概率分布 P ( D i ) P(D_i) P(Di),同时计算经过拉普拉斯平滑后的概率 P L ( D i ) P^L(D_i) PL(Di)
  3. 计算条件属性经过拉普拉斯平滑后的条件概率 P L ( X i ∣ D i ) P^L(X_i|D_i) PL(XiDi)
    通过前两步已经形成模型,则可以用于预测数据。
    预测数据
  4. 读入一条数据;
  5. 根据数据对应的每个属性的取值,选取 P L ( X i ∣ D i ) P^L(X_i|D_i) PL(XiDi)
  6. 根据公式计算 P ( D i ∣ X ) P(D_i|X) P(DiX)
  7. 选择概率最大的 作为最大后验假设。则返回该假设作为预测标签

3.算法实现完整代码

/**
 * MyNaiveBayes.java
 *
 * @author zjy
 * @date 2022/5/10
 * @Description:
 * @version V1.0
 */
package swpu.zjy.ML.NB;

import weka.core.Instance;
import weka.core.Instances;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Arrays;

public class MyNaiveBayes {

    /**
     * 内部类,用于存储高斯分布中的期望mu和方差sigma
     * 用以对数组型数据集进行Naive Bayes分类
     */
    private class GaussianParamters {
        double mu;
        double sigma;

        public GaussianParamters(double mu, double sigma) {
            this.mu = mu;
            this.sigma = sigma;
        }

        @Override
        public String toString() {
            return "GaussianParamters{" +
                    "mu=" + mu +
                    ", sigma=" + sigma +
                    '}';
        }
    }

    //标称属性
    public static final int NOMINAL = 0;
    //数值属性
    public static final int NUMERICAL = 1;
    //数据属性类别
    private int dataType = NOMINAL;


    //数据集实体
    Instances dataset;

    //决策属性取值个数
    int numClasses;

    //数据集数据个数
    int numInstances;

    //数据的条件属性个数
    int numConditions;

    //预测标签 length = numInstance
    int predicts[];

    //决策属性概率分布 length = numClasses
    double[] classDistribution;

    //决策属性经过拉普拉斯平滑后的概率分布 length = numClasses
    double[] classDistributionLaplacian;
    /**
     * 条件属性个数,用以计算P(xi|Di)
     * 第一维是决策属性 length = numClasses
     * 第二维是条件属性 length = numConditions
     * 第三维是条件属性取值出现次数 length = 该条件属性可取值个数
     */
    double[][][] conditionalCounts;
    /**
     * 条件属性经过拉普拉斯平滑后的概率,各维度长度同上
     * 第三维是条件属性取值出现的概率
     */
    double[][][] conditionalLaplacianOdds;
    /**
     * 存放高斯分布参数 用以预测数值型数据
     * 第一维是决策属性 length = numClasses
     * 第二维是条件属性 length = numConditions
     */
    GaussianParamters[][] gaussianParameters;

    /**
     * 构造方法,根据传入数据集文件路径,初始化数据集实体及相关参数
     *
     * @param dataSetFileName 数据集文件路径
     */
    public MyNaiveBayes(String dataSetFileName) {
        try {
            FileReader fileReader = new FileReader(dataSetFileName);
            dataset = new Instances(fileReader);
            fileReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        dataset.setClassIndex(dataset.numAttributes() - 1);
        //初始化数据集参数
        numClasses = dataset.numClasses();
        numConditions = dataset.numAttributes() - 1;
        numInstances = dataset.numInstances();
    }

    /**
     * 设置数据集数据类型
     *
     * @param dataType 数据类型
     */
    public void setDataType(int dataType) {
        this.dataType = dataType;
    }

    /**
     * 计算决策属性的概率分布和经过拉普拉斯平滑后的概率分布
     */
    public void calculateClassDistribution() {
        //step1.初始化向量
        classDistribution = new double[numClasses];
        classDistributionLaplacian = new double[numClasses];

        //step2.统计决策属性出现次数
        double[] tempCnt = new double[numClasses];
        int tempType;
        for (int i = 0; i < numInstances; i++) {
            tempType = (int) dataset.instance(i).classValue();
            tempCnt[tempType]++;
        }
        //step3.计算决策属性概率分布及拉普拉斯平滑后的概率
        for (int i = 0; i < numClasses; i++) {
            classDistribution[i] = tempCnt[i] / numInstances;
            classDistributionLaplacian[i] = (tempCnt[i] + 1) / (numInstances + numClasses);
        }

        System.out.println("Class distribution: " + Arrays.toString(classDistribution));
        System.out.println("Class distribution Laplacian: " + Arrays.toString(classDistributionLaplacian));

    }

    /**
     * 计算条件属性的概率分布即P(xi|Di)
     */
    public void calculateConditionalOdds() {
        //step1.初始化向量,最后一维暂留
        conditionalCounts = new double[numClasses][numConditions][];
        conditionalLaplacianOdds = new double[numClasses][numConditions][];

        //step2.根据每一个条件属性的取值个树初始最后一维
        int tempCnt;
        for (int i = 0; i < numClasses; i++) {
            for (int j = 0; j < numConditions; j++) {
                tempCnt = dataset.attribute(j).numValues();
                conditionalCounts[i][j] = new double[tempCnt];
                conditionalLaplacianOdds[i][j] = new double[tempCnt];
            }
        }

        //step3.统计训练集中的条件属性对应取值出现次数
        int[] tempClassCount = new int[numClasses];
        int tempClass, tempValue;
        for (int i = 0; i < numInstances; i++) {
            tempClass = (int) dataset.instance(i).classValue();
            tempClassCount[tempClass]++;
            for (int j = 0; j < numConditions; j++) {
                tempValue = (int) dataset.instance(i).value(j);
                conditionalCounts[tempClass][j][tempValue]++;
            }
        }
        //step4.计算条件属性拉普拉斯平滑后的概率
        for (int i = 0; i < numClasses; i++) {
            for (int j = 0; j < numConditions; j++) {
                int tempNumvalue = dataset.attribute(j).numValues();
                for (int k = 0; k < tempNumvalue; k++) {
                    conditionalLaplacianOdds[i][j][k] = (conditionalCounts[i][j][k] + 1) / (tempClassCount[i] + tempNumvalue);
                }
            }
        }
        System.out.println("Conditional probabilities: " + Arrays.deepToString(conditionalCounts));
    }

    /**
     * 计算数值型数据的高斯分布参数mu与sigma
     */
    public void calculateGausssianParameters() {
        gaussianParameters = new GaussianParamters[numClasses][numConditions];

        double[] tempValuesArray = new double[numInstances];
        int tempNumValues = 0;
        double tempSum = 0;

        for (int i = 0; i < numClasses; i++) {
            for (int j = 0; j < numConditions; j++) {
                tempSum = 0;

                //求和+统计个数
                tempNumValues = 0;
                for (int k = 0; k < numInstances; k++) {
                    if ((int) dataset.instance(k).classValue() != i) {
                        continue;
                    }

                    tempValuesArray[tempNumValues] = dataset.instance(k).value(j);
                    tempSum += tempValuesArray[tempNumValues];
                    tempNumValues++;
                }
                //求期望
                double tempMu = tempSum / tempNumValues;
                //求方差
                double tempSigma = 0;
                for (int k = 0; k < tempNumValues; k++) {
                    tempSigma += (tempValuesArray[k] - tempMu) * (tempValuesArray[k] - tempMu);
                }
                tempSigma /= tempNumValues;
                tempSigma = Math.sqrt(tempSigma);
                gaussianParameters[i][j] = new GaussianParamters(tempMu, tempSigma);
            }
        }
        System.out.println(Arrays.deepToString(gaussianParameters));
    }

    /**
     * 对标称属性的数据进行分类
     *
     * @param instance 数据元组
     * @return 预测标签
     */
    public int classifyNominal(Instance instance) {
        //记录最大概率
        double tempMaxOdds = -Double.MAX_VALUE;
        //记录标签
        int classIndex = 0;
        for (int i = 0; i < numClasses; i++) {
            //Pl(Di)
            double tempClassfiyOdds = Math.log(classDistributionLaplacian[i]);
            for (int j = 0; j < numConditions; j++) {
                int tempConditionValue = (int) instance.value(j);
                //sum(Pl(xi|Di))
                tempClassfiyOdds += Math.log(conditionalLaplacianOdds[i][j][tempConditionValue]);
            }
            if (tempClassfiyOdds > tempMaxOdds) {

                tempMaxOdds = tempClassfiyOdds;
                classIndex = i;
            }
        }
        return classIndex;
    }

    /**
     * 对数值属性数据进行分类,原理同标称属性分类一致,差别在于使用概率密度p代替概率P
     *
     * @param instance 数据元组
     * @return 预测标签
     */
    public int classifyNumerical(Instance instance) {
        // Find the biggest one
        double tempBiggest = -10000;
        int resultBestIndex = 0;

        for (int i = 0; i < numClasses; i++) {
            double tempClassProbabilityLaplacian = Math.log(classDistributionLaplacian[i]);
            double tempPseudoProbability = tempClassProbabilityLaplacian;

            //计算概率
            for (int j = 0; j < numConditions; j++) {
                double tempAttributeValue = instance.value(j);
                double tempSigma = gaussianParameters[i][j].sigma;
                double tempMu = gaussianParameters[i][j].mu;

                tempPseudoProbability += -Math.log(tempSigma) - (tempAttributeValue - tempMu)
                        * (tempAttributeValue - tempMu) / (2 * tempSigma * tempSigma);
            }

            if (tempBiggest < tempPseudoProbability) {
                tempBiggest = tempPseudoProbability;
                resultBestIndex = i;
            }
        }

        return resultBestIndex;
    }

    /**
     * 对一个数据进行分类,根据数据类型选择不同方法
     *
     * @param paraInstance 待预测的数据元组
     * @return 预测标签
     */
    public int classify(Instance paraInstance) {
        if (dataType == NOMINAL) {
            return classifyNominal(paraInstance);
        } else if (dataType == NUMERICAL) {
            return classifyNumerical(paraInstance);
        }

        return -1;
    }

    /**
     * 对数据集所有数据进行分类
     */
    public void classify() {
        predicts = new int[numInstances];
        for (int i = 0; i < numInstances; i++) {
            predicts[i] = classify(dataset.instance(i));
        }
    }

    /**
     * 统计准确率
     *
     * @return 准确率
     */
    public double computeAccuracy() {
        double tempCorrect = 0;
        for (int i = 0; i < numInstances; i++) {
            if (predicts[i] == (int) dataset.instance(i).classValue()) {
                tempCorrect++;
            }
        }
        double resultAccuracy = tempCorrect / numInstances;
        return resultAccuracy;
    }

    /**
     * 标称属性数据分类测试
     */
    public static void testNominal() {
        System.out.println("Hello, Naive Bayes. I only want to test the nominal data.");
        String tempFilename = "E:\\DataSet\\weather.arff";

        MyNaiveBayes tempLearner = new MyNaiveBayes(tempFilename);
        tempLearner.setDataType(NOMINAL);
        tempLearner.calculateClassDistribution();
        tempLearner.calculateConditionalOdds();
        tempLearner.classify();

        System.out.println("The accuracy is: " + tempLearner.computeAccuracy());
    }

    /**
     * 数值属性数据分类测试
     */
    public static void testNumerical() {
        System.out.println(
                "Hello, Naive Bayes. I only want to test the numerical data with Gaussian assumption.");

        String tempFilename = "E:\\JAVA项目\\mytest\\src\\main\\java\\swpu\\zjy\\ML\\DataSet\\iris.arff";

        MyNaiveBayes tempLearner = new MyNaiveBayes(tempFilename);
        tempLearner.setDataType(NUMERICAL);
        tempLearner.calculateClassDistribution();
        tempLearner.calculateGausssianParameters();
        tempLearner.classify();

        System.out.println("The accuracy is: " + tempLearner.computeAccuracy());
    }

    public static void main(String[] args) {
//        testNominal();

        testNumerical();

    }


}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/340798
推荐阅读
相关标签
  

闽ICP备14008679号