Naive Bayes.

朴素贝叶斯(Naive Bayes)是一种软分类方法,它要求输入数据$X$的特征取值是离散的,给出可能属于每一个类别$Y$的概率值。

对于二分类问题,每个样本的类别随机变量$y$服从伯努利(Bernoulli)分布,所有样本的类别变量$Y$服从二项(Binormial)分布;对于多分类问题,每个样本的类别随机变量$y$服从类别(CategorialMultinoulli)分布,所有样本的类别变量$Y$服从多项(Multinormial)分布。对于输入数据$X$的每个特征$x_d$,也服从类别分布。

给定数据集$(X,Y)$,朴素贝叶斯把分类模型建模为后验概率$P(Y|X)$:

\[P(Y|X) = \frac{P(X,Y)}{P(X)}= \frac{P(X|Y)P(Y)}{P(X)}\]

其中先验概率$P(Y)$可以通过类别$Y$求得;对条件概率$P(X|Y)$引入条件独立性假设,假设数据$X$的特征之间是独立的:

\[\begin{aligned} P(X=x|Y=c) &= P(X_1=x_1,...,X_d=x_d|Y=c) \\ & = \prod_d P(X_d=x_d|Y=c) \end{aligned}\]

根据训练数据集学习先验概率和条件概率。训练完成后,对于测试数据$X$,通过后验概率最大化判断其所属的类别:

\[\begin{aligned} c^* &= \mathcal{\arg \max}_{c} P(Y=c|X) = \mathcal{\arg \max}_{c} \frac{P(X|Y=c)P(Y=c)}{P(X)} \\ & \propto \mathcal{\arg \max}_{c} P(X|Y=c)P(Y=c) \\ & = \mathcal{\arg \max}_{c} \prod_d P(X_d=x_d|Y=c)P(Y=c) \end{aligned}\]

在实践中,概率的连乘会导致较小的数值;为了提高方法的稳定性,计算上式的对数似然:

\[\sum_d \log P(X_d=x_d|Y=c) + \log P(Y=c)\]

⚪ 讨论:后验概率最大化等价于0-1损失的期望风险最小化

定义分类的0-1损失:

\[L(Y, f(X)) = \begin{cases} 0, & Y=f(X) \\ 1, & Y\neq f(X) \end{cases}\]

则模型$f(x)$的期望风险为:

\[\begin{aligned} \Bbb{E}_{P(X,Y)}[L(Y, f(X))] &= \sum_X \sum_Y L(Y, f(X))P(X,Y) \\ & = \sum_X \sum_Y L(Y, f(X))P(Y|X)P(X)\\ & = \sum_X \sum_c L(Y=c, f(X))P(Y=c|X)P(X) \\ & = \Bbb{E}_{P(X)} [\sum_c L(Y=c, f(X))P(Y=c|X)] \end{aligned}\]

模型$f(x)$的最优值通过期望风险最小化寻找:

\[\begin{aligned} f(x)& = \mathcal{\arg \min}_{f} \Bbb{E}_{P(X)} [\sum_c L(Y=c, f(X))P(Y=c|X)] \\ & = \mathcal{\arg \min}_{y} [\sum_c L(Y=c, y)P(Y=c|X=x)] \\ & = \mathcal{\arg \min}_{y} [\sum_c P(y \neq c|X=x)] \\ & = \mathcal{\arg \min}_{y} [1- P(y = c|X=x)] \\ & = \mathcal{\arg \max}_{y} [P(y = c|X=x)] \end{aligned}\]

因此0-1损失的期望风险最小化等价于后验概率最大化。

⚪ 朴素贝叶斯方法的贝叶斯估计

条件概率$P(X_d=x_d | Y=c)$的估计值可能为$0$,会影响到后验概率的计算。条件概率的贝叶斯估计为:

\[P(X_d=x_d | Y=c) = \frac{\sum_i I(X^i_d = x_d,Y^i=c)+\lambda}{\sum_iI(Y^i=c)+S_d \lambda}\]

上式相当于在随机变量各个取值的频数上增加一个正数$\lambda$。当$\lambda=0$时等价于极大似然估计;当$\lambda=1$时称为拉普拉斯平滑(Laplace smoothing)

⚪ 实现朴素贝叶斯

class NaiveBayes():
    def __init__(self):
        self.model = {}

    def fit(self, datas, labels):
        # 获取分类的类别, 输入np.array格式, 尺寸为[N, D], [N]
        self.keys = set(labels.tolist())        
        for key in self.keys:
            # 计算P(Y)
            PY = labels.tolist().count(key)/len(labels)
            # 收集标签为Y的数据
            index = np.where(labels==key)[0].tolist()
            data_Y = datas[index]
            # 计算P(X|Y)
            PX_Y = {}
            for d in range(datas.shape[1]):
                data = data_Y[:,d].tolist()
                values = set(data)
                N = len(data)
                PX_Y[d] = {}
                for value in values:
                    PX_Y[d][value] = float(data.count(value)/N)
            # 模型保存
            self.model[key] = {}
            self.model[key]["PY"] = PY
            self.model[key]["PX_Y"] = PX_Y

    def predict(self, data):
        # 预测一个数据的类别,尺寸为[D]
        results = {}
        eps = 0.00001
        for key in self.keys:
            # 获取P(Y)
            PY = self.model.get(key, eps).get("PY")
            # 分别获取P(X|Y)
            model_X = self.model.get(key, eps).get("PX_Y")
            PX_Y = []
            for d in range(len(data)):
                pb = model_X.get(d, eps).get(data[d],eps)
                PX_Y.append(pb)
            result = np.log(PY) + np.sum(np.log(PX_Y))
            results[key] = result
        return results