写在前面
在这一篇文章之后,我们就对八大基础机器学习模型有个大致的概念了,包括上一篇《4.3回归》中提到的线性回归、多项式回归,以及这一篇中会提到的knn、决策树、集成模型、逻辑回归、SVM支持向量机、朴素贝叶斯。
1.python基础;
2.ai模型概念+基础;
3.数据预处理;
4.机器学习模型--1.聚类;2.降维;3.回归(预测);4.分类;
5.正则化技术;
6.神经网络模型--1.概念+基础;2.几种常见的神经网络模型;
7.对回归、分类模型的评价方式;
8.简单强化学习概念;
9.几种常见的启发式算法及应用场景;
10.机器学习延申应用-数据分析相关内容--1.A/B Test;2.辛普森悖论;3.蒙特卡洛模拟;
以及其他的与人工智能相关的学习经历,如数据挖掘、计算机视觉-OCR光学字符识别等。
本文目录
K-最近邻(K-Nearest Neighbors, KNN)
1. ID3(Iterative Dichotomiser 3)
3. CART(Classification and Regression Trees)
支持向量机(SVM,Support Vector Machine)
K-最近邻(K-Nearest Neighbors, KNN)
概念
K-最近邻(KNN)是一种非参数化(不对数据的分布进行明确的假设,并且在训练过程中不涉及模型的参数化拟合)的监督学习算法(在第二篇《ai模型基础+概念》中具体介绍过),可以用于分类和回归任务。它通过比较新数据点与训练数据集中的样本之间的距离,利用最近的K个邻居的信息来进行预测。
超参数
1. K值(邻居数)
- 描述:K值表示用来预测的最近邻居的数量。在KNN算法中,K值直接影响模型的预测结果。
- 影响:
- K值过小:模型会对训练集的噪声点过度敏感,导致过拟合(见下“决策树”部分)。
- K值过大:模型会对多数类产生偏向,忽略局部结构,导致欠拟合。
- 调优方法:通常通过交叉验证(在第二篇中有具体介绍)来选择合适的K值。最常见的选择范围是从1到20,但具体取决于数据集的特征。
2. 距离度量方式
- 描述:KNN算法需要计算数据点之间的距离,不同的距离度量方式会影响最终的结果。常见的距离度量方法(在《4.1聚类》中有具体介绍)有:
- 欧氏距离(Euclidean Distance):最常用的距离度量,计算两个点之间的直线距离。
- 曼哈顿距离(Manhattan Distance):计算两个点在轴上的距离之和。
- 余弦相似度(Cosine Similarity):用于衡量两个向量的夹角,常用于文本数据或高维稀疏数据。
- 闵可夫斯基距离(Minkowski Distance):包括欧氏距离和曼哈顿距离的推广形式,当p=2时为欧氏距离,p=1时为曼哈顿距离。
- 影响:不同的数据结构适合不同的距离度量方式,因此选择合适的度量方法可以提高模型性能。
3. 权重(weights)
- 描述:KNN可以选择是否为邻居的距离加权。默认情况下,所有K个邻居的权重相同,称为“均等权重”。但也可以根据距离进行加权,距离越近的点权重越大,常见的加权方式有:
- uniform:所有邻居的权重相同。
- distance:距离越近的邻居权重越大,通常采用反比权重 1/d,即距离越小,权重越大。
- 影响:在一些情况下,使用距离加权可以提高模型的预测准确性,特别是当离测试点最近的邻居具有更强的代表性时。
4. 算法(algorithm)
- 描述:KNN在实现上有不同的算法来加速邻居搜索,尤其是在大规模数据集上。常用的实现算法有:
- brute force:暴力搜索(数据结构基础),计算所有点的距离,时间复杂度为O(n)。
- kd-tree:通过构建kd-tree来加速KNN的搜索,适用于中等规模的低维数据。
- ball-tree:类似于kd-tree,但更适合处理高维数据。
- 影响:不同算法适合不同的数据集规模和维度。通常,暴力搜索适合小数据集,kd-tree和ball-tree则适合更大的数据集。
brute force
概念
暴力搜索通过逐一遍历比较所有数据点与查询点之间的距离,找到最近邻。
目标
确保找到目标点的最近邻,无需预先构建数据结构。
算法原理
- 对于查询点,计算与每个数据点的距离。
- 找到最小距离对应的数据点,返回该点作为最近邻。
查询过程
查询时,从根节点开始,判断查询点是否在当前节点的球内。如果在,就递归搜索该节点的子树。若不在,则直接忽略该子树。这种方式能够排除与查询点距离较远的部分,从而加速最近邻查询。
KD-tree (K-Dimensional Tree)
概念
KD-tree是一种对空间进行递归划分的树形数据结构,专门用于处理多维数据(通常是几何数据),比如在KNN算法中,它可以有效加速最近邻查询。
目标
通过将数据划分成多个子区域,KD-tree使得最近邻查询可以避免对每个点进行遍历,缩小需要搜索的范围,减少计算复杂度。
算法原理
- 递归划分:KD-tree通过选择某一维度上的数据进行划分,递归地将数据空间分成两部分。一般情况下,会选择每一维的中位数来划分,从而保证每个子区域拥有接近相同数量的点。
- 节点的划分方式:每个节点将数据空间分成两部分,左子树包含比当前节点坐标小的数据,右子树包含比当前节点坐标大的数据。每一层的划分维度通常是轮流选择的,比如第一层按x维划分,第二层按y维划分,第三层按z维划分,依次类推。
查询过程
最近邻查询时,从树的根节点开始,递归搜索最近的子树,直到找到叶子节点。找到候选点后,再回溯检查另一个子树是否可能包含更近的点。
ball-tree
概念
Ball-tree是一种用于加速高维空间中数据查询的树结构,类似于KD-tree,但它通过“球”(ball)来分割数据,适用于更高维度的数据集。Ball-tree会将数据分割为一系列的超球体,查询时只需要检查可能包含最近邻的球体。
目标
Ball-tree的目标是通过使用包围球体将数据分区,使得在高维空间中搜索最近邻居时能更有效地排除无关区域。
算法原理
- 递归划分:与KD-tree不同,Ball-tree不是简单地沿某个轴进行划分,而是根据数据的分布,计算一个球体(以质心为中心的超球),并将数据分成两个子集。
- 球体的划分方式:对于每个节点,Ball-tree会计算该节点包含的数据的质心和最大半径,并通过不同的方法划分出两个球体,将数据集递归划分成两个部分。每个节点保存一个超球体,描述它所包含的数据范围。
- 子树划分:继续递归划分,直到每个叶子节点包含的数据量少于设定的阈值。
查询过程
查询时,从根节点开始,判断查询点是否在当前节点的球内。如果在,就递归搜索该节点的子树。若不在,则直接忽略该子树。这种方式能够排除与查询点距离较远的部分,从而加速最近邻查询。
三者对比
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# 创建数据集
X, y = make_classification(n_samples=300, n_features=2, n_redundant=0, n_repeated=0, n_classes=2, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 在 make_classification 函数中,这几个参数用于控制生成的数据集的特征。它们的具体含义如下:
# n_informative:这是生成数据集中信息量的特征数量。信息特征是对分类目标有影响的特征,模型可以利用这些特征进行有效的预测。
# n_redundant:这是生成数据集中冗余特征的数量。冗余特征是指那些与信息特征高度相关的特征,它们并不提供额外的信息,通常是从信息特征的线性组合中生成的。
# n_repeated:这是生成数据集中重复特征的数量。重复特征是完全相同的特征,通常是对信息特征的复制。它们不会对模型的预测能力产生额外的贡献。
# 当设置这些参数时,它们的总和必须小于总特征数 n_features,因为这意味着生成的数据集有足够的特征来包含所有类型的特征(信息、冗余和重复)。
# 使用KNN分类器
knn_brute = KNeighborsClassifier(n_neighbors=5, algorithm='brute')
knn_kd = KNeighborsClassifier(n_neighbors=5, algorithm='kd_tree')
knn_ball = KNeighborsClassifier(n_neighbors=5, algorithm='ball_tree')
# 训练模型
knn_brute.fit(X_train, y_train)
knn_kd.fit(X_train, y_train)
knn_ball.fit(X_train, y_train)
# 预测
y_pred_brute = knn_brute.predict(X_test)
y_pred_kd = knn_kd.predict(X_test)
y_pred_ball = knn_ball.predict(X_test)
# 可视化结果
plt.figure(figsize=(15, 5))
plt.subplot(131)
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_pred_brute, cmap='coolwarm')
plt.title('KNN (Brute Force) Prediction')
plt.subplot(132)
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_pred_kd, cmap='coolwarm')
plt.title('KNN (KD-Tree) Prediction')
plt.subplot(133)
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_pred_ball, cmap='coolwarm')
plt.title('KNN (Ball-Tree) Prediction')
plt.show()
虽然在小规模数据集上,暴力搜索可能与其他方法表现相似,但在大规模数据集上,KD-tree和Ball-tree将更有效。
5. p值(闵可夫斯基距离的指数参数)
- 描述:在使用闵可夫斯基距离时,p值决定了具体使用的距离度量方式:
- 当 p = 1 时,闵可夫斯基距离就是曼哈顿距离。
- 当 p = 2 时,闵可夫斯基距离就是欧氏距离。
- 影响:p值会影响到不同邻居之间距离的计算方式,进而影响分类效果。常用的p值为1和2。
Python中KNN的超参数设置
在Scikit-learn中,可以通过以下方式设置KNN的超参数:
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(
n_neighbors=5, # K值
metric='minkowski', # 距离度量方式,默认为闵可夫斯基距离
p=2, # 闵可夫斯基距离的p值
weights='uniform', # 权重,默认为均等权重
algorithm='auto' # 搜索算法,auto会自动选择合适的算法
)
目标
KNN的目标是根据训练数据集中的样本,预测新样本所属的类别(分类任务)或预测其具体数值(回归任务)。对于分类任务,KNN通过“投票”来决定分类结果;对于回归任务,它通过“平均值”来进行预测。
假设
KNN算法的一个基本假设是相似的数据点具有相似的输出,即距离较近的数据点通常有相同的标签或相似的数值。这意味着在特征空间中,数据点的分布较为规则且平滑。
常见应用场景
- 图像分类:根据相似的像素分布,KNN可以对图像进行分类。
- 文本分类:在自然语言处理中,用于根据词频或文本相似度对文档进行分类。
- 推荐系统:基于用户的历史行为或偏好,KNN可以用来推荐相似的商品。
- 金融数据分析:用于股票价格预测、风险评估等。
算法原理
KNN通过计算样本与数据集中所有点的距离,选择距离最近的K个邻居,根据邻居的类别来预测新的样本类别或数值。
最常见的距离度量是欧氏距离(Euclidean Distance)(在《4.1聚类》中有详细介绍)
分类
- 计算测试点与训练数据集中的每个点的距离。
- 选择距离最近的K个点(邻居)。
- 对这K个邻居的类别进行投票,得票最多的类别作为预测结果。
回归
- 计算测试点与训练数据集中的每个点的距离。
- 选择距离最近的K个点。
- 计算这K个邻居的目标值的平均值(或加权平均值),作为预测结果。
算法步骤
- 准备数据:包括训练集和测试集。
- 计算距离:对测试集中的每个点,计算它与训练集中的每个样本的距离。
- 选择K个最近邻居:根据计算得到的距离,选择K个最近的样本点。
- 分类/回归:
- 分类:根据K个邻居的类别进行投票,选出占多数的类别作为结果。
- 回归:对K个邻居的值进行平均,得到预测值。
- 输出结果。
代码
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
# 生成一个简单的二维分类数据集
X, y = make_classification(n_samples=100, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_classes=2, n_clusters_per_class=1, random_state=42)
# 绘制原始数据集
plt.figure(figsize=(8, 5))
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k', cmap=ListedColormap(('orange', 'cornflowerblue')))
plt.title('Original 2D Classification Dataset')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend(['Class 0', 'Class 1'])
plt.grid()
plt.show()
# 分割数据为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建KNN分类器,并设置K值为5
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
# 生成网格以进行决策边界可视化
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02))
# 预测整个网格上的分类结果
Z = knn.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制决策边界
plt.contourf(xx, yy, Z, alpha=0.8, cmap=ListedColormap(('orange', 'cornflowerblue')))
# 绘制训练集和测试集
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolors='k', marker='o', label='Train')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, edgecolors='k', marker='x', label='Test')
plt.title('KNN Classification (k=5)')
plt.legend()
plt.show()
输出
原始数据集的样子:
分类后的结果:
在上面的代码中,生成了一个二维的分类数据集并使用KNN进行分类。图中显示了KNN的决策边界,训练集和测试集样本的分布。
- 橙色区域和蓝色区域表示分类器的决策边界。
- 圆圈标记为训练数据,叉号标记为测试数据(不同特征类别的数据中有各自的训练及测试数据)。
决策树(Decision Tree)
概念
决策树是一种树状结构的机器学习算法,既可以用于分类问题(分类树)也可以用于回归问题(回归树)。它通过对数据特征的分裂来构建一棵树,从根节点开始,每个内部节点代表一个特征,每个叶节点代表最终的输出类别或值。其目标是通过最优的特征分裂点,最大化信息增益或其他度量标准(如Gini指数或均方误差)(见下),从而提高模型的预测能力。
属于非参数模型,因此不需要像线性回归那样对数据有线性或分布上的假设。
超参数
max_depth
:树的最大深度。通过限制树的深度可以防止过拟合(见下)。min_samples_split
:在内部节点再分裂之前,节点中至少要包含的样本数。min_samples_leaf
:叶子节点中最少的样本数,控制叶子节点的大小。criterion
:用于分裂节点的准则(分类树:gini
或entropy(见下)
;回归树:mse
、mae(第7篇《评估》中会具体介绍)
等)。max_features
:用于最佳分裂的最大特征数,限制可以减少计算开销。splitter
:选择分裂策略,可以是best
(选择最佳分割点)或random
(随机选择分割点)。
目标
决策树的目标是基于特征将数据划分到不同的叶子节点,使得每个叶子节点中的数据尽可能“纯净”,即属于同一个类别或有相似的数值。
信息增益
决策树算法计算信息增益时,常用的衡量标准主要包括以下几种:
1. 熵(Entropy)
定义为数据集的纯度(信息论中的概念):
其中, 是第 i 类的概率。
条件熵(Conditional Entropy):表示在给定特征的条件下,数据集的熵;
信息增益:熵-条件熵,信息增益越大,表示特征𝐴对于分类的贡献越大。
2. 增益率
增益率对信息增益进行了修正,以避免偏向于具有较多取值的特征。
分裂信息:用于度量特征取值的多样性,修正信息增益的偏向性。其计算公式为:
- 其中,𝐴是特征, 是特征 𝐴的所有可能取值, 表示取值为 v 的数据子集,S 是数据集。
增益率=信息增益/分裂信息,增益率考虑了特征的多样性,能够更公平地评估特征的有效性。
3. 基尼系数(gini)
基尼指数越小,表示数据集的纯度越高。除二分类问题外,不适合解决多分类问题。
决策树三种算法(面经)
1. ID3(Iterative Dichotomiser 3)
- 基本原理:ID3算法通过计算信息增益来选择特征进行划分。它选择信息增益最大的特征作为决策节点。
- 特征处理:主要处理离散特征,对连续特征需要进行离散化处理。
- 树的构建:生成的树可能较大,容易出现过拟合。可生成多叉树。
2. C4.5
- 基本原理:C4.5是ID3的改进版本,使用增益率而非信息增益来选择特征,减少偏向于多值特征的问题。
- 特征处理:可以处理连续和离散特征,支持特征的缺失值。
- 剪枝策略:引入了后剪枝机制(见下),以减少过拟合,提高模型的泛化能力。
- 树的构建:生成的决策树通常较小且更稳定。可生成多叉树。
3. CART(Classification and Regression Trees)
- 基本原理:CART既可以用于分类也可以用于回归,采用基尼指数作为分类的标准(或均方误差用于回归)。
- 特征处理:可以处理连续和离散特征,不需要离散化。
- 剪枝策略:采用成本复杂度剪枝,通过交叉验证(第二篇中具体介绍)选择最优的剪枝参数。
- 树的构建:采用二叉树结构,每个节点只有两个子节点(即二分法)。即只能构建二叉树。
在使用sklearn的DecisionTreeClassifier中,默认情况下会使用CART算法。ID3和C4.5算法scikit-learn并不提供直接实现。
优缺点
优点
- 易于理解和解释:决策树结构直观,容易可视化,可以通过树形图展示决策过程,便于解释。
- 无需特征缩放:不需要对特征进行标准化或归一化处理,直接使用原始数据。
- 处理非线性数据:能够捕捉复杂的非线性关系,不要求数据线性可分。
- 适用性广:可用于分类和回归问题,适用场景广泛。
- 处理缺失值:能够处理缺失值,对于缺失特征的数据也具有较好的鲁棒性。
缺点
- 容易过拟合:决策树模型容易在训练数据上过拟合,尤其是在树的深度较大时,导致泛化能力下降。
- 对小变化敏感:小的变化可能导致决策树结构的大幅度变化,因此不够稳定。
- 偏向于某些特征:在特征数目不平衡时,决策树可能偏向于某些特征,影响决策效果。
- 决策边界不平滑:决策树的决策边界通常是不平滑的,可能无法很好地处理某些类型的分类任务。
- 计算复杂度:对于大数据集,构建决策树的计算复杂度较高,可能导致训练时间较长。
步骤
- 选择最优特征:根据某种标准(如信息增益或基尼指数)选择当前最优的特征进行数据分割。
- 数据分割:将数据集分成不同的子集,递归进行分裂。
- 递归构建子树:对每个子集递归重复上述步骤,直到满足停止条件(如达到最大深度或叶子节点的样本数小于设定的阈值)。
- 叶子节点输出:最终每个叶子节点对应一个分类结果或回归值。
过拟合
过拟合是机器学习中常见的一种现象,指模型在训练数据上表现非常好,但在新数据(测试集)上效果很差。换句话说,模型过度“记住”了训练数据中的细节和噪声,而没有学到普遍的规律,因此对新的数据缺乏泛化能力。
假设你要训练一个模型来预测学生的考试成绩。你使用了过多的特征,比如学生的座位号、考试当天的天气等无关因素。如果模型太复杂(比如你选择了一个高阶多项式回归模型),它会在训练数据上表现得很好,准确地预测出所有成绩。但当你用新的数据进行测试时,由于模型对噪声和无关特征的过度拟合,预测结果可能会非常不准确。
直观的图示
- 欠拟合:模型太简单,没能捕捉到数据的模式。
- 适度拟合:模型正好捕捉了数据的主要规律,泛化能力好。
- 过拟合:模型太复杂,过度记住了训练数据中的细节和噪声,泛化能力差。
判断过拟合的方式
1. 训练损失(train loss)和验证损失(val loss)的变化趋势:
- 正常情况: 当模型在训练时,训练损失和验证损失都会随着训练轮数的增加而减少,并且验证损失和训练损失差距不大,表明模型在训练数据和未见过的数据(验证集)上都表现良好。
- 过拟合情况: 随着训练的继续,训练损失会不断减少,甚至趋近于零,而验证损失开始上升。这说明模型在训练集上表现非常好,但在验证集上性能变差,即模型对训练数据过拟合了。也有可能训练损失和验证损失同时大幅升高。
2. 验证集正确率:
- 同样的现象可能出现在分类问题中,验证集的准确率先上升然后开始下降,也表明模型开始过拟合。
在这个例子中,模型对数据进行了过拟合,因为使用了非常高阶(25阶)的多项式来拟合一个简单的正弦函数。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
# 生成简单的数据
np.random.seed(42)
X = np.linspace(0, 10, 100).reshape(-1, 1)
y = np.sin(X) + np.random.normal(0, 0.25, X.shape) # 将噪声的标准差为0.25
# 分割训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
train_loss = []
val_loss = []
# 可视化 overfitting 高阶多项式回归
plt.figure(figsize=(12, 6))
# 拟合高阶多项式回归(例如 degree=25)
degree = 25
poly = PolynomialFeatures(degree=degree)
X_poly = poly.fit_transform(X)
model = LinearRegression()
model.fit(X_poly, y)
y_pred = model.predict(X_poly)
# 绘制原始数据与拟合曲线
plt.subplot(1, 2, 1)
plt.scatter(X, y, color="blue", label="Data")
plt.plot(X, y_pred, color="red", label=f"Overfit Model (degree={degree})")
plt.legend()
plt.title("Overfitting Example with High-Degree Polynomial")
# 训练不同复杂度(多项式阶数)模型,并记录train loss和val loss
for degree in range(1, 25):
poly = PolynomialFeatures(degree=degree)
X_train_poly = poly.fit_transform(X_train)
X_val_poly = poly.transform(X_val)
model = LinearRegression()
model.fit(X_train_poly, y_train)
y_train_pred = model.predict(X_train_poly)
y_val_pred = model.predict(X_val_poly)
train_loss.append(mean_squared_error(y_train, y_train_pred))
val_loss.append(mean_squared_error(y_val, y_val_pred))
# 可视化训练集与验证集损失
plt.subplot(1, 2, 2)
plt.plot(range(1, 25), train_loss, label="Train Loss")
plt.plot(range(1, 25), val_loss, label="Validation Loss")
plt.xlabel("Degree of Polynomial")
plt.ylabel("Loss")
plt.title("Train vs Validation Loss")
plt.legend()
plt.tight_layout()
plt.show()
左图是原始数据点(蓝点)和拟合的曲线(红线),可以明显看到红线已经不成sin函数的样子了;右图是不同阶数多项式的损失值,可以看到在20阶后验证损失就有明显的上升趋势。
避免过拟合的常见方法
- 正则化(Regularization):如L1或L2正则化,可以限制模型的复杂度。(会在下一篇《正则化》具体介绍)
- 增加训练数据/数据增强(Data Augmentation):更多数据可帮助模型学到更通用的规律。
- 早停(Early Stopping): 当验证集的损失开始上升时,提前停止训练
- 交叉验证:通过交叉验证来评估模型的性能,确保模型能泛化到未见过的数据。(第二篇)
决策树防止过拟合
1. 剪枝
减小树的复杂度,防止过拟合。
- 预剪枝:预先设置一个阈值,若小于阈值则不再分裂。边建立决策树边进行剪枝。(限制深度、叶子节点个数、信息增益率等)
- 后剪枝:从底向上剪枝,若某节点的样本数量小于某个阈值,则该节点及其子节点都不再分裂。建立完后进行剪枝。
- 双向剪枝:同时使用预剪枝和后剪枝。
2. 数据增强
增加训练数据量,使用数据增强(第三篇《数据预处理》有具体介绍)技术,帮助模型更好地学习潜在的模式,减少过拟合。
3. 特征选择
通过限制每次分裂考虑的特征数量(使用max_features参数),减少模型复杂性,从而降低过拟合的可能性。
4. 设置超参数
限制树的深度、设置最小样本分裂数、设置最小叶子节点样本数。
5. 使用随机森林、集成树模型。
代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
# 生成一个简单的二维分类数据集
X, y = make_classification(n_samples=100, n_features=2, n_informative=2, n_redundant=0, random_state=42)
# 分割数据为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建并训练决策树分类器
clf = DecisionTreeClassifier(criterion='gini', max_depth=4, random_state=42)
clf.fit(X_train, y_train)
# 预测测试集
y_pred = clf.predict(X_test)
# 可视化决策树结构
plt.figure(figsize=(10, 6))
plot_tree(clf, filled=True, feature_names=['Feature 1', 'Feature 2'], class_names=['Class 0', 'Class 1'], rounded=True)
plt.title('Decision Tree Visualization')
plt.show()
# 生成决策边界网格
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02))
# 预测网格上的分类
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制决策边界
plt.contourf(xx, yy, Z, alpha=0.8, cmap=plt.cm.coolwarm)
# 绘制训练集和测试集的散点图
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolors='k', marker='o', label='Train')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, edgecolors='k', marker='x', label='Test')
plt.title('Decision Tree Decision Boundaries')
plt.legend()
plt.show()
集成算法(树集成模型)
集成学习通过结合多个模型的优点,能有效提高预测性能和鲁棒性,适应性强,是解决复杂机器学习问题的有力工具。
bagging
随机森林是bagging的一种,采用多棵树的平均值作为最终的预测值。
- 随机:数据随机采样,避免了过拟合。
- 对每个基模型的预测结果进行平均(回归)或投票(分类)来得到最终的预测结果。可以并行化处理。
boosting
根据第一个模型的预测结果调整训练样本的权重,错分样本的权重增加,正确分类样本的权重减少。对所有基模型的预测结果进行加权平均或加权投票来得到最终预测结果。每个基模型依赖于前一个模型的结果,不能并行化处理。
- AdaBoost(调节权重项)、GBDT(梯度提升决策树)(XGBoost、lightgbm)。
XGBoost是对传统GBDT的一种改进和扩展。除了使用梯度提升的基本思想外,还引入了许多增强特性,包括正则化、并行处理、缺失值处理等,通常在准确性和鲁棒性方面表现更佳。通过优化数据存储和计算过程(如采用块结构、并行处理和缓存),显著提高了模型训练的速度。
bagging对比boosting
1. 工作原理
Bagging(Bootstrap Aggregating):
- 并行训练:通过从训练数据中随机抽样生成多个子集(有放回抽样),然后在这些子集上并行训练多个基学习器(通常是同类型的模型)。
- 结果聚合:对于分类问题,通常使用投票法进行结果整合;对于回归问题,使用平均值。
- 目的:减少模型的方差,提高稳定性,主要用于防止过拟合。
Boosting:
- 顺序训练:基学习器是按顺序训练的,每个学习器在训练时关注之前模型错误分类的样本。每个新模型会加大对之前被错误分类样本的权重。
- 结果加权:最终结果通过加权投票或加权平均的方式整合,每个模型的贡献根据其性能而定。
- 目的:减少模型的偏差,逐步提高模型的准确性。
2. 适用性
Bagging:
- 通常适用于高方差模型(如决策树),通过减少方差来提高稳定性。
- 适合并行计算,因为各个模型之间相互独立。
Boosting:
- 通常适用于高偏差模型(如弱学习器),通过不断改进模型来提高准确性。
- 不适合并行计算,因为每个模型依赖于前一个模型的输出。
3. 例子
- Bagging:随机森林(Random Forest)是Bagging的一种常见实现,结合了多个决策树的预测结果。
- Boosting:AdaBoost和Gradient Boosting(包括XGBoost、LightGBM等)是Boosting的典型实现,逐步增强模型的能力。
代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.ensemble import BaggingClassifier, AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from matplotlib.colors import ListedColormap
# 生成二维分类数据集
X, y = make_classification(n_samples=300, n_features=2, n_informative=2, n_redundant=0, random_state=42, n_clusters_per_class=1)
# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建Bagging分类器和Boosting分类器(AdaBoost)
bagging_clf = BaggingClassifier(estimator=DecisionTreeClassifier(), n_estimators=100, random_state=42)
boosting_clf = AdaBoostClassifier(estimator=DecisionTreeClassifier(max_depth=1), n_estimators=100, random_state=42)
# 训练模型
bagging_clf.fit(X_train, y_train)
boosting_clf.fit(X_train, y_train)
# 预测并评估
y_pred_bagging = bagging_clf.predict(X_test)
y_pred_boosting = boosting_clf.predict(X_test)
print(f"Bagging Accuracy: {accuracy_score(y_test, y_pred_bagging)}")
print(f"Boosting Accuracy: {accuracy_score(y_test, y_pred_boosting)}")
# 创建网格以进行决策边界可视化
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02))
# 可视化决策边界
def plot_decision_boundary(model, title):
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.8, cmap=ListedColormap(('orange', 'cornflowerblue')))
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolor='k', marker='o', label='Train Data')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, edgecolor='k', marker='x', label='Test Data')
plt.title(title)
plt.legend()
plt.show()
# 可视化Bagging和Boosting的决策边界
plot_decision_boundary(bagging_clf, "Bagging Decision Boundary")
plot_decision_boundary(boosting_clf, "Boosting Decision Boundary")
输出
Bagging Accuracy: 0.9444444444444444
Boosting Accuracy: 0.9111111111111111
代码讲解
- Bagging:我们使用
BaggingClassifier
来集成多个决策树(100个基决策树)。 - Boosting:我们使用
AdaBoostClassifier
来训练多个浅层决策树,每个树的最大深度为1。 - 数据集:生成了一个简单的二维分类数据集,并将其划分为训练集和测试集。
- 决策边界:我们可视化了 Bagging 和 Boosting 模型的决策边界,显示它们在二维平面上如何划分不同类别。
结果可视化
- Bagging:决策边界通常比较平滑,因为它通过平均多个模型来降低方差。
- Boosting:决策边界往往更加细致,特别是在数据点密集的区域,因为它会逐步纠正错误分类的样本。
堆叠模型(stacking)
将多个模型的预测结果作为输入,训练一个新的模型来融合多个模型的预测结果。通过训练一个元模型来组合多个不同类型的基模型的预测结果,提高模型性能。
- 基础模型(Base Models): 这些模型是并行训练的,每个模型独立进行预测。它们可以是不同种类的模型,如决策树、k 近邻、随机森林等。这些基础模型的预测结果会被输入到元模型中。基础模型的作用是捕捉数据中的不同特征模式或决策边界。
- 元模型(Meta Model): 元模型用于结合基础模型的输出预测结果,生成最终的预测。它通常是一个简单的模型(如逻辑回归),对基础模型的输出结果进行进一步处理和学习,找到最优的组合方式。元模型的作用是学习如何加权或组合基础模型的预测,以提高整体模型的性能。
我们可以创建一个简单的 Stacking 模型,其中使用两个基础模型(比如决策树和 k 近邻)作为一层模型,并用逻辑回归(见下)作为“元模型”来整合基础模型的预测。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import StackingClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from matplotlib.colors import ListedColormap
# 生成二维分类数据集
X, y = make_classification(n_samples=300, n_features=2, n_informative=2, n_redundant=0, random_state=42, n_clusters_per_class=1)
# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建基础模型(决策树和KNN)
base_estimators = [
('decision_tree', DecisionTreeClassifier(max_depth=4)),
('knn', KNeighborsClassifier(n_neighbors=5))
]
# 创建堆叠分类器,使用逻辑回归作为元模型
stacking_clf = StackingClassifier(estimators=base_estimators, final_estimator=LogisticRegression())
# 训练堆叠模型
stacking_clf.fit(X_train, y_train)
# 基础模型的预测
decision_tree_pred = stacking_clf.named_estimators_['decision_tree'].predict(X_test)
knn_pred = stacking_clf.named_estimators_['knn'].predict(X_test)
# 输出基础模型的预测结果
print("Decision Tree Predictions:", decision_tree_pred)
print("KNN Predictions:", knn_pred)
# 元模型的最终预测
y_pred = stacking_clf.predict(X_test)
print("Final Stacking Predictions:", y_pred)
print(f"Stacking Accuracy: {accuracy_score(y_test, y_pred)}")
# 创建网格以进行决策边界可视化
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02))
# 可视化基础模型和堆叠模型的决策边界
def plot_decision_boundary(model, ax, title):
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.8, cmap=ListedColormap(('orange', 'cornflowerblue')))
ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, marker='o', label='Train Data')
ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, marker='x', label='Test Data')
ax.set_title(title)
# 创建图形
fig, axs = plt.subplots(1, 3, figsize=(18, 5))
# 决策树的决策边界
plot_decision_boundary(stacking_clf.named_estimators_['decision_tree'], axs[0], "Decision Tree Decision Boundary")
# KNN的决策边界
plot_decision_boundary(stacking_clf.named_estimators_['knn'], axs[1], "KNN Decision Boundary")
# Stacking模型的决策边界
plot_decision_boundary(stacking_clf, axs[2], "Stacking Decision Boundary")
# 添加图例
for ax in axs:
ax.legend()
plt.tight_layout()
plt.show()
堆叠模型的结果很明显结合了单独决策树和单独knn的分类结果,这也体现在准确率的提升上。
Decision Tree Predictions: [0 1 1 0 1 1 1 1 0 1 0 0 1 0 1 0 0 0 1 1 1 1 1 0 0 0 0 1 0 0 0 1 1 1 1 0 1
0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 0 1 1 0 1 1 1 0 0 1 0 1 0 0 0 1 1 1
0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0]
KNN Predictions: [0 1 1 0 1 1 1 1 0 1 0 0 0 0 1 0 0 0 1 1 1 1 1 0 0 0 0 1 0 0 0 1 1 1 1 0 1
0 1 0 0 1 1 0 1 0 0 0 0 0 1 1 1 0 1 1 0 1 1 0 1 1 1 0 0 1 0 1 0 0 0 1 1 1
0 1 0 0 1 1 0 1 0 0 0 0 0 0 0 0]
Final Stacking Predictions: [0 1 1 0 1 1 1 1 0 1 0 0 0 0 1 0 0 0 1 1 1 1 1 0 0 0 0 1 0 0 0 1 1 1 1 0 1
0 1 0 0 1 1 0 1 0 0 0 0 0 1 1 1 0 1 1 0 1 1 0 1 1 1 0 0 1 0 1 0 0 0 1 1 1
0 1 0 0 1 1 0 1 0 0 0 0 0 0 0 0]
Stacking Accuracy: 0.9555555555555556
逻辑回归
概念
逻辑回归(Logistic Regression)是一种用于二分类问题的线性模型。尽管名字中带有“回归”,但它其实是一种分类算法,旨在预测一个样本属于某一类别的概率。逻辑回归通过一个Sigmoid 函数将线性回归的输出映射到一个[0, 1]之间的概率值。通过设定一个阈值(通常为0.5),逻辑回归可以将概率值转化为具体的类别。
逻辑回归模型通常会受特征尺度影响,因此需要对数据进行标准化,特别是在使用正则化时。标准化可以使特征的尺度一致,防止某些特征对模型的贡献过大。
有种说法是神经网络模型(会在第六篇《神经网络》中具体介绍)中的每个神经元就是一个逻辑回归模型(处理方式相似),原因是(除了神经网络通过引入隐藏层和非线性激活函数,能够学习和表示复杂的非线性关系之外):
1. 激活函数:
- 在逻辑回归中,使用的是 sigmoid 函数,它将线性组合后的输入映射到 0 到 1 的区间。
- 神经网络中的神经元也常常使用类似的激活函数(如 sigmoid、ReLU、tanh 等),来处理输入并生成输出。
2. 输入到输出的线性组合:
- 在逻辑回归模型中,输入变量通过线性组合(权重与输入相乘并加上偏置)计算一个值,然后通过激活函数得到最终输出。
- 神经网络中的每个神经元也是将输入变量做线性组合,然后通过激活函数转换输出。
3. 参数优化:
- 逻辑回归和神经网络都使用类似的优化算法(如 梯度下降)来调整模型参数,最小化损失函数。
目标
其目标是预测二分类(或多分类)标签。逻辑回归的核心是通过逻辑函数(sigmoid 函数)(见下)将线性组合的自变量映射到0和1之间的概率值*。例如,如果预测概率大于0.5,则预测为类别1;否则预测为类别0。这种概率到类别的映射使得逻辑回归成为分类模型。常用于医疗诊断(患病与否)、垃圾邮件判断。逻辑回归还可以扩展为多分类问题,通常采用“多项式逻辑回归”或“一对多”策略,将二分类的逻辑回归推广到多个类别的分类。
超参数
- 正则化系数
C
: 控制正则化强度,C
值越小,正则化强度越大。正则化用于防止过拟合。 - 正则化方法
penalty
: 常见的有l1
(Lasso,L1正则)和l2
(Ridge,L2正则)。(会在第五篇中具体介绍) - 最大迭代次数
max_iter
: 控制算法的最大迭代次数。 - 求解器
solver
: 指定求解逻辑回归模型的方法(如'liblinear'
,'lbfgs'
,'sag'
,'saga'
等)。
假设
- 样本与特征之间存在线性关系(尽管预测的是分类,底层假设仍是线性关系)。
- 每个样本的对数几率可以用特征的线性组合来表示。
- 样本独立,同一数据集中的样本彼此之间没有相关性。
缺点
对非线性问题表现不佳。数据量较大时表现也不佳。
算法原理
1. Sigmoid 函数:将线性回归的输出映射为 [0, 1] 的概率。
2. 损失函数:逻辑回归使用对数损失函数(log-loss 或 binary cross-entropy)。
面经:为什么逻辑回归使用对数损失而不是平方损失?
逻辑回归使用对数损失(Logistic Loss)而不是平方损失(Mean SquaredError)的主要原因是,对数损失函数能够更好地处理分类问题。对数损失函数可以将模型输出(即预测值)转换为概率值,并计算预测概率和实际标签之间的距离。而平方损失函数只适用于回归问题,其对于分类问题来说并不够合适。此外,对数损失函数具有凸性质,可以使得梯度下降在训练模型时更快地收敛。而平方损失函数不具有凸性质,容易受到离群点(outliers)的影响,收敛速度会变慢。因此,逻辑回归使用对数损失函数是更加合适的选择。
3. 梯度下降:通过梯度下降算法更新权重参数,以最小化损失函数。
逻辑回归的步骤
- 准备数据:加载数据集,进行数据预处理(如标准化)(在《3.数据预处理中有具体介绍》)。
- 定义模型:初始化逻辑回归模型,选择合适的超参数。
- 训练模型:使用训练数据训练模型,求解权重参数。
- 模型预测:使用模型对测试数据进行预测。
- 模型评估:计算模型的准确率、混淆矩阵等评估指标。
- 可视化结果:可视化分类决策边界。
代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from matplotlib.colors import ListedColormap
# 生成二维分类数据集
X, y = make_classification(n_samples=300, n_features=2, n_informative=2, n_redundant=0, random_state=42, n_clusters_per_class=1)
# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 标准化数据
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 创建并训练逻辑回归模型
model = LogisticRegression()
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
# 计算模型准确率
accuracy = model.score(X_test, y_test)
print(f"Model Accuracy: {accuracy * 100:.2f}%")
# 生成网格以进行决策边界可视化
x_min, x_max = X_train[:, 0].min() - 1, X_train[:, 0].max() + 1
y_min, y_max = X_train[:, 1].min() - 1, X_train[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02))
# 预测整个网格上的分类结果
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制决策边界
plt.contourf(xx, yy, Z, alpha=0.8, cmap=ListedColormap(('orange', 'cornflowerblue')))
# 绘制训练集和测试集
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, marker='o', label='Train Data')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, marker='x', label='Test Data')
plt.title('Logistic Regression Decision Boundary')
plt.legend()
plt.show()
输出:
Model Accuracy: 95.56%
支持向量机(SVM,Support Vector Machine)
概念
是一种用于分类和回归分析的监督学习模型(在第二篇《ai模型基础》中有过介绍)。该模型若用于分类任务,尤其在高维空间中表现优越,适合复杂的决策边界。SVM 的核心思想是通过构造一个超平面,将不同类别的数据点进行分离。SVM 尝试找到一个最优超平面,使得两类数据的边界最大化,以增强模型的泛化能力。
对于分类边界函数,所有alpha不为0的x叫做支持向量,这些是真正发挥作用的数据点。非边界上的点alpha必然为0。
分类间隔:指的是分隔超平面(即决策边界)到样本点的最小距离。支持向量机的最佳决策边界是使得两个类别的距离最大化的分离超平面。(把距离投影到法向量方向上)
目标
SVM 的目标是找到一个超平面,将不同类别的样本点分隔开,并且尽量最大化分类边界的间隔(即支持向量与超平面的距离)。这样可以使模型在面对新数据时有更好的分类效果。
假设
SVM 假设数据是线性可分的,但通过引入核函数(kernel)(见下),SVM 也可以处理非线性可分的数据。
常见的应用场景
- 分类问题(如文本分类、图像分类等)
- 回归问题(SVM 也可以用于回归,称为 SVR)
- 异常检测(检测异常点)
数据标准化
SVM 对特征值的量纲非常敏感,通常需要标准化数据(第三篇《数据预处理》有过具体介绍),将特征值缩放到相同的范围(如 [0, 1] 或 [-1, 1])。这使得 SVM 能够更好地确定超平面的边界。
算法原理
1. 线性可分:
2. 优化问题:
3. 软间隔:
4. 核函数:
如果数据是非线性可分的,SVM 使用核函数将数据映射到更高维的特征空间,使其变得线性可分。常见的核函数有:
面经:核函数的作用
- 提升维度:核函数将输入空间映射到高维空间,降低了计算复杂度。
- 线性化:核函数能够将输入空间中的样本点映射到高维空间,在高维空间线性可分,使得样本点能够被很好地分割。
- 正则化:核函数通过在高维空间中的映射,可以有效地控制决策边界的复杂度和宽度。宽度指的是分类间隔的大小,决策边界的宽度可以影响模型的泛化能力。宽度越宽,通常意味着更好的泛化能力,防止过拟合。
超参数
- C 参数:控制模型的惩罚强度。较大的 C 值意味着较少的容错,模型会更加严格地分类训练数据,但容易过拟合。
- 核函数:决定数据的映射方式,影响模型的表现。
- γ:在高斯径向基(RBF) 核中,γ 控制单个训练样本的影响范围。较大的 γ 值会导致模型更加复杂,可能过拟合;较小的 γ 值会导致欠拟合。
C的选择对于支持向量机模型的性能至关重要。通常,𝐶的取值范围会在以下几个数量级之间变化:
- 1. 小的𝐶值(如0.01,0.1):模型对误分类样本较为容忍,会有更大的间隔,但可能欠拟合训练数据。这适用于噪声较大的数据集,或当你希望模型具有更好的泛化能力时。
- 2. 适中的𝐶值(如1,10,100):这是比较常见的取值范围,可以通过*交叉验证*来选择一个合适的𝐶值。大多数情况下,适中的𝐶值能够在模型复杂度和泛化能力之间取得良好的平衡。
- 3. 大的𝐶值(如1000,10000):模型对误分类样本不容忍,间隔较小,更加严格地分类训练数据,容易过拟合。这适用于数据噪声较小且希望模型对训练集高度准确的情况。
SVM的分类
软间隔支持向量机(Soft Margin SVM)
数据中有一些噪音点,不要求把两类点完全分得开。允许样本点的分类间隔不相等。
硬间隔支持向量机(Hard Margin SVM)
要求样本点的分类间隔相同。
多类支持向量机(MCSVM)
允许样本点属于多个类别。
线性支持向量机(LSVM)
将输入空间中的样本点映射到高维空间,然后使用线性分类器进行分类。
非线性支持向量机(NUSVM)
将输入空间中的样本点映射到高维空间,然后使用非线性分类器进行分类。
最大边距支持向量机(MMSVM)
通过最大化间隔和最小化方差,使得支持向量的间隔最大化。
概率支持向量机(PSVM)
通过对输入空间中的样本点赋予概率,使得分类结果具有不确定性。
序列支持向量机(SSVM)
将输入序列映射到高维空间,然后使用支持向量机进行分类。
核序列支持向量机(KSSVM)
将输入序列映射到高维空间,然后使用核函数进行分类。
面经:感知机模型与SVM模型的异同
感知机是最早的神经网络模型之一,用于二分类任务。它的基本结构包括输入层、权重和激活函数。
共同点:
- 分类任务:两者都用于二分类任务。
不同点:
1. 目标
- 感知机:通过迭代更新权重,直到所有样本被正确分类。它不考虑最大化间隔的原则。
- SVM:旨在最大化分类间隔,寻找离决策边界最近的样本点(支持向量),从而增强模型的泛化能力。
2. 算法
- 感知机:简单的迭代算法,基于误分类样本更新权重。可以在某些情况下不收敛(例如数据线性不可分)。
- SVM:使用优化技术(如拉格朗日乘子法)(高等数学基础知识)解决最大化间隔的问题,确保找到最佳的决策边界,具有更好的鲁棒性。
常见步骤
- 数据标准化(重要)
- 选择核函数(线性、RBF、多项式等)
- 设置超参数(C 和 γ)
- 训练模型
- 评估模型性能(交叉验证、混淆矩阵等)
- 可视化决策边界(适用于二维数据)
代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
# 生成二维分类数据
X, y = datasets.make_blobs(n_samples=100, centers=2, random_state=42, cluster_std=1.0)
# 分割数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 创建SVM模型,使用RBF核
svm = SVC(kernel='rbf', C=1.0, gamma=0.5)
svm.fit(X_train, y_train)
# 预测并评估
y_pred = svm.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy:.2f}")
# 可视化决策边界和支持向量
def plot_svm_decision_boundary(X, y, model):
# 创建网格点
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 500), np.linspace(y_min, y_max, 500))
# 预测网格点的分类
Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 绘制决策边界和间隔边界
plt.contour(xx, yy, Z, levels=[-1, 0, 1], linestyles=['--', '-', '--'], colors='k')
# 绘制支持向量
plt.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1], s=100, facecolors='none', edgecolors='k', label="Support Vectors")
# 绘制数据点
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', edgecolors='k')
plt.title("SVM Decision Boundary with Support Vectors")
plt.legend()
plt.show()
# 可视化SVM决策边界、支持向量及间隔边界
plot_svm_decision_boundary(X_train, y_train, svm)
输出:
Accuracy: 1.00
-
决策边界:代码中的
plt.contour
函数绘制了三条线:- 中间的实线表示分类器的决策边界。
- 两侧的虚线表示支持向量到决策边界的距离,即间隔边界。
-
支持向量:用空心圆圈标注了支持向量,它们是距离决策边界最近的数据点。
-
SVM的决策依据:SVM 的决策依据是根据支持向量的位置构建超平面,然后尝试最大化支持向量到决策边界的距离(间隔)。
朴素贝叶斯(Naive Bayes)
概念
这是一类基于贝叶斯定理(概率论与数理统计基础知识)的简单而强大的分类算法。其“朴素”体现在它假设特征之间相互独立,不考虑特征之间的相关性。这一假设使得模型计算效率高,但在某些实际情况下可能与现实不符。
贝叶斯网络是一种有向无环图(DAG),其节点表示随机变量,边表示变量之间的条件依赖关系。贝叶斯网络通过联合概率分布来表示复杂的概率关系,并使用贝叶斯推理进行推断和预测。
- 对于高斯朴素贝叶斯,由于假设特征服从正态分布,通常不需要标准化数据。
- 对于多项式或伯努利朴素贝叶斯,数据无需标准化,因为它们处理的是离散或二元特征。
超参数
不同类型的朴素贝叶斯分类器有不同的参数:
- 高斯朴素贝叶斯(GaussianNB):假设特征服从正态分布,主要参数是平滑系数
var_smoothing
,用于处理数值上的极小值。 - 多项式朴素贝叶斯(MultinomialNB):主要用于离散型数据,如文本分类,超参数包括
alpha
,用于拉普拉斯平滑。 - 伯努利朴素贝叶斯(BernoulliNB):用于二元特征数据,也有类似的平滑参数
alpha
。
目标
朴素贝叶斯的目标是通过特征 X 预测类别 C 的概率,即最大化后验概率 P(C∣X)。
假设
条件独立性假设:给定类别 C 时,特征 Xi 之间是相互独立的。该假设是“朴素”的核心,但在实际问题中往往不成立。
优缺点
优点
- 简单高效,适用于大规模数据集。
- 对缺失数据不敏感。
- 适用于多类分类问题。
缺点
- 特征之间的独立性假设在现实中很难成立。
- 对特征之间有强依赖关系的数据表现不好。
常见应用场景
- 文本分类(如垃圾邮件过滤、情感分析):多项式朴素贝叶斯广泛用于自然语言处理任务。
- 文档分类:可以根据词频来分类文档。
- 情感分析:用二元特征表示正面和负面情感,适合使用伯努利朴素贝叶斯。
算法
朴素贝叶斯根据贝叶斯定理计算出每个类别的后验概率,并选择后验概率最大的类别作为预测结果。 其核心步骤如下:
- 计算先验概率 P(C):通过训练集中每个类别的频率计算。
- 计算似然概率 P(X∣C):根据给定类别 C 下,每个特征 Xi 的概率。
- 应用贝叶斯定理 计算后验概率 P(C∣X),选择概率最大的类别。
代码
使用 高斯朴素贝叶斯(GaussianNB)对二维数据集进行分类,并展示决策边界以及贝叶斯原理的分类过程。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from matplotlib.colors import ListedColormap
# 生成二维分类数据集
X, y = make_classification(n_samples=100, n_features=2, n_informative=2, n_redundant=0, n_clusters_per_class=1, random_state=42)
# 分割数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建高斯朴素贝叶斯模型并训练
gnb = GaussianNB()
gnb.fit(X_train, y_train)
# 可视化决策边界
def plot_decision_boundary(X, y, model, title):
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.8, cmap=ListedColormap(('orange', 'cornflowerblue')))
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k', marker='o', cmap=ListedColormap(('orange', 'cornflowerblue')))
plt.title(title)
plt.show()
# 绘制训练集的决策边界
plot_decision_boundary(X_train, y_train, gnb, "Naive Bayes Decision Boundary (Training Set)")
- 决策边界:根据贝叶斯定理生成的分类边界将数据点分为两类。
- 颜色表示类别:橙色和蓝色分别表示两类样本。模型根据概率分布选择类条件下最可能的分类结果。
总结
在机器学习中,常见的分类算法包括 KNN、决策树、集成模型(如 Bagging 和 Boosting)、逻辑回归、SVM(支持向量机)和朴素贝叶斯。KNN 通过计算距离对样本进行分类,适合小数据集,但在大数据集上效率较低。决策树 利用特征划分数据,简单直观,但易过拟合。集成模型 通过多个弱学习器的组合提升模型性能,其中 Bagging 通过并行方式降低方差,Boosting 通过序列方式降低偏差。逻辑回归 是一种线性模型,常用于二分类任务,适合特征和输出间具有线性关系的数据。SVM 通过寻找最大化分类间隔的超平面进行分类,适用于高维数据。朴素贝叶斯 基于贝叶斯定理,简单高效,适合文本分类等独立特征场景。
这些算法各有特点和适用场景,选择时应根据数据的特征、规模和任务需求综合考虑。