最近邻
概述
基于最近邻的监督学习方法分两类:分类,针对的是具有离散标签的数据;回归,针对的是具有连续标签的数据基于最近邻的无监督学习方法用于聚类分析。
最近邻方法原理是从训练样本中找到与查询点在距离上最近的预定数量或范围的多个点,然后依据这些点来预测查询点的标签。从训练样本中找出点的数量可以是用户定义的常量,这叫ķ最近邻学习即KNN,也可以通过用户定义的查询点的距离半径范围得出,这叫基于半径的最近邻学习即RNN。
数据之间的距离可以理解为数据之间的相似度。距离可以通过多种方式来度量,如欧几里得距离,曼哈顿距离等。标准欧几里得是最常见的选择。
最近邻学习方法称为非泛化机器学习方法,因为只是简单的“记住”了其所有的训练数据,死记硬背下所有历史数据,在新数据面前就与所有的历史数据比较从而找出最相似的历史数据。而泛化的机器学习方法在给定的样本数据进行训练之后会形成概念模型,在新数据面前则依据概念模型直接推导计算得出结论。
无监督最近邻
无监督最近邻的任务就是从训练样本中找到与查询点在距离上最近的预定数量或范围的多个点。需要找出点的个数可以是用户定义的常量,这叫ķ最近邻即KNN ,也可以通过用户定义的新点的距离半径范围得出,这叫基于半径的最近邻即RNN。
KNN无监督最近邻示例
import numpy as np import matplotlib.pyplot as plt from sklearn import neighbors # random the data as the training data x = 5 * np.random.random((50, 2)) y = np.array([[1, 3], [4, 2]]) # knn n_neighbors = 5 # create color maps from matplotlib.colors import ListedColormap cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF']) # fit the training data from sklearn.neighbors import NearestNeighbors nbrs = NearestNeighbors(n_neighbors = n_neighbors, algorithm = 'auto'); nbrs.fit(x) # get the nearest neighbors distances, indices = nbrs.kneighbors(y) print "distance:",distances print "indices:",indices # get the selection of nearest neighbors selected = nbrs.kneighbors_graph(y).toarray() print "selected:",selected # plot the point plt.plot(y[:,0], y[:,1], 'g+') # plot the area t = np.linspace(0, np.pi * 2, 50) x_t = np.cos(t) y_t = np.sin(t) for i in range(y.shape[0]) : plt.plot(x_t * distances[i, -1] + y[i, 0], y_t * distances[i, -1] + y[i, 1]) # all selected selected = selected[0, :].astype(np.bool) | selected[1, :].astype(np.bool) selected = selected.astype(np.int32) # plot the selection plt.scatter(x[:, 0], x[:, 1], c = selected, cmap = cmap_bold, edgecolor = 'k', s = 20) plt.show()
RNN无监督最近邻示例:
import numpy as np import matplotlib.pyplot as plt from sklearn import neighbors # random the data as the training data x = 5 * np.random.random((50, 2)) y = np.array([[1, 3], [4, 2]]) # rnn n_radius = 1 # create color maps from matplotlib.colors import ListedColormap cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF']) # fit the training data from sklearn.neighbors import NearestNeighbors nbrs = NearestNeighbors(radius = n_radius, algorithm = 'auto'); nbrs.fit(x) # get the nearest neighbors distances, indices = nbrs.radius_neighbors(y) print "distance:",distances print "indices:",indices # get the selection of nearest neighbors selected = nbrs.radius_neighbors_graph(y).toarray() print "selected:",selected # plot the point plt.plot(y[:,0], y[:,1], 'g+') # plot the area t = np.linspace(0, np.pi * 2, 50) x_t = np.cos(t) y_t = np.sin(t) for i in range(y.shape[0]) : plt.plot(x_t * n_radius + y[i, 0], y_t * n_radius + y[i, 1]) # all selected selected = selected[0, :].astype(np.bool) | selected[1, :].astype(np.bool) selected = selected.astype(np.int32) # plot the selection plt.scatter(x[:, 0], x[:, 1], c = selected, cmap = cmap_bold, edgecolor = 'k', s = 20) plt.show()
最近邻分类
最近邻分类属于非泛化学习或基于实例的学习,他不会从训练数据上学习去构造一个泛化的概念模型。基于最邻近方法从样本集合中找出的点通过投票得出最具代表性的标签作为查询点的标签,一般情况下,从训练样本集合中找出的多个点使用统一的权重来投票查询点的标签,在某些情况下,需要进行加权,分配的权重与查询点的距离成反比,即与查询点的距离越大,权重越小。
一个查询点的ķ个最近邻分类方法为KNN算法,一个查询点的固定半径ř内的最近邻分类方法为RNN方法。
KNN最近邻分类示例:
import numpy as np import matplotlib.pyplot as plt from sklearn import neighbors # import some data to play with from sklearn import datasets iris = datasets.load_iris() # only take the first two features. # we could avoid this ugly slicing by using a two-dim dataset x = iris.data[:, :2] y = iris.target # k n_neighbors = 15 # create color maps from matplotlib.colors import ListedColormap cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF']) cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF']) # knn n_neighbors = 15 for weights in ['uniform', 'distance']: # we create an instance of Neighbors classifier and fit the data clf = neighbors.KNeighborsClassifier(n_neighbors, weights = weights) clf.fit(x, y) # plot the decision boundary. # For that, we will assign a color to each point in the mesh[x_min, x_max] * [y_min, y_max] x_min, x_max = x[:, 0].min() - 1, x[:, 0].max() + 1 y_min, y_max = x[:, 1].min() - 1, x[:, 1].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, .02), np.arange(y_min, y_max, .02)) # predict z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) # put the result into a color plot z = z.reshape(xx.shape) plt.figure() plt.pcolormesh(xx, yy, z, cmap = cmap_light) # plot also the training points plt.scatter(x[:, 0], x[:, 1], c = y, cmap = cmap_bold, edgecolor = 'k', s = 20) plt.xlim(xx.min(), xx.max()) plt.ylim(yy.min(), yy.max()) plt.title("3-Class classification (k = %i, weithts = '%s')" % (n_neighbors, weights)) plt.show()
RNN最近邻分类示例
基于半径最近邻分类方法中需要半径参数,而给出合适的半径参数非常困难,需要进行数据的归一化预处理,并且RNN对数据的分布也有要求。后续介绍数据归一化后补充RNN最近邻分类示例。
最近邻回归
最近邻回归是用在数据标签为连续变量的场景下,查询点的标签是由它的最近邻点的标签的均值计算而来。最近邻回归在计算查询点的标签时同最近邻分类一样可以使用权重,可以是统一的权重或者距离权重。
同样最近邻回归也有KNN和RNN两种,基于查询点的ķ个最近邻实现和基于查询点的固定半径ř内的邻点实现。
KNN最近邻回归示例:
import numpy as np import matplotlib.pyplot as plt from sklearn import neighbors np.random.seed(0) x = np.sort(5 * np.random.rand(40, 1), axis = 0) t = np.linspace(0, 5, 500)[:, np.newaxis] y = np.sin(x).ravel() # add noise to targets y[::5] += 1 * (0.5 - np.random.rand(8)) # knn n_neighbors = 5 for i, weights in enumerate(['uniform', 'distance']) : reg = neighbors.KNeighborsRegressor(n_neighbors, weights = weights) reg.fit(x, y) y_pred = reg.predict(t) plt.subplot(2, 1, i + 1) plt.scatter(x, y, c = 'k', label = 'data') plt.plot(t, y_pred, c = 'g', label = 'prediction') plt.axis('tight') plt.legend() plt.title("KNeighborsRegressor (k = %i, weights = '%s')" % (n_neighbors, weights)) plt.tight_layout() plt.show()
RNN最近邻回归示例
基于半径最近邻回归方法中需要半径参数,而给出合适的半径参数非常困难,需要进行数据的归一化预处理,并且RNN对数据的分布也有要求。后续介绍数据归一化后补充RNN最近邻回归示例。
最近邻算法
无论无监督最近邻,还是最近邻分类或者最近邻回归,最为核心的是如何计算训练样本中与查询点距离最近的多个点。最为直接的方法是求解查询点与训练样本中每一个点的距离,根据距离大小取距离最邻近的多个点即可,这种最邻近算法为暴力方法(蛮力)。
对于d维的Ñ个样本数据来说,这个方法的复杂度是-O [d * N ^ 2],对于小数据样本来说,暴力最近邻是非常不错的。当样本数Ñ增大,暴力最近邻变得不切实际了,甚至不可行。
树方法是一种优化的最近邻计算方法。其基于树的数据结构试图通过有效的编码样本的聚合距离信息来减少所需的距离计算量。基本思想是,若甲点距离乙点非常远,乙点距离ç点非常近,可知甲点与ç点距离也非常远,不需要明确计算阿与ç点之间的距离。通过这种方式,最近邻计算复杂度可以降低为-O [d * N *log(N)]。在大样本数据下,相对于暴力最邻近计算有显着改善。
目前树方法有KD树方法和Ball树方法,KD树在数据维度较低情况下表现优异,而Ball tree方法则在高维度数据情况下表现优异。