使用Sklearn kNN算法和用户定义的度量标准

32

目前我正在进行一个项目,可能需要使用kNN算法来查找给定点P的前k个最近邻居,我正在使用Python的sklearn包来完成这项工作,但我们预定义的度量标准不是默认值之一。所以我必须使用用户定义的度量标准。根据sklearn文档,可以在这里这里找到。

最新版本的sklearn kNN似乎支持用户定义的度量标准,但我找不到如何使用它:

import sklearn
from sklearn.neighbors import NearestNeighbors
import numpy as np
from sklearn.neighbors import DistanceMetric
from sklearn.neighbors.ball_tree import BallTree
BallTree.valid_metrics

假设我定义了一个度量叫做 mydist=max(x-y),然后使用 DistanceMetric.get_metric 将其转换为 DistanceMetric 对象:

dt=DistanceMetric.get_metric('pyfunc',func=mydist)

从文件中看,这一行应该是这样的

nbrs = NearestNeighbors(n_neighbors=4, algorithm='auto',metric='pyfunc').fit(A)
distances, indices = nbrs.kneighbors(A)

但是我应该把dt放在哪里呢?谢谢。


2
即使我在其中放入func=mydist,原因是参数algorithm=auto不接受用户定义的度量标准,无论是algorithm=kd_tree还是algorithm=brute,都无法使nbrs = NearestNeighbors(n_neighbors=4, algorithm='auto',metric='pyfunc').fit(A)正常工作。只有algorithm=ball_tree可以接受。 - user2926523
3个回答

37
您需要将度量值作为 metric 参数传递,并将其他指标参数作为关键字参数传递给 NN 构造函数。
>>> def mydist(x, y):
...     return np.sum((x-y)**2)
...
>>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])

>>> nbrs = NearestNeighbors(n_neighbors=4, algorithm='ball_tree',
...            metric='pyfunc', func=mydist)
>>> nbrs.fit(X)
NearestNeighbors(algorithm='ball_tree', leaf_size=30, metric='pyfunc',
         n_neighbors=4, radius=1.0)
>>> nbrs.kneighbors(X)
(array([[  0.,   1.,   5.,   8.],
       [  0.,   1.,   2.,  13.],
       [  0.,   2.,   5.,  25.],
       [  0.,   1.,   5.,   8.],
       [  0.,   1.,   2.,  13.],
       [  0.,   2.,   5.,  25.]]), array([[0, 1, 2, 3],
       [1, 0, 2, 3],
       [2, 1, 0, 3],
       [3, 4, 5, 0],
       [4, 3, 5, 0],
       [5, 4, 3, 0]]))

3
我正在使用scikit-learn 0.18.dev0版本,但是我遇到了以下错误——“_init_params()”接收到了一个意外的关键字参数“func”。 - Shishir Pandey
7
您可以查看以下提交记录:https://github.com/scikit-learn/scikit-learn/commit/ad751a3b6996a4c209c1a243d396aa6930d4acc4,神经网络的签名已更改。我猜您只需直接将“mydist”作为“metric”参数传递即可。 - alko
1
如何为稀疏向量定义自定义度量?使用此方法,我会得到以下错误信息: ValueError: metric 'pyfunc' not valid for sparse input - mpr

22

关于之前回答的一点补充。如何使用一个需要额外参数的用户自定义指标。

>>> def mydist(x, y, **kwargs):
...     return np.sum((x-y)**kwargs["metric_params"]["power"])
...
>>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
>>> Y = np.array([-1, -1, -2, 1, 1, 2])
>>> nbrs = KNeighborsClassifier(n_neighbors=4, algorithm='ball_tree',
...            metric=mydist, metric_params={"power": 2})
>>> nbrs.fit(X, Y)
KNeighborsClassifier(algorithm='ball_tree', leaf_size=30,                                                                                                                                                          
       metric=<function mydist at 0x7fd259c9cf50>, n_neighbors=4, p=2,
       weights='uniform')
>>> nbrs.kneighbors(X)
(array([[  0.,   1.,   5.,   8.],
       [  0.,   1.,   2.,  13.],
       [  0.,   2.,   5.,  25.],
       [  0.,   1.,   5.,   8.],
       [  0.,   1.,   2.,  13.],
       [  0.,   2.,   5.,  25.]]),
 array([[0, 1, 2, 3],
       [1, 0, 2, 3],
       [2, 1, 0, 3],
       [3, 4, 5, 0],
       [4, 3, 5, 0],
       [5, 4, 3, 0]]))

3
我认为函数中应该是kwargs["power"]而不是kwargs["metric_params"]["power"]。至少这是我在sklearn '0.16.1'版本中观察到的行为。 - benbo
@benbo 你是对的:我修复了代码并添加了一点注释(我编辑了Mahmoud的帖子)。 - payne

0

在尝试使用用户定义的度量时,只有设置algorithm='brute'才能使用KNeighborsRegressor()。

否则,当在JupyterLab中使用时,fit()可以正常工作,但predict()会失败并显示错误信息'returned NULL without setting an error';当在Google Colab中使用时,则会出现'SystemError: error return without exception set'的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接