43

我正在使用 DBSCAN 使用 Scikit-Learn (Python 2.7) 对一些数据进行聚类:

from sklearn.cluster import DBSCAN
dbscan = DBSCAN(random_state=0)
dbscan.fit(X)

但是,我发现没有内置函数(除了“fit_predict”)可以将新数据点 Y 分配给原始数据 X 中标识的集群。K-means 方法有一个“预测”功能,但我希望能够对 DBSCAN 做同样的事情。像这样的东西:

dbscan.predict(X, Y)

因此可以从 X 中推断出密度,但返回值(集群分配/标签)仅适用于 Y。据我所知,此功能在 R 中可用,因此我假设它在 Python 中也以某种方式可用。我似乎找不到任何文档。

此外,我尝试寻找 DBSCAN 不能用于标记新数据的原因,但我没有找到任何理由。

4

7 回答 7

31

虽然 Anony-Mousse 有一些优点(聚类确实不是分类),但我认为分配新点的能力是有用的。*

基于关于DBSCAN的原始论文和 github.com/scikit-learn 上的 robertlaytons 想法eps我建议遍历核心点并将新点内的第一个核心点分配给集群。然后根据用于聚类的定义,保证您的点至少是分配聚类的边界点。(请注意,您的观点可能被视为噪音,而不是分配给集群)

我做了一个快速的实现:

import numpy as np
import scipy as sp

def dbscan_predict(dbscan_model, X_new, metric=sp.spatial.distance.cosine):
    # Result is noise by default
    y_new = np.ones(shape=len(X_new), dtype=int)*-1 

    # Iterate all input samples for a label
    for j, x_new in enumerate(X_new):
        # Find a core sample closer than EPS
        for i, x_core in enumerate(dbscan_model.components_): 
            if metric(x_new, x_core) < dbscan_model.eps:
                # Assign label of x_core to x_new
                y_new[j] = dbscan_model.labels_[dbscan_model.core_sample_indices_[i]]
                break

    return y_new

通过聚类获得的dbscan_model = DBSCAN(...).fit(X)标签(和从相同数据上的同一模型获得的标签(dbscan_predict(dbscan_model, X))有时会有所不同。我不太确定这是某个地方的错误还是随机性的结果。

编辑:我认为上述不同预测结果的问题可能源于边界点可能靠近多个集群的可能性。如果您对此进行测试并找到答案,请更新。可以通过每次改组核心点或选择最接近的而不是第一个核心点来解决歧义。

*)手头的案例:我想评估从我的数据子集获得的集群是否对其他子集有意义,或者只是一个特殊情况。如果它泛化它支持集群的有效性和应用的预处理的早期步骤。

于 2016-02-17T14:07:09.217 回答
24

聚类不是分类。

聚类未标记。如果你想把它挤进一种预测心态(这不是最好的主意),那么它本质上是在不学习的情况下进行预测。因为没有可用于聚类的标记训练数据。它必须根据所看到的内容为数据制作新的标签。但是您不能在单个实例上执行此操作,您只能“批量预测”。

但是 scipys DBSCAN 有问题:

random_state:numpy.RandomState,可选:

用于初始化中心的生成器。默认为 numpy.random。

DBSCAN 不会“初始化中心”,因为 DBSCAN 中没有中心。

几乎唯一可以将新点分配给旧集群的聚类算法是 k-means(及其许多变体)。因为它使用先前的迭代聚类中心执行“1NN 分类”,然后更新中心。但是大多数算法不像 k-means 那样工作,所以你不能复制它。

如果要对新点进行分类,最好根据聚类结果训练分类器。

R 版本可能正在做的是使用 1NN 分类器进行预测;可能通过为点分配噪声标签的额外规则,如果它们的 1NN 距离大于 epsilon,则可能也仅使用核心点。也许不吧。

获取 DBSCAN 论文,它没有讨论“预测”IIRC。

于 2015-01-07T21:32:19.720 回答
6

这里有一个稍微不同且更有效的实现。此外,不是取 eps 半径内的第一个最佳核心点,而是取离样本最近的核心点。

def dbscan_predict(model, X):

    nr_samples = X.shape[0]

    y_new = np.ones(shape=nr_samples, dtype=int) * -1

    for i in range(nr_samples):
        diff = model.components_ - X[i, :]  # NumPy broadcasting

        dist = np.linalg.norm(diff, axis=1)  # Euclidean distance

        shortest_dist_idx = np.argmin(dist)

        if dist[shortest_dist_idx] < model.eps:
            y_new[i] = model.labels_[model.core_sample_indices_[shortest_dist_idx]]

    return y_new
于 2018-07-25T10:12:46.993 回答
4

虽然它不是完全相同的算法,但您可以使用 sklearn HDBSCAN 对新点进行近似预测。见这里

它是这样工作的:

clusterer = hdbscan.HDBSCAN(min_cluster_size=15, prediction_data=True).fit(data)
test_labels, strengths = hdbscan.approximate_predict(clusterer, test_points)
于 2019-07-30T19:20:08.597 回答
3

很好的答案已经在这里发布了这个问题。我的建议是试试HDBSCAN。它提供了approximate_predict()一种可能是您需要的方法。

于 2020-06-16T04:21:46.387 回答
1

我们先试着了解一下DBSCAN基于密度的聚类的一些基本知识,下图总结了基本概念。

在此处输入图像描述 让我们首先创建一个将使用 DBSCAN 进行聚类的示例 2D 数据集。下图显示了数据集的外观。

import numpy as np
import matplotlib.pylab as plt
from sklearn.cluster import DBSCAN

X_train = np.array([[60,36], [100,36], [100,70], [60,70],
    [140,55], [135,90], [180,65], [240,40],
    [160,140], [190,140], [220,130], [280,150], 
    [200,170], [185, 170]])
plt.scatter(X_train[:,0], X_train[:,1], s=200)
plt.show()

在此处输入图像描述

现在让我们使用scikit-learnDBSCAN 的实现来集群:

eps = 45
min_samples = 4
db = DBSCAN(eps=eps, min_samples=min_samples).fit(X_train)
labels = db.labels_
labels
# [ 0,  0,  0,  0,  0,  0,  0, -1,  1,  1,  1, -1,  1,  1]
db.core_sample_indices_
# [ 1,  2,  4,  9, 12, 13]

从以上结果可以看出

  • 算法找到了6个核心点
  • 找到了 2 个聚类(标签为 0、1)和几个异常值(噪声点)。

让我们使用以下代码片段可视化集群:

def dist(a, b):
    return np.sqrt(np.sum((a - b)**2))

colors = ['r', 'g', 'b', 'k']
for i in range(len(X_train)):
    plt.scatter(X_train[i,0], X_train[i,1], 
                s=300, color=colors[labels[i]], 
                marker=('*' if i in db.core_sample_indices_ else 'o'))
                                                            
    for j in range(i+1, len(X_train)):
        if dist(X_train[i], X_train[j])  < eps:
            plt.plot([X_train[i,0], X_train[j,0]], [X_train[i,1], X_train[j,1]], '-', color=colors[labels[i]])
            
plt.title('Clustering with DBSCAN', size=15)
plt.show()
  • 簇 0 中的点为红色
  • 簇 1 中的点为绿色
  • 异常点为黑色
  • 核心点用'*'标记。
  • 如果两个点在 ϵ-nbd 内,则它们由一条边连接。

在此处输入图像描述

最后,让我们实现predict()预测新数据点聚类的方法。实施基于以下几点:

  • 为了使新点x属于一个簇,它必须是从簇中的一个核心点直接密度可达的。

  • 我们将计算离集群最近的核心点,如果它与x的距离在 ε 之内,我们将返回核心点的标签,否则点x将被声明为噪声点(离群点)。

  • 请注意,这与训练算法不同,因为我们不再允许任何点成为新的核心点(即核心点的数量是固定的)。

  • 接下来的代码片段就是predict()基于上面的思路实现的功能

    def predict(db, x):
      dists = np.sqrt(np.sum((db.components_ - x)**2, axis=1))
      i = np.argmin(dists)
      return db.labels_[db.core_sample_indices_[i]] if dists[i] < db.eps else -1
    
    X_test = np.array([[100, 100], [160, 160], [60, 130]])
    for i in range(len(X_test)):
       print('test point: {}, predicted label: {}'.format(X_test[i], 
                                                   predict(db, X_test[i])))
    # test point: [100 100], predicted label: 0
    # test point: [160 160], predicted label: 1
    # test point: [ 60 130], predicted label: -1
    

下一个动画展示了如何使用predict()上面定义的函数标记几个新的测试点。

在此处输入图像描述

于 2021-09-19T00:07:41.683 回答
0
DBSCAN.fit_predict(X, y=None, sample_weight=None)

从https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html阅读更多信息

于 2021-11-18T01:56:58.740 回答