机器学习(二):k近邻法(kNN)

更多内容关注公众号:数学的旋律
在这里插入图片描述


tb店铺搜:FUN STORE玩物社,专业买手挑选送礼好物

引言

    k近邻法(k-nearest neighbor, kNN)是一种基本分类与回归方法,其基本做法是:给定测试实例,基于某种距离度量找出训练集中与其最靠近的k个实例点,然后基于这k个最近邻的信息来进行预测。
    通常,在分类任务中可使用“投票法”,即选择这k个实例中出现最多的标记类别作为预测结果;在回归任务中可使用“平均法”,即将这k个实例的实值输出标记的平均值作为预测结果;还可基于距离远近进行加权平均或加权投票,距离越近的实例权重越大。
    k近邻法不具有显式的学习过程,事实上,它是懒惰学习(lazy learning)的著名代表,此类学习技术在训练阶段仅仅是把样本保存起来,训练时间开销为零,待收到测试样本后再进行处理。
    本文只讨论分类问题中的k近邻法。

一、k近邻法的三要素

    距离度量、k值的选择及分类决策规则是k近邻法的三个基本要素。根据选择的距离度量(如曼哈顿距离或欧氏距离),可计算测试实例与训练集中的每个实例点的距离,根据k值选择k个最近邻点,最后根据分类决策规则将测试实例分类。
    如图1,根据欧氏距离,选择k=4个离测试实例最近的训练实例(红圈处),再根据多数表决的分类决策规则,即这4个实例多数属于“-类”,可推断测试实例为“-类”。
    k近邻法1968年由Cover和Hart提出。
欧氏距离的kNN

图1
1.距离度量

    特征空间中的两个实例点的距离是两个实例点相似程度的反映。K近邻法的特征空间一般是n维实数向量空间Rn。使用的距离是欧氏距离,但也可以是其他距离,如更一般的Lp距离或Minkowski距离。
    设特征空间X是n维实数向量空间 R n R^n Rn x i , x j ∈ X x_i,x_j∈X xi,xjX x i = ( x i ( 1 ) , x i ( 2 ) , ⋯   , x i ( n ) ) T x_i=(x_i^{(1)},x_i^{(2)},\cdots,x_i^{(n)})^T xi=(xi(1),xi(2),,xi(n))T x j = ( x j ( 1 ) , x j ( 2 ) , ⋯   , x j ( n ) ) T x_j=(x_j^{(1)},x_j^{(2)},\cdots,x_j^{(n)})^T xj=(xj(1),xj(2),,xj(n))T x i , x j x_i,x_j xi,xj L p L_p Lp 距离定义为
L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p L_p(x_i,x_j)=(\sum_{l=1}^{n}|{x_i}^{(l)}-{x_j}^{(l)}|^p)^{\frac{1}{p}} Lp(xi,xj)=(l=1nxi(l)xj(l)p)p1这里p≥1。
当p=1时,称为曼哈顿距离(Manhattan distance),即
L 1 ( x i , x j ) = ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ L_1(x_i,x_j)=\sum_{l=1}^{n}|{x_i}^{(l)}-{x_j}^{(l)}| L1(xi,xj)=l=1nxi(l)xj(l)当p=2时,称为欧氏距离(Euclidean distance),即
L 2 ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j)=(\sum_{l=1}^{n}|{x_i}^{(l)}-{x_j}^{(l)}|^2)^{\frac{1}{2}} L2(xi,xj)=(l=1nxi(l)xj(l)2)21当p=∞时,它是各个坐标距离的最大值,即
L ∞ ( x i , x j ) = m a x l   ∣ x i ( l ) − x j ( l ) ∣ L_\infty(x_i,x_j)=max_l\ |{x_i}^{(l)}-{x_j}^{(l)}| L(xi,xj)=maxl xi(l)xj(l)
证明:
以二维实数向量空间(n=2)为例说明曼哈顿距离和欧氏距离的物理意义。
① 曼哈顿距离
L 1 ( x i , x j ) = ∑ l = 1 2 ∣ x i ( l ) − x j ( l ) ∣ = ∣ x i ( 1 ) − x j ( 1 ) ∣ + ∣ x i ( 2 ) − x j ( 2 ) ∣ L_1(x_i,x_j)=\sum_{l=1}^{2}|{x_i}^{(l)}-{x_j}^{(l)}|=|{x_i}^{(1)}-{x_j}^{(1)}|+|{x_i}^{(2)}-{x_j}^{(2)}| L1(xi,xj)=l=12xi(l)xj(l)=xi(1)xj(1)+xi(2)xj(2)图2绿色线即曼哈顿距离物理意义,其中横向线条表示|x1i-x1j|,竖向线条表示|x2i-x2j|。
② 欧氏距离
L 2 ( x i , x j ) = ( ∑ l = 1 2 ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 = ∣ x i ( 1 ) − x j ( 1 ) ∣ 2 + ∣ x i ( 2 ) − x j ( 2 ) ∣ 2 2 L_2(x_i,x_j)=(\sum_{l=1}^{2}|{x_i}^{(l)}-{x_j}^{(l)}|^2)^{\frac{1}{2}}=\sqrt[2]{|{x_i}^{(1)}-{x_j}^{(1)}|^2+|{x_i}^{(2)}-{x_j}^{(2)}|^2} L2(xi,xj)=(l=12xi(l)xj(l)2)21=2xi(1)xj(1)2+xi(2)xj(2)2 图2红色线即欧氏距离物理意义,根据勾股定理可得。
欧氏距离与曼哈顿距离

图2
2.k值的选择

    k值的选择会对k近邻法的结果产生重大影响。在应用中,k值一般取一个比较小的数值,通常采用交叉验证法来选取最优的k值。

3.分类决策规则

    k近邻法中的分类决策规则往往是多数表决,即由输入实例的k个邻近的训练实例中的多数类,决定输入实例的类。



二、k近邻算法及代码实现(python)

1.算法

算法1(k近邻法)
输入:训练集
D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } D=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\} D={(x1,y1),(x2,y2),,(xN,yN)}其中, x i ∈ X ⊆ R n x_i∈X ⊆ R^n xiXRn为实例的特征向量, y i ∈ Y = { c 1 , c 2 , ⋯   , c k } y_i∈Y=\{c_1,c_2,\cdots,c_k\} yiY={c1,c2,,ck}为实例的类别,i=1,2,…,N。
输出:实例x所属的类别y
① 根据给定的距离度量,在训练集D中找出与x最近邻的k个点,涵盖这k个点的x的领域记作 N k ( x ) N_k(x) Nk(x)
② 在 N k ( x ) N_k(x) Nk(x)中根据分类决策规则(如多数表决)决定x的类别y
y = a r g   m a x c j ∑ x i ∈ N k ( x ) I ( y j = c j ) ,       i = 1 , 2 , ⋯   , N ; j = 1 , 2 , ⋯   , K y=arg\ max_{c_j}\sum_{x_i\in N_k(x)}I(y_j=c_j),\ \ \ \ \ i=1,2,\cdots,N;j=1,2,\cdots,K y=arg maxcjxiNk(x)I(yj=cj),     i=1,2,,N;j=1,2,,K上式中,I为指示函数,即当yi=ci时I为1,否则I为0。

2.代码实现(python)

以下代码来自Peter Harrington《Machine Learing in Action》
距离度量为欧氏距离,分类决策规则为多数表决。classify0()函数有4个输入参数:用于分类的输入向量是inX,输入的训练集为dataSet,类别为labels,k表示用于选择最近邻的数目。
代码如下(保存为kNN.py):

# -- coding: utf-8 --
from numpy import *
import operator

def createDataSet():
    # 创建训练集
    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels = ['A','A','B','B']
    return group, labels
	
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    # 根据欧式距离计算训练集中每个样本到测试点的距离
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    # 计算完所有点的距离后,对数据按照从小到大的次序排序
    sortedDistIndicies = distances.argsort()
    # 确定前k个距离最小的元素所在的主要分类,最后返回发生频率最高的元素类别
    classCount={}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

运行命令如下:
kNN.py运行命令
group和labels为训练集,其中group为特征向量,labels为类别。输入测试实例[0,0],选取与测试实例距离最近的3个元素,最后返回这3个元素中发生频率最高的元素类别,即为kNN近邻法的预测值。



三、k近邻法的实现:kd树

    实现k近邻法时,主要考虑的问题是如何对训练数据进行快速k近邻搜索,这点在特征空间的维数大及训练数据容量大时尤其必要。
    k近邻法最简单的实现方法是线性扫描(linear scan),这时要计算输入实例与每一个训练实例的距离,当训练集很大时,计算非常耗时。为了提高k近邻法搜索的效率,可以考虑使用特殊的结构存储训练数据,以减少计算距离的次数。具体方法很多,下面介绍其中的kd树方法(kd树是存储k维空间数据的树结构,这里的k与k近邻法的k意义不同)。

1.构造kd树

    kd树是二叉树,是一种对k维空间中实例点进行存储以便对其进行快速检索的树形数据结构。kd树表示对k维空间的一个划分(partition),构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,构成一系列的k维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。
    通常,依次选择坐标轴对空间切分,选择训练实例点在选定坐标轴上的中位数(一组数据按大小顺序排列起来,处于中间的一个数或最中间两个数的平均值。本文在最中间有两个数时选择最大值作中位数)为切分点,这样得到的kd树是平衡的。注意,平衡的kd树搜索时未必是最优的。

2.算法

算法2(构造平衡kd树)
输入:k维空间数据集
D = { x 1 , x 2 , ⋯   , x n } D=\{x_1,x_2,\cdots,x_n\} D={x1,x2,,xn}其中 x i = ( x i ( 1 ) , x i ( 2 ) , ⋯   , x i ( n ) ) T , i = 1 , 2 , ⋯   , N x_i=(x_i^{(1)},x_i^{(2)},\cdots,x_i^{(n)})^T,i=1,2,\cdots,N xi=(xi(1),xi(2),,xi(n))Ti=1,2,,N
输出:kd树
① 开始:构造根结点,根结点对应于包含D的k维空间的超矩形区域。
   选择x1为坐标轴,以D中所有实例的x1坐标的中位数为切分点,将根结点对应的超矩形区域切分为两个子区域。切分由通过切分点并与坐标轴x1垂直的超平面实现。
   由根结点生成深度为1的左、右子结点:左子结点对应坐标x1小于切分点的子区域,右子结点对应坐标x1大于切分点的子区域。将落在切分超平面上的实例点保存在根结点。
② 重复:对深度为j的结点,选择xl为切分的坐标轴,l=j(mod k)+1,以该结点的区域中所有实例点的xl坐标的中位数为切分点,将该结点对应的超矩形区域切分为两个子区域。切分由通过切分点并与坐标轴xl垂直的超平面实现。
   由该结点生成深度为j+1的左、右子结点:左子结点对应坐标xl小于切分点的子区域,右子结点对应坐标xl大于切分点的子区域。将落在切分超平面上的实例点保存在该结点。
③ 直到两个子区域没有实例存在时停止,从而形成kd树的区域划分。
例1 给定一个二维空间的数据集: D = { ( 2 , 3 ) T , ( 5 , 4 ) T , ( 9 , 6 ) T , ( 4 , 7 ) T , ( 8 , 1 ) T , ( 7 , 2 ) T } D=\{(2,3)^T,(5,4)^T,(9,6)^T,(4,7)^T,(8,1)^T,(7,2)^T\} D={(2,3)T,(5,4)T,(9,6)T,(4,7)T,(8,1)T,(7,2)T}构造一个平衡kd树。
解:D为二维空间,则k=2。
① D中6个实例的x1坐标的中位数为7,则以(7 , 2)为切分点,由通过切分点并与坐标轴x1垂直的平面将超矩形区域切分为两个子区域。根结点为(7 , 2),左区域包括:(2 , 3) , (5 , 4) , (4 , 7),右区域包括:(8 , 1) , (9 , 6),深度为1
② 对深度为j=1的结点,选择l=j(mod k)+1=1(mod 2)+1=2即x2为切分的坐标轴。则左区域的切分点为(5 , 4) ,左子区域为(2 , 3),右子区域为(4 , 7);右区域的切分点为(9 , 6) ,左子区域为(8 , 1)。
如此递归,最后得到的平衡kd树如下所示:

例1

3.搜索kd树(未完,待续)





以上全部内容参考书籍如下:
李航《统计学习方法》
周志华《机器学习》
Peter Harrington《Machine Learing in Action》

  • 34
    点赞
  • 186
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 以下是机器学习鸢尾花数据集knn的源代码: ``` import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score # 加载鸢尾花数据集 iris = load_iris() X = iris.data y = iris.target # 将数据集分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # 创建KNN分类器 knn = KNeighborsClassifier(n_neighbors=3) # 训练模型 knn.fit(X_train, y_train) # 预测测试集结果 y_pred = knn.predict(X_test) # 计算准确率 accuracy = accuracy_score(y_test, y_pred) print("准确率:", accuracy) ``` 该代码使用了sklearn库中的KNeighborsClassifier类来创建KNN分类器,并使用鸢尾花数据集进行训练和测试。最后计算出了模型的准确率。 ### 回答2: 机器学习鸢尾花数据集是一个经典的数据集,包含了150个样本,每个样本有4个特征,分为3个类别。对于这个数据集,可以使用knn(K-Nearest Neighbors)算进行分类。下面是KNN在Python中的实现: 首先,我们需要导入所需的库:numpy和sklearn ```python import numpy as np from sklearn import datasets from sklearn.neighbors import KNeighborsClassifier ``` 然后,我们需要加载数据集: ```python iris = datasets.load_iris() X = iris.data y = iris.target ``` 接下来,我们将数据集划分为训练集和测试集: ```python # 划分训练集和测试集 indices = np.random.permutation(len(X)) X_train = X[indices[:-30]] y_train = y[indices[:-30]] X_test = X[indices[-30:]] y_test = y[indices[-30:]] ``` 然后,我们可以使用sklearn库中的KNeighborsClassifier类来建立模型: ```python # 创建一个knn分类器实例,k=3 knn = KNeighborsClassifier(n_neighbors=3) # 用训练集训练模型 knn.fit(X_train, y_train) # 对测试集进行预测 y_pred = knn.predict(X_test) ``` 最后,我们可以使用sklearn库中的metrics类来评估模型: ```python # 评估模型,输出准确率 from sklearn import metrics print(metrics.accuracy_score(y_test, y_pred)) ``` 完整的源代码如下: ```python import numpy as np from sklearn import datasets from sklearn.neighbors import KNeighborsClassifier from sklearn import metrics iris = datasets.load_iris() X = iris.data y = iris.target # 划分训练集和测试集 indices = np.random.permutation(len(X)) X_train = X[indices[:-30]] y_train = y[indices[:-30]] X_test = X[indices[-30:]] y_test = y[indices[-30:]] # 创建一个knn分类器实例,k=3 knn = KNeighborsClassifier(n_neighbors=3) # 用训练集训练模型 knn.fit(X_train, y_train) # 对测试集进行预测 y_pred = knn.predict(X_test) # 评估模型,输出准确率 print(metrics.accuracy_score(y_test, y_pred)) ``` 以上便是KNN在Python中使用鸢尾花数据集进行分类的源代码实现。 ### 回答3: 鸢尾花数据集是机器学习中比较经典的数据集之一,数据集包含了三种不同种类的鸢尾花,分别是山鸢尾、变色鸢尾和维吉尼亚鸢尾。在对这个数据集进行机器学习分类时,通常使用k近邻(k-nearest neighbors,简称knn)。 KNN的原理是通过计算相邻点之间的距离来确定未知数据的类别。KNN是一种有监督的学习方,它需要训练数据集和已知数据类别,然后通过对新样本与已知样本的距离进行计算,确定样本属于哪一个类别。 下面是使用python的sklearn库实现KNN对鸢尾花数据集进行分类的原始代码: ``` python from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score # 加载鸢尾花数据集 iris = load_iris() # 将数据集分成测试集和验证集 X_train, X_test, y_train, y_test = train_test_split( iris.data, iris.target, test_size=0.3, random_state=42) # 定义KNN分类器,设置K值为3 knn = KNeighborsClassifier(n_neighbors=3) # 训练KNN分类器 knn.fit(X_train, y_train) # 预测测试集的类别 y_pred = knn.predict(X_test) # 计算分类器的准确率 accuracy = accuracy_score(y_test, y_pred) print("模型的准确率为:", accuracy) ``` 这段代码首先使用sklearn库中的load_iris函数加载鸢尾花数据集,然后使用train_test_split函数将数据集分成训练集和测试集。接着定义KNN分类器,并使用fit方将训练集数据喂入分类器进行训练。最后使用predict方预测测试集的类别,并使用accuracy_score方计算分类器的准确率。输出结果为模型的准确率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值