my Naive Bayes

原理

贝叶斯定理: P ( Y ∣ X ) = P ( X Y ) P ( X ) = P ( X ∣ Y ) P ( Y ) P ( X ) P(Y|X)=\frac{P(XY)}{P(X)}=\frac{P(X|Y) P(Y)}{P(X)} P(YX)=P(X)P(XY)=P(X)P(XY)P(Y)

其中, P ( X ∣ Y ) = P ( X ( 1 ) = x ( 1 ) , ⋯   , X ( n ) = x ( n ) ∣ Y = c k ) ⟶ 条 件 独 立 假 设 = ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) P(X|Y)=P(X^{(1)}=x^{(1)}, \cdots, X^{(n)}=x^{(n)}|Y=c_{k}) \stackrel{条件独立假设}{\longrightarrow}=\prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_{k}) P(XY)=P(X(1)=x(1),,X(n)=x(n)Y=ck)=j=1nP(X(j)=x(j)Y=ck)

那么分子 = P ( Y = c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) ,      k = 1 , 2 … K =P(Y=c_k)\prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_{k}), \ \ \ \ k=1,2…K =P(Y=ck)j=1nP(X(j)=x(j)Y=ck),    k=1,2K

分母 = ∑ Y P ( X Y ) = ∑ Y P ( X ∣ Y ) P ( Y ) = ∑ k P ( Y = c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) ,      k = 1 , 2 … K =\sum_Y{P(XY)=\sum_Y{P(X|Y)P(Y)}=\sum_k{P(Y=c_k)\prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_{k})}}, \ \ \ \ k=1,2…K =YP(XY)=YP(XY)P(Y)=kP(Y=ck)j=1nP(X(j)=x(j)Y=ck),    k=1,2K

由此得到朴素贝叶斯分类器可表示为:

y = f ( x ) = a r g m a x c k P ( Y = c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) ∑ k P ( Y = c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) ∝ a r g m a x c k P ( Y = c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) y=f(x)=\mathop{argmax}\limits_{c_k}\frac{P(Y=c_k)\prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_{k})}{\sum_k{P(Y=c_k)\prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_{k})}} \propto \mathop{argmax}\limits_{c_k} P(Y=c_k)\prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_{k}) y=f(x)=ckargmaxkP(Y=ck)j=1nP(X(j)=x(j)Y=ck)P(Y=ck)j=1nP(X(j)=x(j)Y=ck)ckargmaxP(Y=ck)j=1nP(X(j)=x(j)Y=ck)

三种模型

根据概率的不同计算形式,有以下三种常用的模型:

  • 多项式模型:数据服从多项式分布,适用于特征离散的情况。

    先验概率 P ( Y = c k ) = ∑ i = 1 N I ( y i = c k ) + λ N + K λ , k = 1 , 2 , ⋯   , K P\left(Y=c_{k}\right)=\frac{\sum_{i=1}^{N} I\left(y_{i}=c_{k}\right) + \lambda}{N+K\lambda}, \quad k=1,2, \cdots, K P(Y=ck)=N+Kλi=1NI(yi=ck)+λ,k=1,2,,K

    条件概率 P ( X ( j ) = a j l ∣ Y = c k ) = ∑ i = 1 N I ( x i ( j ) = a j l , y i = c k ) + λ ∑ i = 1 N I ( y i = c k ) + S j λ , j = 1 , 2 , ⋯   , n ; l = 1 , 2 , ⋯   , S j ; k = 1 , 2 , ⋯   , K P(X^{(j)}=a_{j l}|Y=c_{k})=\frac{\sum_{i=1}^{N} I\left(x_{i}^{(j)}=a_{j l}, y_{i}=c_{k}\right) + \lambda}{\sum_{i=1}^{N} I\left(y_{i}=c_{k}\right) + S_j\lambda}, \quad j=1,2, \cdots, n ; \quad l=1,2, \cdots, S_{j} ; \quad k=1,2, \cdots, K P(X(j)=ajlY=ck)=i=1NI(yi=ck)+Sjλi=1NI(xi(j)=ajl,yi=ck)+λ,j=1,2,,n;l=1,2,,Sj;k=1,2,,K

    其中, x i ( j ) x_{i}^{(j)} xi(j)是第 i i i个样本的第 j j j个特征, a j l a_{jl} ajl是第 j j j个特征的可能取的第 l l l个值, k k k是样本类别。

  • 伯努利模型:数据服从多元伯努利分布,每个特征的取值只能是1和0,适用于特征离散的情况。

  • 高斯模型:数据服从高斯分布(正态分布),适用于特征连续的情况。

    P ( x i ∣ y k ) = 1 2 π σ y k , i 2 e − ( x i − μ y k , i 2 ) 2 2 σ y k , i 2 P\left(x_{i}|y_{k}\right)=\frac{1}{\sqrt{2 \pi \sigma_{y_{k}, i}^{2}}} e^{-\frac{(x_{i}-\mu_{y_k, i}^2)^2}{2 \sigma_{y_k, i}^{2}}} P(xiyk)=2πσyk,i2 1e2σyk,i2(xiμyk,i2)2

    其中, μ y k , i \mu_{y_k,i} μyk,i表示类别为 y k y_k yk的样本中,第 i i i维特征的均值, σ y k , i \sigma_{y_k,i} σyk,i表示类别为 y k y_k yk的样本中,第 i i i维特征的方差。

模型实现

多项式模型

import numpy as np


class NB:
    def __init__(self, lambda_):
        self.lambda_ = lambda_  # 拉普拉斯平滑
        self.feat_val = [0, 1]  # 文本分类中每一列特征的取值
        self.py = {}
        self.pxy = {}

    def fit(self, X, y):
        N, M = X.shape
        data = np.hstack((X, y.reshape(N, 1)))

        # 统计标签中的类别及其个数,即\sum{I(y_i=c_k)}
        unique_y, counts_y = np.unique(y, return_counts=True)
        y_info = dict(zip(unique_y, counts_y))

        # 对每一个类别进行遍历
        for ck, ck_count in y_info.items():
            # 计算P(Y=c_k)
            self.py['P(Y={})'.format(ck)] = (ck_count + self.lambda_) / (N + len(unique_y) * self.lambda_)

            # 取出标签=ck的所有行
            tmp_data = data[data[:, -1] == ck]

            # 对每一个特征遍历
            for col in range(M):
                # 统计类别为ck且该列特征下每个取值的个数,即\sum{I(x_ij=a_jl,y_i=c_k)}
                unique_feat, counts_feat = np.unique(tmp_data[:, col], return_counts=True)
                feat_info = dict(zip(unique_feat, counts_feat))
                # 如果该类别下的特征的取值全相等,那也需要把其它取值也加入到feat_info中
                if len(feat_info) != len(self.feat_val):
                    for v in self.feat_val:
                        feat_info[v] = feat_info.get(v, 0)
                # 对该特征下的每一个不同取值进行遍历
                for feat_val, feat_count in feat_info.items():
                    # 计算P(X^{j}=a_{j_l}|Y=c_k)
                    self.pxy['P(X({})={}|Y={})'.format(col + 1, feat_val, ck)] = (feat_count + self.lambda_) / (
                        (ck_count + len(feat_info) * self.lambda_))

    def predict(self, x):
        res = {}
        for k, v in self.py.items():
            p = np.log(v)
            ck = k.split('=')[-1][:-1]
            for i in range(len(x)):
                # 计算P(Y=c_k)\prod{P(X^{(j)}=x^{(j)}|Y=c_{k})}
                p = p + np.log(self.pxy['P(X({})={}|Y={})'.format(i + 1, x[i], ck)])
            res[ck] = p
        # print(res)

        max_p = float('-inf')
        max_cate = float('-inf')
        for cate, p in res.items():
            if p > max_p:
                max_p = p
                max_cate = cate

        return max_cate, max_p

    def score(self, Xtest, ytest):
        c = 0
        for x, y in zip(Xtest, ytest):
            cate, p = self.predict(x)
            if int(cate) == int(y):
                c += 1
        return c / len(Xtest)
# 垃圾邮件检测
def spam_test():
    # 读入数据
    doc_list = []
    class_list = []
    for filename in ['ham', 'spam']:
        for i in range(1, 26):
            with open("./email/" + filename + '/' + str(i) + '.txt') as f:
                words = f.read()
                words = text_parse(words)
            doc_list.append(' '.join(words))
            if filename == 'ham':
                class_list.append(1)
            else:
                class_list.append(-1)

    # 单词计数
    vec = CountVectorizer()
    words = vec.fit_transform(doc_list)
    words = pd.DataFrame(words.toarray(), columns=vec.get_feature_names())
    # 转为二值,单词出现为0,没出现为1
    words[words > 0] = 1

    # 构建数据集
    X = words.values
    y = np.array(class_list)
    Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, test_size=0.2)

    # 训练
    model = NB(lambda_=0.2)
    model.fit(np.array(Xtrain), np.array(ytrain))

    # 测试
    score = model.score(Xtest, ytest)
    print('正确率:{}'.format(score))

高斯模型

import numpy as np

class GNB:
    def __init__(self):
        self.parameters = {}
        self.classes = []

    def fit(self, X, y):
        self.classes = list(np.unique(y))

        for c in self.classes:
            # 计算每个类别的平均值,方差,先验概率
            X_Index_c = X[np.where(y == c)]
            X_index_c_mean = np.mean(X_Index_c, axis=0, keepdims=True)
            X_index_c_var = np.var(X_Index_c, axis=0, keepdims=True)
            prior = X_Index_c.shape[0] / X.shape[0]
            self.parameters["class" + str(c)] = {"mean": X_index_c_mean, "var": X_index_c_var, "prior": prior}
        # print(self.parameters)

    def predict(self, X):
        # 取概率最大的类别返回预测值
        output = []
        for y in self.classes:
            # 先验概率
            prior = np.log(self.parameters["class" + str(y)]["prior"])

            # 后验概率:一维高斯分布的概率密度函数
            mean = self.parameters["class" + str(y)]["mean"]
            var = self.parameters["class" + str(y)]["var"]

            eps = 1e-4
            numerator = np.exp(-(X - mean) ** 2 / (2 * var + eps))
            denominator = np.sqrt(2 * np.pi * var + eps)

            # 取对数防止数值溢出
            posterior = np.sum(np.log(numerator / denominator), axis=1, keepdims=True).T
            prediction = prior + posterior
            output.append(prediction)

        output = np.reshape(output, (len(self.classes), X.shape[0]))
        prediction = np.argmax(output, axis=0)
        return prediction

    def score(self, X_test, y_test):
        pred = self.predict(X_test)
        right = (y_test - pred == 0.0).sum()

        return right / float(len(X_test))
# 鸢尾花分类
def iris_test():
    iris = load_iris()  # 鸢尾花数据集
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['label'] = iris.target
    df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
    data = np.array(df.iloc[:100, :])
    # 构建数据集
    X, y = data[:, :-1], data[:, -1]
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

    # 训练
    model = GNB()
    model.fit(X_train, y_train)

    # 测试
    ck = model.predict(np.array([4.4, 3.2, 1.3, 0.2]).reshape(1, -1))
    print("预测的类别是:{}".format(ck))

    score = model.score(X_test, y_test)
    print('正确率:{}'.format(score))

常见面试题

  1. 朴素贝叶斯中的“朴素”是什么含义?

    假定所有的特征在数据集中是独立同分布的,但这个假设在现实生活中很不真实,因此很“naive”。

  2. 实际项目中,概率值往往是很小的小数,连续微小小数相乘容易造成下溢出使乘积为0,因此可以取自然对数,将连乘变为连加。

  3. 朴素贝叶斯的优缺点

    • 优点:朴素贝叶斯模型有着坚实的数学基础,以及稳定的分类效率;对小规模数据表现很好,能够处理多分类任务,适合增量式训练;适用于分类任务,对预测样本进行预测时,过程简单速度快。
    • 缺点:特征条件独立性假设在实际应用中往往是不成立,在特征的属性相关性较小时性能较好;先验概率很多时候是基于假设或者已有的训练数据所得的;对输入数据的表达形式很敏感(离散、连续、值极大极小)。
  4. 为什么引入条件独立性假设?

    为了避免贝叶斯定理求解时面临的组合爆炸问题。

    P ( X ∣ Y ) = P ( X ( 1 ) = x ( 1 ) , ⋯   , X ( n ) = x ( n ) ∣ Y = c k ) P(X|Y)=P(X^{(1)}=x^{(1)}, \cdots, X^{(n)}=x^{(n)}|Y=c_{k}) P(XY)=P(X(1)=x(1),,X(n)=x(n)Y=ck)

    参数个数为 K ∏ j = 1 n S j K \prod_{j=1}^nS_j Kj=1nSj个,这就导致条件概率分布的参数数量为指数级别。

  5. 朴素贝叶斯是一个生成模型,它通过学习已知样本,计算出联合概率,再求条件概率。

    • 生成模式:由数据学得联合概率分布,再求出条件概率分布 P ( Y ∣ X ) P(Y|X) P(YX)的预测模型;

      常见的生成模型有:朴素贝叶斯、隐马尔可夫模型、高斯混合模型、文档主题生成模型(LDA)、限制玻尔兹曼机

    • 判别模式:由数据学得决策函数或条件概率分布的预测模型

      常见的判别模型有:K近邻、SVM、决策树、感知机、线性判别分析(LDA)、线性回归、传统的神经网络、逻辑斯蒂回归、boosting、条件随机场

参考资料

《统计学习方法》
《机器学习实战》
https://blog.csdn.net/manduner/article/details/90169101
https://www.cnblogs.com/lovewhale1997/p/11265029.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值