课程2. 机器学习方法论
- 训练算法并评估其质量
- 将样本分成训练和测试。
- 分层
- 交叉验证方法
- sklearn 接口
- 算法
- 模型训练
- 模型的应用
- 质量评估
- 数据预处理
- 标准缩放
- Violinplot
- 数据集
- 使用模型
- Pipeline
在上一讲中,我们讨论了机器学习专家面临的挑战。无论解决的问题类型和解决方法如何,机器学习和数据科学领域的所有专家都会使用一套规则、方法和途径。今天的讲座将专门讨论我们将在课程结束前使用的技术,以及对于那些决定继续学习机器学习和神经网络的人来说,更进一步的学习。
因此,在今天的讲座中,我们将假设我们手中不仅有一个数据集 X X X、一组标签 y y y和一个问题陈述,而且还有一个算法 A A A,我们知道如何训练它(即选择该算法的参数,以便算法能够以足够的质量解决问题)。在接下来的讲座中,我们将阐明什么是这样的算法,如何训练这些算法,以及如何衡量它们的质量。今天我们将讨论如何确保算法得到正确训练,并熟悉“sklearn”库中的一些函数。
训练算法并评估其质量
机器学习算法的工作分为两个阶段:
- 训练算法
- 检查训练算法的质量
从某种意义上来说,你可以把教授算法的工作视为向学生教授一门学科的工作。与标准学校课程相比,算法的训练相对应,在标准学校课程中,学生在老师的监督下解决问题并立即得到反馈,如果犯了错误,他们就有机会纠正关于正确解决问题的方法的想法。模型测试阶段对应于学生必须展示其所获得的知识的测试,同时**(a)*无法获得正确答案 **(b)*无法获得老师的反馈,因此没有机会调整解决问题的策略。
将样本分成训练和测试。
让我们立即规定,在这种情况下,样本是指一组可供分析的对象。总的来说,术语“样本”、“数据集”、“一组数据”等。在我们的对话框架内,除了极少数例外,其他同义词都可以互换,我们将对此进行单独讨论。
在基本情况下,我们将样本分为所谓的训练样本和测试样本。这样做是必要的,这样您就不会因为使用与训练时相同的数据来评估算法的工作质量而误导自己。 对模型在训练样本上的性能质量的评估不具有指示性。毕竟,我们的算法总是可以调整其参数,以便在这个特定的训练样本上取得良好的结果,但不可能将其应用于任何其他数据。
这与学校里老师的逻辑是一样的:如果你给学生布置作业,他可以看看课本后面的答案,然后简单地记住答案,而无需理解主题。因此,在考试中需要布置与之前家庭作业不同的任务。在这种情况下,训练样本就是家庭作业,它的目的是训练学生,教他解决某一类问题。并且测试样本是一个控制任务。好学生和坏学生都可以很好地完成作业,但只有好学生才能很好地参加考试。分成训练样本和测试样本也是同样的道理。
在 sklearn
中,函数 sklearn.model_selection.train_test_split
是专门为了将样本划分为训练集和测试集而实现的:
from sklearn.model_selection import train_test_split
作为示例,我们在二维平面上生成一个圆形的样本:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as snssns.set_theme()DATASET = []for x in np.linspace(-1, 1, 50):for y in np.linspace(-np.sqrt(1 - x**2), np.sqrt(1 - x**2), 50):DATASET.append([x, y])DATASET = np.array(DATASET)plt.figure(figsize=(8, 8))
plt.scatter(DATASET[:, 0], DATASET[:, 1])
输出:
作为标签,我们为每个点分配其坐标的总和:
y = np.sum(DATASET, axis=-1)
假设我们想按照 3:1 的比例将该样本分为训练和测试。那么我们样本中的 0.25 个对象应该作为测试样本。此外,我们希望标签集合 y y y也能根据对象的划分方式正确地划分为测试集和训练集。
让我们为此使用train_test_split
函数。
X_train, X_test, y_train, y_test = train_test_split(DATASET, y, test_size=0.25)
plt.figure(figsize=(8, 8))
plt.scatter(X_train[:, 0], X_train[:, 1], label="train")
plt.scatter(X_test[:, 0], X_test[:, 1], label="test")
plt.legend()
输出:
我们可以看到,数据集点在“训练”和“测试”之间随机分布。这种随机性有时是必要的,以确保获得的样本具有代表性(即,在特定样本中观察到的某些属性与一般观察到的相同属性不同)。为了阐明这个想法,请考虑以下示例:
假设您想要训练一个模型来从照片中区分玫瑰和郁金香。您请植物园的生物学家朋友为您收集一些这些花的照片。朋友们对训练 ML 模型的复杂性一无所知,他们做了以下事情:首先,他们拍摄了 75 张郁金香样本,然后拍摄了 25 张玫瑰样本,将收到的照片从 1 编号到 100,然后以这种形式交给您。如果将样本以 2:1 的比例分成训练和测试而不进行混合,会发生什么情况?您的算法将判定世界上所有的花都是郁金香,因为输入的所有数据都包含“郁金香”标签。
这只是最简单的例子;实际上,数据中隐藏的依赖关系可能要复杂得多。因此,几乎总是需要对样本进行打乱。
但这也带来了自身的问题。显然,评估模型质量的结果取决于将样本划分为“训练”和“测试”的方法。在好的情况下,这种依赖性非常弱,并且一般来说,哪些特定对象最终进入“训练”、哪些进入“测试”并不重要。但不幸的是,这种依赖通常仍然是显而易见的。如果有人决定在自己的计算机上重现您所获得的结果,那么由于样本混洗引入了随机元素,他们的结果可能会与您的结果不同。
此外,ML 算法本身通常存在各种随机结构。我们还需要研究诸如“随机森林”、“神经网络”、“决策树”等算法。它们都在数据处理中不同程度地利用了各种随机元素。所有这些结构的综合效应会累积起来,使得完全不可能重现您的结果!
这可以很简单地处理。
问题是,计算机生成的随机数实际上并不是随机的。有一种算法,可以唯一地恢复所有生成的随机值。正是由于这个原因,这样的数字通常被称为伪随机。但其实并没有那么简单:这个算法是基于某个预先设定的数字的,这个数字通常被称为random_seed
(有时候也不同,比如,它常被称为random_state
)。这是一种生成的伪随机数的访问密钥。在基本情况下,他们会尝试让这个数字尽可能的随机。例如,他们采用处理器的当前时钟时间——这个值很难预测。实际上,random_seed
是一个传统意义上的随机数,因为没有人会提前猜到在你按下按钮执行算法之前你会花多少时间在茶歇上,而 random_seed
将从处理器中取出作为某个变量的当前值。 但是,知道了random_seed
,你就可以毫无疑问地恢复你在工作中使用的所有随机值!
正是由于这个原因,这个值通常是单独设置的。
碰巧的是,许多程序员尝试使用数字 42 作为random_seed
。这只是一种传统,没必要在这里寻找什么深层含义。
因此,始终建议在开始工作之前编写初始化行random_seed
,并且在所有算法中,如果可能,自己写入random_seed
的值。
# 为 np.random 模块设置种子的行
np.random.seed(42)X_train, X_test, y_train, y_test = train_test_split(DATASET, y, test_size=0.25, random_state=42
)
plt.figure(figsize=(8, 8))
plt.scatter(X_train[:, 0], X_train[:, 1], label="train")
plt.scatter(X_test[:, 0], X_test[:, 1], label="test")
plt.legend()
让我们确保相同的random_seed
会给我们相同的结果,而不同的random_seed
会给我们不同的结果:
X_train, X_test_1, y_train, y_test_1 = train_test_split(DATASET, y, test_size=0.25, random_state=42
)
X_train, X_test_2, y_train, y_test_2 = train_test_split(DATASET, y, test_size=0.25, random_state=56
)
plt.figure(figsize=(10, 10))
plt.scatter(X_test_1[:, 0], X_test_1[:, 1],label="Test with random_state=42",color="green",alpha=0.7,s=40
)
plt.scatter(X_test_2[:, 0], X_test_2[:, 1],label="Test with random_state=56",color="red",alpha=0.7,s=40
)
plt.legend()
plt.tight_layout()
X_train, X_test_1, y_train, y_test_1 = train_test_split(DATASET, y, test_size=0.25, random_state=42
)
X_train, X_test_2, y_train, y_test_2 = train_test_split(DATASET, y, test_size=0.25, random_state=42
)
plt.figure(figsize=(10, 10))
plt.scatter(X_test_1[:, 0], X_test_1[:, 1],label="Test with random_state=42", color="green", alpha=0.7, s=40
)
plt.scatter(X_test_2[:, 0], X_test_2[:, 1],label="Test with random_state=42", color="red", alpha=0.7, s=40
)
plt.legend()
plt.tight_layout()
它们完全重叠,这正是我们想要实现的。
在哪些情况下,对样本进行混洗是有害的?
它们确实发生了。有时数据结构本身暗示相邻元素之间的关系。例如,在许多时间序列问题中,需要保留数据的原始序列。
在这种情况下,我们可以将“train_test_split”函数的“shuffle”参数的值设置为“False”
X_train, x_test, y_train, y_test = train_test_split(DATASET, y, test_size=0.25, shuffle=False
)
plt.figure(figsize=(8, 8))
plt.scatter(X_train[:, 0], X_train[:, 1], label="train")
plt.scatter(x_test[:, 0], x_test[:, 1], label="test")
plt.legend()
分层
将样本分成训练样本和测试样本时,必须注意确保这些样本尽可能彼此相似。这种相似性的标准包括,例如,保留所有特征的主要样本参数和目标变量(数学期望、标准差等)。将样本分成训练和测试时可能犯的一个非常常见的错误示例是在解决分类问题时改变目标变量中的类别比例。样本经常是不平衡的,也就是说,样本中类别的比例是不相等的。例如,数据集中只有 5% 的对象可能属于类 1 1 1,而多达 95% 的对象属于类 0 0 0。在这种情况下,将样本随机分成训练和测试可能会导致训练和测试样本中的类别比例非常不同,如下例所示。
np.random.seed(42)# 让我们定义合成类标签,其比例约为 5% - 95%
Y_full = np.random.choice([0, 1], size=[100], p=[0.95, 0.05])
print((Y_full == 1).sum())
输出:5
现在让我们检查一下,当以 4:1 的比例分割此类样本时,训练和测试样本中的标签比例会发生什么变化
y_train, y_test = train_test_split(Y_full, test_size=0.2, random_state=42)
print((y_train == 1).sum())
输出:5
所有第 1 类的对象都包含在训练集中。也就是说,训练样本中的类别比例现在是 6.25% 对 93.75%,测试样本中的类别比例现在是 0% 对 100%。明显的区别。
这个问题可以用分层方法来解决。这是一种使两个样本中的类别分布保持相同的方法。在sklearn
中,使用train_test_split
函数时有一个选项可以激活此方法。为此,将具有原始分布的标签向量作为“分层”参数传递给此函数。
y_train, y_test = train_test_split(Y_full, test_size=0.2, random_state=42, stratify=Y_full)
print((y_train == 1).sum())
输出:4
使用分层时,不必担心训练和测试样本会相对于目标变量的原始分布发生“偏移”,从而使它们更具有代表性。
注意:有时在实践中样本不是分成两部分,而是分成三部分。在这种情况下,第三部分称为验证。这样做有两个原因:
-
一些机器和深度学习算法可能需要相当长的时间来学习,并且跟踪算法在学习过程中质量的变化非常有用。在这种情况下,验证集用于评估模型训练期间的质量并得出有关算法如何学习的中间结论。验证样本对算法来说也是“未显示”的;它模仿测试样本。
-
对于大多数机器学习算法来说,都存在超参数的选择问题,即算法参数无法自动确定,需要专家手动选择。选择超参数也是算法的一种训练,因为它们会对质量产生重大影响。在这种情况下,验证样本类似于训练样本,但用于选择超参数。也就是说,根据算法的质量,在验证样本上选择模型超参数,但随后仍需要对模型进行整体测试——同时考虑手动选择的超参数和自动选择的参数。
一般来说,将样本分成三部分而不是两部分主要是神经网络工作的典型做法,在我们的课程中几乎不会遇到。
现在让我们想象一个不太可能发生但还远非理想的情况。一个差劲的学生完全是在对题目没有任何理解的情况下,偶然写出了一张好试卷。即使按照贫困学生的标准来看,考试也可能太简单了,或者答案可能只是猜测而已。生活中任何事情都有可能发生。在统计学上也是如此。我们为算法形成的测试样本可能不是很有代表性,可能有“偏差”,或者可能只是偶然适合某个特定的算法。 这让我们想到,使用一整套测试样本来更准确地评估算法是一个好主意。允许进行此类评估的方法之一是交叉验证方法。
交叉验证方法
为了评估我们模型的质量,我们将使用交叉验证方法(交叉检查,交叉验证)—— 一种评估分析模型及其在独立数据上的行为的方法。在评估模型时,可用数据被分成 n n n个部分(我们选择这个数字)。然后基于数据的 n n n−1 部分对模型进行训练,并使用数据的剩余部分进行测试。该过程重复 n n n次。因此, n n n条数据中的每一条都用于测试。结果是对所选模型的有效性的评估,并且最统一地使用了可用数据。
sklearn 接口
未来我们将会定期使用著名的机器学习库 sklearn
。该库专门设计用于与各种经典机器学习工具配合使用。 sklearn
是一个非常有活力、不断发展的框架,为数据分析领域的整个开源社区设定了标准。 sklearn
的成就之一是标准化和普及了使用模型的便捷界面。我们将研究该界面的主要通用元素,了解这些元素将使您能够轻松使用所有 sklearn
工具。
算法
sklearn
库收集了许多不同性质的ML算法。这些算法分为几个模块,其中我们主要感兴趣的是:
sklearn.linear_model
sklearn.tree
sklearn.svm
sklearn.ensemble
sklearn.cluster
sklearn.neighbors
这些模块中介绍的所有算法,除少数例外,都有两个关键功能,分别对应于启动学习和预测过程。
模型训练
在 sklearn
中训练模型是通过调用 .fit(X, y)
(对于监督学习算法)和 .fit(X)
(对于无监督学习算法)来完成的。 .fit
函数是算法类的一种方法,当调用时,它会根据数据集对象 X
的特征矩阵以及在监督学习的情况下的正确答案向量 y
来选择算法的最佳参数。
让我们举个例子。在这种情况下,使用哪种具体算法来解决问题根本不重要。我们只是想演示该算法的一般工作方案。首先,我们将生成一个数据集。
X = np.linspace(-np.pi, np.pi, 1000).reshape(-1, 1)
y = np.cos(X)plt.plot(X, y)
# 将样本分为训练集和测试集
X_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.3, shuffle=True, random_state=42)
# 让我们从 sklearn.ensemble 模块中选择一些算法
from sklearn.ensemble import RandomForestRegressor# 创建导入类的对象
regressor = RandomForestRegressor()# 让我们通过调用 .fit() 函数来训练它
regressor.fit(X_train, y_train)
模型的应用
为了对模型在训练期间“未看到”的数据进行预测,使用了 .predict(X)
函数。调用此函数可以让您获得矩阵X
中每个对象的预测。此功能与监督学习算法相关。
# 让我们对测试样本进行算法的预测
y_pred = regressor.predict(x_test)
plt.scatter(x_test, y_test, label='truth observations');
plt.scatter(x_test, y_pred, s=5, c='r', label='predictions');
plt.legend()
plt.show();
质量评估
sklearn
对分类和回归模型的质量有广泛的衡量指标。它在sklearn.metrics
模块中实现。我们将在课程中介绍这些指标,但今天我想指出一种衡量模型质量的简单方法——使用 .score(X, y)
函数。该函数调用模型对数据集“X”的预测过程,并使用以下两个函数之一将结果预测与真实标签“y”进行比较:
- 准确度(正确答案的比例),如果我们谈论的是分类任务
- 如果我们讨论的是回归问题,则为 R 2 R^2 R2(判定系数)(我们将在未来的某节课中详细讨论这个指标)
regressor.score(x_test, y_test)
输出:0.9999782907844347
数据预处理
在我们的课程中,我们将看到不同的 ML 算法需要不同的数据预处理方法。这些方法可能包括将特征减少到单一尺度、解码分类特征、规范化、文本特征的转换等等。为了与这些机制协同工作,我们创建了一个单独的模块,它也遵守接口规则。预处理器(转换器)方法与算法方法差别不大,只是允许算法进行预测的“预测”函数被允许预处理器转换数据的“转换”函数所取代。让我们举一个最流行的预处理器的例子。
标准缩放
标准缩放是用于规范化样本的标准统计程序:
x ^ = x − μ σ \hat{x} = \frac{x - μ}{σ} x^=σx−μ
其中 μ = E [ x ] μ = E[x] μ=E[x] 是 数学期望,通常用简单样本估计值(数据集中 x x x 的平均值)代替, σ σ σ 是 标准差,通常也是通过数据集计算的。
这可以分别使用“np.mean()”和“np.std()”来完成。
现在我们可以很简单地编写“scale”函数:
def scale(X):"""数据集中的缩放特征。参数:X(类似数组):输入数据数组。返回:类似数组:缩放的特征值。"""x_mean = X.mean(axis=0)x_std = X.std(axis=0)X_scaled = (X - x_mean)/x_stdreturn X_scaled
让我们生成一个人工样本并检查所有坐标是否实际缩放。
scales = np.array([1, 0.1, 10])
x = np.random.randn(1000, 3) * scales.reshape(1, -1)
print(x)
Violinplot
让我们使用“violinplot”的可视化方法。
Violinplot
是一种可视化直方图或分布的方式,其中频率特征沿横轴绘制,而纵轴用于标记测量这些特征的值。
简单来说,我们取一些直方图并将其转向一侧。除了直方图,您还可以使用概率分布密度。
例如,让我们看一下典型的正态分布图是什么样的。
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as pltsns.set_theme()data = np.random.randn(1000)
df = pd.DataFrame({'data': data})fig, ax = plt.subplots(figsize=(10, 6))
sns.histplot(data=df['data'], ax=ax, kde=True)ax.set_xlabel('Values')
ax.set_ylabel('Quantity')
ax.set_title('Data Distribution')plt.tight_layout()
plt.show()
在这种情况下,“Violinplot”会有些不同:
fig, ax = plt.subplots(figsize=(10, 6))
sns.violinplot(data=df['data'], ax=ax)ax.set_xlabel('Values')
ax.set_ylabel('Quantity')
ax.set_title('Data Distribution')plt.tight_layout()
plt.show()
例如,均匀分布将如下所示:
data = np.random.uniform(size=10000)
df = pd.DataFrame({'data': data})fig, ax = plt.subplots(figsize=(10, 6))
sns.violinplot(data=df['data'], ax=ax)ax.set_xlabel('Values')
ax.set_ylabel('Quantity')
ax.set_title('Data Distribution')plt.tight_layout()
plt.show()
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as pltdef draw_violin(x):"""绘制多色菱形图。Args:x (array-like):具有三列的数据数组。Returns:None"""df = pd.DataFrame({'value': x[:,0].tolist() + x[:,1].tolist() + x[:,2].tolist(),'feature': ['feature 1']*1000 + ['feature 2']*1000 + ['feature 3']*1000,'coord': [1]*1000 + [2]*1000 + [3]*1000})plt.figure(figsize=(10,10))sns.violinplot(data=df, x='coord', y='value', hue='feature')plt.title('Multiple data distributions')plt.tight_layout()plt.show()draw_violin(x)
现在,我们可以看到,所有标志的规模都存在显著差异。让我们应用“scale”函数:
x_scaled = scale(x)
print(x_scaled)
draw_violin(x_scaled)
我们可以看到,现在所有特征都被带到了单一尺度,并且具有相同的尺度。请注意,平均值根本没有改变。
当然,sklearn
库中也存在这样的功能。
推荐的实现包含在sklearn.preprocessing.StandardScaler
类中。
StandardScaler
类的对象属于所谓的数据转换器,也就是说,它们对数据进行转换。在这方面,它们支持.fit()
,.transform()
和.fit_transform()
函数。
.transform
和 .fit_transform
是 .predict
和 .fit_predict
的完全类似物,用于我们不需要解决预测问题而只是以某种方式转换数据的情况。 sklearn
中的所有数据“转换器”都实现了这些功能。
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()x_sk = sc.fit_transform(x)
draw_violin(x_sk)
数据集
sklearn
可以访问许多现成的经典数据集。此访问是通过sklearn.datasets
模块的函数实现的。例如,通过这个模块你可以下载我们已经熟悉的经典数据集菲舍尔鸢尾花。
from sklearn.datasets import load_irisX, y = load_iris(return_X_y=True)
X.shape
使用模型
与 ML 模型正确协作所需的功能在 sklearn.model_selection
中实现。例如,此模块中实现了train_test_split
函数。在此模块中,您还可以访问交叉验证工具。
交叉验证方法作为sklearn
库中的功能块实现。要访问cross_val_score
函数,您需要从sklearn.model_selection
模块导入它。
cross_val_score
将返回一个数字数组(score_1 … score_n)。
这里, s c o r e i score_i scorei指的是当第 i i i“段”数据被选为测试样本,其余数据作为训练样本时,对算法性能的好坏的评估。这里的质量可以表示不同的指标,它是一个可配置的参数,但在基本情况下, s c o r e score score 表示算法正确答案的比例。
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_wine
from sklearn.neighbors import KNeighborsClassifier# 加载另一个经典葡萄酒数据集
X, y = load_wine(return_X_y=True)# 让我们创建一个 KNN 分类器的实例(我们将在下一讲中介绍这一点)
clf = KNeighborsClassifier(n_neighbors=5)# 让我们进行交叉验证
scores = cross_val_score(clf, X, y, cv=10)# 让我们得出平均值
print(f"Средняя оценка: {scores.mean():.4f}")# 让我们推导出评分的分布
plt.figure(figsize=(10, 6))
sns.histplot(scores, kde=True)
plt.title('Distribution of KNN scores')
plt.xlabel('Score')
plt.ylabel('Quantity')
plt.tight_layout()
plt.show()
scores
scores.mean()
输出:
array([0.66666667, 0.66666667, 0.61111111, 0.61111111, 0.61111111,
0.61111111, 0.72222222, 0.66666667, 0.82352941, 0.76470588])
0.6754901960784313
提供的代码应解释如下:
使用 sklearn.model_selection.crossval
函数,我们将样本分成 c v cv cv=5 个元素,并对每个元素执行上述过程,获得 5 个质量分数(默认设置 accuracy_score
指标)。这些分数存储在“分数”数组中。
我们将以下参数传递给“cross_val_score”函数:
-
KNeighborsClassifier (clf)
类的对象。任何支持.fit()
、.predict()
和(如果相应度量需要).predict_proba()
方法的分类器都可以选择作为该参数。这些分别是学习、预测和获取对象属于特定类别的概率的函数。这非常方便,因为它允许我们不仅使用来自“sklearn”的类对象作为参数“clf”,还可以使用来自其他库(甚至我们自己的类)的类对象作为参数“clf”。 -
训练样本 X X X
-
对训练样本对象 y y y的响应
-
c v cv cv - 我们将样本分成的“块”的数量
关于此功能的更多详细信息,请参阅文档。
这组数字告诉了我们很多:
从平均值我们可以确定我们的算法的平均水平有多好。交叉验证期间获得的一组指标的平均值被视为对构建算法质量的有效评估。
但这里也存在一些困难。假设输出结果为以下一组数字:
[ 0.5 , 0.52 , 0.9 , 0.55 ] [0.5, 0.52, 0.9, 0.55] [0.5,0.52,0.9,0.55]
这里的平均值将是0.61,这显然与第三名的异常好成绩有关。这一结果可能是由于某个子样本的偶然性或偏差造成的。这不能被认为是绝对正确的。同样,我们也可能被一系列数值差异很大的数字的平均值所误导。例如,如果我们得到以下结果作为交叉验证的输出:
[ 0.1 , 0.99 , 0.09 , 0.95 , 0.94 , 0.05 ] [0.1, 0.99, 0.09, 0.95, 0.94, 0.05] [0.1,0.99,0.09,0.95,0.94,0.05]
显然,你不能相信这里的平均值。应该寻找错误。
可以通过评估结果数组中的标准差来跟踪这种情况:标准差越小,使用平均值进行的质量评估就越可靠。
Pipeline
sklearn
有一个方便的工具,可以优化提供的所有代码。关键在于,标准机器学习任务具有相当可预测的解决路径:对数据应用一系列转换、训练分类器并计算质量指标。为了简化这个过程,我们开发了“Pipeline”类。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split# 加载数据集
X, y = load_wine(return_X_y=True)# 拆分训练和测试
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, shuffle=True)# 创建管道并进行训练
pipe = Pipeline([('scaler', StandardScaler()),('classifier', KNeighborsClassifier(n_neighbors=5))
])
pipe.fit(X_train, y_train)# 模型评估
score = pipe.score(X_test, y_test)print(f"模型评估: {score:.4f}")
print(f"错误率: {sum(pipe.predict(X_test) != y_test) / len(y_test):.4f}")
输出:
模型评估: 0.9630
错误率: 0.0370