跳转至

朴素贝叶斯

朴素贝叶斯(Naive Bayes)法是基于贝叶斯定理和特征条件独立假设的分类方法,实现简单,学习与预测的效率都很高,是一种常用的生成学习方法

分类

  • 监督学习方法
  • 概率模型
  • 参数化模型
  • 生成学习方法

基本方法

设训练数据集为

\[ T=\{(x_1,y_1),(x_2,y_2),···,(x_N,y_N)\},\quad x\in \mathcal{X},y\in \mathcal{Y} \]

其中输入空间 \(\mathcal{X}\subseteq R^n\)\(n\) 维空间向量的集合,输出空间为类标记集合 \(\mathcal{Y}=\{c_1,c_2,···,c_K\}\)

\(X\)\(Y\) 分别为定义在输入、输出空间上的随机变量,朴素贝叶斯法基于贝叶斯定理首先将最终目标,即条件概率分布 \(P(Y=c_k \mid X=x)\) 转化为求解联合概率分布 \(P(X=x,Y=c_k)\)

\[ \begin{aligned} P(Y=c_k \mid X = x) &= \frac{P(X=x,Y=c_k)}{P(X=x)}\\ &=\frac{P(Y=c_k)P(X=x \mid Y=c_k)}{\sum_{Y}P(Y)P(X = x \mid Y)} \end{aligned} \]

为此,朴素贝叶斯需要首先学习到先验概率

\[ P(Y=c_k),\quad k=1,2,···,K \]

以及相应条件概率分布

\[ \begin{aligned} P(X=x \mid Y=c_k)=&(X^{(1)}=x^{(1)},···,X^{(n)}=x^{(n)}\mid Y=c_k)\\ &k=1,2,···,K \end{aligned} \]

问题在于朴素贝叶斯对于条件概率分布 \(P(X=x \mid Y=c_k)\) 的估计,在实际的样本中该分布有着指数级数量的参数需要估计

\(X^{(i)}\) 可取值有 \(S_i\) 个,\(i=1,2,···,n\)\(Y\) 的取值有 \(K\) 个,则所需学习参数个数为 \(K\prod_{i=1}^{n}S_i\)

为此朴素贝叶斯对条件概率作了一个较强的条件独立性假设:

\[ \begin{aligned} P(X=x \mid Y=c_k)&=(X^{(1)}=x^{(1)},···,X^{(n)}=x^{(n)}\mid Y=c_k)\\ &=\prod_{i=1}^{n}P(X^{(i)}=x^{(i)} \mid Y=c_k) \end{aligned} \]

具体而言,该假设认为用于分类的特征在类确定的前提下是条件独立的。这一假设使参数学习变得简单(参数个数变为了 \(K\sum_{i=1}^{n}S_i\) ),但会牺牲一定的准确率

于是,在学习到参数模型后,朴素贝叶斯分类器可表示为:

\[ y = f(x) = \mathop{\arg\max}\limits_{c_k}\frac{P(Y=c_k)\prod_{i}P(X^{(i)}=x^{(i)} \mid Y=c_k)}{\sum_{k}P(Y)\prod_{i}P(X^{(i)} = x^{(i)} \mid Y=c_k)} \]

因为对所有 \(c_k\) 上式分母都相同,所以只需:

\[ y = f(x) = \mathop{\arg\max}\limits_{c_k}P(Y=c_k)\prod_{i}P(X^{(i)}=x^{(i)} \mid Y=c_k) \]

参数估计

在朴素贝叶斯法中,学习意味着估计 \(P(Y=c_k)\)\(P(X^{(j)}=x^{(j)}\mid Y=c_k)\)

常见的估计方法为极大似然估计与贝叶斯估计,同时基于不同的分布假设(如正态分布),我们还能进一步对所需求得的参数量进行简化,这点将在后面的代码部分进行解释

极大似然估计

对先验概率的估计为

\[ P(Y=c_k)=\frac{\sum_{i=1}^NI(y_i=c_k)}{N},\quad k=1,2,···,K \]

对条件概率的估计为

\[ \begin{aligned} P(X^{(j)}=a_{jl}\mid Y=c_k)=\frac{\sum_{i=1}^NI(x_i^{(j)}=a_{jl},y_i=c_k)}{\sum_{i=1}^NI(y_i=c_k)}\\ j=1,2,···,n; \quad l=1,2,···,S_j;\quad k=1,2,···,K \end{aligned} \]

其中第 \(j\) 个特征 \(x^{(i)}\) 可能取值的集合为 \(\{a_{j1},a_{j2},···,a_{jS_j}\}\)

贝叶斯估计

用极大似然估计可能会出现所要估计的概率值为0的情况。这会影响到后续概率的计算结果,使分类产生偏差

为此常用贝叶斯估计解决这个问题:

\[ P_{\lambda}(Y=c_k)=\frac{\sum_{i=1}^NI(y_i=c_k)+\lambda}{N+K\lambda} \]
\[ P_{\lambda}(X^{(j)}=a_{jl}\mid Y=c_k)=\frac{\sum_{i=1}^NI(x_i^{(j)}=a_{jl},y_i=c_k)+\lambda}{\sum_{i=1}^NI(y_i=c_k)+S_j\lambda} \]

经过上式操作,依然能证明其仍为一种概率分布

\(\lambda\)\(1\) 时,被称为拉普拉斯平滑(Laplacian smoothing)

代码实现

这里实现的为 GaussianNaiveBayesClassifier1,具体的,其对 \(P(X^{(j)}=x^{(j)}\mid Y=c_k)\) 的假设为正态分布:

\[ P(X^{(j)}=x^{(j)}\mid Y=c_k)=\frac{1}{\sqrt{2 \pi \sigma_{c_k}^2}} \exp \left(-\frac{\left(x_i-\mu_{c_k}\right)^2}{2 \sigma_{c_k}^2}\right) \]

可以看到基于如上假设,我们只需要通过极大似然估计出极少的参数(\(\sigma_{c_k}\)\(\mu_{c_k}\))便能完成学习

Python
class GaussianNBClassifier:
    def __init__(self, eps=1e-6):
        self.labels = None
        self.hyperparameters = {"eps": eps}
        self.parameters = {
            "mean": None,  # shape: (K, M)
            "sigma": None,  # shape: (K, M)
            "prior": None,  # shape: (K,)
        }

    def fit(self, X, y):
        P = self.parameters
        H = self.hyperparameters

        self.labels = np.unique(y)

        K = len(self.labels)
        N, M = X.shape

        P["mean"] = np.zeros((K, M))
        P["sigma"] = np.zeros((K, M))
        P["prior"] = np.zeros((K,))

        for i, c in enumerate(self.labels):
            X_c = X[y == c, :]

            P["mean"][i, :] = np.mean(X_c, axis=0)
            P["sigma"][i, :] = np.var(X_c, axis=0) + H["eps"]
            P["prior"][i] = X_c.shape[0] / N
        return self

    def predict(self, X):
        return self.labels[self._log_posterior(X).argmax(axis=1)]

    def _log_posterior(self, X):
        K = len(self.labels)
        log_posterior = np.zeros((X.shape[0], K))
        for i in range(K):
            log_posterior[:, i] = self._log_class_posterior(X, i)
        return log_posterior

    def _log_class_posterior(self, X, class_idx):
        P = self.parameters
        mu = P["mean"][class_idx]
        prior = P["prior"][class_idx]
        sigsq = P["sigma"][class_idx]

        log_likelihood = -0.5 * np.sum(np.log(2 * np.pi * sigsq))
        log_likelihood -= 0.5 * np.sum(((X - mu) ** 2) / sigsq, axis=1)
        return log_likelihood + np.log(prior)

完整代码已上传至Repo

参考资料

  • 《统计学习方法(第二版)》李航

  1. https://scikit-learn.org/stable/modules/naive_bayes.html#gaussian-naive-bayes 


最后更新: 2024-01-12
创建日期: 2023-03-09

评论