机器学习-朴素贝叶斯分类器
简介
朴素贝叶斯(Naive Bayes)是基于贝叶斯定理和概率论预测样本类别的概率算法,而朴素一词的来源就是假设各特征之间相互独立。朴素贝叶斯属于监督学习的生成模型,实现简单,没有迭代,并有坚实的数学理论(即贝叶斯定理)作为支撑。在大量样本下会有较好的表现,不适用于输入向量的特征条件有关联的场景。
关于贝叶斯定理的详细介绍
名词介绍
拿西瓜是否成熟举例,假设判断西瓜成熟的特征有:瓜蒂是否脱落,敲打的声音-浊响|清脆
- 先验概率:
先验概率是根据以往经验和分析得到的概率,先验概率无需样本数据,不受任何条件的影响。比如有1000个西瓜,其中熟瓜600,涩瓜400,则先验概率p(熟瓜)=0.6,p(涩瓜)=0.4,并不受瓜蒂和敲打声音的影响。再比如硬币正反面概率各为0.5也是先验概率。 - 后验(条件)概率:
已知结果或者某一条件(结果也是一种条件),求知条件或者结果的概率。比如已知某西瓜瓜蒂脱落,判断瓜成熟的概率,即p(成熟|瓜蒂脱落)。 - 拉普拉斯平滑(Laplace smoothing):
它的思想非常简单,就是对先验概率的分子(划分的计数)加1,分母加上类别数;对条件概率分子加1,分母加上对应特征的可能取值数量。这样在解决零概率问题的同时,也保证了概率和依然为1。
直接的作用就是防止模型“过拟合”,提高了模型的泛化性能。
也是解决样本为0的时概率为0的问题,因为若p(B)为0,则P(B|A)也为0,而实际并非如此。 - 概率密度函数:
用于描述连续型随机变量所服从的概率分布,由此判断不可列的连续型随机变量的概率。
公式
-
贝叶斯公式:
-
朴素贝叶斯基本公式:
即P(B|A) = P(A|B)P(B) -
概率密度函数:
-
正态分布:
概率分布
- 高斯分布型:
正态曲线呈钟型,两头低,中间高,左右对称因其曲线呈钟形,因此人们又经常称之为钟形曲线 - 多项式型:
用于离散值模型里。比如文本分类问题里面我们提到过,我们不光看词语是否在文本中出现,也得看出现次数。如果总词数为n,出现词数为m的话,有点像掷骰子n次出现m次这个词的场景。 - 伯努利型:
亦称“零一分布”、“两点分布”:一个事情有两种可能的结果,其中结果为1的发生概率为a,结果2发生的概率为1-a
数据集及代码实现
数据集是使用了分层抽样的十折交叉验证的,所以分为了十个名字相似的各类样本相近的数据集桶文件,如下:
该数据集为国会投票的样本:
分析数据集:
democrat为民主党,republican为共和党,即标签,表示类别
此后y和n表示特征,共16个特征,每个特征两个类别
代码实现
定义容器对象
存储从数据集中提取的数据,存储对数据统计分析的结果
数据分为三类:标签类别(democrat/republican),字符型特征(y/n),数值型(即连续型,该数据集没有此类型)
'''
params:
bucketPrefix: 桶文件名的前缀
testBucketNumber:测试数据所在的桶的编号
dataFormat: 数据文件格式列表
'''
total=0 #记录总数据量
classes={
} #存类别出现的次数
counts={
} #每个类别对应的属性值出现的次数
self.format = dataFormat.strip().split('\t') # 切分每一列的数据类型
'''
存储数值型数据信息
'''
totals={
} # 每种类别对应的值的总和
numericValues={
} #存每种类别对应的列的取值
'''
存储计算出的概率
'''
self.prior={
} #先验概率
self.condition={
} #条件概率
循环提取十个桶文件中的数据,分门别类存储
十个桶文件中根据传入的参数指定一个为测试数据集,其他九个都为训练数据集
for i in range(1,11): #循环10次
if i!=testBucketNumber: #判断是否为测试集
filename='%s-%02i'%(bucketPrefix,i)
f=open(filename,'r')
lines = f.readlines()
f.close()
for line in lines:
total=total+1 #统计总数
fields = line.strip().split('\t') #分割文件的每列
vector =[] #存储字符型特征
'''
保存每一条数据的数值型数据
'''
nums=[]
#分门别类存储标签和不同类型的特征
for i in range(len(fields)):
if self.format[i]=='attr':
vector.append(fields[i])
elif self.format[i]=='class':
category=fields[i]
elif self.format[i]=='num':
nums.append(float(fields[i]))
#记录每个列别出现的次数
classes.setdefault(category,0)
counts.setdefault(category,{
})
classes[category]+=1
'''
记录每个数值型数据
'''
totals.setdefault(category,{
})
numericValues.setdefault(category,{
})
#循环处理么每条记录出现的属性值
#循环vector 取出每列值
col=0
for columnValue in vector:
col+=1
counts[category].setdefault(col,{
})
counts[category][col].setdefault(columnValue,0)
counts[category][col][columnValue]+=1
'''
counts形成的结构为:
{
'democrat':
{
1: {'y': 64, 'n': 48},
2: {'n': 64, 'y': 48},
...
16: {'y': 105, 'n': 7}
},
'republican':
{
1: {'y': 22, 'n': 76},
2: {'y': 46, 'n': 52},
...
16: {'n': 30, 'y': 68}
}
}
'''
'''
保存每个数值型数据与类别的对应字典
'''
col=0
for columnValue in nums:
col+=1
totals[category].setdefault(col,0)
totals[category][col]+=columnValue
numericValues[category].setdefault(col,[])
numericValues[category][col].append(columnValue)
计算先验概率,条件概率
多项式类型数据的概率,利用贝叶斯公式(引入拉普拉斯平滑)计算先验概率,再计算条件概率
#开始计算概率
# classes: {'democrat': 112, 'republican': 98}
for (category, count) in classes.items():
self.prior[category] = count/total
#条件概率: 注意:columns是一个字典
for (category, columns) in counts.items():
self.condition.setdefault(category,{
})
for (col,valueCount) in columns.items():
self.condition[category].setdefault(col, {
})
# 此处引入拉普拉斯平滑处理已存在值
for (attrValue, count) in valueCount.items():
# 此处先计算所有在样本中已存在值的条件概率 ( nc+(mp))/(N+m)
m=len(counts[category][col].items())
self.condition[category][col][attrValue] = (count+1)/(classes[category]<