引言
本篇文章将学习两个简单而又经典的机器学习算法:朴素贝叶斯和支持向量机(SVM)。为什么选择这两个算法?因为它们代表了机器学习中两种不同的类型:贝叶斯算法基于概率统计,可以直观的展现结果,而SVM基于几何间隔,能力强大而精确。通过学习它们,我们将更加理解机器学习的基本思维方式。
一、朴素贝叶斯算法——概率的智慧
1、贝叶斯定理:从原因推结果,从结果推原因
这里我们举一个经典的例子来理解贝叶斯:假设学校中男生占60%,女生占40%。男生全部穿长裤,女生一半穿长裤一半穿裙子。现在看到一位穿长裤的学生,请问这是女生的概率是多少?
其中正向概率(已知性别,求穿裤子概率)是直接的统计:
随机选一人是男生且穿长裤的概率 = 60% × 100% = 0.6,随机选一人是女生且穿长裤的概率 = 40% × 50% = 0.2,因此,随机选一人穿长裤的总概率 = 0.6 + 0.2 = 0.8。
而我们贝叶斯要解决的正是逆向概率(已知穿裤子,求是女生概率):
我们想要知道的是:当“穿长裤”这个证据已经发生时,该学生是女生的概率是多少?
用贝叶斯公式表示:
P(女生|穿长裤) = [P(穿长裤|女生) × P(女生)] / P(穿长裤) = (50% × 40%) / (60% + 20%) = 25%尽管女生只占40%,且只有一半女生穿长裤,但当我们观察到“穿长裤”这一证据后,该学生是女生的概率就从最初的40%(先验的概率)更新为了25%(后验的概率)。贝叶斯推断的关键就在于这种基于证据的概率动态更新过程。
2、朴素贝叶斯分类器:简单而强大
朴素贝叶斯模型的朴素之处,在于其假设所有特征在给定类别下相互独立,这一简化在现实中常不成立。然而,它却能有效地工作,原因在于:该假设极大简化了计算,使其能快速处理高维数据,同时又避免了复杂模型容易产生的过拟合问题。更重要的是,对于分类任务,模型只需比较概率的相对大小,即使独立性假设不满足,其概率估计的偏差也往往不影响最终的分类结果。因此,这种假设以可控的近似换取了强大的实用性与计算效率。
我们用一个简单的例子来解释:
假设我们有以下数据,判断一个邮件是否是垃圾邮件:
邮件内容关键词 是否垃圾邮件 "优惠"、"免费" 是 "会议"、"报告" 否 "免费"、"赢奖" 是 "项目"、"进度" 否现在来了新邮件:"优惠"、"赢奖"、"免费"
计算是垃圾邮件的概率: P(垃圾|"优惠","赢奖","免费") ∝ P(垃圾) × P("优惠"|垃圾) × P("赢奖"|垃圾) × P("免费"|垃圾) 计算不是垃圾邮件的概率: P(正常|"优惠","赢奖","免费") ∝ P(正常) × P("优惠"|正常) × P("赢奖"|正常) × P("免费"|正常)比较两者大小,取概率大的作为预测结果
3、三种贝叶斯分类器
朴素贝叶斯模型为适应不同类型的数据,衍生出了几种主要类型。其核心“特征条件独立”的朴素假设不变,但对特征分布的预设不同:
1.高斯朴素贝叶斯假设连续型特征服从正态分布,适用于如身高、温度等数据。
2.多项式朴素贝叶斯专为处理离散计数特征设计,尤其适合文本分类中的词频统计。
3.伯努利朴素贝叶斯则面向二值化(是/否、出现/未出现)特征,常用于文档分类中是否包含特定词汇的分析。
4、贝叶斯算法的优缺点
贝叶斯算法的优点有:
首先,它的数学基础坚实、模型结构清晰,便于我们理解和解释。同时,它的计算效率极高,能快速处理大规模数据集,不仅在大数据上表现优异,对小规模的数据也有良好的适应能力。另外,它支持增量学习,当新数据到来时,无需重新训练整个模型,仅通过直接更新概率估计即可完成迭代,这在实践中有很高的可用性与实用性。
但它又有不足之处
其最关键的缺点在于“特征条件独立”这一基本假设太过于理想化,现实数据中的特征往往相互关联,这使得模型在理论上存在瑕疵。此外,该模型对输入数据的表达形式较为敏感,不同的特征工程方法可能明显影响其最终性能。因此,它在处理特征间存在强依赖关系或数据表达不当时,效果可能不尽如人意。
二、支持向量机(SVM)——边界的力量
1、SVM的直观理解:最佳的分界线
我们来说一个故事:王子要救公主,魔鬼在桌上放了两种颜色的球,让王子用一根棍子分开它们。要求是:即使魔鬼再放更多球,这根棍子仍然能很好地区分。第一次,王子随便一放很轻松的分开了桌上现有的球,魔鬼又放了更多球,有些球站错了队。这时王子忽然意识到:棍子应该放在离两边球都最远的位置,这样即使有新球加入,也不容易分错。
王子最初随意放置棍子导致分类错误,类比于普通的分类器容易过拟合。而他后来的领悟:应将棍子放在离两侧球类最远的位置,正是SVM寻找“最大间隔”超平面的精髓。这样的分界线不仅对现有数据分类,更确保了最佳的抗干扰能力。当魔鬼(新测试数据)加入更多球时,这条基于最外围关键球(即“支持向量”)所确立的最宽“隔离带”,就能最可靠地保持分类效果。
2 、SVM的数学之美
1.超平面方程
超平面是支持向量机(SVM)用于分类的决策边界,它的方程形式随数据维度提升而扩展。在二维空间中,它表现为一条直线(y = kx + b),在三维空间中,则为一个平面(Ax + By + Cz + D = 0),而当维度增至n维时,便成为超平面,其通用方程为 ω₁x₁ + ω₂x₂ + ... + ωₙxₙ + b = 0。其中,ω 是决定超平面方向的法向量,b 是偏移量。SVM的核心目标正是通过优化算法,找到那个能将不同类别样本分开、且间隔最大的最优超平面。
在二维空间,分界线是直线:y = kx + b 在三维空间,分界线是平面:Ax + By + Cz + D = 0 在更高维度,我们称之为超平面:ω₁x₁ + ω₂x₂ + ... + ωₙxₙ + b = 02. 间隔最大化
SVM的核心目标并非仅仅找到一个分界超平面,而是要找到那个具有“最大间隔”的分界超平面。因为:间隔越大,意味着分类决策的置信度越高、容错空间越大,从而使模型的泛化能力越强,对未来新数据的预测更为稳健。其数学实现的关键步骤在于:首先,定义支持向量(离超平面最近的样本点)到超平面的几何距离公式为 d = |ω·x + b| / ||ω||。SVM的优化目标即是最大化这个距离。为简化计算,通过缩放ω和b,可以令支持向量处的函数值满足 y_i(ω·x_i + b) = 1。此时,最大化间隔问题便等价于在约束条件 y_i(ω·x_i + b) ≥ 1 下,最小化 ||ω||²(或等价地最小化 ½||ω||²),从而转化成一个经典的凸二次规划问题。
支持向量到超平面的距离:d = |ω·x + b| / ||ω|| 我们希望最大化这个距离 通过缩放,令支持向量满足:y_i(ω·x_i + b) = 1 优化问题转化为:最小化||ω||²,满足y_i(ω·x_i + b) ≥ 13.支持向量的重要性
经过训练后,最终的决策超平面完全由位于间隔边界上的那少数几个样本“支持向量”所决定。在间隔边界之外的所有样本,无论是远离边界还是非常遥远,它们在确定超平面位置时不算数。只有这些作为边界上的支持向量,它们的位置直接定义了间隔的宽度和边界的方向。所以,即使我们删除所有非支持向量的样本,训练得到的模型依然不变。这一特性带来了两大优势:一是模型不易受多数内部点的影响,二是计算高效,预测时只需计算新样本与这些少数支持向量的关系即可。
3、处理线性不可分:核技巧
现实中的数据往往不是线性可分的。魔鬼使了个坏,把球摆成了这样:
红球 蓝球 红球 蓝球 红球这时王子一拍桌子,球都飞了起来,他在空中插入一张纸,完美分开了两种颜色的球。这就是核技巧的本质:将数据映射到更高维度,使其线性可分。
SVM的解决思路就是通过一个隐式的映射函数 φ,将数据投射到一个更高维的特征空间。在那个高维空间中,原本纠缠的数据变得线性可分(就像一张纸在空中分开了球)。它的精妙之处在于,它无需复杂地计算高维映射 φ(x) 本身,而只需通过一个核函数 K(x, y) 来计算原始空间中样本对在高维空间的内积,极大地降低了计算成本。
常用核函数:
线性核:本质是在原始空间线性分割,是高效的基础选项。(K(x,y) = x·y) 多项式核:通过多项式的次数(d)显式地控制映射的维度与复杂度。(K(x,y) = (γx·y + r)^d) 高斯核(RBF):最常用也最强大。它实际上将数据映射到了无限维空间,并通过参数 γ 精细控制单个样本的影响范围,从而能拟合极其复杂的非线性边界。(K(x,y) = exp(-γ||x-y||²))4、处理噪声:软间隔
为处理现实数据中的噪声与异常点,SVM引入了软间隔的概念,允许部分样本跨越边界或分类错误,以此提升模型的泛化能力。其关键在于惩罚因子C,它控制着模型对分类错误的容忍程度:C值越大,对误分类的惩罚越严厉,迫使模型尽可能正确分类所有样本,但这往往导致间隔变窄,可能引发过拟合,C值越小,则允许更多的样本违背间隔规则,从而获得更宽的间隔和更简单的模型,增强了稳健性但可能欠拟合。因此,C的调节本质上是“间隔宽度”与“分类准确度”之间的一种权衡,帮助我们找到一个既不过于严格、也不过于松懈的最优决策边。
5、SVM的优缺点
支持向量机(SVM)的主要优点为:
首先,它在高维特征空间中表现良好,尤其适合文本等数据。其次,其模型具有稀疏性,决策仅依赖于少数支持向量,因此内存效率高。第三,通过最大化间隔的核心目标,它通常会表现出很强的泛化能力。最后,借助核技巧,它能灵活地将数据映射到高维空间,从而有效解决复杂的非线性分类问题。
支持向量机(SVM)也存在一些固有的局限:
首先,其训练过程涉及复杂的二次规划求解,在处理大规模数据集时计算速度较慢。其次,模型的性能高度依赖于惩罚参数C和核函数的选择,调参需要一定的经验和技巧。最后,SVM的输出是直接的分类决策,而非天然的概率估计,要获得样本属于各类别的概率需要额外的计算。
三、从理论到实践——完整项目示例
这里用一个完整的简单项目来直观的演示两种算法:
1. 数据加载与探索
import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import load_iris import numpy as np # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False iris = load_iris() # 加载数据集 X = iris.data y = iris.target导入必要的库:pandas(数据处理)、matplotlib(可视化)、sklearn.datasets(数据集)、numpy(数值计算),设置中文字体是为了让图表能正确显示中文标签,load_iris() 加载的是一个包含150个样本、4个特征、3个类别的经典数据集。
2. 数据分割
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42 )train_test_split 将数据随机分成训练集和测试集,test_size=0.3:30%的数据作为测试集,70%作为训练集,random_state=42:设置随机种子,确保每次分割结果一致,最后结果是4个数组:训练特征、测试特征、训练标签、测试标签。
3. 数据标准化
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) # 计算均值和标准差,并转换训练集 X_test_scaled = scaler.transform(X_test) # 使用训练集的参数转换测试集StandardScaler 执行标准化:(x - 均值) / 标准差,fit_transform 只对训练集使用:计算训练集的均值和标准差,然后转换训练集
,transform 对测试集使用:使用训练集计算的参数转换测试集。
4. 模型训练与评估 - 朴素贝叶斯
from sklearn.naive_bayes import GaussianNB from sklearn.metrics import accuracy_score, classification_report # 模型1: 朴素贝叶斯 print("=" * 50) print("朴素贝叶斯模型") print("=" * 50) nb_model = GaussianNB() nb_model.fit(X_train_scaled, y_train) nb_pred = nb_model.predict(X_test_scaled) nb_accuracy = accuracy_score(y_test, nb_pred) print(f"准确率: {nb_accuracy:.4f}") print("\n分类报告:") print(classification_report(y_test, nb_pred, target_names=iris.target_names))可视化部分:
# 朴素贝叶斯可视化 plt.figure(figsize=(10, 6)) plt.scatter(range(len(y_test)), y_test, alpha=0.6, label='真实标签', marker='o', s=80) plt.scatter(range(len(y_test)), nb_pred, alpha=0.6, label='预测标签', marker='x', s=80) # 标记错误预测的点 errors = np.where(nb_pred != y_test)[0] if len(errors) > 0: plt.scatter(errors, y_test[errors], color='red', s=150, alpha=0.8, label='错误预测', marker='s', edgecolors='black') plt.title(f"朴素贝叶斯模型预测结果\n准确率: {nb_accuracy:.4f}", fontsize=14) plt.xlabel('样本索引', fontsize=12) plt.ylabel('类别', fontsize=12) plt.legend(fontsize=11) plt.grid(True, alpha=0.3) plt.xticks(fontsize=11) plt.yticks(fontsize=11) plt.tight_layout() plt.show()5. 模型训练与评估 - 线性SVM
# 模型2: 线性SVM print("\n" + "=" * 50) print("线性SVM模型") print("=" * 50) svm_linear = SVC(kernel='linear', C=1.0, random_state=42) svm_linear.fit(X_train_scaled, y_train) svm_linear_pred = svm_linear.predict(X_test_scaled) svm_linear_accuracy = accuracy_score(y_test, svm_linear_pred) print(f"准确率: {svm_linear_accuracy:.4f}") print("\n分类报告:") print(classification_report(y_test, svm_linear_pred, target_names=iris.target_names))kernel='linear':使用线性核函数,C=1.0:正则化参数,控制分类错误的惩罚程度,random_state=42:确保结果可重复。
6. 线性SVM可视化
# 线性SVM可视化 plt.figure(figsize=(10, 6)) plt.scatter(range(len(y_test)), y_test, alpha=0.6, label='真实标签', marker='o', s=80) plt.scatter(range(len(y_test)), svm_linear_pred, alpha=0.6, label='预测标签', marker='x', s=80) # 标记错误预测的点 errors = np.where(svm_linear_pred != y_test)[0] if len(errors) > 0: plt.scatter(errors, y_test[errors], color='red', s=150, alpha=0.8, label='错误预测', marker='s', edgecolors='black') plt.title(f"线性SVM模型预测结果\n准确率: {svm_linear_accuracy:.4f}", fontsize=14) plt.xlabel('样本索引', fontsize=12) plt.ylabel('类别', fontsize=12) plt.legend(fontsize=11) plt.grid(True, alpha=0.3) plt.xticks(fontsize=11) plt.yticks(fontsize=11) plt.tight_layout() plt.show()7. 模型训练与评估 - 高斯核SVM
# 模型3: 高斯核SVM print("\n" + "=" * 50) print("高斯核SVM模型") print("=" * 50) svm_rbf = SVC(kernel='rbf', C=1.0, gamma=0.1, random_state=42) svm_rbf.fit(X_train_scaled, y_train) svm_rbf_pred = svm_rbf.predict(X_test_scaled) svm_rbf_accuracy = accuracy_score(y_test, svm_rbf_pred) print(f"准确率: {svm_rbf_accuracy:.4f}") print("\n分类报告:") print(classification_report(y_test, svm_rbf_pred, target_names=iris.target_names))kernel='rbf':使用径向基函数(高斯核),gamma=0.1:控制核函数的宽度,影响每个训练样本的影响范围。
8.高斯核SVM可视化
# 高斯核SVM可视化 plt.figure(figsize=(10, 6)) plt.scatter(range(len(y_test)), y_test, alpha=0.6, label='真实标签', marker='o', s=80) plt.scatter(range(len(y_test)), svm_rbf_pred, alpha=0.6, label='预测标签', marker='x', s=80) # 标记错误预测的点 errors = np.where(svm_rbf_pred != y_test)[0] if len(errors) > 0: plt.scatter(errors, y_test[errors], color='red', s=150, alpha=0.8, label='错误预测', marker='s', edgecolors='black') plt.title(f"高斯核SVM模型预测结果\n准确率: {svm_rbf_accuracy:.4f}", fontsize=14) plt.xlabel('样本索引', fontsize=12) plt.ylabel('类别', fontsize=12) plt.legend(fontsize=11) plt.grid(True, alpha=0.3) plt.xticks(fontsize=11) plt.yticks(fontsize=11) plt.tight_layout() plt.show()9.运行结果
这里的数据集较为简单,所以准确率会普遍较高,感兴趣的话可以找一些大型的数据集来测试并观察结果。
我们到这就学完了贝叶斯和SVM算法了,在下一章里我们将学习另几种机器学习中的经典算法。