简介
该 K-近邻(KNN)算法是一种有监督的机器学习算法,用于分类、回归以及离群点检测。它在最基本的形式上非常容易实现,但可以执行相当复杂的任务。它是一种懒惰的学习算法,因为它没有一个专门的训练阶段。相反,它使用所有的数据进行训练,同时对一个新的数据点或实例进行分类(或回归)。
KNN是一种非参数学习算法,这意味着它对基础数据没有任何假设。这是一个非常有用的功能,因为大多数现实世界的数据并不真正遵循任何理论假设,例如线性分离性、均匀分布等。
在本指南中,我们将看到KNN如何通过Python的Scikit-Learn库来实现。在此之前,我们将首先探讨如何使用KNN并解释其背后的理论。之后,我们将看看我们将使用的加州住房数据集来说明KNN算法和它的几种变化。首先,我们将看看如何实现KNN算法的回归,然后是KNN分类和离群点检测的实现。最后,我们将总结一下该算法的一些优点和缺点。
什么时候应该使用KNN?
假设你想租一套公寓,最近发现你朋友的邻居可能会在2周内把她的公寓出租。由于该公寓还没有出现在租赁网站上,你怎么能尝试估计它的租值呢?
比方说,你的朋友支付1200美元的租金。你的租金价值可能在这个数字左右,但公寓并不完全相同(朝向、面积、家具质量等),所以,最好能有更多其他公寓的数据。
通过询问其他邻居,并查看在租房网站上列出的同一栋楼的公寓,最接近的三套相邻公寓的租金分别是1200美元、1210美元、1210美元和1215美元。这些公寓与你朋友的公寓在同一个街区和楼层。
其他公寓距离较远,在同一楼层,但在不同的街区,租金分别为1400美元、1430美元、1500美元和1470美元。似乎它们更贵,因为在晚上有更多的阳光照射。
考虑到该公寓的距离,你的估计租金似乎是1210美元左右。这就是**K-Nearest Neighbors (KNN)**算法的总体思路!它对新的分类或回归。它根据新数据与已有数据的接近程度对其进行分类或回归。
将实例转化为理论
当估计值是一个连续的数字时,如租值,KNN被用于 回归.但我们也可以根据最低和最高租金将公寓分为不同类别,例如。当数值是离散的,使其成为一个类别时,KNN被用于 分类.
还有一种可能,就是估计哪些邻居与其他邻居有很大的不同,以至于他们可能会停止支付租金。这与检测哪些数据点离得很远,以至于它们不适合任何数值或类别是一样的,当这种情况发生时,KNN被用于 离群点检测.
在我们的例子中,我们也已经知道了每个公寓的租金,这意味着我们的数据已经被标记了。KNN使用以前标记的数据,这使它成为一个 监督学习算法.
KNN以其最基本的形式极易实现,却能完成相当复杂的分类、回归或异常点检测任务。
每次有一个新的点加入到数据中,KNN只使用数据的一部分来决定这个新增点的值(回归)或类别(分类)。由于它不需要再看所有的点,这使它成为一个. 懒惰学习算法.
KNN也不假设任何关于基础数据特征的东西,它不期望数据适合某种类型的分布,如均匀分布,或者是线性可分离的。这意味着它是一个 非参数学习算法.这是一个非常有用的特性,因为大多数现实世界的数据并不真正遵循任何理论上的假设。
视觉化的KNN的不同用途
正如已经表明的那样,KNN算法背后的直觉是所有监督机器学习算法中最直接的一种。该算法首先计算一个新数据点与所有其他训练数据点的距离。
注意:距离可以用不同的方式测量。你可以使用Minkowski、Euclidean、Manhattan、Mahalanobis或Hamming公式,仅举几个指标。对于高维数据,欧氏距离经常开始失效(高维度是......奇怪的),而曼哈顿距离则被使用。
在计算完距离后,KNN会选择一些最近的数据点--2、3、10,或者真的,任何整数。这个点数(2、3、10等)就是K-Nearest Neighbors中的K!
在最后一步,如果是回归任务,KNN将计算出预测的K最近点的平均加权和。如果是分类任务,新的数据点将被分配到大多数选定的K-最近的点所属的类别中。
让我们借助一个简单的例子来直观地了解该算法的作用。考虑一个有两个变量的数据集,K为3。
在执行回归时,任务是根据3个最近点的平均加权和,找到一个新数据点的值。
KNN与K = 3
,当 用于回归的:
KNN算法将首先计算新点与所有点的距离。然后,它找到与新点距离最小的3个点。这显示在上面的第二个图中,其中三个最近的点,47
,58
, 和79
已经被包围起来了。之后,它计算47
,58
和79
的加权和--在这种情况下,权重等于1--我们考虑所有的点都是相等的,但我们也可以根据距离分配不同的权重。在计算了加权和之后,新的点值是61,33
。
而在进行分类时,KNN的任务是将一个新的数据点,归入"Purple"
或"Red"
类。
KNN与K = 3
,当 用于分类:
KNN算法将以与之前相同的方式开始,通过计算新点与所有点的距离,找到与新点距离最小的3个最近的点,然后,不计算数字,而是将新点分配到三个最近的点中大多数属于的类别,即红色类别。因此,新的数据点将被分类为"Red"
。
离群点检测过程与上述两者不同,我们将在回归和分类实现后的实施过程中更多地谈论它。
注意:本教程中提供的代码已经在以下Jupyter笔记本中执行和测试。
Scikit-Learn加州住房数据集
我们将使用加州住房数据集来说明KNN算法如何工作。该数据集来自1990年的美国人口普查。数据集的一行代表一个街区组的人口普查情况。
在这一节中,我们将介绍加州住房数据集的细节,这样你就可以对我们将要处理的数据有一个直观的了解。在你开始工作之前,了解你的数据是非常重要的。
区块组是美国人口普查局发布样本数据的最小地理单位。除了街区组,另一个术语是家庭,家庭是居住在一个家庭里的一群人。
该数据集由九个属性组成:
MedInc
- 街区组的收入中位数HouseAge
- 街区组内的房龄中位数AveRooms
- 平均房间数(每户提供)。AveBedrms
- 平均卧室数(每户提供)。Population
- 小区组的人口AveOccup
- 家庭成员的平均数量Latitude
- 小区组纬度Longitude
- 区块组经度MedHouseVal
- 加州各区的房屋价值中值(几十万美元)
数据集已经是Scikit-Learn库的一部分,我们只需要导入它并将其作为数据框架加载。
from sklearn.datasets import fetch_california_housing
# as_frame=True loads the data in a dataframe format, with other metadata besides it
california_housing = fetch_california_housing(as_frame=True)
# Select only the dataframe part and assign it to the df variable
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-Nearest Neighbors回归
到目前为止,我们已经了解了我们的数据集,现在可以进行KNN算法的其他步骤了。
为KNN回归预处理数据
预处理是回归和分类任务之间的第一个差异出现的地方。由于本节是关于回归的,我们将相应地准备我们的数据集。
对于回归,我们需要预测另一个房屋价值中位数。为此,我们将把MedHouseVal
归入y
,把所有其他列归入X
,只需去掉MedHouseVal
。
y = df['MedHouseVal']
X = df.drop(['MedHouseVal'], axis = 1)
通过查看我们的变量描述,我们可以看到我们在测量方面存在差异。为了避免猜测,让我们使用describe()
方法来检查。
# .T transposes the results, transforming rows into columns
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
mean
在这里,我们可以看到MedInc
的值大约是3.87
,HouseAge
的值大约是28.64
,这使得它比MedInc
大7.4倍。其他特征也有平均值和标准差的差异--要看到这一点,看一下mean
和std
的值,观察它们之间的距离。对于MedInc
std
大约是1.9
,对于HouseAge
,std
是12.59
,同样适用于其他特征。
我们使用的是一种基于距离的算法,而基于距离的算法在不在同一尺度上的数据中受到很大的影响,比如这个数据。点的比例可能(而且在实践中,几乎总是如此)扭曲了数值之间的真实距离。
为了执行特征缩放,我们稍后将使用Scikit-Learn的StandardScaler
类。如果我们现在就应用缩放(在训练-测试分割之前),计算将包括测试数据,有效地将测试数据信息泄露到管道的其他部分。不幸的是,这种数据泄露通常会被跳过,导致不可复制的或虚幻的发现。
将数据分割成训练集和测试集
为了能够在不泄漏数据的情况下扩展我们的数据,同时也为了评估我们的结果和避免过度拟合,我们将把我们的数据集分成训练和测试两部分。
创建训练和测试分片的直接方法是Scikit-Learn的train_test_split
方法。这个分割并不是在某个点上线性分割,而是随机地对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) # 20640
len(X_train) # 15480
len(X_test) # 5160
很好!X_test
我们现在可以在X_train
集上拟合数据缩放器,并在不泄露任何数据的情况下将X_train
和X_test
缩放到X_train
。
建议:如果你想了解更多关于train_test_split()
方法、训练-测试-验证分离的重要性,以及如何将验证集也分离出来,请阅读我们的 "Scikit-Learn的train_test_split() - 训练、测试和验证集".
用于KNN回归的特征缩放
通过导入StandardScaler
,实例化它,根据我们的训练数据进行拟合(防止泄漏),并转换训练和测试数据集,我们可以进行特征缩放。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# Fit only on X_train
scaler.fit(X_train)
# Scale both X_train and X_test
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()
,观察mean
和std
的变化。
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使得训练回归器和分类器非常简单。让我们从sklearn.neighbors
模块中导入KNeighborsRegressor
类,将其实例化,并将其适用于我们的训练数据。
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)。
- 平均绝对误差(MAE)。当我们从实际值中减去预测值,得到误差,将这些误差的绝对值相加,得到其平均值。这个指标给出了模型每次预测的总体误差的概念,越小(越接近0)越好。
注意:你可能还会在方程中遇到y
和ŷ
(读作y-hat)的符号。y
是指实际值,ŷ
是指预测值。
- 平均平方误差(MSE)。它与MAE指标类似,但它将误差的绝对值进行平方。另外,与MAE一样,越小,或越接近0,越好。MSE值的平方是为了使大的误差变得更大。需要注意的是,由于其数值的大小以及它们与数据不在同一尺度上的事实,它通常是一个难以解释的指标。
- 均方根误差(RMSE)。试图通过获取其最终值的平方根来解决与MSE有关的解释问题,以便将其缩回到与数据相同的单位。当我们需要展示或显示数据的实际值与误差时,它更容易解释,也很好。它显示了数据的变化程度,因此,如果我们的RMSE为4.35,我们的模型可能会出现错误,因为它在实际值上增加了4.35,或者需要4.35才能达到实际值。越接近于0,也就越好。
mean_absolute_error()
sklearn.metrics
和 的方法可以用来计算这些指标,从下面的片段中可以看出。mean_squared_error()
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.44
,和0.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所理解或解释。
值为0.67
,我们可以看到,我们的模型解释了67%的数据方差。这已经超过了50%,这还可以,但不是很好。我们有什么办法可以做得更好吗?
我们使用了一个预定的K值,即5
,所以,我们使用5个邻居来预测我们的目标,这不一定是最好的数字。为了了解哪个是理想的K数,我们可以分析我们的算法错误,并选择损失最小的K。
寻找KNN回归的最佳K
理想情况下,你会看到哪个指标更适合你的环境--但通常测试所有指标是很有趣的。只要你能测试所有的指标,就去做吧。在这里,我们将展示如何只用平均绝对误差来选择最佳K值,但是你可以把它改成任何其他指标,并比较结果。
要做到这一点,我们将创建一个for循环,运行有1到X个邻居的模型。在每个交互作用中,我们将计算MAE,并将K的数量与MAE的结果一起绘制出来。
error = []
# Calculating MAE error for K values between 1 and 39
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)
现在,让我们绘制error
s:
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')
看一下这个图,似乎最低的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')
你也可以使用内置的min()
函数获得最低误差和该点的索引(对列表有效),或者将列表转换成NumPy数组并获得argmin()
(具有最低值的元素的索引)。
import numpy as np
print(min(error)) # 0.43631325936692505
print(np.array(error).argmin()) # 11
我们从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.44
到0.43
,0.43
到0.41
,0.65
到0.64
,各自的度量都有了。这不是一个非常大的改进,但它仍然是一个改进。
**注意:**在这个分析中,进一步做探索性数据分析(EDA)和残差分析可能有助于选择特征并取得更好的结果。
我们已经看到了如何使用KNN进行回归--但是如果我们想对一个点进行分类而不是预测它的值呢?现在,我们可以看一下如何使用KNN进行分类。
使用Scikit-Learn的K-Nearest Neighbors进行分类
在这个任务中,我们不是预测一个连续值,而是要预测这些区块组所属的类别。为了做到这一点,我们可以将各区的房屋价值中位数划分为具有不同房屋价值范围的组或仓。
当你想使用一个连续值进行分类时,你通常可以将数据分仓。通过这种方式,你可以预测群体,而不是数值。
用于分类的数据预处理
让我们创建数据仓,将我们的连续值转化为类别。
# Creating 4 categories and assigning them to a MedHouseValCat column
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
列来创建bin,我们需要从MedHouseVal
列和MedHouseValCat
列中删除X
。这样,DataFrame
将包含数据集的前8列(即属性、特征),而我们的y
将只包含MedHouseValCat
分配的标签。
**注意:**你也可以用.iloc()
来选择列,而不是丢弃它们。当丢弃时,只需注意你需要在分配y
值之前分配X
值,因为你不能将丢弃的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)
我们将再次使用Scikit-Learn的标准值:75%的训练数据和25%的测试数据。这意味着我们将拥有与之前回归中相同的训练和测试记录数。
分类的特征缩放
由于我们处理的是同一个未经处理的数据集及其不同的测量单位,我们将再次进行特征缩放,方法与回归数据的方法相同。
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,得到一个百分比。
**注意:**在任何真实的数据上获得100%的准确性是非常困难的,如果发生这种情况,要注意可能会发生一些泄漏或错误的事情--在理想的准确性值上没有达成共识,它也是取决于上下文的。根据错误的代价(如果我们相信分类器,但结果却是错误的,这有多糟糕),可接受的错误率可能是5%、10%甚至30%。
让我们给我们的分类器打分。
acc = classifier.score(X_test, y_test)
print(acc) # 0.6191860465116279
通过观察所得的分数,我们可以推断出我们的分类器有~62%的类是正确的。这已经有助于分析,尽管只知道分类器做对了什么,就很难改进它。
在我们的数据集中有4个类--如果我们的分类器在1、2、3类中有90%是正确的,但在4类中只有30%是正确的呢?
某个类的系统性故障,与类之间共享的平衡性故障相比,都可以得到62%的准确率。准确率并不是实际评估的一个真正好的指标--但确实可以作为一个好的代理。更多的时候,在平衡的数据集中,62%的准确率是相对均匀的。而且,更多的时候,数据集并不平衡,所以我们又回到了原点,准确率是一个不充分的衡量标准。
我们可以使用其他指标深入研究结果,以便能够确定这一点。这一步也与回归不同,这里我们将使用。
- 混淆矩阵。要想知道我们的预测在多大程度上是正确的或错误的 各类.正确的和正确预测的值被称为真阳性,那些被预测为阳性但不是阳性的值被称为假阳性。真阴性和假阴性的命名方法同样适用于负值。
- 精度。来了解哪些正确的预测值被我们的分类器认为是正确的。精度将把这些真阳性值除以任何被预测为阳性的东西。
$
精度={frac{text{true positive}}{text{true positive}}
。
+ 虚假阳性}}
召回率={frac{text{true positive}}{text{true positive}}
。
+ frac{text{false negative}}
\text{f1-score} = 2* \frac{text{precision} * \text{recall} * \f1-score* `text{recall}}{text{precision}}。+text{recall}}