文章目录
前言
毕设涉及对点云进行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)