Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

Python 和 Scikit-Learn 中的 K-最近邻算法指南

介绍

K最近邻(KNN) 算法是一种用于分类、回归和异常值检测的监督机器学习算法。 它以最基本的形式非常容易实现,但可以执行相当复杂的任务。 这是一种惰性学习算法,因为它没有专门的训练阶段。 相反,它使用所有数据进行训练,同时对新数据点或实例进行分类(或回归)。

KNN 是一个 非参数学习算法,这意味着它不对基础数据进行任何假设。 这是一个非常有用的功能,因为大多数现实世界的数据并没有真正遵循任何理论假设,例如线性可分性、均匀分布等。

在本指南中,我们将了解如何使用 Python 的 Scikit-Learn 库实现 KNN。 在此之前,我们将首先探索如何使用 KNN 并解释其背后的理论。 之后,我们就来看看 加州住房数据集 我们将用它来说明 KNN 算法及其几个变体。 首先,我们将看看如何实现 KNN 算法进行回归,然后是 KNN 分类和异常值检测的实现。 最后,我们将总结该算法的一些优缺点。

什么时候应该使用 KNN?

假设您想租一套公寓,最近发现您朋友的邻居可能会在 2 周内将她的公寓出租。 由于该公寓还没有在出租网站上,您如何尝试估算其租金价值?

假设您的朋友支付了 1,200 美元的租金。 您的租金价值可能在这个数字附近,但公寓并不完全相同(朝向、面积、家具质量等),因此,如果有更多关于其他公寓的数据会很好。

通过询问其他邻居并查看租赁网站上列出的同一栋楼的公寓,最接近的三个相邻公寓租金分别为 1,200 美元、1,210 美元、1,210 美元和 1,215 美元。 这些公寓与您朋友的公寓位于同一街区和楼层。

其他更远的公寓位于同一楼层,但位于不同的街区,租金分别为 1,400 美元、1,430 美元、1,500 美元和 1,470 美元。 由于晚上有更多的阳光,它们似乎更贵。

考虑到公寓的邻近性,您的估计租金似乎约为 1,210 美元。 这是什么的一般想法 K-最近邻 (KNN) 算法可以! 它根据新数据与现有数据的接近程度对新数据进行分类或回归。

将例子转化为理论

当估计值为连续数时,如租金值,KNN用于 回归. 但例如,我们也可以根据最低和最高租金将公寓分为几类。 当值是离散的,使其成为一个类别时,KNN 用于 分类.

还有可能估计哪些邻居与其他邻居如此不同以至于他们可能会停止支付租金。 这与检测哪些数据点距离太远以至于它们不适合任何值或类别相同,当这种情况发生时,KNN 用于 异常检测.

在我们的示例中,我们还已经知道每间公寓的租金,这意味着我们的数据被标记了。 KNN 使用以前标记的数据,这使其成为 监督学习算法.

KNN 以其最基本的形式非常容易实现,但执行相当复杂的分类、回归或异常值检测任务。

每次向数据中添加一个新点时,KNN 仅使用数据的一部分来确定该添加点的值(回归)或类别(分类)。 由于它不必再次查看所有点,这使它成为 懒惰学习算法.

KNN 也不假设任何有关基础数据特征的信息,它不期望数据适合某种类型的分布,例如均匀分布或线性可分。 这意味着它是一个 非参数学习算法. 这是一个非常有用的功能,因为大多数现实世界的数据并没有真正遵循任何理论假设。

可视化 KNN 的不同用途

正如已经证明的那样,KNN 算法背后的直觉是所有监督机器学习算法中最直接的一种。 该算法首先计算 距离 一个新的数据点到所有其他训练数据点。

请注意: 距离可以用不同的方式测量。 您可以使用 Minkowski, 欧几里德、Manhattan、Mahalanobis 或 Hamming 公式,仅举几个指标。 对于高维数据,欧几里得距离通常会开始失败(高维是……很奇怪),而改用曼哈顿距离。

在计算完距离后,KNN 会选择一些最近的数据点——2、3、10,或者实际上是任何整数。 这个点数(2、3、10 等)是 K 在 K 近邻中!

在最后一步,如果是回归任务,KNN 会计算 K 个最近点的平均加权和用于预测。 如果是分类任务,新的数据点将被分配到大多数被选中的 K-最近点所属的类。

让我们借助一个简单的示例来可视化运行中的算法。 考虑一个具有两个变量且 K 为 3 的数据集。

执行回归时,任务是根据最近的 3 个点的平均加权和来找到新数据点的值。

KNN 与 K = 3, 什么时候 用于回归:

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

KNN 算法将从计算新点到所有点的距离开始。 然后它找到与新点距离最短的 3 个点。 这在上面的第二个图中显示,其中三个最近的点, 47, 5879 被包围了。 之后,它计算加权和 47, 5879 – 在这种情况下,权重等于 1 – 我们将所有点视为相等,但我们也可以根据距离分配不同的权重。 计算加权和后,新的点值为 61,33.

并且在执行分类时,KNN 任务将新数据点分类到 "Purple" or "Red" 类。

KNN 与 K = 3, 什么时候 用于分类:

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。
Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

KNN 算法将以与以前相同的方式开始,通过计算新点与所有点的距离,找到距离新点最近的 3 个最近点,然后,它不是计算一个数字,而是分配三个最近点中的大多数属于红色类的新点。 因此,新的数据点将被分类为 "Red".

离群点检测过程与上述两者不同,我们将在回归和分类实现之后再讨论它。

备注:本教程中提供的代码已执行并使用以下内容进行测试 Jupyter笔记本.

Scikit-Learn 加州住房数据集

我们将使用 加州住房数据集 来说明 KNN 算法的工作原理。 该数据集来自 1990 年美国人口普查。 数据集的一行代表一个区块组的人口普查。

在本节中,我们将详细介绍加州住房数据集,以便您可以直观地了解我们将使用的数据。 在开始处理数据之前了解数据非常重要。

A 阻止 group 是美国人口普查局发布样本数据的最小地理单位。 除了街区组,另一个术语是家庭,家庭是居住在一个家庭中的一群人。

数据集由九个属性组成:

  • MedInc – 街区组的收入中位数
  • HouseAge – 街区组中的房屋年龄中位数
  • AveRooms – 平均房间数(每户提供)
  • AveBedrms – 平均卧室数量(每户提供)
  • Population – 阻止群体人口
  • AveOccup – 家庭成员的平均人数
  • Latitude – 块组纬度
  • Longitude – 块组经度
  • MedHouseVal – 加州地区的房价中位数(数十万美元)

数据集是 已经是 Scikit-Learn 库的一部分,我们只需要导入它并将其加载为数据框:

from sklearn.datasets import fetch_california_housing

california_housing = fetch_california_housing(as_frame=True)

df = california_housing.frame

直接从 Scikit-Learn 导入数据,不仅导入列和数字,还包括数据描述作为 Bunch 对象——所以我们刚刚提取了 frame. 数据集的更多详细信息可用 此处.

让我们导入 Pandas 并查看前几行数据:

import pandas as pd
df.head()

执行代码将显示我们数据集的前五行:

	MedInc  HouseAge  AveRooms  AveBedrms  Population  AveOccup   Latitude  Longitude  MedHouseVal
0 	8.3252 	41.0 	  6.984127 	1.023810   322.0 	   2.555556   37.88 	-122.23    4.526
1 	8.3014 	21.0 	  6.238137 	0.971880   2401.0 	   2.109842   37.86 	-122.22    3.585
2 	7.2574 	52.0 	  8.288136 	1.073446   496.0 	   2.802260   37.85 	-122.24    3.521
3 	5.6431 	52.0 	  5.817352 	1.073059   558.0 	   2.547945   37.85 	-122.25    3.413
4 	3.8462 	52.0 	  6.281853 	1.081081   565.0 	   2.181467   37.85 	-122.25    3.422

在本指南中,我们将使用 MedInc, HouseAge, AveRooms, AveBedrms, Population, AveOccup, Latitude, Longitude 预测 MedHouseVal. 类似于我们的动机叙述。

现在让我们直接进入回归的 KNN 算法的实现。

使用 Scikit-Learn 进行 K-最近邻回归

到目前为止,我们已经了解了我们的数据集,现在可以继续 KNN 算法中的其他步骤。

KNN 回归的预处理数据

预处理是回归和分类任务之间的第一个差异出现的地方。 由于这部分都是关于回归的,我们将相应地准备我们的数据集。

对于回归,我们需要预测另一个中值房屋价值。 为此,我们将分配 MedHouseValy 和所有其他列 X 只需放下 MedHouseVal:

y = df['MedHouseVal']
X = df.drop(['MedHouseVal'], axis = 1)

通过查看我们的变量描述,我们可以看到我们在测量方面存在差异。 为了避免猜测,让我们使用 describe() 检查方法:


X.describe().T

结果是:

			count 	  mean 		   std 			min 		25% 		50% 		75% 		max
MedInc 		20640.0   3.870671 	   1.899822 	0.499900 	2.563400 	3.534800 	4.743250 	15.000100
HouseAge 	20640.0   28.639486    12.585558 	1.000000 	18.000000 	29.000000 	37.000000 	52.000000
AveRooms 	20640.0   5.429000 	   2.474173 	0.846154 	4.440716 	5.229129 	6.052381 	141.909091
AveBedrms 	20640.0   1.096675 	   0.473911 	0.333333 	1.006079 	1.048780 	1.099526 	34.066667
Population 	20640.0   1425.476744  1132.462122 	3.000000 	787.000000 	1166.000000 1725.000000 35682.000000
AveOccup 	20640.0   3.070655 	   10.386050 	0.692308 	2.429741 	2.818116 	3.282261 	1243.333333
Latitude 	20640.0   35.631861    2.135952 	32.540000 	33.930000 	34.260000 	37.710000 	41.950000
Longitude 	20640.0   -119.569704  2.003532    -124.350000 -121.800000 	-118.490000 -118.010000 -114.310000

在这里,我们可以看到 mean 价值 MedInc3.87mean 价值 HouseAge 大约是 28.64, 使其大 7.4 倍 MedInc. 其他特征在均值和标准差方面也存在差异——要了解这一点,请查看 meanstd 值并观察它们之间的距离。 为了 MedInc std1.9,为 HouseAge, std is 12.59 这同样适用于其他功能。

我们使用的算法基于 距离 和基于距离的算法在不同规模的数据(例如这些数据)中受到很大影响。 点的比例可能(实际上几乎总是如此)扭曲了值之间的实际距离。

为了执行特征缩放,我们将使用 Scikit-Learn 的 StandardScaler 以后上课。 如果我们现在应用缩放(在训练测试拆分之前),计算将包括测试数据,有效地 泄漏 测试数据信息到管道的其余部分。 这种 数据泄漏 不幸的是,通常会被跳过,从而导致无法重现或虚幻的发现。

将数据拆分为训练集和测试集

为了能够在不泄漏的情况下扩展我们的数据,同时也为了评估我们的结果并避免过度拟合,我们将数据集划分为训练和测试拆分。

创建训练和测试拆分的一种直接方法是 train_test_split 来自 Scikit-Learn 的方法。 拆分不会在某个点线性拆分,而是随机采样 X% 和 Y%。 为了使该过程可重现(使该方法始终对相同的数据点进行采样),我们将设置 random_state 论证某个 SEED:

from sklearn.model_selection import train_test_split

SEED = 42
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=SEED)

这段代码抽取了 75% 的数据用于训练,25% 的数据用于测试。 通过改变 test_size 例如,到 0.3 时,您可以使用 70% 的数据进行训练并使用 30% 的数据进行测试。

通过将 75% 的数据用于训练和 25% 用于测试,在 20640 条记录中,训练集包含 15480 条,测试集包含 5160 条。我们可以通过打印完整数据集和拆分数据的长度来快速检查这些数字:

len(X)       
len(X_train) 
len(X_test)  

伟大的! 我们现在可以将数据缩放器安装在 X_train 设置和缩放两者 X_trainX_test 不泄露任何数据 X_testX_train.

KNN 回归的特征缩放

通过进口 StandardScaler,实例化它,根据我们的训练数据拟合它(防止泄漏),并转换训练和测试数据集,我们可以执行特征缩放:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()

scaler.fit(X_train)


X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

请注意: 因为你经常打电话 scaler.fit(X_train) 其次是 scaler.transform(X_train) - 你可以叫一个单一的 scaler.fit_transform(X_train) 其次是 scaler.transform(X_test) 缩短通话时间!

现在我们的数据被缩放了! 缩放器仅维护数据点,而不是列名,当应用于 DataFrame. 让我们用列名再次将数据组织到一个 DataFrame 中并使用 describe() 观察变化 meanstd:

col_names=['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude']
scaled_df = pd.DataFrame(X_train, columns=col_names)
scaled_df.describe().T

这将给我们:

			count 		mean 			std 		min 		25% 		50% 		75% 		max
MedInc 		15480.0 	2.074711e-16 	1.000032 	-1.774632 	-0.688854 	-0.175663 	0.464450 	5.842113
HouseAge 	15480.0 	-1.232434e-16 	1.000032 	-2.188261 	-0.840224 	0.032036 	0.666407 	1.855852
AveRooms 	15480.0 	-1.620294e-16 	1.000032 	-1.877586 	-0.407008 	-0.083940 	0.257082 	56.357392
AveBedrms 	15480.0 	7.435912e-17 	1.000032 	-1.740123 	-0.205765 	-0.108332 	0.007435 	55.925392
Population 	15480.0 	-8.996536e-17 	1.000032 	-1.246395 	-0.558886 	-0.227928 	0.262056 	29.971725
AveOccup 	15480.0 	1.055716e-17 	1.000032 	-0.201946 	-0.056581 	-0.024172 	0.014501 	103.737365
Latitude 	15480.0 	7.890329e-16 	1.000032 	-1.451215 	-0.799820 	-0.645172 	0.971601 	2.953905
Longitude 	15480.0 	2.206676e-15 	1.000032 	-2.380303 	-1.106817 	0.536231 	0.785934 	2.633738

观察所有标准差现在的情况 1 手段也变小了。 这就是我们的数据 更统一! 让我们训练和评估一个基于 KNN 的回归器。

训练和预测 KNN 回归

Scikit-Learn 直观且稳定的 API 使训练回归器和分类器变得非常简单。 让我们导入 KNeighborsRegressor 班级从 sklearn.neighbors 模块,对其进行实例化,并将其拟合到我们的训练数据中:

from sklearn.neighbors import KNeighborsRegressor
regressor = KNeighborsRegressor(n_neighbors=5)
regressor.fit(X_train, y_train)

在上面的代码中, n_neighbors 是价值 K,或算法在选择新的房价中值时将考虑的邻居数量。 5 是默认值 KNeighborsRegressor(). K 没有理想值,经过测试和评估后选择,但是,开始, 5 是 KNN 的常用值,因此被设置为默认值。

最后一步是对我们的测试数据进行预测。 为此,请执行以下脚本:

y_pred = regressor.predict(X_test)

我们现在可以评估我们的模型对我们有标签(基本事实)的新数据(测试集)的泛化程度!

评估 KNN 回归的算法

评估算法最常用的回归指标是平均绝对误差 (MAE)、均方误差 (MSE)、均方根误差 (RMSE) 和决定系数 (R2):

  1. 平均绝对误差(MAE):当我们从实际值中减去预测值时,得到误差,将这些误差的绝对值相加并得到它们的平均值。 该指标给出了模型每个预测的总体误差的概念,越小(接近 0)越好:

$$
mae = (frac{1}{n})sum_{i=1}^{n}left | 实际 - 预测正确 |
$$

请注意: 您可能还会遇到 yŷ (读作 y-hat)方程中的符号。 这 y 指的是实际值和 ŷ 到预测值。

  1. 均方误差 (MSE):它类似于 MAE 度量,但它平方误差的绝对值。 此外,与 MAE 一样,越小或接近 0 越好。 MSE 值是平方的,以便使较大的误差更大。 需要密切注意的一件事是,由于其值的大小以及它们与数据的规模不同这一事实,它通常是一个难以解释的指标。

$$
mse = sum_{i=1}^{D}(实际 - 预测)^2
$$

  1. 均方根误差 (RMSE): 尝试通过获取其最终值的平方根来解决 MSE 引发的解释问题,以便将其缩放回相同的数据单位。 当我们需要显示或显示带有错误的数据的实际值时,它更容易解释和更好。 它显示了数据可能有多少变化,因此,如果我们的 RMSE 为 4.35,我们的模型可能会出错,因为它将 4.35 添加到实际值,或者需要 4.35 才能达到实际值。 越接近0,也越好。

$$
rmse = sqrt{ sum_{i=1}^{D}(实际 - 预测)^2}
$$

mean_absolute_error()mean_squared_error() 的方法 sklearn.metrics 可用于计算这些指标,如以下代码段所示:

from sklearn.metrics import mean_absolute_error, mean_squared_error

mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = mean_squared_error(y_test, y_pred, squared=False)

print(f'mae: {mae}')
print(f'mse: {mse}')
print(f'rmse: {rmse}')

上述脚本的输出如下所示:

mae: 0.4460739527131783 
mse: 0.4316907430948294 
rmse: 0.6570317671884894

R2 可以直接计算 score() 方法:

regressor.score(X_test, y_test)

哪些输出:

0.6737569252627673

结果表明我们的KNN算法整体误差和平均误差在 0.440.43. 此外,RMSE 表明我们可以通过添加高于或低于数据的实际值 0.65 或减去 0.65. 那有多好?

让我们看看价格是什么样的:

y.describe()
count    20640.000000
mean         2.068558
std          1.153956
min          0.149990
25%          1.196000
50%          1.797000
75%          2.647250
max          5.000010
Name: MedHouseVal, dtype: float64

平均值是 2.06 和平均值的标准差是 1.15 所以我们的分数~0.44 算不上明星,但也不算太差。

与 R2,最接近我们得到的 1(或 100),越好。 R2 告诉数据有多少变化,或数据 方差 被理解或 解释 通过 KNN。

$$
R^2 = 1 – frac{sum(Actual – Predicted)^2}{sum(Actual – Actual Mean)^2}
$$

价值为 0.67,我们可以看到我们的模型解释了 67% 的数据方差。 已经超过 50%,这还可以,但不是很好。 我们有什么办法可以做得更好吗?

我们使用了一个预先确定的 K,其值为 5,所以,我们使用 5 个邻居来预测我们的目标,这不一定是最好的数字。 要了解哪个是理想的 K 数,我们可以分析我们的算法错误并选择最小化损失的 K。

为 KNN 回归找到最佳 K

理想情况下,您会看到哪个指标更适合您的环境——但测试所有指标通常很有趣。 只要您可以测试所有这些,就去做。 在这里,我们将展示如何仅使用平均绝对误差来选择最佳 K,但您可以将其更改为任何其他指标并比较结果。

为此,我们将创建一个 for 循环并运行具有 1 到 X 个邻居的模型。 在每次交互中,我们将计算 MAE 并将 K 的数量与 MAE 结果一起绘制:

error = []


for i in range(1, 40):
    knn = KNeighborsRegressor(n_neighbors=i)
    knn.fit(X_train, y_train)
    pred_i = knn.predict(X_test)
    mae = mean_absolute_error(y_test, pred_i)
    error.append(mae)

现在,让我们绘制 errors:

import matplotlib.pyplot as plt 

plt.figure(figsize=(12, 6))
plt.plot(range(1, 40), error, color='red', 
         linestyle='dashed', marker='o',
         markerfacecolor='blue', markersize=10)
         
plt.title('K Value MAE')
plt.xlabel('K Value')
plt.ylabel('Mean Absolute Error')

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

从图中可以看出,最低 MAE 值似乎是当 K 为 12. 让我们通过绘制更少的数据来仔细查看该图以确保:

plt.figure(figsize=(12, 6))
plt.plot(range(1, 15), error[:14], color='red', 
         linestyle='dashed', marker='o',
         markerfacecolor='blue', markersize=10)
plt.title('K Value MAE')
plt.xlabel('K Value')
plt.ylabel('Mean Absolute Error')

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

查看我们的 Git 学习实践指南,其中包含最佳实践、行业认可的标准以及随附的备忘单。 停止谷歌搜索 Git 命令,实际上 学习 它!

您还可以使用内置的获取最低错误和该点的索引 min() 函数(适用于列表)或将列表转换为 NumPy 数组并获取 argmin() (具有最低值的元素的索引):

import numpy as np 

print(min(error))               
print(np.array(error).argmin()) 

我们从 1 开始计算邻居,而数组是从 0 开始的,所以第 11 个索引是 12 个邻居!

这意味着我们需要 12 个邻居才能预测 MAE 误差最低的点。 我们可以使用 12 个邻居再次执行模型和指标来比较结果:

knn_reg12 = KNeighborsRegressor(n_neighbors=12)
knn_reg12.fit(X_train, y_train)
y_pred12 = knn_reg12.predict(X_test)
r2 = knn_reg12.score(X_test, y_test) 

mae12 = mean_absolute_error(y_test, y_pred12)
mse12 = mean_squared_error(y_test, y_pred12)
rmse12 = mean_squared_error(y_test, y_pred12, squared=False)
print(f'r2: {r2}, nmae: {mae12} nmse: {mse12} nrmse: {rmse12}')

以下代码输出:

r2: 0.6887495617137436, 
mae: 0.43631325936692505 
mse: 0.4118522151025172 
rmse: 0.6417571309323467

有了 12 个邻居,我们的 KNN 模型现在解释了数据中 69% 的方差,并且损失更少,从 0.440.43, 0.430.410.650.64 与各自的指标。 这不是一个很大的改进,但它仍然是一个改进。

请注意: 在此分析中更进一步,进行探索性数据分析 (EDA) 和残差分析可能有助于选择特征并获得更好的结果。

我们已经看到了如何使用 KNN 进行回归——但是如果我们想对一个点进行分类而不是预测它的值怎么办? 现在,我们可以看看如何使用 KNN 进行分类。

使用带有 Scikit-Learn 的 K-最近邻进行分类

在这个任务中,我们想要预测这些块组所属的类别,而不是预测一个连续值。 为此,我们可以将地区的房价中值划分为具有不同房价范围的组或 垃圾箱.

当您想使用连续值进行分类时,通常可以对数据进行分箱。 通过这种方式,您可以预测组,而不是值。

预处理数据以进行分类

让我们创建数据箱以将我们的连续值转换为类别:


df["MedHouseValCat"] = pd.qcut(df["MedHouseVal"], 4, retbins=False, labels=[1, 2, 3, 4])

然后,我们可以将数据集拆分为其属性和标签:

y = df['MedHouseValCat']
X = df.drop(['MedHouseVal', 'MedHouseValCat'], axis = 1)

由于我们使用了 MedHouseVal 列来创建垃圾箱,我们需要删除 MedHouseVal 列和 MedHouseValCat 来自的列 X。 这样, DataFrame 将包含数据集的前 8 列(即属性、特征),而我们的 y 将仅包含 MedHouseValCat 分配的标签。

请注意: 您还可以使用选择列 .iloc() 而不是丢弃它们。 丢弃时,请注意您需要分配 y 赋值前的值 X 值,因为您不能分配 a 的已删除列 DataFrame 到内存中的另一个对象。

将数据拆分为训练集和测试集

正如回归所做的那样,我们还将数据集划分为训练和测试拆分。 由于我们有不同的数据,我们需要重复这个过程:

from sklearn.model_selection import train_test_split

SEED = 42
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=SEED)

我们将再次使用 75% 训练数据和 25% 测试数据的标准 Scikit-Learn 值。 这意味着我们将拥有与之前回归中相同的训练和测试记录数。

分类的特征缩放

由于我们正在处理相同的未处理数据集及其不同的度量单位,我们将再次执行特征缩放,就像我们对回归数据所做的那样:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
scaler.fit(X_train)

X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

分类训练和预测

在对数据进行分箱、拆分和缩放之后,我们最终可以在其上拟合分类器。 对于预测,我们将再次使用 5 个邻居作为基线。 您还可以实例化 KNeighbors_ 没有任何参数的类,它将自动使用 5 个邻居。 在这里,而不是导入 KNeighborsRegressor,我们将导入 KNeighborsClassifier, 班级:

from sklearn.neighbors import KNeighborsClassifier

classifier = KNeighborsClassifier()
classifier.fit(X_train, y_train)

装好后 KNeighborsClassifier,我们可以预测测试数据的类别:

y_pred = classifier.predict(X_test)

是时候评估预测了! 在这种情况下,预测类会比预测值更好吗? 让我们评估一下算法,看看会发生什么。

评估 KNN 进行分类

为了评估 KNN 分类器,我们还可以使用 score 方法,但它执行不同的度量,因为我们正在对分类器而不是回归器进行评分。 分类的基本指标是 accuracy – 它描述了我们的分类器有多少预测是正确的。 最低准确度值为 0,最高为 1。我们通常将该值乘以 100 以获得百分比。

$$
准确度 = frac{text{正确预测数}}{text{预测总数}}
$$

请注意: 任何真实数据都很难获得 100% 的准确度,如果发生这种情况,请注意可能会发生一些泄漏或错误——对于理想的准确度值没有共识,而且它也取决于上下文。 取决于 错误成本 (如果我们信任分类器,结果证明它是错误的,那该有多糟糕),可接受的错误率可能是 5%、10% 甚至 30%。

让我们为分类器打分:

acc =  classifier.score(X_test, y_test)
print(acc) 

通过查看结果分数,我们可以推断出我们的分类器有大约 62% 的类是正确的。 这已经有助于分析,尽管只知道分类器正确,很难改进它。

我们的数据集中有 4 个类——如果我们的分类器有 90% 的 1、2 和 3 类正确, 但是只有 第 30 类的 4% 对?

与类之间共享的平衡失败相比,某个类的系统性失败都可以产生 62% 的准确度分数。 准确度并不是实际评估的一个很好的衡量标准——但确实可以作为一个很好的代理。 通常情况下,对于平衡的数据集,62% 的准确率是相对均匀分布的。 此外,数据集往往是不平衡的,所以我们又回到了原点,准确度是一个不足的指标。

我们可以使用其他指标更深入地研究结果,以便能够确定这一点。 这一步也不同于回归,这里我们将使用:

  1. 混淆矩阵: 要知道我们做对或错了多少 每堂课. 正确且正确预测的值称为 真正的积极 那些被预测为阳性但不是阳性的被称为 误报. 相同的命名法 真正的负面假阴性 用于负值;
  2. 平台精度:了解哪些正确的预测值被我们的分类器认为是正确的。 Precision 会将那些真正的阳性值除以任何预测为阳性的值;

$$
精度 = frac{text{true positive}}{text{true positive} + text{false positive}}
$$

  1. 记得: 了解我们的分类器识别出多少真阳性。 召回率是通过将真正的阳性除以任何应该被预测为阳性的东西来计算的。

$$
回忆 = frac{文本{真阳性}}{文本{真阳性} + 文本{假阴性}}
$$

  1. F1分数: 是平衡的还是 谐波均值 精度和召回率。 最小值为 0,最大值为 1。当 f1-score 等于 1,这意味着所有类都被正确预测了——这是一个很难用真实数据获得的分数(几乎总是存在例外)。

$$
文本{f1-分数} = 2* 压裂{文本{精度} * 文本{召回}}{文本{精度} + 文本{召回}}
$$

请注意: 也存在加权 F1 分数,它只是一个 F1,不会对所有类别应用相同的权重。 重量通常由类别决定 SUPPORT – 有多少实例“支持”F1 分数(属于某个类别的标签的比例)。 支持度越低(类的实例越少),该类的加权 F1 越低,因为它更不可靠。

confusion_matrix()classification_report() 的方法 sklearn.metrics 模块可用于计算和显示所有这些指标。 这 confusion_matrix 使用热图可以更好地可视化。 分类报告已经给了我们 accuracy, precision, recallf1-score,但您也可以从 sklearn.metrics.

要获取指标,请执行以下代码段:

from sklearn.metrics import classification_report, confusion_matrix

import seaborn as sns


classes_names = ['class 1','class 2','class 3', 'class 4']
cm = pd.DataFrame(confusion_matrix(yc_test, yc_pred), 
                  columns=classes_names, index = classes_names)
                  

sns.heatmap(cm, annot=True, fmt='d');

print(classification_report(y_test, y_pred))

上述脚本的输出如下所示:

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

              precision    recall  f1-score   support

           1       0.75      0.78      0.76      1292
           2       0.49      0.56      0.53      1283
           3       0.51      0.51      0.51      1292
           4       0.76      0.62      0.69      1293

    accuracy                           0.62      5160
   macro avg       0.63      0.62      0.62      5160
weighted avg       0.63      0.62      0.62      5160

结果表明,KNN 能够以 5160% 的准确率对测试集中的所有 62 条记录进行分类,高于平均水平。 支持相当相等(数据集中的类分布均匀),因此加权 F1 和未加权 F1 将大致相同。

我们还可以看到 4 个类中每个类的度量结果。 由此,我们可以注意到 class 2 精度最低,最低 recall, 和最低的 f1-score. Class 3 就在后面 class 2 因为得分最低,然后,我们有 class 1 最好的分数其次 class 4.

通过查看混淆矩阵,我们可以看到:

  • class 1 大多被误认为 class 2 238 例
  • class 2 class 1 在 256 个条目中,并为 class 3 260 例
  • class 3 大多被误认为 class 2, 374 个条目, 和 class 4, 在 193 例中
  • class 4 被错误地归类为 class 3 对于 339 个条目,并且作为 class 2 在 130 例中。

另外,请注意对角线显示的是真正的正值,当查看它时,很明显可以看到 class 2class 3 具有最不正确的预测值。

有了这些结果,我们可以通过进一步检查来深入分析,找出发生这种情况的原因,并了解 4 个类是否是对数据进行分类的最佳方式。 也许值来自 class 2class 3 彼此太近了,所以很难区分它们。

始终尝试使用不同数量的 bin 测试数据,看看会发生什么。

除了任意数量的数据箱外,我们还选择了另一个任意数量,即 K 个邻居的数量。 当确定最大化或最小化度量值的 K 数量时,我们应用于回归任务的相同技术可以应用于分类。

为 KNN 分类找到最佳 K

让我们重复对回归所做的事情,并绘制 K 值的图表和测试集的相应指标。 您还可以选择哪个指标更适合您的上下文,在这里,我们将选择 f1-score.

这样,我们将绘制 f1-score 对于 1 到 40 之间的所有 K 值的测试集的预测值。

首先,我们导入 f1_scoresklearn.metrics 然后为 K-Nearest Neighbors 分类器的所有预测计算其值,其中 K 的范围从 1 到 40:

from sklearn.metrics import f1_score

f1s = []


for i in range(1, 40):
    knn = KNeighborsClassifier(n_neighbors=i)
    knn.fit(X_train, y_train)
    pred_i = knn.predict(X_test)
    
    f1s.append(f1_score(y_test, pred_i, average='weighted'))

下一步是绘制 f1_score 值对 K 值。 与回归不同的是,这次我们将选择使误差最小的K值,而不是选择使误差最小的值 f1-score.

执行以下脚本来创建绘图:

plt.figure(figsize=(12, 6))
plt.plot(range(1, 40), f1s, color='red', linestyle='dashed', marker='o',
         markerfacecolor='blue', markersize=10)
plt.title('F1 Score K Value')
plt.xlabel('K Value')
plt.ylabel('F1 Score')

输出图如下所示:

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

从输出中,我们可以看到 f1-score 当 K 值为最高时 15. 让我们用 15 个邻居重新训练我们的分类器,看看它对我们的分类报告结果做了什么:

classifier15 = KNeighborsClassifier(n_neighbors=15)
classifier15.fit(X_train, y_train)
y_pred15 = classifier15.predict(X_test)
print(classification_report(y_test, y_pred15))

输出:

              precision    recall  f1-score   support

           1       0.77      0.79      0.78      1292
           2       0.52      0.58      0.55      1283
           3       0.51      0.53      0.52      1292
           4       0.77      0.64      0.70      1293

    accuracy                           0.63      5160
   macro avg       0.64      0.63      0.64      5160
weighted avg       0.64      0.63      0.64      5160

请注意,我们的指标在 15 个邻居中得到了改进,我们的准确率达到了 63% 甚至更高 precision, recallf1-scores,但我们仍然需要进一步查看垃圾箱以尝试了解为什么 f1-score 班级 23 仍然很低。

除了使用 KNN 进行回归和确定块值和分类之外,我们还可以使用 KNN 来检测哪些平均块值与大多数块值不同——那些不遵循大多数数据正在执行的块值。 换句话说,我们可以使用 KNN 检测异常值.

使用 Scikit-Learn 实现 KNN 进行异常值检测

异常值检测 使用另一种不同于我们之前为回归和分类所做的方法。

在这里,我们将看到每个邻居与数据点的距离。 让我们使用默认的 5 个邻居。 对于一个数据点,我们将计算到每个 K 近邻的距离。 为此,我们将从 Scikit-learn 导入另一种 KNN 算法,该算法不特定于回归或分类,简称为 NearestNeighbors.

导入后,我们将实例化一个 NearestNeighbors 有 5 个邻居的类——你也可以用 12 个邻居来实例化它,以在我们的回归示例中识别异常值,或者用 15 个来实例化它,对分类示例执行相同的操作。 然后我们将拟合我们的训练数据并使用 kneighbors() 找到每个数据点和邻居索引的计算距离的方法:

from sklearn.neighbors import NearestNeighbors

nbrs = NearestNeighbors(n_neighbors = 5)
nbrs.fit(X_train)

distances, indexes = nbrs.kneighbors(X_train)

现在我们有每个数据点的 5 个距离——它自己和它的 5 个邻居之间的距离,以及一个标识它们的索引。 让我们看一下前三个结果和数组的形状,以便更好地可视化它。

要查看前三个距离形状,请执行:

distances[:3], distances.shape
(array([[0.        , 0.12998939, 0.15157687, 0.16543705, 0.17750354],
        [0.        , 0.25535314, 0.37100754, 0.39090243, 0.40619693],
        [0.        , 0.27149697, 0.28024623, 0.28112326, 0.30420656]]),
 (3, 5))

观察有 3 行,每行有 5 个距离。 我们还可以查看和邻居的索引:

indexes[:3], indexes[:3].shape

结果是:

(array([[    0,  8608, 12831,  8298,  2482],
        [    1,  4966,  5786,  8568,  6759],
        [    2, 13326, 13936,  3618,  9756]]),
 (3, 5))

在上面的输出中,我们可以看到 5 个邻居中每一个的索引。 现在,我们可以继续计算 5 个距离的平均值,并绘制一个图表,计算 X 轴上的每一行,并在 Y 轴上显示每个平均距离:

dist_means = distances.mean(axis=1)
plt.plot(dist_means)
plt.title('Mean of the 5 neighbors distances for each data point')
plt.xlabel('Count')
plt.ylabel('Mean Distances')

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

请注意,图中的一部分平均距离具有统一的值。 平均值不太高或太低的那个 Y 轴点正是我们需要识别以切断异常值的点。

在这种情况下,平均距离为 3。让我们用水平虚线再次绘制图形,以便能够发现它:

dist_means = distances.mean(axis=1)
plt.plot(dist_means)
plt.title('Mean of the 5 neighbors distances for each data point with cut-off line')
plt.xlabel('Count')
plt.ylabel('Mean Distances')
plt.axhline(y = 3, color = 'r', linestyle = '--')

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

这条线标志着平均距离,高于它的所有值都在变化。 这意味着所有带有 mean 以上距离 3 是我们的异常值。 我们可以使用以下方法找出这些点的索引 np.where(). 此方法将输出 True or False 对于每个指数关于 mean 在3之上 条件:

import numpy as np


outlier_index = np.where(dist_means > 3)
outlier_index

上面的代码输出:

(array([  564,  2167,  2415,  2902,  6607,  8047,  8243,  9029, 11892,
        12127, 12226, 12353, 13534, 13795, 14292, 14707]),)

现在我们有了离群点索引。 让我们在数据框中找到它们:


outlier_values = df.iloc[outlier_index]
outlier_values

结果是:

		MedInc 	HouseAge AveRooms 	AveBedrms 	Population 	AveOccup 	Latitude 	Longitude 	MedHouseVal
564 	4.8711 	27.0 	 5.082811 	0.944793 	1499.0 	    1.880803 	37.75 		-122.24 	2.86600
2167 	2.8359 	30.0 	 4.948357 	1.001565 	1660.0 	    2.597809 	36.78 		-119.83 	0.80300
2415 	2.8250 	32.0 	 4.784232 	0.979253 	761.0 	    3.157676 	36.59 		-119.44 	0.67600
2902 	1.1875 	48.0 	 5.492063 	1.460317 	129.0 	    2.047619 	35.38 		-119.02 	0.63800
6607 	3.5164 	47.0 	 5.970639 	1.074266 	1700.0 	    2.936097 	34.18 		-118.14 	2.26500
8047 	2.7260 	29.0 	 3.707547 	1.078616 	2515.0 	    1.977201 	33.84 		-118.17 	2.08700
8243 	2.0769 	17.0 	 3.941667 	1.211111 	1300.0 	    3.611111 	33.78 		-118.18 	1.00000
9029 	6.8300 	28.0 	 6.748744 	1.080402 	487.0 		2.447236 	34.05 		-118.78 	5.00001
11892 	2.6071 	45.0 	 4.225806 	0.903226 	89.0 		2.870968 	33.99 		-117.35 	1.12500
12127 	4.1482 	7.0 	 5.674957 	1.106998 	5595.0 		3.235975 	33.92 		-117.25 	1.24600
12226 	2.8125 	18.0 	 4.962500 	1.112500 	239.0 		2.987500 	33.63 		-116.92 	1.43800
12353 	3.1493 	24.0 	 7.307323 	1.460984 	1721.0 		2.066026 	33.81 		-116.54 	1.99400
13534 	3.7949 	13.0 	 5.832258 	1.072581 	2189.0 		3.530645 	34.17 		-117.33 	1.06300
13795 	1.7567 	8.0 	 4.485173 	1.120264 	3220.0 		2.652389 	34.59 		-117.42 	0.69500
14292 	2.6250 	50.0 	 4.742236 	1.049689 	728.0 		2.260870 	32.74 		-117.13 	2.03200
14707 	3.7167 	17.0 	 5.034130 	1.051195 	549.0 		1.873720 	32.80 		-117.05 	1.80400

我们的异常值检测完成。 这就是我们发现偏离一般数据趋势的每个数据点的方式。 我们可以看到,我们的训练数据中有 16 个点应该进一步查看、调查、可能处理,甚至从我们的数据中删除(如果输入错误)以改善结果。 这些点可能是由于输入错误、平均块值不一致,甚至两者兼而有之。

KNN 的优缺点

在本节中,我们将介绍使用 KNN 算法的一些优缺点。

优点

  • 很容易实现
  • 它是一种惰性学习算法,因此不需要对所有数据点进行训练(仅使用 K 最近邻进行预测)。 这使得 KNN 算法比其他需要使用整个数据集进行训练的算法快得多,例如 支持向量机, 线性回归等等。
  • 由于 KNN 在进行预测之前不需要训练,因此可以无缝添加新数据
  • 使用 KNN 只需要两个参数,即 K 的值和距离函数

缺点

  • KNN 算法不适用于高维数据,因为在大量维的情况下,点之间的距离会变得“奇怪”,而我们使用的距离度量也站不住脚
  • 最后,KNN 算法不适用于分类特征,因为很难找到具有分类特征的维度之间的距离

走得更远——手持式端到端项目

Python 和 Scikit-Learn PlatoBlockchain 数据智能中的 K-最近邻算法指南。 垂直搜索。 哎。

在这个指导项目中——您将学习如何构建强大的传统机器学习模型和深度学习模型,利用集成学习和训练元学习者从一组 Scikit-Learn 和 Keras 模型中预测房价。

使用基于 Tensorflow 构建的深度学习 API Keras,我们将试验架构,构建堆叠模型的集合并训练 元学习者 神经网络(一级模型)来计算房子的价格。

深度学习是惊人的——但在使用它之前,建议也尝试使用更简单的技术来解决问题,例如 浅层学习 算法。 我们的基准性能将基于 随机森林回归 算法。 此外——我们将探索通过 Scikit-Learn 通过以下技术创建模型集合 装袋 和 表决.

这是一个端到端的项目,和所有机器学习项目一样,我们将从 探索性数据分析,其次是 数据预处理 最后 浅层建筑 和 深度学习模型 以适应我们之前探索和清理过的数据。

结论

KNN 是一种简单而强大的算法。 它可用于许多任务,例如回归、分类或异常值检测。

KNN 已被广泛用于查找文档相似性和模式识别。 它还被用于开发推荐系统以及计算机视觉的降维和预处理步骤——尤其是人脸识别任务。

在本指南中——我们使用 Scikit-Learn 的 K-Nearest Neighbor 算法实现了回归、分类和异常值检测。

时间戳记:

更多来自 堆栈滥用