【机器学习】贝叶斯分类(理论及代码实现)

2024-02-05 15:06:47 浏览数 (1)

一、理论部分

这部分涉及的理论知识比较多。深入浅出,公式就不多讲了。

具体可以参考 概率论与数理统计教材、周志华的西瓜书、李航的统计学习方法。

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、选取特征。关于如何选取特征,这里不进行深度讨论。我们只针对例题,我们选取年龄大,工龄长,学历高是否合理。我们知道贝叶斯假设是各个特征独立,那么我们选取特征的时候尽量不要有冗余,特征之间相关性不要太大。

0 人点赞