站点图标 AI技术聚合

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

一、理论部分

这部分涉及较多的理论知识。通俗易懂,公式不多说。

详见《概率论与数理统计教材》、周志华《西瓜书》、李航《统计学习(Statistical learning)法》。

1.1、极大似然估计(Maximum Likelihood Estimation)。

直接例子

例:假设(Hypothesis)袋子里有n个球,n无限大。只有黑球和白球,每次有放回的 从袋子随机拿100次球。80次白球,20次黑球。问:袋子里白球概率为 什么值的时候 概率最大?

解析:假设(Hypothesis)袋子里白球概率为p。我们现在知道的是我们拿100次的结果。相当于用这100次估算n个球里白球的概率。更为经典的是硬币的正反面问题,当我们投足够多的时候,就越接近真实概率值。这个思想对我们来讲非常关键。

解:设袋子里白球概率为p

从题意来看,我们每次拿球都放回去了,抢球的事件是独立同分布(Independent and identically distributed)(Distribution)的。

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。

使用已知数据预测未知数据(先验(Prior)概率分布(probability distribution)(Distribution)和后验概率(posterior probability)分布(Distribution))。

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(是高层|所有数据集(Dataset)) = 3/10  P(不是高层|所有数据集(Dataset))= 7/10

P(年龄大|是高层)= 1/3   P(工龄长|是高层)= 2/3   P(学历高|是高层) = 1/1

P(年龄大|不是高层)= 1/7   P(工龄长|不是高层)= 2/7   P(学历高|不是高层) = 2/7

假设(Hypothesis)他属于顶部:

P1 = P(是高层|所有数据集(Dataset)) * P(年龄大|是高层)* P(工龄长|是高层)* P(学历高|是高层)

= 3/10 * 1/3 * 2/3 * 1/1 = 6/90 = 0.0667

假设(Hypothesis)他不属于顶层:

P0 =  P(是高层|所有数据集(Dataset))*P(年龄大|是高层)* P(工龄长|是高层)*P(学历高|是高层)

= 7/10 * 1/7 * 2/7 * 2/7 = 28/3430 = 0.00816

由 P1 > P0:

我们预测他属于顶级

我们用10组数据(训练集)的分布(Distribution),估计出全公司的人员分布(Distribution)。利用分布(Distribution)各个特征互相独立。计算出概率,哪种概率大 我们预测新的样本属于哪一类。下面用代码来实现下。

二、代码实现

2.1、代码解释

我们先看一下先写的完整代码。

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))    # 预测下我们的训练集

定义一个函数,因为我们是二分类(Binary classification)结果是yes/no,特征也只有yes/no。所以我们定义个求概率的函数。这个比较简单。输入(input)两个值如4,1 返回两个概率值0.8,0.2。

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的概率,并存入相应的列表里。

    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那种概率大。最后返回出整个结果。思路和整个代码都比较简单。

    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 

数据集(Dataset)用于判断是否为高级数据。

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、完整代码

代码可以直接运行

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、如何实现多分类(Multi-class classification)。

这部分只有二分类(Binary classification),要想多分类(Multi-class classification)就不能只是0与1这么简单。模型要兼容多分类(Multi-class classification),可以对结果集利用np.unique(),然后取出值进行分类。特征单个种类分类多,也可以使用这种方法。如果比如身高这些特征符合正态分布(normal distribution)(Distribution),需要用正态分布(normal distribution)(Distribution)概率进行计算。

2、可以发现,如果通过此数据集(Dataset),P(学历不高|是高层)= 0,因为我们是连乘计算概率,所以会出现一种情况是,只要他学历不高,那么他是高层概率为0。按照常理讲这是不合理的。解决方式:增加数据训练集;拉普拉斯平滑(Smoothing)(Laplace smoothing)处理,调整probability 函数计算概率的方式 ,分子加一,分母加特征种数。这部分也比较简单,具体可以参考文章开头说的。

3、如何提升预测准度。

3.1、提高训练集数据的质量。基本所有机器学习(machine learning)对训练集的数据要求都很高,贝叶斯更是如此,如果你选取的数据质量不高,没有代表性,抽选随机性不高。对模型影响很大。

3.2、增加训练集的数量。这个非常好理解,我们投硬币,投的次数越多,正面概率越解决1/2。更接近真实值。

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

文章出处登录后可见!

已经登录?立即刷新
退出移动版