朴素贝叶斯与逻辑回归都是二分类(乃至多分类)任务的经典机器学习算法,它们在文本分类、信用评分、医学诊断、垃圾邮件识别等场景应用广泛。
核心区别概览
我们首先列出两者最核心的区别:
- 生成式 vs 判别式模型:朴素贝叶斯是生成式模型,建模类条件密度与先验分布,再用贝叶斯法则得到后验;逻辑回归是判别式模型,直接建模条件分布。
- 特征独立性假设:朴素贝叶斯引入朴素的条件独立性假设,即给定类别后各特征相互独立;逻辑回归不需要这种假设,可以自然处理相关特征。
- 决策边界形状:逻辑回归在标准设定下是线性判别边界;朴素贝叶斯的边界取决于类条件分布,在高斯且方差不同时往往为二次曲线(非线性),方差相同时近似线性。
此外,朴素贝叶斯对小样本、稀疏高维(如词袋模型)非常敏感友好,训练速度快,但概率输出可能偏极端;逻辑回归需要足够数据来稳定估计参数,概率校准通常更好,并通过正则化抵御过拟合。两者在类不平衡、概率校准、缺失值处理、特征工程等方面也有所不同。
下面从原理到公式逐层深入分析。
朴素贝叶斯原理
朴素贝叶斯的核心思想是通过建模类条件密度 ( P(\mathbf{x} | y) ) 与先验 ( P(y) ),利用贝叶斯公式得到后验概率:
[
P(y | \mathbf{x}) = \frac{P(y) P(\mathbf{x} | y)}{P(\mathbf{x})}
]
在朴素假设下(给定类别后各特征条件独立),有:
[
P(\mathbf{x} | y) = \prod_{j=1}^d P(x_j | y)
]
这使得高维密度估计简化为各维边缘密度乘积,大幅降低参数规模与计算复杂度。
三种常见朴素贝叶斯变体
根据特征类型,常见 NB 模型有:
- 高斯朴素贝叶斯:用于连续特征,假设每个特征在给定类别下服从高斯分布。
- 多项式朴素贝叶斯:用于计数型特征(如词袋模型中的词频),采用拉普拉斯平滑避免零概率问题。
- 伯努利朴素贝叶斯:用于二值特征(如词是否出现)。
后验与判别规则
分类决策使用最大后验概率(MAP):
[
\hat{y} = \arg\maxy P(y) \prod{j=1}^d P(x_j | y)
]
进一步分析对数几率:
[
\log \frac{P(y=1 | \mathbf{x})}{P(y=0 | \mathbf{x})} = \log \frac{P(y=1)}{P(y=0)} + \sum_{j=1}^d \log \frac{P(x_j | y=1)}{P(x_j | y=0)}
]
这条式子揭示了 NB 的决策由各维证据比的加和组成。高斯 NB 的边界形状:若各维方差相同,边界退化为线性;若方差不同,则保留二次项,边界为二次曲线(抛物线/椭圆型),这是 NB 能产生非线性边界的来源。
参数估计与平滑
- 先验 ( P(y) ):用样本频率估计。
- 高斯参数:均值和方差用类内样本的最大似然估计。
- 多项式/伯努利参数:用加性平滑(如拉普拉斯平滑)避免零概率问题。
计算后验时,使用对数避免数值下溢,计算稳定且复杂度低,适合海量维度与稀疏特征。
逻辑回归原理
逻辑回归直接建模后验概率的形式:
[
P(y=1 | \mathbf{x}; \mathbf{w}) = \sigma(\mathbf{w}^T \mathbf{x} + b) = \frac{1}{1 + e^{-(\mathbf{w}^T \mathbf{x} + b)}}
]
其中 ( \sigma ) 是 Sigmoid 函数,几率与对数几率为:
[
\log \frac{P(y=1 | \mathbf{x})}{P(y=0 | \mathbf{x})} = \mathbf{w}^T \mathbf{x} + b
]
这意味着决策边界是 ( \mathbf{w}^T \mathbf{x} + b = 0 ) 的超平面,线性可分。
最大似然与损失函数
对样本 ( (x_i, y_i) ),以伯努利似然:
[
L(\mathbf{w}) = \prod_{i=1}^n P(y_i | x_i; \mathbf{w})^{y_i} (1 - P(y_i | x_i; \mathbf{w}))^{1-y_i}
]
取负对数得到交叉熵损失:
[
\mathcal{L}(\mathbf{w}) = -\sum_{i=1}^n \left[ y_i \log P(y_i | x_i; \mathbf{w}) + (1-y_i) \log (1 - P(y_i | x_i; \mathbf{w})) \right]
]
加入 L2 正则化以防过拟合:
[
\mathcal{L}_{\text{reg}}(\mathbf{w}) = \mathcal{L}(\mathbf{w}) + \lambda |\mathbf{w}|_2^2
]
梯度与优化
损失对参数的梯度:
[
\nabla \mathcal{L} = \sum_{i=1}^n (P(y_i | x_i; \mathbf{w}) - y_i) x_i
]
使用梯度下降、LBFGS 或牛顿法等优化。Hessian 矩阵为正定,确保优化稳定性。
与朴素贝叶斯的联系
当类条件分布为高斯且各维方差在两类间相同时,NB 的对数几率可化为线性函数,与 LR 形式一致。差别在于:NB 通过生成过程估计参数,再间接得到后验;LR 直接通过判别式最大似然优化。在样本足够大时,LR 通常分类性能更优;在样本很少或特征稀疏时,NB 可能优势更明显。
核心区别与侧重点总结
- 生成式 vs 判别式:NB 学习数据生成过程,可解释性强;LR 专注分类边界,通常性能更优。
- 特征独立性:NB 需条件独立假设,在词袋等场景实用;LR 无需该假设,能处理相关特征。
- 决策边界:LR 为线性边界;NB 在高斯方差不同时产生非线性边界。
- 概率校准:LR 在样本充足时校准更好;NB 输出概率常偏极端,但排序能力可能不错。
- 数据规模与速度:NB 训练极快,适合高维稀疏数据;LR 需迭代优化,训练时间较长但可扩展。
- 缺失值与特征工程:NB 可天然忽略缺失维度;LR 通常需显式缺失值处理。
- 类不平衡:LR 可通过权重调优改善;NB 先验反映不平衡,但需评估 PR 曲线。
- 可解释性:LR 系数可解释特征影响;NB 参数有生成语义。
完整Python实战案例
以下Python代码模拟两类高斯分布,类间方差不同,使朴素贝叶斯决策边界非线性,逻辑回归保持线性,并比较在类别不平衡下的性能。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, auc, precision_recall_curve, average_precision_score, brier_score_loss, log_loss
from sklearn.calibration import calibration_curve
rng = np.random.RandomState(42)
# 1) 构造二维高斯数据(类别不平衡 + 类间方差不同)
n_total = 6000
ratio_pos = 0.3
n_pos = int(n_total * ratio_pos)
n_neg = n_total - n_pos
# 类0(负类)参数
mean0 = np.array([0.0, 0.0])
cov0 = np.array([[1.0, 0.6],
[0.6, 3.0]])
# 类1(正类)参数
mean1 = np.array([2.5, 2.0])
cov1 = np.array([[3.5, -0.8],
[-0.8, 1.0]])
X0 = rng.multivariate_normal(mean0, cov0, size=n_neg)
X1 = rng.multivariate_normal(mean1, cov1, size=n_pos)
X = np.vstack([X0, X1])
y = np.hstack([np.zeros(n_neg), np.ones(n_pos)])
# 打乱数据
perm = rng.permutation(n_total)
X = X[perm]
y = y[perm]
# 训练/测试划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.35, random_state=42, stratify=y)
# 2) 训练模型
gnb = GaussianNB(var_smoothing=1e-9)
lr = LogisticRegression(C=1.0, penalty='l2', solver='lbfgs', max_iter=1000)
gnb.fit(X_train, y_train)
lr.fit(X_train, y_train)
# 3) 预测与评估
proba_gnb = gnb.predict_proba(X_test)[:, 1]
proba_lr = lr.predict_proba(X_test)[:, 1]
# ROC曲线
fpr_gnb, tpr_gnb, _ = roc_curve(y_test, proba_gnb)
fpr_lr, tpr_lr, _ = roc_curve(y_test, proba_lr)
auc_gnb = auc(fpr_gnb, tpr_gnb)
auc_lr = auc(fpr_lr, tpr_lr)
# Precision-Recall曲线
prec_gnb, rec_gnb, _ = precision_recall_curve(y_test, proba_gnb)
prec_lr, rec_lr, _ = precision_recall_curve(y_test, proba_lr)
ap_gnb = average_precision_score(y_test, proba_gnb)
ap_lr = average_precision_score(y_test, proba_lr)
# 校准曲线
frac_pos_gnb, mean_pred_gnb = calibration_curve(y_test, proba_gnb, n_bins=10, strategy='uniform')
frac_pos_lr, mean_pred_lr = calibration_curve(y_test, proba_lr, n_bins=10, strategy='uniform')
# Brier分数与对数损失
brier_gnb = brier_score_loss(y_test, proba_gnb)
brier_lr = brier_score_loss(y_test, proba_lr)
logloss_gnb = log_loss(y_test, proba_gnb)
logloss_lr = log_loss(y_test, proba_lr)
# 4) 可视化分析
# (A) 决策边界
plt.figure(figsize=(7,6))
x_min, x_max = X[:,0].min()-1.5, X[:,0].max()+1.5
y_min, y_max = X[:,1].min()-1.5, X[:,1].max()+1.5
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 300),
np.linspace(y_min, y_max, 300))
grid = np.c_[xx.ravel(), yy.ravel()]
Z_gnb = gnb.predict_proba(grid)[:, 1].reshape(xx.shape)
Z_lr = lr.predict_proba(grid)[:, 1].reshape(xx.shape)
cont = plt.contourf(xx, yy, Z_lr, levels=np.linspace(0,1,21), cmap='turbo', alpha=0.7)
plt.colorbar(cont).set_label('LR predicted P(y=1)')
plt.contour(xx, yy, Z_gnb, levels=[0.5], colors=['#FF00FF'], linewidths=2.5)
plt.contour(xx, yy, Z_lr, levels=[0.5], colors=['#00E5FF'], linewidths=2.5, linestyles='--')
plt.scatter(X_train[y_train==0,0], X_train[y_train==0,1], s=18, c='#FF6D00', alpha=0.75, label='Train 0')
plt.scatter(X_train[y_train==1,0], X_train[y_train==1,1], s=22, c='#00C853', alpha=0.85, marker='^', label='Train 1')
plt.title('(A) 决策边界:LR(线性) vs GNB(非线性)')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()
# (B) ROC曲线
plt.figure(figsize=(7,6))
plt.plot(fpr_gnb, tpr_gnb, color='#FF00FF', lw=2.5, label=f'GNB ROC (AUC={auc_gnb:.3f})')
plt.plot(fpr_lr, tpr_lr, color='#00E5FF', lw=2.5, linestyle='--', label=f'LR ROC (AUC={auc_lr:.3f})')
plt.plot([0,1], [0,1], linestyle=':', color='gray')
plt.title('(B) ROC 曲线')
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.legend()
plt.show()
# (C) Precision-Recall曲线
plt.figure(figsize=(7,6))
plt.plot(rec_gnb, prec_gnb, color='#FF00FF', lw=2.5, label=f'GNB PR (AP={ap_gnb:.3f})')
plt.plot(rec_lr, prec_lr, color='#00E5FF', lw=2.5, linestyle='--', label=f'LR PR (AP={ap_lr:.3f})')
plt.title('(C) Precision-Recall 曲线')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.legend()
plt.grid(alpha=0.2)
plt.show()
# (D) 概率校准曲线
fig, ax = plt.subplots(figsize=(7,6))
ax.plot(mean_pred_gnb, frac_pos_gnb, marker='o', color='#FF00FF', lw=2.0, label=f'GNB (Brier={brier_gnb:.3f}, LogLoss={logloss_gnb:.3f})')
ax.plot(mean_pred_lr, frac_pos_lr, marker='s', color='#00E5FF', lw=2.0, linestyle='--', label=f'LR (Brier={brier_lr:.3f}, LogLoss={logloss_lr:.3f})')
ax.plot([0,1], [0,1], linestyle=':', color='gray')
ax.set_title('(D) 概率校准曲线')
ax.set_xlabel('Mean predicted probability')
ax.set_ylabel('Fraction of positives')
ax.legend()
ax.grid(alpha=0.2)
ax2 = ax.twinx()
ax2.hist(proba_gnb, bins=20, range=(0,1), alpha=0.25, color='#FF00FF')
ax2.hist(proba_lr, bins=20, range=(0,1), alpha=0.25, color='#00E5FF')
ax2.set_ylabel('Count')
plt.show()
可视化分析
决策边界:LR线性 vs GNB非线性
展示两个模型在二维空间的决策边界形状差异。在类条件方差不同的情况下,高斯 NB 生成式建模自然产生非线性边界;LR 的边界保持线性,验证了 NB 边界取决于类条件分布的理论。
ROC 曲线:排序能力评估
评估模型的排序能力(AUC),不受类别不平衡影响很大。即便 NB 概率校准较弱,其排序能力可能仍然不俗;LR 往往有较高 AUC,取决于任务结构。
Precision-Recall 曲线:不平衡场景下的性能
对于类别不平衡(30% 正类),PR 曲线更能体现模型对少数类的识别能力。LR 常在 PR 曲线上表现较好,但 NB 在稀疏高维也可能优秀。
概率校准曲线:可靠性的关键
校准曲线显示预测概率的可信度。LR 通常概率更接近真实(点更贴近对角线),而 NB 的点可能偏离对角线(过度自信或保守),且直方图显示 NB 更易输出极端概率。这强调了当需要可靠概率(如风控阈值设置)时,LR 的优势。
总结
朴素贝叶斯(生成式)与逻辑回归(判别式)代表两种不同的统计建模哲学。NB 通过建模类条件分布与先验进行推断,核心是条件独立假设;LR 直接建模后验概率,优化交叉熵损失,边界线性,概率校准常更优。
理论上,高斯 NB 在类间共享方差时与 LR 的对数几率形式一致;但实践中,由于估计路径不同,两者的性能、鲁棒性与概率可靠性都有差别。在小样本、稀疏高维文本等场景中,NB 是快速有效的基线;在需要可靠概率、特征相关性强或边界需要灵活控制的场景,LR 更具优势。