朴素贝叶斯–分类器
原文地址:http://banzhiyan.net/index.php/2015/07/23/naive_bayesian_classification/
- 有一个待分类的样本 x={a1,a2,a3,⋯,am} ,每一个a都是这个样本的一个属性值。
- 类别的集合为 C={y1,y2,⋯,yn}
- 分别计算
x
属于
y1,y2⋯yn 的概率: P(y1|x),P(y2|x),⋯,P(yn|x) - 得到第三步中概率最大的分类作为 x 的分类
关键是求第三步中的每一个
根据贝叶斯公式
P(yi|x)=P(yi)P(x|yi)P(x)
因为分母对于所有的类别
yi
都是一样的,所以我们只需要求分子部分最大的值就可以了,即
P(yi)P(x∣yi)
其中
P(yi)
是可以直接统计训练样本中每一个类别出现的频率来近似每一个类别的概率就可以了
分子的另外一部分
P(x∣yi)
无法直接求的,因为待分类的
x
并不一定会出现在训练样本中,这样对于一个未出现在训练样本中的新样本,它们的属于每一个分类的概率都是0,也就失去了分类的意义。
所以这里需要做一个假设,在每一个
P(aj|yi)
是可以直接根据训练样本计算出来的,只需要统计一下每一个类别下每一个特征出现的频率
对于样本的每个特征
aj
要根据不同的取值类型进行不同的处理。
- 若 aj 取值是标称型变量,如性别(男、女、xxx),天气(晴、阴、雨等),婚姻状况(已婚、未婚)等,或者离散型变量,如筛子的点数(1,2,3,4,5,6),花瓣个数(2,3,4)等可以直接进行计算,筛子点数虽然取值是数字,但是只是表示类别不一样,完全可以换成a,b,c,d,e,f来代表,这些变量的取值天然就是有类别的
- 若 aj 取值是数值型变量,如身高、年龄、工资等,这些数据取值是连续的,在计算频率之前需要人为地先对数据进行划分类别。因为根据连续型分布概率,每一个具体数值出现的概率都是0,这并没有什么意义。例如,我们可以将身高分为 [,160](160,180](180,] 三个等级,分别代表矮、中、高三个类别。
另外,年龄表面上看是离散型数据,也具有标称型数据的特点,但是实际上年龄是连续型变量,我们只是在使用过程中默认将25.543转换为25或者26岁,已经对年龄进行了一次分类。但是,有时这种分类并不是很完美,如34岁和35岁,虽然在数值上完全不一样,在计算的时候也是属于不同的类别,但是在整个生命的长河中,这两个年龄的差别是很小的。如果直接进行计算会产生过度拟合的问题。所以还是需要再进行一次分类,如老、中、轻三类。
上面是否需要进行分类的规则并不准确,如花瓣个数,对于百合、兰花这种花瓣个数两只手就能数出来的并不需要再进行一次分类,但是对于菊花这种,还是需要对花瓣个数再进行一次分类。
总之,每个属性数值怎样处理的原则是:
a.保证每一个特征的取值个数有限
b.每一个值之间的差异尽可能大
# encoding=utf-8
import numpy as np
class Bayes:
def __init__(self):
self.data_size = 0 # 训练样本长度
self.labels = None # 标注值,即所有分类
self.p_a_y_s = None # 每个属性值在每个分类下出现的频率值
self.features_values = None # 所有属性和唯一的属性值
self.train_data = None # 训练样本
@staticmethod
def distinct_value(train_data, col_num): # 计算每一个属性或者标注的所有唯一值
values = set()
for data in train_data:
values.add(data[col_num])
return list(values)
def model(self, train_data): # 建立贝叶斯模型
shape = np.shape(train_data)
self.data_size = shape[0] # 训练样本个数
cols_num = shape[1] # 每个样本属性和标注的个数
self.labels = self.distinct_value(train_data, cols_num - 1)
# p_a_y_s数据格式:[[[p(a1|y1),p(a1|y2)], [p(a2|y1),p(a2|y2)]], [[p(b1|y1),p(b1|y2)], [p(b2|y1),p(b2|y2)]]]
self.p_a_y_s = []
self.features_values = []
for i in range(cols_num - 1):
feature_values = self.distinct_value(train_data, i)
self.features_values.append(feature_values)
pa = []
for _ in feature_values:
pa.append([0 for _ in self.labels]) # 将每个属性值在每个标注下的概率初始化为0
self.p_a_y_s.append(pa)
self.train_data = train_data
def cal_prob(self, m, j, i): # 计算第m个属性的第j个属性值在第i个分类中的频率
n_y_i = n_a_j = 0
for data in self.train_data:
if data[-1] == self.labels[i]:
n_y_i += 1
if data[m] == self.features_values[m][j]:
n_a_j += 1
return n_a_j * 1.0 / n_y_i
def train(self):
for m in range(len(self.p_a_y_s)):
p_a_y = self.p_a_y_s[m]
for j in range(len(p_a_y)):
p_ai_y = p_a_y[j]
for i in range(len(p_ai_y)):
p_ai_y[i] = self.cal_prob(m, j, i)
def test(self, test_data):
correct_num = 0
for data in test_data:
p = [1 for _ in self.labels]
for m in range(len(data) - 1):
feature_value = data[m]
j = self.features_values[m].index(feature_value)
for i in range(len(self.labels)):
p[i] *= self.p_a_y_s[m][j][i] # 累计第m个属性的第j个属性值在第i个分类下的概率
c = self.labels[p.index(max(p))] # 取概率最大的分类作为预测的分类
print data, c
if c == data[-1]: # 如果预测的分类与测试标注的分类一致
correct_num += 1
print "correct rate : %.2f%%" % (correct_num * 100.0 / len(test_data))
ds = [[1, 2, 3, 's', 'd', 'a'], [2, 2, 4, 'n', 'e', 'e'], [2, 1, 4, 'd', 'e', 'c'], [8, 1, 4, 'd', 'e', 'b'], [2, 1, 4, 'd', 'e', 'c']]
b = Bayes()
b.model(ds)
b.train()
b.test([[1, 2, 3, 's', 'd', 'a'], [2, 2, 4, 'n', 'e', 'e'], [2, 1, 4, 'd', 'e', 'c']])