机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧

目录

初识K-近邻算法

距离度量

K值选择

kd树

数据集划分

特征预处理

莺尾花种类预测(实操)

交叉验证与网格搜索


初识K-近邻算法

K-近邻算法(K-Nearest Neighbor,KNN)是一种基本的分类和回归算法。它的基本思想是通过找出与新对象最近邻的 K 个训练数据,在分类时将新对象归类为出现频率最高的类别。这个算法是机器学习里面一个比较经典的算法,总体来说KNN算法是相对比较容易理解的算法。

定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。

距离公式:两个样本的距离可以通过如下公式计算,又叫欧式距离。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第1张图片

KNN算法流程总结

1)计算已知类别数据集中的点与当前点之间的距离。

2)按距离递增次序排序。

3)选取与当前点距离最小的k个点。

4)统计前k个点所在的类别出现的频率。

5)返回前k个点出现频率最高的类别作为当前点的预测分类。

关于K-近邻算法API的使用,要知道我们机器学习的流程是:

1)获取数据集 2)数据基本处理 3)特征工程 4)机器学习 5)模型评估

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第2张图片

接下来我们开始使用机器学习的算法库:Scikit-learn。其包括许多知名的机器学习算法的实现,文档完善、容易上手、具有丰富的API。其包含的内容:分类、聚类、回归、特征工程、模型选择、调优等功能,非常适合新手小白上手操作。接下来我们终端执行如下命令,开始对其进行安装:

pip install scikit-learn -i https://pypi.mirrors.ustc.edu.cn/simple

接下来通过代码演示如何使用KNeighborsClassifier类构建一个基本的K-近邻分类器并进行数据预测:

from sklearn.neighbors import KNeighborsClassifier

# 构造数据
x = [[1], [2], [10], [20]]
y = [0, 0, 1, 1]

# 模型训练
estimator = KNeighborsClassifier(n_neighbors=1) # 实例化一个估计器对象
estimator.fit(x, y) # 调用fit方法进行训练

# 数据预测
ret1 = estimator.predict([[-1000]]) # 它与训练点[1]的距离最近,标记为[0]
print(ret1)

ret2 = estimator.predict([[1000]]) # 它与训练点[20]的距离最近,标记为[1]
print(ret2)

最终呈现的结果如下所示:

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第3张图片

距离度量

距离度量是用来衡量数据对象之间相似性或差异性的方法。它通常用于K-近邻算法、聚类分析、降维算法等各种机器学习任务中。

在机器学习过程中,对于函数dist(.,.,),若它是一“距离度量”,则需要满足一些基本性质:

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第4张图片

常见的距离度量包括欧氏距离、曼哈顿距离、切比雪夫距离、闵可夫斯基距离、标准化欧式距离、余弦距离、杰卡德距离等。这些距离度量方法可以根据数据的特点和应用场景进行选择,以便更准确地描述数据对象之间的相似性或差异性。

1)欧氏距离是最常见的距离度量方式,用于衡量数据空间中两点之间的直线距离。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第5张图片

2)曼哈顿距离衡量的是两点在各个坐标轴上的距离总和,也称为城市街区距离。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第6张图片

3)切比雪夫距离也称为棋盘距离,是一种用于衡量向量和点之间的距离的度量方式。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第7张图片

4)闵可夫斯基距离是这两者的泛化,可以根据具体需要调整参数p来实现不同的距离度量方式。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第8张图片

5)标准化欧式距离是一种对欧式距离进行标准化处理的方法,通常用于数据的相似性度量。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第9张图片

6)余弦距离是一种衡量向量之间相似性的度量方式,常用于文本挖掘、信息检索等领域。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第10张图片

7)杰卡德距离是一种用于衡量集合之间差异性的度量方式,常用于计算两个集合的相似性或差异程度。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第11张图片

合适的距离度量方式可以帮助机器学习算法更好地理解和处理数据,从而提高模型的性能和准确度。在实际应用中,选择合适的距离度量方式是非常重要的,因为它直接影响着模型的表现和结果的准确性。

K值选择

在机器学习中,K 值通常用于 K 近邻算法(K-Nearest Neighbors, KNN)中,代表着最近邻的数量。KNN 是一种基本的分类和回归算法,其基本思想是根据样本间的距离来判断新样本的类别或数值。

KNN 算法的工作原理是:对于一个待分类的样本,它的类别取决于其 K 个最近邻样本中最常见的类别。K 值的选择会直接影响到 KNN 的性能和结果。

K 值的选择对 KNN 算法的影响主要体现在以下两个方面

1)容易受噪声数据影响:当 K 值较小时,模型更容易受到噪声数据的干扰。如果 K=1,那么分类结果完全取决于最近邻的类别,这可能导致对噪声数据过度敏感。而当 K 值较大时,模型对噪声数据的影响会减小。

2)模型的复杂度和偏差-方差权衡:较小的 K 值会使得模型更加复杂,因为它需要考虑更多的最近邻数据点。这可能导致模型过拟合,出现高方差的情况。而较大的 K 值会使得模型更简单,可能会导致欠拟合,出现高偏差的情况。

因此,在选择 K 值时需要进行适当的调整和折衷。一般来说,K 值的选择可以通过交叉验证或网格搜索等方法进行模型选择和调优。具体选择哪个 K 值取决于数据集的大小、类别的平衡性、噪声数据的程度以及模型的复杂度等因素。在实际应用中,可以通过尝试不同的 K 值,并评估模型的性能来选择最优的 K 值。

K 值的选择问题

1)选择较小的K值,就相当于用较小的领域中的训练实例进行预测,“学习"近似误差会减小,只有与输入实例较近或相似的训练实例才会对预测结果起作用,与此同时带来的问题是“学习"的估计误差会增大,换句话说,K值的减小就意味着整体模型变得复杂,容易发生过拟合;

2)选择较大的K值,就相当于用较大领域中的训练实例进行预测,其优点是可以减少学习的估计误差,但缺点是学习的近似误差会增大。这时候,与输入实例较远(不相似的)训练实例也会对预测器作用,使预测发生错误,且K值的增大就意味着整体的模型变得简单。

3)K=N(N为训练样本个数),则完全不足取,因为此时无论输入实例是什么,都只是简单的预测它属于在训练实例中最多的类,模型过于简单,忽略了训练实例中大量有用信息。

名词解释

近似误差:

1)对现有训练集的训练误差,关注训练集。

2)如果近似误差过小可能会出现过拟合的现象,对现有的训练集能有很好的预测,但是对未知的测试样本将会出现较大偏差的预测。

3)模型本身不是最接近最佳模型。

估计误差:

1)可以理解为对测试集的测试误差,关注测试集。

2)估计误差小说明对未知数据的预测能力好。

3)模型本身最接近最佳模型。

在实际应用中,K值一般取一个比较小的数值,例如采用交叉验证法(简单来说,就是把训练数据在分成两组:训练集和验证集)来选择最优的K值。

kd树

KD 树(KD-Tree)是一种用于快速查找多维空间中最近邻点的数据结构。它是一种二叉树,每个节点代表一个超矩形区域,并且树的每一级都以不同的轴对数据进行分割。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第12张图片

为了避免每次都重新计算一遍距离,算法会把距离信息保存在一棵树里,这样在计算之前从树里查询距离信息,尽量避免重新计算。其基本原理是,如果A和B距离很远,B和C距离很近,那么A和C的距离也很远。有了这个信息,就可以在合适的时候跳过距离远的点。

根据KNN每次需要预测一个点时,我们都需要计算训练数据集里每个点到这个点的距离,然后选出距离最近的k个点进行投票。当数据集很大时,这个计算成本非常高。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第13张图片

最近邻域搜索: kd树(K-dimensiontree)是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。kd树是一种二叉树,表示对k维空间的一个划分,构造kd树相当于不断地用垂直于坐标轴的超平面将K维空间切分,构成一系列的K维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第14张图片

kd树的构造

1)构造根结点,使根结点对应于K维空间中包含所有实例点的超矩形区域;

2)通过递归的方法,不断地对k维空间进行切分,生成子结点。在超矩形区域上选择一个坐标轴和在此坐标轴上的一个切分点,确定一个超平面,这个超平面通过选定的切分点并垂直于选定的坐标轴,将当前超矩形区域切分为左右两个子区域(子结点);这时,实例被分到两个子区域。

3)上述过程直到子区域内没有实例时终止(终止时的结点为叶结点)。在此过程中,将实例保存在相应的结点上。

4)通常,循环的选择坐标轴对空间切分,选择训练实例点在坐标轴上的中位数为切分点,这样得到的kd树是平衡的(平衡二叉树:它是一棵空树,或其左子树和右子树的深度之差的绝对值不超过1,且它的左子树和右子树都是平衡二叉树。

KD树中每个节点是一个向量,和二叉树按照数的大小划分不同的是,KD树每层需要选定向量中的某一维,然后根据这一维按左小右大的方式划分数据。在构建KD树时,关键需要解决2个问题:

1)选择向量的哪一维进行划分;

是随机选择某一维或按顺序选择,但是更好的方法应该是在数据比较分散的那一维进行划分(分散的程度可以根据方差来衡量)

2)如何划分数据;

可以使构建的树比较平衡,可以每次选择中位数来进行划分。

数据集划分

机器学习一般的数据集会划分为两个部分:

1)训练数据:用于训练,构建模型

2)测试数据:在模型校验时使用,用于评估模型是否有效

划分比例:

训练集:70%、80%、75%

测试集:30%、20%、25%

API划分:

参数:

1)数据集的特征值

2)数据集的标签值

3)test_size测试集的大小,一般为float

4)random_state随机数种子,不同的种子会造成不同的随机采样结果。相同的种子采样结果相同。

return:

x_train、x_test、y_train、y_test

下面这段代码是用于将数据集(iris.data 和 iris.target)划分为训练集和测试集的工具。如下:

train_test_split

函数是来自 scikit-learn 库中的一个方法,用于将数据集划分为训练集和测试集。
iris.data

是输入的特征数据,其中包含了花朵的四个特征值(萼片长度、萼片宽度、花瓣长度、花瓣宽度)。
iris.target

是目标数据,其中包含了对应每个样本的分类标签(0、1、2 分别代表三个不同的鸢尾花品种)。
test_size=0.2

指定了测试集的大小,这里是将数据集的 20% 分配给测试集,剩余 80% 作为训练集。
random_state=22

是为了保证每次运行代码时都得到相同的划分结果,它指定了随机数种子。

函数的返回值是四个数组:x_train、x_test、y_train 和 y_test。其中,x_train 和 y_train 是训练集的特征和标签数据,x_test 和 y_test 是测试集的特征和标签数据。

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# 小数据集获取
iris = load_iris()

# 数据集划分
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22)
print("训练集的特征值是:\n", x_train)
print("训练集的目标值是:\n", y_train)
print("测试集的特征值是:\n", x_test)
print("测试集的目标值是:\n", y_test)

print("训练集的目标值的形状是:\n", y_train.shape)
print("测试集的目标值的形状是:\n", y_test.shape)

通过将数据集划分为训练集和测试集,我们可以使用训练集来训练机器学习模型,然后使用测试集来评估模型的性能和准确度。这样可以帮助我们了解模型的泛化能力和预测能力。 

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第15张图片

特征预处理

特征预处理(Feature Preprocessing)是指对原始数据进行清洗、转换和归一化等操作,以使其适合机器学习算法的输入要求和提高算法的性能。

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第16张图片

在进行机器学习任务之前,通常需要对原始数据进行预处理,因为原始数据可能存在以下问题:

1)缺失值:数据中可能存在缺失值,即某些属性的取值为空。缺失值可能会影响模型的训练和预测,因此需要进行缺失值处理,可以通过填充均值、中位数、众数,或者使用插值方法来处理缺失值。

2)异常值:数据中可能存在异常值,即与其他样本明显不同的极端取值。异常值可能是由于测量误差或数据录入错误引起的,需要进行异常值检测和处理,可以通过删除异常值或用合理的取值替代异常值。

3)数据类型转换:数据可能包含不同的数据类型,如文本、类别型数据、数值型数据等。不同的算法对于不同的数据类型有不同的处理方式,因此需要将数据转换为适合算法处理的数据类型。

4)特征缩放:不同的特征可能具有不同的取值范围和单位,这会对某些机器学习算法产生影响。为了避免某些特征对模型的影响过大,需要对特征进行缩放,常见的缩放方法包括标准化和归一化。

5)特征选择:在原始数据中可能存在冗余或无关的特征,这些特征可能会增加模型的复杂度,降低模型的泛化能力。因此,需要对特征进行选择,选取对目标变量有较强相关性的特征。

通过特征预处理,可以使数据更加干净、可靠,并为机器学习算法提供更有用和可解释的特征,从而提高模型的准确性和性能。这里博主不可能全部讲解到上面的内容,这里就拿特征缩放进行举例:

标准化:通过对原始数据进行变换把数据变换到均值为0,标准差为1范围内:

对于标准化来说:如果出现异常点,由于具有一定数据量,少量的异常点对于平均值的影响并不大,从而方差改变较小。 

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第17张图片

下面这段代码的核心是利用 StandardScaler 对象对数据进行标准化处理,并打印出标准化后的数据、每一列的方差和每一列的平均值。

import pandas as pd
from sklearn.preprocessing import StandardScaler

def stand():
    # 标准化演示
    data = pd.read_csv("dating.txt", sep='\t')
    print(data)
    # 实例化
    transfer = StandardScaler()
    # 进行转换,调用fit_transform
    ret_data = transfer.fit_transform(data[["milage", "Liters", "Consumtime"]])
    print("标准化之后的数据为:\n", ret_data)
    print("每一列的方差为:\n", transfer.var_)
    print("每一列的平均值为:\n", transfer.mean_)

stand()

最终呈现的结果为:

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第18张图片

归一化:通过对原始数据进行变换把数据映射到(默认为[0,1])之间。其公式如下:

对于归一化来说:如果出现异常点,影响了最大值和最小值,那么结果显然会发生改变。这里通过下面的这个例子来理解上面的过程:

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第19张图片

下面这段代码的核心是利用 MinMaxScaler 对象对数据进行归一化处理,从而将数据缩放到指定的特征范围内,这在机器学习和数据分析中是非常常见的预处理步骤。

import pandas as pd
from sklearn.preprocessing import MinMaxScaler

def minmax():
    # 归一化演示
    data = pd.read_csv("dating.txt", sep='\t')
    print(data)
    # 实例化
    transfer = MinMaxScaler(feature_range=(3, 5)) # 指定归一化后的特征范围
    # # 进行转换,调用fit_transform
    ret_data = transfer.fit_transform(data[["milage", "Liters", "Consumtime"]])
    print("归一化之后的数据为:\n", ret_data)

minmax()

最终呈现的效果如下:

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第20张图片

莺尾花种类预测(实操)

导入模块

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier

获取数据

iris = load_iris()

数据基本处理:这段代码的主要作用是将数据集划分为训练集和测试集,以便在机器学习模型的训练和评估过程中使用。通过将数据集划分为独立的训练集和测试集,可以更好地评估模型对新数据的泛化能力。其中设置的随机种子 random_state=22 保证了每次划分的结果都是固定的,有助于结果的复现性。

x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22)

特征工程-特征预处理:在这段代码中,首先在训练集上调用 fit_transform() 方法计算并应用标准化转换,得到标准化后的训练集特征数据 x_train。然后,在测试集上调用 transform() 方法使用训练集上计算得到的均值和标准差来进行标准化转换,得到标准化后的测试集特征数据 x_test。

transfer = StandardScaler()
x_train = transfer.fit_transform(x_train) # 计算均值和标准差
x_test = transfer.transform(x_test)

机器学习-KNN:在这段代码中,首先通过创建 KNeighborsClassifier 对象来实例化一个 K 近邻分类器,然后使用 fit() 方法对该分类器进行训练。训练过程就是让分类器从训练集中学习特征与标签之间的关系,从而构建一个能够进行分类预测的模型。

estimator = KNeighborsClassifier(n_neighbors=5) # 实例化一个估计器
estimator.fit(x_train, y_train) # 模型训练

模型评估:这段代码的主要作用是评估训练好的 K 近邻分类器在测试集上的表现。首先通过 predict() 方法进行预测,然后通过比较预测值与真实值来评估分类器的准确率。最终输出预测值、预测值与真实值的对比结果以及分类器在测试集上的准确率。

y_pre = estimator.predict(x_test) # 预测值结果输出
print("预测值是: \n", y_pre)
print("预测值与真实值的对比是: \n", y_pre == y_test)

score = estimator.score(x_test, y_test) # 准确率计算
print("准确率为: \n", score)

当然我们可以关闭随机数种子,让准确率发生变化,最终呈现的效果如下:

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第21张图片

总结

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第22张图片

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第23张图片

交叉验证与网格搜索

交叉验证:将拿到的训练数据,分为训练和验证集。以下图为例:将数据分成4份,其中一份作为验证集。然后经过4次(组)的测试,每次都更换不同的验证集。即得到4组模型的结果,取平均值作为最终结果。又称4折交叉验证。

我们之前知道数据分为训练集和测试集,但是为了让从训练得到模型结果更加准确。做以下处理:

训练集:训练集+验证集;测试集:测试集

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第24张图片

交叉验证的目的:为了让被评估的模型更加准确可信。这个只是让被评估的模型更加准确可信,那么怎么选择或者调优参数呢?这里需要用到下面的网格搜索:

网格搜索:通常情况下,有很多参数是需要手动指定的(如k-近邻算法中的K值),这种叫超参数。但是手动过程繁杂,所以需要对模型预设几种超参数组合。每组超参数都采用交叉验证来进行评估。最后选出最优参数组合建立模型。

以下是代码示例:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier

# 1.获取数据
iris = load_iris()

# 2.数据基本处理
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22)

# 3.特征工程-特征预处理
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train) # 计算均值和标准差
x_test = transfer.transform(x_test)

# 4.机器学习-KNN
estimator = KNeighborsClassifier() # 实例化一个估计器
estimator.fit(x_train, y_train)  # 调用 fit 方法进行训练

# 4.1 模型调优-交叉验证,网格搜索
param_grid = {"n_neighbors": [1, 3, 5, 7]}
grid_search = GridSearchCV(estimator, param_grid=param_grid, cv=5)
grid_search.fit(x_train, y_train)  # 对已经训练过的分类器应用 GridSearchCV

# 5.模型评估
y_pre = grid_search.predict(x_test) # 预测值结果输出
print("预测值是: \n", y_pre)
print("预测值与真实值的对比是: \n", y_pre == y_test)

score = grid_search.score(x_test, y_test) # 准确率计算
print("准确率为: \n", score)

# 查看交叉验证网格搜索的一些属性
print("在交叉验证中,得到的最好结果是: \n", grid_search.best_score_)
print("在交叉验证中,得到的最好模型是: \n", grid_search.best_estimator_)
print("在交叉验证中,得到的模型结果是: \n", grid_search.cv_results_)

最终呈现的结果为:

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧_第25张图片

你可能感兴趣的:(#,机器学习,机器学习,近邻算法,人工智能,k-近邻算法)