一、理论部分
这部分涉及的理论知识比较多。深入浅出,公式就不多讲了。
具体可以参考 概率论与数理统计教材、周志华的西瓜书、李航的统计学习方法。
1.1、极大似然估计。
直接来例子
例:假设袋子里有n个球,n无限大。只有黑球和白球,每次有放回的 从袋子随机拿100次球。80次白球,20次黑球。问:袋子里白球概率为 什么值的时候 概率最大?
解析:假设袋子里白球概率为p。我们现在知道的是我们拿100次的结果。相当于用这100次估算n个球里白球的概率。更为经典的是硬币的正反面问题,当我们投足够多的时候,就越接近真实概率值。这个思想对我们来讲非常关键。
解:设袋子里白球概率为p
由题意知,我们每次取都是有放回的,每次抓取球这个事件属于独立且同分布则
P(80白20黑) = P(白)**80 P(黑)**20 = p**80 (1-p)**20
求p值使得P(80白20黑) 最大。求导,导数取0就可以计算出来。指数不方便求导,就先转log再求导取0也是一样的(x,log(x)同增同减)。计算也比较简单。问题转化成对ln(p)求导取0得:
80/p (-20/(1-p)) = 0
求得 p=0.8。
用已知的数据去预测未知的数据(先验概率分布与后验概率分布)。
1.2、贝叶斯分类。
让我继续想个例子。。。.。。。
举个简单的栗子:判断员工是否是公司高层?
随机抽取10人。现有特征(a1,a2,a3,b) 。a1:年龄是否大, a2:工龄是否长,a3:学历是否高。b:是否高层(0代表否,1代表是)
(0,0,0,0)
(0,0,0,0)
(0,0,0,0)
(0,0,1,0)
(0,1,1,0)
(1,0,0,0)
(0,1,0,0)
(0,0,1,1)
(0,1,1,1)
(1,1,1,1)
【数据自己想的,实际数据要根据实际获取】
现在随机抽一个人,他年龄大,工龄长,学历高。预测他是否属于高层?
解:计算概率
P(是高层|所有数据集) = 3/10 P(不是高层|所有数据集)= 7/10
P(年龄大|是高层)= 1/3 P(工龄长|是高层)= 2/3 P(学历高|是高层) = 1/1
P(年龄大|不是高层)= 1/7 P(工龄长|不是高层)= 2/7 P(学历高|不是高层) = 2/7
假设他是属于高层:
P1 = P(是高层|所有数据集) * P(年龄大|是高层)* P(工龄长|是高层)* P(学历高|是高层)
= 3/10 * 1/3 * 2/3 * 1/1 = 6/90 = 0.0667
假设他不属于高层:
P0 = P(是高层|所有数据集)*P(年龄大|是高层)* P(工龄长|是高层)*P(学历高|是高层)
= 7/10 * 1/7 * 2/7 * 2/7 = 28/3430 = 0.00816
由 P1 > P0:
我们预测他属于高层
我们用10组数据(训练集)的分布,估计出全公司的人员分布。利用分布各个特征互相独立。计算出概率,哪种概率大 我们预测新的样本属于哪一类。下面用代码来实现下。
二、代码实现
2.1、代码解释
先来看下写的完整代码。
代码语言:javascript复制import numpy as np
class BayesModel():
def __init__(self):
super(BayesModel,self).__init__()
self.ng_result = [] # 存放ng的概率
self.ok_result = [] # 存放ok的概率
def probability(self,a,b):
p_a = a/(a b)
p_b = 1-p_a
return p_a,p_b
def fit(self,x,y):
columns = x.shape[1] # 为了获取特征的数量,我们上述例子是3个特征 (年龄,工龄,学历)
data = np.c_[x,y] # 连接到同一个numpy,方便操作
ng_data = data[(data[:,-1] == 0)] # 把结果ng的数据取出来
ok_data = data[(data[:,-1] == 1)] # 把结果ok的数据取出来
ng_len,ok_len = ng_data.shape[0],ok_data.shape[0]
self.ng,self.ok = self.probability(ng_len,ok_len)
for i in range(columns):
ng_len_ng = ng_data[(ng_data[:,i] == 0)].shape[0]
ng_len_ok = ng_len - ng_len_ng
ok_len_ng = ok_data[(ok_data[:,i] == 0)].shape[0]
ok_len_ok = ok_len - ok_len_ng
self.ng_result.append(self.probability(ng_len_ng,ng_len_ok))
self.ok_result.append(self.probability(ok_len_ng,ok_len_ok))
def predict(self,data_data):
list_result = [] # 存放预测的结果
for datas in data_data:
ng,ok = self.ng,self.ok
for index,data in enumerate(datas):
if data == 0:
ng = ng*self.ng_result[index][0]
ok = ok*self.ok_result[index][0]
else:
ng = ng*self.ng_result[index][1]
ok = ok*self.ok_result[index][1]
if ng>ok:
list_result.append(0)
else:
list_result.append(1)
return list_result
if __name__ == '__main__':
x1 = np.array([0,0,0,0,0,1,0,0,0,1]).reshape(-1,1)
x2 = np.array([0,0,0,0,1,0,1,0,1,1]).reshape(-1,1)
x3 = np.array([0,0,0,1,1,0,0,1,1,1]).reshape(-1,1)
x = np.c_[x1,x2,x3]
y = np.array([0,0,0,0,0,0,0,1,1,1]).reshape(-1,1)
print(x)
print(y)
bayes = BayesModel()
bayes.fit(x,y)
predict = bayes.predict([[1,1,1],[0,1,1],[1,1,0]])
print(bayes.predict(x)) # 预测下我们的训练集
定义一个函数,因为我们是二分类结果是yes/no,特征也只有yes/no。所以我们定义个求概率的函数。这个比较简单。输入两个值如4,1 返回两个概率值0.8,0.2。
代码语言:javascript复制def probability(self,a,b):
p_a = a/(a b)
p_b = 1-p_a
return p_a,p_b
构建模型,把概率数据保存到对应的列表里。
1、先算出结果为ng与ok的数据概率。并把这两部分数据提取出来,ng_data,ok_data
2、对ng_data,ok_data计算出各个特征的ng/ok的概率,并存入相应的列表里。
代码语言:javascript复制 def fit(self,x,y):
columns = x.shape[1] # 为了获取特征的数量,我们上述例子是3个特征 (年龄,工龄,学历)
data = np.c_[x,y] # 连接到同一个numpy,方便操作
ng_data = data[(data[:,-1] == 0)] # 把结果ng的数据取出来
ok_data = data[(data[:,-1] == 1)] # 把结果ok的数据取出来
ng_len,ok_len = ng_data.shape[0],ok_data.shape[0]
self.ng,self.ok = self.probability(ng_len,ok_len)
for i in range(columns):
ng_len_ng = ng_data[(ng_data[:,i] == 0)].shape[0]
ng_len_ok = ng_len - ng_len_ng
ok_len_ng = ok_data[(ok_data[:,i] == 0)].shape[0]
ok_len_ok = ok_len - ok_len_ng
self.ng_result.append(self.probability(ng_len_ng,ng_len_ok))
self.ok_result.append(self.probability(ok_len_ng,ok_len_ok))
这段是预测,由函数fit已经构建完模型,数据存到列表。现在想预测新的样本集。
先看传入进来的data_data是几组数据,然后每组数据进行枚举,概率相乘,最后判断ng与ok那种概率大。最后返回出整个结果。思路和整个代码都比较简单。
代码语言:javascript复制 def predict(self,data_data):
list_result = [] # 存放预测的结果
for datas in data_data:
ng,ok = self.ng,self.ok
for index,data in enumerate(datas):
if data == 0:
ng = ng*self.ng_result[index][0]
ok = ok*self.ok_result[index][0]
else:
ng = ng*self.ng_result[index][1]
ok = ok*self.ok_result[index][1]
if ng>ok:
list_result.append(0)
else:
list_result.append(1)
return list_result
数据集用的是上述判断是否为高层的数据。
代码语言:javascript复制if __name__ == '__main__':
x1 = np.array([0,0,0,0,0,1,0,0,0,1]).reshape(-1,1)
x2 = np.array([0,0,0,0,1,0,1,0,1,1]).reshape(-1,1)
x3 = np.array([0,0,0,1,1,0,0,1,1,1]).reshape(-1,1)
x = np.c_[x1,x2,x3]
y = np.array([0,0,0,0,0,0,0,1,1,1]).reshape(-1,1)
print(x)
print(y)
bayes = BayesModel()
bayes.fit(x,y)
predict = bayes.predict([[1,1,1],[0,1,1],[1,1,0]])
2.2、完整代码
代码可以直接运行的
代码语言:javascript复制import numpy as np
class BayesModel():
def __init__(self):
super(BayesModel,self).__init__()
self.ng_result = [] # 存放ng的概率
self.ok_result = [] # 存放ok的概率
def probability(self,a,b):
p_a = a/(a b)
p_b = 1-p_a
return p_a,p_b
def fit(self,x,y):
columns = x.shape[1] # 为了获取特征的数量,我们上述例子是3个特征 (年龄,工龄,学历)
data = np.c_[x,y] # 连接到同一个numpy,方便操作
ng_data = data[(data[:,-1] == 0)] # 把结果ng的数据取出来
ok_data = data[(data[:,-1] == 1)] # 把结果ok的数据取出来
ng_len,ok_len = ng_data.shape[0],ok_data.shape[0]
self.ng,self.ok = self.probability(ng_len,ok_len)
for i in range(columns):
ng_len_ng = ng_data[(ng_data[:,i] == 0)].shape[0]
ng_len_ok = ng_len - ng_len_ng
ok_len_ng = ok_data[(ok_data[:,i] == 0)].shape[0]
ok_len_ok = ok_len - ok_len_ng
self.ng_result.append(self.probability(ng_len_ng,ng_len_ok))
self.ok_result.append(self.probability(ok_len_ng,ok_len_ok))
def predict(self,data_data):
list_result = [] # 存放预测的结果
for datas in data_data:
ng,ok = self.ng,self.ok
for index,data in enumerate(datas):
if data == 0:
ng = ng*self.ng_result[index][0]
ok = ok*self.ok_result[index][0]
else:
ng = ng*self.ng_result[index][1]
ok = ok*self.ok_result[index][1]
if ng>ok:
list_result.append(0)
else:
list_result.append(1)
return list_result
if __name__ == '__main__':
x1 = np.array([0,0,0,0,0,1,0,0,0,1]).reshape(-1,1)
x2 = np.array([0,0,0,0,1,0,1,0,1,1]).reshape(-1,1)
x3 = np.array([0,0,0,1,1,0,0,1,1,1]).reshape(-1,1)
x = np.c_[x1,x2,x3]
y = np.array([0,0,0,0,0,0,0,1,1,1]).reshape(-1,1)
print(x)
print(y)
bayes = BayesModel()
bayes.fit(x,y)
predict = bayes.predict([[1,1,1],[0,1,1],[1,1,0]])
print(bayes.predict(x)) # 预测下我们的训练集看下结果
2.3、改进思路
1、如何实现多分类。
这部分只有二分类,要想多分类就不能只是0与1这么简单。模型要兼容多分类,可以对结果集利用np.unique(),然后取出值进行分类。特征单个种类分类多,也可以使用这种方法。如果比如身高这些特征符合正态分布,需要用正态分布概率进行计算。
2、可以发现,如果通过此数据集,P(学历不高|是高层)= 0,因为我们是连乘计算概率,所以会出现一种情况是,只要他学历不高,那么他是高层概率为0。按照常理讲这是不合理的。解决方式:增加数据训练集;拉普拉斯平滑处理,调整probability 函数计算概率的方式 ,分子加一,分母加特征种数。这部分也比较简单,具体可以参考文章开头说的。
3、如何提升预测准度。
3.1、提高训练集数据的质量。基本所有机器学习对训练集的数据要求都很高,贝叶斯更是如此,如果你选取的数据质量不高,没有代表性,抽选随机性不高。对模型影响很大。
3.2、增加训练集的数量。这个非常好理解,我们投硬币,投的次数越多,正面概率越解决1/2。更接近真实值。
3.3、选取特征。关于如何选取特征,这里不进行深度讨论。我们只针对例题,我们选取年龄大,工龄长,学历高是否合理。我们知道贝叶斯假设是各个特征独立,那么我们选取特征的时候尽量不要有冗余,特征之间相关性不要太大。