近邻(Nearest Neighbor)算法既可以用于监督学习(分类),也可以用于非监督学习(聚类),它通过按照一定方法查找距离预测样本最近的n个样本,并根据这些样本的特征对预测样本做出预测。
在sklearn里,所有的近邻算法位于sklearn.neighbors下,共包含下列13个方法:
NearestNeighbors
:knn算法NearestCentroid
:类似knn,但是它不是找和样本距离最近的样本,而是找距离其最近的不同类的样本中心KNeighborsClassifier
:knn分类器RadiusNeighborsClassifier
:基于半径距离而不是k的nn分类器KNeighborsRegressor
:knn回归器RadiusNeighborsRegressor
:基于半径距离而不是k的nn回归器KernelDensity
:核密度估计LSHForest
:局部敏感哈希森林KDTree
BallTree
DistanceMetric
kneighbors_graph
radius_neighbors_graph
其中,前8个为具体算法,后5个为对应算法使用的实现方法。
knn算法
以监督学习为例,传统的knn算法流程如下:
- 给定k值
- 确定距离度量,譬如欧氏距离
- 计算待预测样本到训练集中所有样本的
- 取距离由小到大排列的前k个样本,这k个样本中属于同一类个数最多的那个类即是该待预测样本所属类别。
上述的knn的传统形式便是用NearestNeighbors来实现的,它返回的仅仅是近邻而不是分类结果,分类器由KNeighborsClassifier实现,当然这两者区别不大(同理,回归器由KNeighborsRegressor实现)。
NearestCentroid
如果我们不再是求解到所有样本的距离,而是求解到不同类别样本中心的距离,距离哪个样本中心最近,我们即认为该待预测样本属于哪个类,这就是NearestCentroid算法,该算法能降低计算量,但是对于某些分布的样本来说,准确率不高。
基于半径的nn算法
如果我们不给定k值,而是给定一个距离范围(Radius),我们求解待预测样本到训练集中所有样本的距离小于该半径的样本,这些样本中属于同一类个数最多的那个类即是我们认为待预测的分类,也即RadiusNeighborsClassifier和RadiusNeighborsRegressor的功能。
PS:在sklearn里,NearestNeighbors也可以基于半径查找最近邻(它同时继承了KNeighborsMixin和RadiusNeighborsMixin),它的初始化方法接受的参数包括n_neighbors和radius,如果我们需要基于k值的结果,则调用kneighbors();如果需要基于半径的结果,则调用radius_neighbors()方法即可。
KDTree和BallTree
这两者都是一种空间的划分方式,用来在找最近邻时提升检索效率和降低计算量的,前者将样本空间进行矩形(高维就是超立方体)划分,在数据维度小于20维效率很高,是基于欧氏距离的;后者进行圆形(高维就是超球)划分,用来解决kdtree在高维失效,它是基于更加一般的距离, |x+y|≤|x|+|y| 。在sklearn里分别用KDTree和BallTree来实现。
knn回归器
即我们求解出待预测样本的k近邻或者某个距离范围内的样本,通过对这些样本进行插值的方式获取待预测样本的属性值,譬如,对k近邻样本的对应属性值求平均。
局部敏感哈希(LSH)
LSH是一种当前很流行的基于相似度的近邻查找算法,我们知道hash的具有O(1)的查找复杂度,这里的LSH亦有异曲同工之妙。
LSH基于一个假设,即在高维空间(原数据的维度)具有高相似性的样本,映射到(哈希函数矩阵)低维空间也具有高相似度,反之亦然。它便是通过找到这样一个哈希函数,将高维数据映射到低维(可以理解为hash表中最终的地址值),每当我们查找一个新样本时,对它使用哈希函数矩阵进行变换,然后再进行查找,会大大提高检索效率。常用的相似性度量有:
欧氏距离
:这个不多说余弦距离
:同上Jaccard相似性
:
J(A,B)=|A∩B||A∪B|,即A和B交集的元素个数/A和B并集的元素个数
以Jaccard相似性为例,流程如下:
- 对所有样本按列排列成一个N*M矩阵,即每一个代表一个样本(共M个),每一行是一个属性(共N个属性)
- 生成k(即最后的数据维度)个随机置换,即1-N的整数的随机排列
- 对每个置换,调换样本矩阵的对应行,譬如:对于置换[2,3,1,4]则用原样本的第2行代替第一行,第2行代替第二行,依次类推
- 对于每个置换后的矩阵,对每一列求解一个特征
- 最终得到K*M矩阵即最终的矩阵
当然,我们使用哈希表时,如果映射函数不够好,会导致映射值不够用,比如我们的映射函数只能取到5个值,而我们有6个截然不同给的样本,这样就会出问题,LSH也会碰到这样的问题。
核密度估计
通常获取到一个样本集,我们希望求解出它在不同位置的概率分布,之后我们便可以通过该分布,直接对新预测样本进行概率预测,这就需要用到核密度估计。
我们知道,分布函数的一阶导数即可以表示密度函数,但是通过样本直方图可以看到,我们的样本集并不可能平滑,也即不可导,为此,我们可以在某些值很小的邻域内对其求导,这里设计到一个h的选取,可以理解为每个求解区间的大小,如果h很大,用于计算的样本量充足,方差会比较小,但是偏差会很大;反之亦然。为此,我们一般需求样本
N→∞,h→0
。实践证明,
h=c∗N−1/5
会是一个比较好的选择,其中
c
是一个常数,当数据近似服从正太分布