【踩坑总结】用sklearn.neighbors.KNeighborsClassifier自定义距离矩阵knn建图


前言

毕设涉及对点云进行knn建图,因为定义的距离计算较为复杂且点云数据量较大,运算很慢。考虑采用sklearn.neighbors.KNeighborsClassifier模块中自定义距离矩阵knn搜索的功能。但官方对此部分介绍很简略,网络相关资源也少。自己各种摸索完成1.0版本后记录下该功能用法和实现细节 :)


一、KNeighborsClassifier官方简介

class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights=’uniform’, 
											algorithm=auto, leaf_size=30, 
											p=2, metric=’minkowski’, 
											metric_params=None, 
											n_jobs=None, **kwargs)

关键参数:
n_neighbors : int,optional(default = 5)
默认情况下kneighbors查询使用的邻居数。就是k-NN的k的值,选取最近的k个点。

algorithm : {‘auto’,‘ball_tree’,‘kd_tree’,‘brute’},可选
快速k近邻搜索算法,默认参数为auto,可以理解为算法自己决定合适的搜索算法。除此之外,用户也可以自己指定搜索算法ball_tree、kd_tree、brute方法进行搜索,brute是蛮力搜索,也就是线性扫描,当训练集很大时,计算非常耗时。kd_tree,构造kd树存储数据以便对其进行快速检索的树形数据结构,kd树也就是数据结构中的二叉树。以中值切分构造的树,每个结点是一个超矩形,在维数小于20时效率高。ball tree是为了克服kd树高纬失效而发明的,其构造过程是以质心C和半径r分割样本空间,每个节点是一个超球体。

metric : 字符串或可调用,默认为’minkowski’
用于树的距离度量,默认度量是minkowski,也就是p=2的欧氏距离(欧几里德度量)。更多可用距离度量,请参阅DistanceMetric文档。如果metric是"precomputed"的,则 X 是距离矩阵,并且在拟合期间必须是方形的。X 可以是稀疏矩阵,在这种情况下,只有"非零"元素可以被视为邻居。

二、使用步骤

1.使用细节

Q1:如何利用该模块实现建图过程?

步骤如下:
1、初始化分类器 Classifier_name = KNeighborsClassifier(algorithm=‘brute’,metric=def_d)
2、fit:Classifier_name.fit(X,Y)
该函数实现根据标签Y,对X元素计算距离、实现拟合和分类。此时,我们自定义的距离计算函数被调用。
3、调用构建邻接矩阵的函数:graph=Classifier_name.kneighbors_graph(X,n_neighbors=5,mode=‘distance’)
X:以X元素为节点
n_neighbors=5:每个节点最近的5个邻居定义为相邻节点
mode='distance’:生成矩阵内部的值为具体的距离值

Q2:传入参数是什么?具体如何被调用?

定义的距离计算矩阵要求至少传入两个参数。但实际计算过程中,都是X中元素被传入。当初始化分类器时定义的算法algorithm='brute’时,传参规则是:X[i]被依次传入a,对应X[i+1]-X[n]被依次传入b,最后计算自己X[i]到自己X[i]的距离。例如,传入X为一个10 * 1的数组时,用邻接矩阵内存储的数字表示计算距离的顺序,生成的10 * 10的邻接矩阵为:
在这里插入图片描述
algorithm='auto’时,传参规则变化了…还没搞懂!
参数个数大于2时,也还没搞懂!

Q3:metric定义的可调用函数有啥要求吗?

有的!!!要求距离有对称性,即d(i,j)=d(j,i)。因为实际计算过程中它只会计算j>=i这一半的距离,然后直接对称得到另一半。若我们定义的距离计算函数不满足对称性,采用该方式无法得到期望结果。
另外,看到另一个相关函数sklearn.neighbors.DistanceMetric中写到自定义距离的要求,感觉应该是通用的要求:在这里插入图片描述

2.具体实现

def def_d(pcd_copy , pcd): #后一个变量为X,前一个变量从何而来?
    global count
    global fenmu
    global d_j
    global last_i
    global z
   
    #pcd_copy到底是什么东西传递进去了????竟然不是坐标值???
    #print("pcd_copy = ",pcd_copy)
    #print("pcd = ", pcd)
    count = count +1
    i = count // fenmu + last_i
    d_j = d_j + 1
    if i <= (pcd_len-2) :
        if i != last_i:
            last_i = i
            count = count - fenmu
            fenmu = fenmu -1
            d_j = i + 1
       
    z = z + 1
    print("z[",i,"][",d_j,"] = ",z)
    return z

count = -1
fenmu = pcd_len - 1
d_j = 0
last_i = 0
z = 0

X = np.asarray(pcd_input1.points)
Y = np.ones(pcd_len) 

knn = KNeighborsClassifier(algorithm='brute',metric=def_d) # 'auto' 点数太多的时候会自动变成'kd_tree'?!!!顺序发生变化!!!!只能用'brute'!
knn.fit(X,Y) #必须要有!可先随意赋值Y不会影响neibor
# X 形状为 [n_samples, n_features]; Y 形状为[n_samples] 1*n
m = knn.kneighbors_graph(X,n_neighbors=k+1,mode='distance') #得到CSR格式的稀疏矩阵!
m1 = m.tocoo()
#np.savetxt(r"F:\ser\v2_plus_d.val", m1.data) 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值