《统计学习方法》中,李航老师简洁地介绍了朴素贝叶斯基础的原理和算法
虽然通篇下来也是满满的公式,但基本都是上层的公式,省略了许多底层的推导
例如:极大似然估计法推出朴素贝叶斯法中的先验概率估计公式?
总的来说对新手十分友好,建议入门之选。
“朴素”一词,到底是何意思?
在贝叶斯公式的基础上,朴素贝叶斯方法做了一个强假设,对于一个结果的发生(y)
导致其发生的因素为x ( x = (x1, x2, …, xn)), 我们认为其中x1,…,xn是n个互相独立的小事件
也就是每一个都对y的发生有影响,但他们之间却相互无影响。
对于大多数初学者,基本都会对贝叶斯产生理解上的困惑
如果你觉得又臭又长不想看,我简单通俗说明一下他们之间思想的不同之处:
对于以下数据的简单代码实现,但不仅限于此数据,扩展以下数据,使其变成多分类,增大数据量,依然可以用本代码来进行预测。
class Bayes:
def __init__(self):
self.t_data = np.array([[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3],
['S', 'M', 'M', 'S', 'S', 'S', 'M', 'M', 'L', 'L', 'L', 'M', 'M', 'L', 'L'],
[-1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1]])
self.p_y = {}
self.p_x1_y = {}
self.p_x2_y = {}
def train_1(self):
count_y = Counter(self.t_data[2])
ys = {}
for y in count_y.keys():
ys[y] = []
self.p_y[y] = count_y[y] / len(self.t_data[0]) # 计算先验概率并对应y值存入字典
for i in range(len(self.t_data[0])): # 遍历数据,根据其y存入对应列表
ys[self.t_data[2][i]].append(self.t_data[:, i])
print('数据切分完成,计算其余先验概率...')
for item in ys.items():
self.train_2(item)
print('学习完毕!可以开始预测')
def train_2(self, _y):
data = np.array(_y[1]) # 先把数据转化为矩阵,便于接下来切片统计运算
count_x1 = Counter(data[:, 0])
count_x2 = Counter(data[:, 1])
for x1 in count_x1.keys(): # 计算相应的概率,存入字典
self.p_x1_y['{}_{}'.format(x1, _y[0])] = count_x1[x1] / len(data)
for x2 in count_x2.keys():
self.p_x2_y['{}_{}'.format(x2, _y[0])] = count_x2[x2] / len(data)
def analyse_input(self): # 计算后验概率并比较
in_data = input('输入x1, x2(空格隔开):').split(' ')
p_p = 0
result = []
for j in self.p_y.keys():
pp = self.p_y[j] * self.p_x1_y['{}_{}'.format(in_data[0], j)] *\
self.p_x2_y['{}_{}'.format(in_data[1], j)]
if pp >= p_p:
if pp > p_p: # 若有出现更大的概率,需要把先前已有的所有结果全部替换
if not result: # 开始的时候列表是空的,如果只写循环替换,其实那个循环根本不会开始。
result.append(j) # 所以必须额外加这一个判断(其实我觉得我这里搞麻烦了。。但是也没想到什么好的解决办法)
else:
for r in range(len(result)):
result[r] = j
elif p_p == pp:
result.append(j)
p_p = pp
result = list(set(result))
if len(result) == 1:
print('预测结果为:{}'.format(result[0]))
else:
print('可能结果为以下几种:', end='')
for e in result:
print(e)
import numpy as np
from collections import Counter
class Bayes:
def __init__(self):
self.t_data = np.array([[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3],
['S', 'M', 'M', 'S', 'S', 'S', 'M', 'M', 'L', 'L', 'L', 'M', 'M', 'L', 'L'],
[-1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1]])
self.p_y = {}
self.p_x1_y = {} # 使用字典方便计算时调用
self.p_x2_y = {}
def train_1(self):
count_y = Counter(self.t_data[2]) # 统计y的种类及数量,用于后续计算
ys = {} # 使该二维分类器不局限于两种类(y)(不失一般性),接下来先统计y的种类,并计算概率,再切分训练数据
for y in count_y.keys():
ys[y] = []
self.p_y[y] = count_y[y] / len(self.t_data[0]) # 计算先验概率并对应y值存入字典
for i in range(len(self.t_data[0])):
ys[self.t_data[2][i]].append(self.t_data[:, i]) # 将数据切分后分别存入字典中的列表,key是对应的y值
print('数据处理完成,开始学习...')
for item in ys.items():
self.train_2(item)
print('学习完毕!可以开始预测')
def train_2(self, _y):
data = np.array(_y[1]) # 先把数据转化为矩阵,便于接下来切片统计运算
count_x1 = Counter(data[:, 0])
count_x2 = Counter(data[:, 1])
for x1 in count_x1.keys(): # 计算相应的概率,存入字典
self.p_x1_y['{}_{}'.format(x1, _y[0])] = count_x1[x1] / len(data)
for x2 in count_x2.keys():
self.p_x2_y['{}_{}'.format(x2, _y[0])] = count_x2[x2] / len(data)
def analyse_input(self): # 计算后验概率并比较
in_data = input('输入x1, x2(空格隔开):').split(' ')
p_p = 0
result = []
for j in self.p_y.keys():
pp = self.p_y[j] * self.p_x1_y['{}_{}'.format(in_data[0], j)] *\
self.p_x2_y['{}_{}'.format(in_data[1], j)]
if pp >= p_p: # 观察到,对于相同的输入,可能出现两种不同预测结果(对于本次数据来说,只有两种结果),要对此做处理
if pp > p_p: # 若有出现更大的概率,需要把先前已有的所有结果全部替换
if not result: # 开始的时候列表是空的,如果直写循环替换,其实那个循环根本不会开始。如果在循环后添加,那将会导致接下来有的结果会重复进入列表(被替换的和被添加的)
result.append(j) # 所以必须额外加这一个判断(其实我觉得我这里搞麻烦了。。但是也没想到什么好的解决办法)
else:
for r in range(len(result)):
result[r] = j
elif p_p == pp:
result.append(j)
p_p = pp
result = list(set(result))
if len(result) == 1:
print('预测结果为:{}'.format(result[0]))
else:
print('可能结果为以下几种:', end='')
for e in result:
print(e)
b = Bayes()
b.train_1()
b.analyse_input()