【机器学习-K近邻算法】

K-近邻算法的快速入门

K-近邻(KNN)算法概念

K Nearest Neighbor算法又叫KNN算法,这个算法是机器学习里面一个比较经典的算法。如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。顾名思义,需要预数据类别时,把它放在一个坐标上,就可以选取离自己最近的几个点,然后根据离自己近的几个点做一个概率推算,最后确定预测的数据所属哪一类。

殴式距离公式

样本之间数据距离公式有很多,欧式距离是比较典型的一种,以下是这的数学公式原理。
在这里插入图片描述
在这里插入图片描述

电影分类案例

以下有几种电影类型,我们需要预测,唐人街探案是所属哪一类电影类型。
在这里插入图片描述
距离是由上面的欧式距离计算出来的,搞笑镜头、拥抱镜头、打斗镜头分别是参数,算法如下:
在这里插入图片描述
算法思想:一个样本与数据集中的k个样本最相似,如果这k个样本中的大多数属于某一个类别.该样本也属于这个类别。在以上的案例中,我们选取了5个样本数据,其中三个为喜剧,两个为爱情,最终确定《唐人街探案》为喜剧片。

K-近邻算法API

Scikit-learn工具

Python语言的机器学习工具,Scikit-learn包括许多知名的机器学习算法的实现,Scikit-learn文档完善,容易上手,丰富的API

Scikit-learn包含内容

在这里插入图片描述
分类、聚类、回归、特征工程、模型选择、调优

K-近邻算法API方法

sklearn.neighbors.KNeighborsClassifier(n_neighbors=5)
n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数

代码过程

from sklearn.neighbors import KNeighborsClassifier

# 构造数据集
x = [[0], [1], [2], [3]]
y = [0, 0, 1, 1]

# 实例化API
estimator = KNeighborsClassifier(n_neighbors=2)

# 使用fit方法进行训练
estimator.fit(x, y)

# 自己造数据进行预测
result = estimator.predict([[0], [1], [4]])
print(result)			
# 运行结果为    [0 0 1]

距离度量

欧式距离(Euclidean Distance)

殴式距离我们上面已经说过了,不再解释

曼哈顿距离(Manhattan Distance)

在曼哈顿街区要从一个十字路口开车到另一个十字路口,驾驶距离显然不是两点间的直线距离。这个实际驾驶距离就是“曼哈顿距离”。曼哈顿距离也称为“城市街区距离”(City Block distance)。
在这里插入图片描述
在这里插入图片描述

切比雪夫距离 (Chebyshev Distance)

国际象棋中,国王可以直行、横行、斜行,所以国王走一步可以移动到相邻8个方格中的任意一个。国王从格子(x1,y1)走到格子(x2,y2)最少需要多少步?这个距离就叫切比雪夫距离。
在这里插入图片描述
在这里插入图片描述

闵可夫斯基距离(Minkowski Distance)

闵氏距离不是一种距离,而是一组距离的定义,是对多个距离度量公式的概括性的表述。

两个n维变量a(x11,x12,…,x1n)与b(x21,x22,…,x2n)间的闵可夫斯基距离定义为:
其中p是一个变参数:

当p=1时,就是曼哈顿距离;

当p=2时,就是欧氏距离;

当p→∞时,就是切比雪夫距离。

根据p的不同,闵氏距离可以表示某一类/种的距离。
缺点:将各个分量的量纲(scale),也就是“单位”相同的看待了,实际上身高的10cm并不能和体重的10kg划等号。

标准化欧氏距离 (Standardized EuclideanDistance)

思路:既然数据各维分量的分布不一样,那先将各个分量都“标准化”到均值、方差相等。假设样本集X的均值(mean)为m,标准差(standard deviation)为s,X的“标准化变量”表示为:
在这里插入图片描述

余弦距离(Cosine Distance)

二维空间中向量A(x1,y1)与向量B(x2,y2)的夹角余弦公式:
在这里插入图片描述
两个n维样本点a(x11,x12,…,x1n)和b(x21,x22,…,x2n)的夹角余弦为:
在这里插入图片描述
在这里插入图片描述
距离公式还包括:汉明距离、杰卡德距离、马氏距离(通过正态分布来决定)、

K值选择

K值也就是我们第一个案例中的K值,在API里n_neighbors这个参数
近似误差:对现有训练集的训练误差,关注训练集,如果近似误差过小可能会出现过拟合的现象,对现有的训练集能有很好的预测,但是对未知的测试样本将会出现较大偏差的预测。模型本身不是最接近最佳模型。
估计误差:可以理解为对测试集的测试误差,关注测试集,估计误差小说明对未知数据的预测能力好,模型本身最接近最佳模型。

K值过小

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

K值过大

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

K==N

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

kd树

问题导入

实现k近邻法时,主要考虑的问题是如何对训练数据进行快速k近邻搜索。
这在特征空间的维数大及训练数据容量大时尤其必要。
k近邻法最简单的实现是线性扫描(穷举搜索),即要计算输入实例与每一个训练实例的距离。计算并存储好以后,再查找K近邻。当训练集很大时,计算非常耗时。
为了提高kNN搜索的效率,可以考虑使用特殊的结构存储训练数据,以减小计算距离的次数。
根据KNN每次需要预测一个点时,我们都需要计算训练数据集里每个点到这个点的距离,然后选出距离最近的k个点进行投票。当数据集很大时,这个计算成本非常高,针对N个样本,D个特征的数据集,其算法复杂度为O(DN^2)。

定义

为了避免每次都重新计算一遍距离,算法会把距离信息保存在一棵树里,这样在计算之前从树里查询距离信息,尽量避免重新计算。其基本原理是,如果A和B距离很远,B和C距离很近,那么A和C的距离也很远。有了这个信息,就可以在合适的时候跳过距离远的点。
在这里插入图片描述
黄色的点作为根节点,上面的点归左子树,下面的点归右子树,接下来再不断地划分,分割的那条线叫做分割超平面(splitting hyperplane),在一维中是一个点,二维中是线,三维的是面。
在这里插入图片描述
黄色节点就是Root节点,下一层是红色,再下一层是绿色,再下一层是蓝色。
利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。

案例分析

给定一个二维空间数据集:T={(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},构造一个平衡kd树。
思路引导:
根结点对应包含数据集T的矩形,选择x(1)轴,6个数据点的x(1)坐标中位数是6,这里选最接近的(7,2)点,以平面x(1)=7将空间分为左、右两个子矩形(子结点);接着左矩形以x(2)=4分为两个子矩形(左矩形中{(2,3),(5,4),(4,7)}点的x(2)坐标中位数正好为4),右矩形以x(2)=6分为两个子矩形,如此递归,最后得到如下图所示的特征空间划分和kd树。
在这里插入图片描述

最近领域的搜索

假设标记为星星的点是 test point, 绿色的点是找到的近似点,在回溯过程中,需要用到一个队列,存储需要回溯的点,在判断其他子节点空间中是否有可能有距离查询点更近的数据点时,做法是以查询点为圆心,以当前的最近距离为半径画圆,这个圆称为候选超球(candidate hypersphere),如果圆与回溯点的轴相交,则需要将轴另一边的节点都放到回溯队列里面来。
在这里插入图片描述

案例:鸢尾花种类预测–数据集介绍

鸢尾花种类预测

Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理。Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集。关于数据集的具体介绍
在这里插入图片描述

scikit-learn中数据集介绍

sklearn.datasets
datasets.load_() # 加载获取流行数据集
获取小规模数据集,数据包含在datasets里
datasets.fetch_
(data_home=None) # 获取大规模数据集,需要从网络上下载,函数的第一个参数是data_home,表示数据集下载的目录,默认是 ~/scikit_learn_data/

from sklearn.datasets import load_iris
# 获取鸢尾花数据集
iris = load_iris()
print("鸢尾花数据集的返回值:\n", iris)
# 返回值是一个继承自字典的Bench
print("鸢尾花的特征值:\n", iris["data"])
print("鸢尾花的目标值:\n", iris.target)
print("鸢尾花特征的名字:\n", iris.feature_names)
print("鸢尾花目标值的名字:\n", iris.target_names)
print("鸢尾花的描述:\n", iris.DESCR)

seaborn库展示分布

Seaborn 是基于 Matplotlib 核心库进行了更高级的 API 封装,可以让你轻松地画出更漂亮的图形。而 Seaborn 的漂亮主要体现在配色更加舒服、以及图形元素的样式更加细腻。
seaborn.lmplot() 是一个非常有用的方法,它会在绘制二维散点图时,自动完成回归拟合
sns.lmplot() 里的 x, y 分别代表横纵坐标的列名,
data= 是关联到数据集,
hue=*代表按照 species即花的类别分类显示,
fit_reg=是否进行线性拟合。

# 内嵌绘图
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import load_iris
# 获取鸢尾花数据集
iris = load_iris()
# 把数据转换成dataframe的格式
iris_d = pd.DataFrame(iris['data'], columns = ['Sepal_Length', 'Sepal_Width', 'Petal_Length', 'Petal_Width'])
iris_d['Species'] = iris.target

def plot_iris(iris, col1, col2):
    sns.lmplot(x = col1, y = col2, data = iris, hue = "Species", fit_reg = False)
    plt.xlabel(col1)
    plt.ylabel(col2)
    plt.title('鸢尾花种类分布图')
    plt.show()
plot_iris(iris_d, 'Petal_Width', 'Sepal_Length')

在这里插入图片描述

数据分割

数据集的划分

机器学习一般的数据集会划分为两个部分:
1.训练数据:用于训练,构建模型
2.测试数据:在模型检验时使用,用于评估模型是否有效
数据集划分api:
sklearn.model_selection.train_test_split(arrays, *options)
1.x 数据集的特征值
2.y 数据集的标签值
3.test_size 测试集的大小,一般为float
4.random_state 随机数种子,不同的种子会造成不同的随机采样结果。相同的种子采样结果相同。
return 测试集特征训练集特征值值,训练标签,测试标签(默认随机取)

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# 1、获取鸢尾花数据集
iris = load_iris()
# 对鸢尾花数据集进行分割
# 训练集的特征值x_train 测试集的特征值x_test 训练集的目标值y_train 测试集的目标值y_test
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
print("x_train:\n", x_train.shape)
# 随机数种子
x_train1, x_test1, y_train1, y_test1 = train_test_split(iris.data, iris.target, random_state=6)
x_train2, x_test2, y_train2, y_test2 = train_test_split(iris.data, iris.target, random_state=6)
print("如果随机数种子不一致:\n", x_train == x_train1)
print("如果随机数种子一致:\n", x_train1 == x_train2)

特征工程

特征处理主要是通过一些转换函数将特征数据转换成更加适合算法模型的特征数据过程

为什么我们要进行归一化/标准化?

特征的单位或者大小相差较大,或者某特征的方差相比其他的特征要大出几个数量级,容易影响(支配)目标结果,使得一些算法无法学习到其它的特征

处理的方法

归一化

通过对原始数据进行变换把数据映射到(默认为[0,1])之间
在这里插入图片描述
API:

from sklearn.preprocessing import MinMaxScaler
# 1、实例化一个转换器类
transfer = MinMaxScaler(feature_range=(0, 1))
# 2、调用fit_transform
data = transfer.fit_transform(data[["列名1","列名2"]])

总结:
最大值与最小值非常容易受异常点影响,所以这种方法鲁棒性较差,只适合传统精确小数据场景。

标准化

通过对原始数据进行变换把数据变换到均值为0,标准差为1范围内
在这里插入图片描述
作用于每一列,mean为平均值,σ为标准差
API:

from sklearn.preprocessing import StandardScaler
# 1、实例化一个转换器类
transfer = StandardScaler()
# 2、调用fit_transform
data = transfer.fit_transform(data[['列名1','列名2']])

交叉验证,网格搜索

什么是交叉验证(cross validation)

交叉验证:将拿到的训练数据,分为训练和验证集。以下图为例:将数据分成4份,其中一份作为验证集。然后经过4次(组)的测试,每次都更换不同的验证集。即得到4组模型的结果,取平均值作为最终结果。又称4折交叉验证。
分析
我们之前知道数据分为训练集和测试集,但是**为了让从训练得到模型结果更加准确。**做以下处理
训练集:训练集+验证集
测试集:测试集
在这里插入图片描述

为什么需要交叉验证

为了让被评估的模型更加准确可信

什么是网格搜索(Grid Search)

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

交叉验证,网格搜索(模型选择与调优)API:

from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import KNeighborsClassifier

# 实例化预估器类
estimator = KNeighborsClassifier()

# 模型选择与调优——网格搜索和交叉验证
# 准备要调的超参数, CV是几折交叉
param_dict = {"n_neighbors": [1, 3, 5]}
estimator = GridSearchCV(estimator, param_grid=param_dict, cv=3)

# fit数据进行训练
estimator.fit(x_train, y_train)

#评估模型效果
# 方法a:比对预测结果和真实值
y_predict = estimator.predict(x_test)
print("比对预测结果和真实值:\n", y_predict == y_test)

# 方法b:直接计算准确率
score = estimator.score(x_test, y_test)
print("直接计算准确率:\n", score)

然后进行评估查看最终选择的结果和交叉验证的结果

print("在交叉验证中验证的最好结果:\n", estimator.best_score_)
print("最好的参数模型:\n", estimator.best_estimator_)
print("每次交叉验证后的准确率结果:\n", estimator.cv_results_)

最终结果

比对预测结果和真实值:
 [ True  True  True  True  True  True  True False  True  True  True  True
  True  True  True  True  True  True False  True  True  True  True  True
  True  True  True  True  True  True  True  True  True  True  True  True
  True  True]
直接计算准确率:
 0.9473684210526315
在交叉验证中验证的最好结果:
 0.9732100521574205
最好的参数模型:
 KNeighborsClassifier()
每次交叉验证后的准确率结果:
 {'mean_fit_time': array([0.00066702, 0.00033331, 0.00033204]), 'std_fit_time': array([0.00047165, 0.00047137, 0.00046957]), 'mean_score_time': array([0.00099993, 0.00100025, 0.00100136]), 'std_score_time': array([6.23849294e-06, 7.37000982e-07, 2.75989459e-06]), 'param_n_neighbors': masked_array(data=[1, 3, 5],
             mask=[False, False, False],
       fill_value='?',
            dtype=object), 'params': [{'n_neighbors': 1}, {'n_neighbors': 3}, {'n_neighbors': 5}], 'split0_test_score': array([0.97368421, 0.97368421, 0.97368421]), 'split1_test_score': array([0.97297297, 0.97297297, 0.97297297]), 'split2_test_score': array([0.94594595, 0.89189189, 0.97297297]), 'mean_test_score': array([0.96420104, 0.94618303, 0.97321005]), 'std_test_score': array([0.01291157, 0.03839073, 0.00033528]), 'rank_test_score': array([2, 3, 1])}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值