ML朴素贝叶斯分类器

贝叶斯分类器

多摘自西瓜书

1.贝叶斯决策论

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

假设有 N N N 种可能的类别标记,即 y = { c 1 , c 2 , . . . , c N } y = { c_1, c_2,…,c_N }y ={c 1 ​,c 2 ​,…,c N ​}, λ i j \lambda_{ij}λi j ​ 是将一个真实标记为 c j c_j c j ​ 的样本误分类为 c i c_i c i ​ 所产生的损失。基于后验概率 P ( c i ∣ x ) P(c_i|x)P (c i ​∣x ) 可获得将样本 x x x 分类为 c i c_i c i ​ 所产生的期望损失或风险, 即在样本 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 (c i ​∣x )=j =1 ∑N ​λi j ​P (c j ​∣x )
我们的任务是寻找一个判定准则 $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 )=c ∈y ar g min ​R (c ∣x )
此时, h ∗ h^
h ∗ 成为贝叶斯最优分类器,与之对应的总体风险 R ( h ∗ ) R(h)R (h ∗) 称为贝叶斯风险。1 − R ( h ∗ ) 1-R(h^)1 −R (h ∗) 反映了分类器所能达到的最好性能,即通过机器学习所能产生的模型精度的理论上限。

具体来说,若目标是最小化分类错误率,则误判损失 λ i j \lambda_{ij}λi j ​ 可写为:
λ 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.λi j ​={0 ,1 ​i f i =j o t h e r w i s e ​
此时条件风险为
R ( c ∣ x ) = 1 − P ( c ∣ x ) R(c|x) = 1-P(c|x)R (c ∣x )=1 −P (c ∣x )

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

因此,欲使用贝叶斯准则来最小化决策风险,首先要获得后验概率 P ( c ∣ x ) P(c|x)P (c ∣x )
P ( c ∣ x ) = P ( x , c ) P ( x ) P(c|x) = \frac{P(x,c)}{P(x)}P (c ∣x )=P (x )P (x ,c )​
基于贝叶斯定理, P ( c ∣ x ) P(c|x)P (c ∣x ) 可写为
P ( c ∣ x ) = P ( c ) P ( x ∣ c ) P ( x ) P(c|x) = \frac{P(c)P(x|c)}{P(x)}P (c ∣x )=P (x )P (c )P (x ∣c )​
其中, P ( c ) P(c)P (c ) 是类先验概率; P ( c ∣ x ) P(c|x)P (c ∣x ) 是样本 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 (c ∣x ) 的问题就转化为如何基于训练数据 D D D 来估计先验 P ( c ) P(c)P (c ) 和似然 P ( x ∣ c ) P(x|c)P (x ∣c ).

2.朴素贝叶斯

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

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

基于属性条件独立性假设,式 ( 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 (c ∣x )=P (x )P (c )P (x ∣c )​=P (x )P (c )​i =1 ∏d ​P (x i ​∣c )

其中 d d d 为属性数目, x i x_i x i ​ 为 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)h n b ​(x )=c ∈y ar g min ​P (c )i =1 ∏d ​P (x i ​∣c )
这就是朴素贝叶斯分类器的表达式.

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

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

举个栗子:

ML朴素贝叶斯分类器

朴素贝叶斯优缺点:

优点:

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

缺点:

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

; 3.朴素贝叶斯代码实现

import numpy as np
import pandas as pd
import copy

class naiveBayesClassifier():
    def __init__(self):
        self.data = None
        self.prior = None

    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()
        for i in range(len(data_count)):
            prior[data_count.index[i]] = data_count[i] / num_sample
        self.prior = prior
        return True

    def cal_prior_laplacian(self):
        prior = {}
        num_sample = len(self.data)
        data_count = self.data.iloc[:,-1].value_counts()
        for i in range(len(data_count)):
            prior[data_count.index[i]] = (data_count.iloc[i] + 1) / (num_sample + len(data_count))
        self.prior = prior

        return True

    def group_data(self):
        data_group_label = {}
        labels = pd.unique(self.data.iloc[:,-1])
        data_group = self.data.groupby(self.data.columns[-1])
        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:
                feature[i] = 1
            elif len(pd.unique(self.data.loc[:,i])) >= len(self.data) * 0.5:
                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])
        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)
        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)))
        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]

        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))
        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__':

    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)
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

仅作学习笔记使用,侵删

Original: https://blog.csdn.net/qq_43627705/article/details/121277649
Author: No’body
Title: ML朴素贝叶斯分类器

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/665177/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球