机器学习实验-朴素贝叶斯分类器

提示:个人联系方式及代码简要说明、运行方式在README.md文件中

实验目的

  • 在真实数据集上实现朴素贝叶斯分类器,并验证其分类效果
  • 了解如何在测试数据集上实现一个机器学习算法
  • 了解如何评价分类效果
  • 了解如果分析实验结果

算法实现原理

假设各条件相互间独立,即$$P(y|x_1,\cdots,x_n)P(y) \propto \prod_{i=1}^{n}P(x_i|y)$$

在训练时训练 $P(y)$ 以及$P(x_i|y)$

测试时输出 $$\hat y = argmax_yP(y)\prod_{i=1}^{n}P(x_i|y)$$

数据集处理

数据集描述

实验给定了Adult数据集,其中adult.train为训练集(32561条数据),adult.test为测试集(16281条数据),每行数据代表一个人,共有15个维度的特征,最后一个特征为该人的收入是否超过了50K。

数据集中部分特征是连续数据,部分数据可能未知(用?表示)

分类器的性能评价指标:

本次实验中,我采用了准确率作为朴素贝叶斯分类器性能的评价指标,计算方法为:

$$Accuracy = \frac{number\ of\ correctly\ classified\ records}{number\ test\ records}$$

数据集的变量及其含义

变量名意义数据特征处理方式
age年龄连续数据分段离散
work_class职业类型离散数据
fnlwgt最终重量(?)连续数据无意义、忽略
education学历等级离散数据
education_num学历的数字等级连续数据重复、忽略
marital-status婚姻状况离散数据
occupation职业离散数据
relationship家庭关系离散数据
race人种离散数据
sex性别离散数据
capital_gain资本利得连续数据离散化
capital_loss资本损失连续数据离散化
hours_per_week每周工作时长连续数据离散化
native-country出生国离散
income收入离散

特殊数据的处理方式

未知数据?的处理

数据集中有未知的数据(?), 我的处理方式是将这类数据直接忽略掉

数据合并

使用R语言对数据进行统计,发现,Never-worked和Without-pay可以合并为Without-pay字段。

数据的离散化

数据规律探索

考虑到R语言对数据处理的优越性,因此采用R语言对数据规律进行探索,利用R语言读入测试集和训练集

1
2
3
4
5
6
# 读取测试集,已清除?
test = read.csv("after.test",
sep=",", header=F, col.names=c("age", "work_class", "fnlwgt", "education", "education_num",
"marital-status", "occupation", "relationship", "race", "sex",
"capital_gain", "capital_loss", "hourr_per_week","native-country", "income"),
fill = FALSE, strip.white = T)

然后对连续数据做统计之后得到如下结果

Age的规律
1
2
3
4
5
6
7
8
> table(train$age)

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
328 447 594 629 621 674 824 752 799 745 789 808 774 813 851 789 837 836 828 852 828 791 786 765 769 741 743 704
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
706 711 683 523 555 575 571 455 448 394 386 343 337 344 332 276 259 213 186 173 136 110 111 90 80 64 54 40
73 74 75 76 77 78 79 80 81 82 83 84 85 86 88 90
49 38 34 29 20 14 15 16 13 7 5 8 3 1 3 35

可知年龄范围为17~90,我们设置其分割粒度为5

资本收益

资本收益

可以从统计结果看出,投资收益相互间差别很大,直接以固定颗粒度分割并不适合,考虑到投资和收入之间存在一定的关系,而且可以很明显的看出收益为0的占了所有数据的大部分,我们将其分为三个类别,没有资本收益,资本收益较少,资本收益较大,。

除去数据中为0的值之后,求得其平均值,中位数,方差如下:

1
2
3
4
5
6
7
8
9
> # 资本收益平均值
> mean(train$capital_gain[train$capital_gain!=0])
[1] 12977.6
> # 资本收益中位数
> median(train$capital_gain[train$capital_gain!=0])
[1] 7298
> # 资本收益方差
> sd(train$capital_gain[train$capital_gain!=0])
[1] 22311.91

可以看出其方差较大,以平均值作为界点不合适,我们取其中位数作为资本收益高低的界点(训练集与测试集这个数据差别不大)

同理,资本损失的数据也有这样的规律,我们也用这样的方法对其进行处理。

1
2
3
4
5
6
7
8
9
> # 资本损失平均值
> mean(train$capital_loss[train$capital_loss!=0])
[1] 1867.898
> # 资本损失中位数
> median(train$capital_loss[train$capital_loss!=0])
[1] 1887
> # 资本损失收益方差
> sd(train$capital_loss[train$capital_loss!=0])
[1] 361.8574

资本损失的差异值不大,我们直接取中位数作为分界点

每周工作时间

每周工作时间

工作时间为1~99, 分割粒度设置为5

各连续变量的范围及对应的分割粒度如下:

变量名范围分割粒度
age17~905
capital_gain0 ~ 999990,0~7298,7298+
capital_loss0 ~ 43560,0~1887, 1887+
hours_per_week1~995

实验结果的分析

训练集规模的影响

问题:训练集的规模对分类效果有什么影响?

选取5%, 50%, 100%的训练集数据训练分类模型

测试数据的选择,经过多方面的对比测试,发现选取的特征值为

1
年龄 工作类别 学历等级 婚姻状况 职业 投资利得 投资损失 收入

时,训练的模型分类效果最好。以此为基础,分别选取 5%, 50%, 100%的训练集数据训练分类模型时的训练结果对比如下:

比例训练集数目准确率
5%149483.71%
50%1507583.99%
100%3016284.12%

由这个表格可以看出,随着数据集的增加,分类器的分类效果越来越好。而实际上,即使只取了5%(1494条训练数据),训练出的分类器分类效果仍然挺好的,可以由此体会到贝叶斯分类的高效性和实用性。

重复随机抽取样本实验(5次),记录最小,最大,平均准确率

随机比例训练集数目准确率
70.13%2113183.99%
47.92%1445884.01%
94.40%2847284.04%
58.65%1768784.14%
11.83%356683.95%
最小值最大值平均值
83.95%84.14%84.03%

综合两个测试结果可以得出如下结论:数据集的规模会对分类效果产生一定影响,但这种影响并不是绝对的,当抽取的训练集具有随机性时,小训练集也有可能会有特别好的分类效果,不过整体来看,训练集规模越大,分类效果越好。

0概率的处理

当测试集中某个数据的某条特征值取了某个值$x_i$,但训练集中该特征值并没有取过该值$x_i$,则在训练时$P(x_i|y) = 0$,由此在做测试时计算其概率$$\hat y = argmax_yP(y)\prod_{i=1}^{n}P(x_i|y)$$时会得到概率为0。

解决方法:通常我们会进行拉普拉斯平滑处理,即在计算条件概率时对每个$x_i$ 做$+\lambda$处理, 对应的总数也需要做$+M\lambda$,经过测试可以发现,在未做拉普拉斯平滑时,训练集取50%时,分类准确率为83.95%, 加上拉普拉斯平滑处理之后,分类准确率为83.99%, 准确率有一定提升。

连续特征以及未知特征的处理

连续数据如何进行处理

说明:【数据的分析及离散化方案】见前面【3.4.3数据的离散化】小节。由于后期经过测试发现特征选取年龄 工作类别 学历等级 婚姻状况 职业 投资利得 投资损失 收入这8个特征时分类效果最好,故前面测试样例的特征选取均选择了这8个,为了测试连续数据分割方案对分类效果的影响,将每周工作时间这一连续特征加入分类的特征中。

测试时,训练集取100%, 拉普拉斯平滑处理的$\lambda = 1$

各连续特征值分割粒度与其分类效果对比

特征分割粒度准确率
age不分割83.816%
age383.831%
age583.751%
age1083.784%
特征分割粒度准确率
hour不分割83.845%
hour383.845%
hour583.752%
hour1083.804%
特征分割粒度准确率
投资收益与损失不分割【85.20%】
投资收益与损失100083.62%
投资收益与损失无,低,高83.75%

注意:测试某一特征时,其余特征分割情况默认为: 年龄分割粒度 5, 每周工作时间分割粒度为5, 投资收益和损失分割为无, 收益/损失低, 收益/损失高

结果分析:这里发现了一些很尴尬的结果,对这三个连续特征值进行不同粒度离散之后发现,【不进行离散】,直接以每一个数据单独作为一个类别进行分类时准确率反而比对其进行不同间距离散之后分类【准确率高】,特别是【投资收益与损失】的两个特征值,不进行离散时其准确率甚至高达 【85.2%】,而进行了等间距离散或者以无,低、高, 结合前面对各个特征的分布情况的统计可以进行如下猜测:

结合前面age, work hour, capital_gain, capital_loss几个特征值的取值特征统计结果,
可以发现,age和work hour在各个取值中虽然较为分散,但是也有小范围集中,简单的等间距离散,对其分类的优化效果不大,甚至于有可能因为粒度过大而使分类效果显著下降, 当分割粒度恰好使得集中数据分在了一个category时,其准确率会略高一点,而如果恰好使之分散开,可能会对分类准确率有反作用,如work hour特征分割粒度为5和10的对比。

对于投资收益和损失这两个特征值,分析其数据特征可以发现,有超过70%的数据是0,而其他数据就较为零散并且差异值极大,简单地等间距分割,或者以中位数,平均数作为临界点进行分割都是不太合理的,因此这种情况下不进行离散化分类效果反而会更好,(不知道高斯分布处理会不会优化其分类效果,由于能力和时间限制,没来得及进行测试)

未知数据如何处理

对于未知数据的处理,我做了两种情况的对比,一种是直接忽略掉这些数据,另一种是将‘?’也视为一种数据和特征

处理方案准确率
忽略未知数据84.12%
视为新类型【84.45%】

可以看出,将未知数据视为一种特殊的新类型时其分类准确率有较大提高,可知这些数据某种程度上也能够反映出其收入的高低

选取的特征值对比

选取的特征值准确率
年龄 工作类别 重量 学历等级 教育年限 婚姻状况 职业 家庭关系 人种 性别 投资利得 投资损失 每周工作时间 出生国 收入79.77%
年龄 工作类别 学历等级 婚姻状况 职业 家庭关系 人种 性别 投资利得 投资损失 每周工作时间 出生国 收入81.85%
年龄 工作类别 学历等级 婚姻状况 职业 家庭关系 性别 投资利得 投资损失 每周工作时间 出生国 收入81.83%
年龄 工作类别 学历等级 婚姻状况 职业 人种 投资利得 投资损失 每周工作时间 收入83.74%
年龄 工作类别 学历等级 婚姻状况 职业 投资利得 投资损失 每周工作时间 收入83.75%
年龄 工作类别 学历等级 婚姻状况 职业 投资利得 投资损失 收入84.12%
年龄 工作类别 教育年限 婚姻状况 职业 收入81.79%

由这个表的对比分析可以明显地感受到特征值的选取对分类效果的影响,【年龄,工作类别,学历等级,婚姻状况,职业,投资利得,投资损失】这几个特征值能够很大程度上反应出其收入的高低,特别是投资收入和损失,这个特征与收入有较大的相关性,这也是为什么在【5.3.1 连续数据如何进行处理】一节中提到对这两个特征不进行分割时其分类效果甚至可以高达85.20%的一个原因。

这也提醒我们在选取训练数据时注意对特征值的选取,有代表性的特征对其分类准确率有促进作用,而一些无关的特征则会对分类效果有负作用。

交叉验证

选取不同比例的测试集数据用作训练,观察其对分类效果的影响

测试集比例训练集+测试集准确率
0%15075 + 083.99%
5%15075 + 75184.02%
50%15075 + 752584.23%
100%15075 + 1506084.26%

[注] 训练集选取的是50%训练集数据

由这个数据对比可以看出,训练模型时加入部分测试集数据,对分类效果有提升作用。

实验总结及结论

本次实验实现了贝叶斯分类算法,并探讨了数据规模、特征值的选择对分类效果的影响,以及连续值,未知值的不同离散方案和处理方案对分类效果的影响,并探讨了拉普拉斯平滑对分类效果的影响。

通过多方面的对比,主要得出了以下一些结论:

  • 特征值的选取对分类效果影响较大,可以根据特征值与类别的相关性大小判断其对分类是有帮助的还是有干扰的。
  • 特征值个数也对分类效果有一定影响,选择合适的,足够的特征作为分类依据是较好的,特征值的选择和个数的选择可通过参数的调整进行尝试后得出最优方案
  • 数据规模对分类效果有一定影响,但是只要特征值选取较好,训练数据较少情况下训练出的模型其分类效果也较好。
  • 连续值的处理方式对分类效果影响较大,对于一些本身比较离散并且该特征值对类型影响较大情况下,分割粒度越小,分类效果会更好,尤其是本实验中投资收入和投资损失两个特征值,不进行分割时其准确率甚至高达85.2%,可明显体会到数据离散粒度对分类效果的影响。
  • 未知数据的处理,未知数据视为一种特殊类型也是一种比较有效的处理方式,背后原因可能是这些没统计到数据的人群可能具有某方面的共性,其收入也会受到这方面的影响。

本次实验中自己收获很大,特别是学会了从不同的方面去评价一个算法的性能的方式的分析方法。此外,对编程也有一些新的启发,在编程时需要注意面向对象编程,考虑到各种可能的变化,本次实验中的贝叶斯分类器的实现方法我参考了【参考资料1 使用python编写朴素贝叶斯分类器】一文的实现方式,这种实现可以达到一种自适应的状态,不论特征值有多少个,是什么类型的数据,只要给出参数,均可对其进行训练和分类,这就方便了我们进行不同的特征值对分类效果的影响分析时的测试,只需要对测试文件和训练文件做对应修改即可。此外,在实现过程中设置了很多参数,可以方便进行训练数据比例,是否随机选取,特征值分割粒度,是否进行拉普拉斯平滑处理等进行设置,极大地方便了测试和分析。

参考资料

打赏