跳至主要内容

使用 Scikit-learn 的朴素贝叶斯分类教程

学习如何使用 Python 的 Scikit-learn 包构建并评估朴素贝叶斯分类器。
· 13分钟 阅读

使用 AI 探索

假设您是一名产品经理,想要将客户评价划分为正面和负面两类。或者作为一名信贷经理,您希望识别哪些贷款申请人是安全的或高风险的?又或者作为一名医疗分析师,您希望预测哪些患者可能患上糖尿病。以上示例都有一个共同的问题:对评论、贷款申请人和患者进行分类。

朴素贝叶斯是一种最直接且快速的分类算法,适合处理大规模数据。朴素贝叶斯分类器已成功应用于垃圾邮件过滤、文本分类、情感分析和推荐系统等领域。它基于贝叶斯概率定理来预测未知类别。

在本教程中,您将学习以下内容:

  • 分类工作流
  • 什么是朴素贝叶斯分类器?
  • 朴素贝叶斯分类器如何工作?
  • 在 Scikit-learn 中构建分类器
  • 零概率问题
  • 它的优点与缺点

若想自己轻松运行本教程中的所有示例代码,您可以免费创建一个 DataLab 工作簿,其中已预装 Python 并包含所有代码示例。想进一步练习 scikit-learn,请查看我们的 使用 Scikit-learn 的监督学习课程!

什么是朴素贝叶斯分类器?

朴素贝叶斯是一种基于贝叶斯定理的统计分类技术。它是最简单的监督学习算法之一。朴素贝叶斯分类器快速、准确且可靠,在大型数据集上具有很高的准确率和速度。

朴素贝叶斯分类器假设某一类别中某个特征的影响独立于其他特征。例如,判断一位贷款申请人是否值得放贷,可能取决于其收入、既往贷款与交易记录、年龄与所在地。即便这些特征彼此相关,模型仍将它们视为相互独立。这个假设简化了计算,因此被称为“朴素”。该假设称为类条件独立。

What is Naive Bayes Classifier?
  • P(h):假设 h 为真的概率(与数据无关),称为 h 的先验概率。

  • P(D):数据出现的概率(与假设无关),称为先验概率。

  • P(h|D):在给定数据 D 的条件下假设 h 的概率,称为后验概率。

  • P(D|h):在假设 h 为真时数据 d 出现的概率,也称为后验概率。

朴素贝叶斯分类工作流

进行分类时,第一步是理解问题并识别潜在的特征和标签。特征是会影响标签结果的特性或属性。比如在贷款发放的场景中,银行经理会关注客户的职业、收入、年龄、所在地、历史贷款记录、交易记录和信用评分。这些特性称为特征,帮助模型对客户进行分类。

分类包含两个阶段:学习阶段和评估阶段。在学习阶段,分类器在给定数据集上训练模型;在评估阶段,测试分类器的性能。性能通常根据准确率、错误率、精确率和召回率等指标进行评估。

Classification Workflow

朴素贝叶斯分类器如何工作?

我们通过一个例子来理解朴素贝叶斯的工作原理。给定天气状况与是否进行体育活动的示例,您需要计算进行体育活动的概率。现在,需要根据天气状况来判断球员是否会参加运动。

方法一(单一特征的情况)

朴素贝叶斯分类器按以下步骤计算事件的概率:

  • 步骤 1:计算给定类别标签的先验概率
  • 步骤 2:针对每个类别,计算与各属性相关的似然概率
  • 步骤 3:将这些数值代入贝叶斯公式,计算后验概率
  • 步骤 4:比较各类别的概率,输入样本归入概率更高的类别

为简化先验与后验概率的计算,您可以使用频数表和似然表这两类表格。它们都有助于计算先验和后验概率。频数表包含所有特征的标签出现次数。有两张似然表:似然表 1 显示标签的先验概率,似然表 2 显示后验概率。

tables

现在,假设您要计算当天气为阴(overcast)时进行比赛的概率。

进行比赛的概率:

P(Yes | Overcast) = P(Overcast | Yes) × P(Yes) / P(Overcast)

步骤 1:计算先验概率

  • P(Overcast) = 4/14 = 0.29
  • P(Yes) = 9/14 = 0.64

步骤 2:计算后验概率

  • P(Overcast | Yes) = 4/9 = 0.44

步骤 3:应用贝叶斯公式 

将先验与后验概率代入第一条公式。

P(Yes | Overcast) = 0.44 × 0.64 / 0.29 = 0.98

类似地,您可以计算不进行比赛的概率:

不进行比赛的概率:

P(No | Overcast) = P(Overcast | No) × P(No) / P(Overcast)

步骤 1:计算先验概率

  • P(Overcast) = 4/14 = 0.29
  • P(No) = 5/14 = 0.36

步骤 2:计算似然

  • P(Overcast | No) = 0/5 = 0

步骤 3:应用贝叶斯公式

将先验与似然概率代入公式,得到后验概率。

P(No | Overcast) = 0 × 0.36 / 0.29 = 0

“Yes” 类的概率更高。因此可以判断,如果天气为阴,球员将会参加运动。

方法二(多特征的情况)

How Naive Bayes classifier works?

现在,假设您要计算当天气为阴且气温适中(mild)时进行比赛的概率。

进行比赛的概率:

P(Play = Yes | Weather = Overcast, Temp = Mild) = P(Weather = Overcast, Temp = Mild | Play = Yes) × P(Play = Yes)

基于朴素贝叶斯的独立性假设:

P(Weather = Overcast, Temp = Mild | Play = Yes) = P(Overcast | Yes) × P(Mild | Yes)

步骤 1:计算先验概率

  • P(Yes) = 9/14 = 0.64

步骤 2:计算似然

  • P(Overcast | Yes) = 4/9 = 0.44
  • P(Mild | Yes) = 4/9 = 0.44

步骤 3:计算联合似然

P(Weather = Overcast, Temp = Mild | Play = Yes) = 0.44 × 0.44 = 0.1936

步骤 4:应用贝叶斯公式

P(Play = Yes | Weather = Overcast, Temp = Mild) = 0.1936 × 0.64 = 0.124

类似地,您可以计算不进行比赛的概率:

不进行比赛的概率:

P(Play = No | Weather = Overcast, Temp = Mild) = P(Weather = Overcast, Temp = Mild | Play = No) × P(Play = No)

基于朴素贝叶斯的独立性假设:

P(Weather = Overcast, Temp = Mild | Play = No) = P(Overcast | No) × P(Mild | No)

步骤 1:计算先验概率

  • P(No) = 5/14 = 0.36

步骤 2:计算似然

  • P(Overcast | No) = 0/5 = 0
  • P(Mild | No) = 2/5 = 0.4

步骤 3:计算联合似然

P(Weather = Overcast, Temp = Mild | Play = No) = 0 × 0.4 = 0

步骤 4:应用贝叶斯公式

P(Play = No | Weather = Overcast, Temp = Mild) = 0 × 0.36 = 0

“Yes” 类的概率更高(0.124 对 0),因此当天气为阴且气温适中时,球员将会参加运动。

在 Scikit-learn 中构建分类器

使用合成数据集的朴素贝叶斯分类器

在第一个示例中,我们将使用 scikit-learn 生成合成数据,并训练与评估高斯朴素贝叶斯算法。

生成数据集

Scikit-learn 为我们提供了一个机器学习生态,方便生成数据集并评估各种机器学习算法。

在这里,我们使用 make_classification() 函数创建一个包含 6 个特征、3 个类别、800 个样本的数据集。

from sklearn.datasets import make_classification

X, y = make_classification(
    n_features=6,
    n_classes=3,
    n_samples=800,
    n_informative=2,
    random_state=1,
    n_clusters_per_class=1,
)

我们将使用 matplotlib.pyplot 的 scatter() 函数来可视化数据集。

import matplotlib.pyplot as plt

plt.scatter(X[:, 0], X[:, 1], c=y, marker="*");

可以看到,这里有三种目标标签,我们将训练一个多分类模型。

训练/测试集划分

在开始训练之前,需要将数据集划分为训练集与测试集,用于模型评估。

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.33, random_state=125
)

构建与训练模型

构建一个通用的高斯朴素贝叶斯模型,并在训练数据集上进行训练。随后,将一个随机测试样本输入模型获取预测值。

from sklearn.naive_bayes import GaussianNB

# Build a Gaussian Classifier
model = GaussianNB()

# Model training
model.fit(X_train, y_train)

# Predict Output
predicted = model.predict([X_test[6]])

print("Actual Value:", y_test[6])
print("Predicted Value:", predicted[0])

实际值与预测值一致。

Actual Value: 0
Predicted Value: 0

模型评估

我们将在未见过的测试数据集上评估模型。首先对测试数据集进行预测,并据此计算准确率和 F1 分数。

from sklearn.metrics import (
    accuracy_score,
    confusion_matrix,
    ConfusionMatrixDisplay,
    f1_score,
)

y_pred = model.predict(X_test)
accuray = accuracy_score(y_pred, y_test)
f1 = f1_score(y_pred, y_test, average="weighted")

print("Accuracy:", accuray)
print("F1 Score:", f1)

使用默认超参数,我们的模型表现相当不错。

Accuracy: 0.8484848484848485
F1 Score: 0.8491119695890328

为可视化混淆矩阵,我们将使用 confusion_matrix 计算真阳性与真阴性,并用 ConfusionMatrixDisplay 搭配标签展示混淆矩阵。

labels = [0,1,2]
cm = confusion_matrix(y_test, y_pred, labels=labels)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)
disp.plot();

我们的模型表现良好,进一步可通过特征缩放、预处理、交叉验证和超参数优化来提升性能。

使用贷款数据集的朴素贝叶斯分类器

接下来我们在真实数据集上训练朴素贝叶斯分类器。除预处理与数据探索外,其余步骤将基本重复。

数据加载

本例中,我们将通过 pandas 的 read_csv 函数从 DataLab 加载贷款数据。

import pandas as pd


df = pd.read_csv('loan_data.csv')
df.head()

数据探索

为更好理解数据集,我们将使用 .info()。

  • 数据集包含 14 列与 9578 行。

  • 除 purpose 外,其余列类型为浮点或整数。

  • 我们的目标列为 not.fully.paid。

df.info()
RangeIndex: 9578 entries, 0 to 9577
Data columns (total 14 columns):
#   Column             Non-Null Count  Dtype 
---  ------             --------------  ----- 
0   credit.policy      9578 non-null   int64 
1   purpose            9578 non-null   object
2   int.rate           9578 non-null   float64
3   installment        9578 non-null   float64
4   log.annual.inc     9578 non-null   float64
5   dti                9578 non-null   float64
6   fico               9578 non-null   int64 
7   days.with.cr.line  9578 non-null   float64
8   revol.bal          9578 non-null   int64 
9   revol.util         9578 non-null   float64
10  inq.last.6mths     9578 non-null   int64 
11  delinq.2yrs        9578 non-null   int64 
12  pub.rec            9578 non-null   int64 
13  not.fully.paid     9578 non-null   int64 
dtypes: float64(6), int64(7), object(1)
memory usage: 1.0+ MB

在本例中,我们将构建模型来预测哪些客户未足额还款。让我们使用 seaborn 的 countplot 探索 purpose 与目标列。

import seaborn as sns
import matplotlib.pyplot as plt

sns.countplot(data=df,x='purpose',hue='not.fully.paid')
plt.xticks(rotation=45, ha='right');

我们的数据集存在类别不平衡,这会影响模型表现。您可以查看 重采样不平衡数据集 教程,实践如何处理不平衡数据。

数据处理

我们将使用 pandas 的 get_dummies() 函数把 purpose 列从分类型转换为整数哑变量。

pre_df = pd.get_dummies(df,columns=['purpose'],drop_first=True)
pre_df.head()

随后定义特征(X)与目标(y)变量,并将数据集划分为训练集与测试集。

from sklearn.model_selection import train_test_split

X = pre_df.drop('not.fully.paid', axis=1)
y = pre_df['not.fully.paid']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.33, random_state=125
)

构建与训练模型

模型构建与训练非常简单。我们将在训练数据集上使用默认超参数训练模型。

from sklearn.naive_bayes import GaussianNB

model = GaussianNB()

model.fit(X_train, y_train);

模型评估

我们将使用准确率与 F1 分数评估模型表现。可以看到高斯朴素贝叶斯算法的结果相当不错。

from sklearn.metrics import (
    accuracy_score,
    confusion_matrix,
    ConfusionMatrixDisplay,
    f1_score,
    classification_report,
)

y_pred = model.predict(X_test)

accuray = accuracy_score(y_pred, y_test)
f1 = f1_score(y_pred, y_test, average="weighted")

print("Accuracy:", accuray)
print("F1 Score:", f1)
Accuracy: 0.8206263840556786
F1 Score: 0.8686606980013266

由于数据不平衡,可以看到混淆矩阵呈现出不同的信息:在少数类目标“未足额还款”上,误标更多。

labels = ["Fully Paid", "Not fully Paid"]
cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)
disp.plot();

如果您在训练或评估过程中遇到问题,可以查看基于 Scikit-learn 的朴素贝叶斯分类教程DataLab 工作簿。其中包含数据集、源代码与输出结果。

零概率问题

假设数据集中不存在任何“高风险贷款”的样本;在这种情况下,后验概率将为零,模型将无法作出预测。这个问题称为“零概率问题”,因为某一类别的出现次数为零。

解决方案是拉普拉斯校正(Laplacian correction)或拉普拉斯平滑。拉普拉斯校正是一种平滑技术。在此您可以假设数据集足够大,给每个类别都增加一行样本不会对概率估计造成实质影响。这样可以避免概率值为零的问题。

例如:设某“高风险贷款”类别在数据库中共有 1000 个训练样本。在该数据库中,收入列中“低收入”为 0 个样本,“中等收入”为 990 个样本,“高收入”为 10 个样本。未经拉普拉斯校正时,这些事件的概率分别为 0、0.990(990/1000)和 0.010(10/1000)。

现在对给定数据集应用拉普拉斯校正。为每个收入取值组合各增加 1 个样本。此时这些事件的概率为:

朴素贝叶斯的优点

  • 不仅方法简单,而且预测快速且准确。
  • 朴素贝叶斯的计算成本很低。
  • 能够高效处理大型数据集。
  • 与连续响应变量相比,在离散响应变量情况下表现更好。
  • 可用于多分类问题。
  • 在文本分析问题中也有良好表现。
  • 当独立性假设成立时,朴素贝叶斯分类器往往优于逻辑回归等其他模型。

朴素贝叶斯的缺点

  • 特征独立性的假设。在实际中几乎不可能完全获得相互独立的预测变量集。
  • 若某个类别在训练集中没有样本,会导致后验概率为零,此时模型无法进行预测。这被称为零概率/零频率问题。

结论

恭喜您读完本教程!

在本教程中,您学习了朴素贝叶斯算法、其工作原理、朴素假设、相关问题、实现方法,以及它的优缺点。过程中,您还学习了在 scikit-learn 中针对二分类与多项分类进行模型构建与评估。

朴素贝叶斯是最简洁且强大的算法之一。尽管近年来机器学习取得了巨大进步,它依然证明了自身价值,已在从文本分析到推荐引擎的诸多应用中成功落地。

如果您想进一步学习 Python 中的 scikit-learn,请参加我们的 使用 scikit-learn 的监督学习 课程,并查看 Scikit-Learn 教程:棒球分析(第一部分)。

主题