ML朴素贝叶斯分类器

贝叶斯分类器

多摘自西瓜书

1.贝叶斯决策论

贝叶斯决策论 (Bayesian decision theory) 是概率框架下实施决策的基本方法。对于分类任务来说,在所有相关概率都已知的理想情形下,贝叶斯决策论考虑如何基于这些概率和误判损失来选择最优的类别标记。

假设有 N N N 种可能的类别标记,即 y = { c 1 , c 2 , . . . , c N } y = \{ c_1, c_2,...,c_N \} y={c1,c2,...,cN}, λ i j \lambda_{ij} λij 是将一个真实标记为 c j c_j cj 的样本误分类为 c i c_i ci 所产生的损失。基于后验概率 P ( c i ∣ x ) P(c_i|x) P(cix) 可获得将样本 x x x 分类为 c i c_i ci 所产生的期望损失或风险, 即在样本 x x x 上的“条件风险”
R ( c i ∣ x ) = ∑ j = 1 N λ i j P ( c j ∣ x ) R(c_i|x) = \sum_{j=1}^{N} \lambda_{ij} P(c_j|x) R(cix)=j=1NλijP(cjx)
我们的任务是寻找一个判定准则 $h: \chi \mapsto y $ 以最小化总体风险
R ( h ) = E [ R ( h ( x ) ∣ x ) ] R(h) = E[R(h(x) | x)] R(h)=E[R(h(x)x)]
显然,对每个样本 x x x, 若 h h h 能最小化条件风险 R ( h ( x ) ∣ x ) R(h(x) | x) R(h(x)x), 则总体风险 R ( h ) R(h) R(h) 也将被最小化。这就产生了贝叶斯准则 (Bayes decision rule): 为最小化总体风险,只需在每个样本上选择那个能使条件风险 R ( h ( x ) ∣ x ) R(h(x) | x) R(h(x)x) 最小的类别标记,即
h ∗ ( x ) = arg ⁡ min ⁡ c ∈ y R ( c ∣ x ) h^*(x) = \mathop{\arg\min}\limits_{c \in y} R(c|x) h(x)=cyargminR(cx)
此时, h ∗ h^* h 成为贝叶斯最优分类器,与之对应的总体风险 R ( h ∗ ) R(h*) R(h) 称为贝叶斯风险。 1 − R ( h ∗ ) 1-R(h^*) 1R(h) 反映了分类器所能达到的最好性能,即通过机器学习所能产生的模型精度的理论上限。

具体来说,若目标是最小化分类错误率,则误判损失 λ i j \lambda_{ij} λij 可写为:
λ i j = { 0 , i f i = j 1 o t h e r w i s e \lambda_{ij} = \left\{\begin{matrix} 0, & if \quad i=j \\ 1 & otherwise \end{matrix}\right. λij={0,1ifi=jotherwise
此时条件风险为
R ( c ∣ x ) = 1 − P ( c ∣ x ) R(c|x) = 1-P(c|x) R(cx)=1P(cx)

于是,最小化分类错误率的贝叶斯最优分类器为
h ∗ ( x ) = arg ⁡ min ⁡ c ∈ y P ( c ∣ x ) h^*(x) = \mathop{\arg\min}\limits_{c \in y} P(c|x) h(x)=cyargminP(cx)
即对每个样本 x x x, 选择能使后验概率 P ( c ∣ x ) P(c|x) P(cx) 最大的类别标记。

因此,欲使用贝叶斯准则来最小化决策风险,首先要获得后验概率 P ( c ∣ x ) P(c|x) P(cx)
P ( c ∣ x ) = P ( x , c ) P ( x ) P(c|x) = \frac{P(x,c)}{P(x)} P(cx)=P(x)P(x,c)
基于贝叶斯定理, P ( c ∣ x ) P(c|x) P(cx) 可写为
P ( c ∣ x ) = P ( c ) P ( x ∣ c ) P ( x ) P(c|x) = \frac{P(c)P(x|c)}{P(x)} P(cx)=P(x)P(c)P(xc)
其中, P ( c ) P(c) P(c) 是类先验概率; P ( c ∣ x ) P(c|x) P(cx) 是样本 x x x 相对于类标记 c c c 的类条件概率, 或称为似然; 分母 p ( x ) p(x) p(x) 是用于归一化的“证据因子”。对于给定样本 x x x, 证据因子 P ( x ) P(x) P(x) 与类标记无关,因此估计 P ( c ∣ x ) P(c|x) P(cx) 的问题就转化为如何基于训练数据 D D D 来估计先验 P ( c ) P(c) P(c) 和似然 P ( x ∣ c ) P(x|c) P(xc).

2.朴素贝叶斯

基于贝叶斯公式 ( 9 ) (9) (9) 来估计后验概率 P ( c ∣ x ) P(c|x) P(cx) 的主要困难在于:类条件概率 P ( x ∣ c ) P(x|c) P(xc) 是所有属性上的联合概率,难以从有限的训练样本直接估计而得。为了避免这个障碍,朴素贝叶斯分类器采用了“属性条件独立性假设”:

对已知类别,假设所有属性相互独立. 换言之,假设每个属性独立的对分类结果发生影响.

基于属性条件独立性假设,式 ( 9 ) (9) (9) 可重写为
P ( c ∣ x ) = P ( c ) P ( x ∣ c ) P ( x ) = P ( c ) P ( x ) ∏ i = 1 d P ( x i ∣ c ) P(c|x) = \frac{P(c)P(x|c)}{P(x)} = \frac{P(c)}{P(x)} \prod_{i=1}^{d} P(x_i|c) P(cx)=P(x)P(c)P(xc)=P(x)P(c)i=1dP(xic)

其中 d d d 为属性数目, x i x_i xi x x x 在第 i i i 个属性上的取值.

由于对所有类别来说 P ( x ) P(x) P(x) 相同,因此基于式 ( 6 ) (6) (6) 的贝叶斯判定准则有
h n b ( x ) = arg ⁡ min ⁡ c ∈ y P ( c ) ∏ i = 1 d P ( x i ∣ c ) h_{nb}(x) = \mathop{\arg\min}\limits_{c \in y} P(c) \prod_{i=1}^{d} P(x_i|c) hnb(x)=cyargminP(c)i=1dP(xic)
这就是朴素贝叶斯分类器的表达式.

D c D_c Dc 表示训练集 D D D 中出现第 c c c 类样本组成的集合, 若有充足的独立同分布样本,则可容易的估计出类先验概率
P ( c ) = ∣ D c ∣ ∣ D ∣ P(c) = \frac{|D_c|}{|D|} P(c)=DDc
对离散属性而言,令 D c , x i D_{c,x_i} Dc,xi 表示 D c D_c Dc 中在第 i i i 个属性上取值为 x i x_i xi 的样本组成的集合, 则条件概率 P ( x i ∣ c ) P(x_i|c) P(xic) 可估计为
P ( x i ∣ c ) = ∣ D c , x i ∣ ∣ D c ∣ P(x_i|c) = \frac{|D_{c,x_i}|}{|D_c|} P(xic)=DcDc,xi
对连续属性而言,可考虑概率密度函数,假定 $p(x_i|c) \sim N(\mu_{c,i},\sigma_{c,i}^{2}) $, 其中 μ c , i \mu_{c,i} μc,i σ c , i 2 \sigma_{c,i}^2 σc,i2 分别是第 c c c 类样本在第 i i i 个属性上取值的均值和方差,则有
p ( x i ∣ c ) = 1 2 π σ c , i e x p ( − ( x i − μ c , i ) 2 2 σ c , i 2 ) p(x_i|c) = \frac{1}{\sqrt{2\pi} \sigma_{c,i}}exp(-\frac{(x_i-\mu_{c,i})^2}{2\sigma_{c,i}^2}) p(xic)=2π σc,i1exp(2σc,i2(xiμc,i)2)
为了避免其他属性携带的信息被训练集中未出现的属性值“抹去”,在估计概率值时通常要进行“平滑”,常用“拉普拉斯修正” (Laplacian correction). 具体来说,令 N N N 表示训练集 D D D 中可能的类别数, N i N_i Ni 表示第 i i i 个属性可能的取值数, 则式 ( 11 ) (11) (11) 和式 ( 12 ) (12) (12) 可修正为
P ( c ) = ∣ D c ∣ + 1 ∣ D ∣ + N P(c) = \frac{|D_c| + 1}{|D| + N} P(c)=D+NDc+1

P ( x i ∣ c ) = ∣ D c , x i ∣ + 1 ∣ D c ∣ + N i P(x_i|c) = \frac{|D_{c,x_i}| + 1}{|D_c| + N_i} P(xic)=Dc+NiDc,xi+1

显然, 拉普拉斯修正避免了因训练样本不充分而导致概率估值为零的问题,并且在训练集变大时,修正过程所引入的先验的影响也会逐渐变得可忽略,使得估值渐趋向于实际概率值。

举个栗子:

在这里插入图片描述


朴素贝叶斯优缺点:

优点:

  • 算法逻辑简单, 易于实现
  • 分类过程中时空开销小

缺点:

理论上,朴素贝叶斯模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为朴素贝叶斯模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好。

3.朴素贝叶斯代码实现

import numpy as np
import pandas as pd
import copy

class naiveBayesClassifier():
    def __init__(self):
        self.data = None
        self.prior = None
        
    # 构造fit函数
    def fit(self,Xtrain,Ytrain):
        data = pd.concat([pd.DataFrame(Xtrain),pd.DataFrame(Ytrain)],axis=1)
        self.data = data
    
    # 计算先验概率
    def cal_prior(self):
        prior = {}
        num_sample = len(self.data) # 样本个数
        data_count = self.data.iloc[:,-1].value_counts() # 对label统计
        for i in range(len(data_count)):
            prior[data_count.index[i]] = data_count[i] / num_sample # 先验概率 公式11
        self.prior = prior
        return True
    
    # 计算先验概率 拉普拉斯修正
    def cal_prior_laplacian(self):
        prior = {}
        num_sample = len(self.data) # 样本个数
        data_count = self.data.iloc[:,-1].value_counts() # 对label统计
        for i in range(len(data_count)):
            prior[data_count.index[i]] = (data_count.iloc[i] + 1) / (num_sample + len(data_count)) # 先验概率 公式14
        self.prior = prior
        #print(prior)
        return True
    
    
    # 对数据按照label进行分组
    def group_data(self):
        data_group_label = {}
        labels = pd.unique(self.data.iloc[:,-1]) # 获取label
        data_group = self.data.groupby(self.data.columns[-1]) # 将数据集按label分组
        for i in labels:
            data_group_label[i] = pd.DataFrame(data_group.get_group(i))
        return data_group_label # 返回分组
    
    # 期望构造一个函数判断哪些属性是离散属性,哪些属性是连续属性
    def judge_feture(self):
        feature = {}
        for i in self.data.columns[:-1]:
            if len(pd.unique(self.data.loc[:,i])) < len(self.data)* 0.5: # 如果某个属性定义域 < 样本数 则认为是离散变量 标记1
                feature[i] = 1
            elif len(pd.unique(self.data.loc[:,i])) >= len(self.data) * 0.5: # 如果某个属性定义域 = 样本数 则认为是连续变量 标记0
                feature[i] = 0
        return feature
    
    # 若属性为离散,计算当前属性的条件概率
    def cal_prob(self,group_data,feature,column):
        prob_label = {}
        for label in group_data.keys():
            prob_label[label] = (np.sum(group_data[label].loc[:,column] == feature)) / len(group_data[label]) # 公式12
        return prob_label
    
    # 若属性为离散,计算当前属性的条件概率 拉普拉斯修正
    def cal_prob_laplacian(self,group_data,feature,column):
        prob_label = {}
        for label in group_data.keys():
            num_features = len(np.unique(X.loc[:,column]))
            prob_label[label] = (np.sum(group_data[label].loc[:,column] == feature) + 1) / (len(group_data[label]) + num_features) # 公式15
        return prob_label
    
    
    # 若属性为连续属性,计算概率密度
    def cal_density(self,group_data,feature,column):
        density = {}
        for label in group_data.keys():
            mu = np.mean(group_data[label].loc[:,column]) # 均值
            sigma = np.std(group_data[label].loc[:,column],ddof=1)
            density[label] = (1 / (np.sqrt(2*np.pi) * sigma)) * np.exp(-np.power((feature-mu),2) / (2 * (sigma ** 2))) # 公式13
        return density
    
    # 构造预测函数预测单个样本
    def predict_single(self,xtest_single):
        xtest_single = pd.DataFrame(xtest_single).T
        feature_judge = self.judge_feture() # 判断连续变量/ 离散变量
        group_data_label = self.group_data() # 测试集按标签分组

        # 循环计算条件概率
        cond_prob = {}
        for i in xtest_single.columns:
            if feature_judge[i]: # 如果为离散属性
                cond_prob[i] = self.cal_prob_laplacian(group_data_label,xtest_single.loc[0,i],i)
            elif not feature_judge[i]: # 如果为连续属性
                cond_prob[i] = self.cal_density(group_data_label,xtest_single.loc[0,i],i)
        result = copy.deepcopy(self.prior)
        for i in self.prior.keys():
            for j in cond_prob.keys():
                result[i] *= cond_prob[j][i] # 计算各个概率
        #pred_lable = max(result, key = result.get)
        return result
    
    # 定义预测函数
    def predict(self,test):
        xtest = pd.DataFrame(test)
        self.cal_prior_laplacian() # 计算先验概率
        pred = []
        xtest.reset_index(inplace=True,drop=True)
        for i in xtest.index:
            result = self.predict_single(xtest.iloc[i,:])
            pred.append(max(result, key = result.get)) # 概率最大的label作为预测标签
        return pred
    
    # 概率预测函数
    def predict_prob(self,test):
        xtest = pd.DataFrame(test)
        self.cal_prior_laplacian() # 计算先验概率
        pred_prob = []
        pred_prob.append([i for i in self.prior.keys()])
        xtest.reset_index(inplace=True,drop=True)
        for i in xtest.index:
            result = self.predict_single(xtest.iloc[i,:])
            pred_prob.append([result[i] for i in self.prior.keys()]) # 保存概率
        return pred_prob


if __name__ == '__main__':
    # 西瓜数据集3.0 p.84 表 4.3
    dataset = {
        '色泽': ['青绿', '乌黑', '乌黑', '青绿', '浅白', '青绿', '乌黑', '乌黑', '乌黑', '青绿', '浅白', '浅白', '青绿', '浅白', '乌黑', '浅白', '青绿'],
        '根蒂': ['蜷缩', '蜷缩', '蜷缩', '蜷缩', '蜷缩', '稍蜷', '稍蜷', '稍蜷', '稍蜷', '硬挺', '硬挺', '蜷缩', '稍蜷', '稍蜷', '稍蜷', '蜷缩', '蜷缩'],
        '敲声': ['浊响', '沉闷', '浊响', '沉闷', '浊响', '浊响', '浊响', '浊响', '沉闷', '清脆', '清脆', '浊响', '浊响', '沉闷', '浊响', '浊响', '沉闷'],
        '纹理': ['清晰', '清晰', '清晰', '清晰', '清晰', '清晰', '稍糊', '清晰', '稍糊', '清晰', '模糊', '模糊', '稍糊', '稍糊', '清晰', '模糊', '稍糊'],
        '脐部': ['凹陷', '凹陷', '凹陷', '凹陷', '凹陷', '稍凹', '稍凹', '稍凹', '稍凹', '平坦', '平坦', '平坦', '凹陷', '凹陷', '稍凹', '平坦', '稍凹'],
        '触感': ['硬滑', '硬滑', '硬滑', '硬滑', '硬滑', '软粘', '软粘', '硬滑', '硬滑', '软粘', '硬滑', '软粘', '硬滑', '硬滑', '软粘', '硬滑', '硬滑'],
        '密度': [0.697, 0.774, 0.634, 0.608, 0.556, 0.403, 0.481, 0.437, 0.666, 0.243, 0.245, 0.343, 0.639, 0.657, 0.360,
               0.593, 0.719],
        '含糖率': [0.460, 0.367, 0.264, 0.318, 0.215, 0.237, 0.149, 0.211, 0.091, 0.267, 0.057, 0.099, 0.161, 0.198, 0.370,
                0.042, 0.103],
        '好瓜': ['是', '是', '是', '是', '是', '是', '是', '是', '否', '否', '否', '否', '否', '否', '否', '否', '否']
    }
	
    # 测试集
    test = {
        '色泽': ['青绿'],
        '根蒂': ['蜷缩'],
        '敲声': ['浊响'],
        '纹理': ['清晰'],
        '脐部': ['凹陷'],
        '触感': ['硬滑'],
        '密度': [0.697],
        '含糖率': [0.460]
    }

    data = pd.DataFrame(dataset)
    X = data.iloc[:, :-1]
    Y = data.iloc[:, -1]
    xtest = pd.DataFrame(test)

    clf = naiveBayesClassifier()
    clf.fit(X, Y)
    print(clf.predict(xtest))
    print(clf.predict_prob(xtest))

4.sklearn库实现

简单实现一下,详细可看:
https://blog.csdn.net/qq_38163244/article/details/109154089

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import BernoulliNB
from sklearn.metrics import accuracy_score

# 导入鸢尾花数据集
iris = load_iris()
x = iris.data[:, :-1]
y = iris.target

# 划分数据集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=666)

# 初始化模型
clf = BernoulliNB(alpha=50,binarize=5) # alpha 无改变,binarize 26%->93%
clf.fit(x_train,y_train)
y_pred = clf.predict(x_test)

accuracy_score(y_test,y_pred)

5.参数

详见:
https://blog.csdn.net/qq_38163244/article/details/109154089


仅作学习笔记使用,侵删

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,那我们来实现一个朴素贝叶斯分类器吧。 首先,我们需要导入需要的库,包括numpy、pandas、sklearn中的train_test_split和MultinomialNB。代码如下: ```python import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB ``` 接下来,我们要读取数据集。这里我们使用UCI机器学习库中的Spambase数据集,该数据集包含了4601封电子邮件,其中1813封是垃圾邮件,2788封是正常邮件。数据集中有57个特征,包括每封邮件中出现的单词的频率、字符的频率等。我们可以使用pandas库读取该数据集,代码如下: ```python data = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/spambase/spambase.data', header=None) ``` 然后,我们将数据集分成训练集和测试集。训练集用来训练模型,测试集用来评估模型的性能。我们可以使用sklearn中的train_test_split函数来实现数据集的分割,代码如下: ```python X_train, X_test, y_train, y_test = train_test_split(data.iloc[:,:-1], data.iloc[:,-1], test_size=0.3, random_state=42) ``` 这里我们将数据集按照7:3的比例分成训练集和测试集。 接下来,我们可以使用MultinomialNB来实现朴素贝叶斯分类器。MultinomialNB适用于多项式分布的数据,这里我们将每个特征的频率作为输入。代码如下: ```python clf = MultinomialNB() clf.fit(X_train, y_train) ``` 在这里,我们使用fit函数对训练集进行拟合,从而得到一个朴素贝叶斯分类器。 最后,我们可以使用测试集来评估模型的性能。代码如下: ```python y_pred = clf.predict(X_test) accuracy = np.mean(y_pred == y_test) print('Accuracy:', accuracy) ``` 这里我们使用predict函数对测试集进行预测,然后计算模型的准确率。 完整代码如下: ```python import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB # 读取数据集 data = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/spambase/spambase.data', header=None) # 分割数据集为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(data.iloc[:,:-1], data.iloc[:,-1], test_size=0.3, random_state=42) # 训练朴素贝叶斯分类器 clf = MultinomialNB() clf.fit(X_train, y_train) # 评估模型性能 y_pred = clf.predict(X_test) accuracy = np.mean(y_pred == y_test) print('Accuracy:', accuracy) ``` 运行该代码,可以得到以下输出: ``` Accuracy: 0.8205099279858907 ``` 这意味着我们的朴素贝叶斯分类器在该测试集上的准确率约为82%。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值