Sklearn kNN 使用用户定义的指标

Posted

技术标签:

【中文标题】Sklearn kNN 使用用户定义的指标【英文标题】:Sklearn kNN usage with a user defined metric 【发布时间】:2014-01-29 21:50:04 【问题描述】:

目前我正在做一个项目,可能需要使用 kNN 算法来找到给定点的前 k 个最近邻居,比如 P.im 使用 python、sklearn 包来完成这项工作,但我们的预定义指标不是一个这些默认指标。所以我必须使用用户定义的指标,来自sklearn的文档,可以找到here和here。

似乎最新版本的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 放在哪里?谢谢

【问题讨论】:

nbrs = NearestNeighbors(n_neighbors=4, algorithm='auto',metric='pyfunc').fit(A) distances, indices = nbrs.kneighbors(A) 即使我把func=mydist 放在那里也不起作用的原因是因为参数algorithm=auto 不接受用户定义的指标,既不接受用户定义的指标,也不接受algorithm=kd_treealgorithm=brute。只有algorithm=ball_tree 接受 【参考方案1】:

您将度量作为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]]))

【讨论】:

我正在使用 scikit-learn 0.18.dev0 版本,我收到以下错误 - _init_params() got an unexpected keyword argument 'func' @ShishirPandey 您可以检查以下提交,github.com/scikit-learn/scikit-learn/commit/…,NN 签名已更改。我想你可以直接将 mydist 作为“metric”参数传递 如何为稀疏向量定义自定义指标?使用这种方法我得到:ValueError: metric 'pyfunc' not valid for sparse input【参考方案2】:

对上一个答案的一个小补充。如何使用带有附加参数的用户定义指标。

>>> 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]]))

【讨论】:

我实际上认为在函数中它需要是 kwargs["power"],而不是 kwargs["metric_params"]["power"] 。至少这是我在 sklearn '0.16.1' 中观察到的行为 @benbo 你是对的:我修复了代码并添加了一点评论(我编辑了 Mahmoud 的帖子)。【参考方案3】:

在尝试使用用户定义的指标时,仅通过设置 algorithm='brute' 才能使用 KNeighborsRegressor()。

否则 fit() 有效,但 predict() 失败,使用 JupyterLab 时出现错误“返回 NULL 而未设置错误”,或使用 Google Colab 时出现“SystemError: error return without exception set”

【讨论】:

以上是关于Sklearn kNN 使用用户定义的指标的主要内容,如果未能解决你的问题,请参考以下文章

sklearn 使用带有自定义指标的 RandomizedSearchCV 并捕获异常

机器学习-------KNN算法的sklearn KNN实践

NearestNeighbors sklearn 的自定义指标

KNN sklearn 中的分类标签

2. KNN和KdTree算法实现

Sklearn:具有字符串值和自定义指标的最近邻