web-dev-qa-db-fra.com

Scikit-Learn: prédire de nouveaux points avec DBSCAN

J'utilise DBSCAN pour regrouper certaines données à l'aide de Scikit-Learn (Python 2.7):

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

Cependant, j'ai trouvé qu'il n'y avait pas de fonction intégrée (à part "fit_predict") qui pouvait affecter les nouveaux points de données, Y, aux clusters identifiés dans les données d'origine, X. La méthode K-means a une "prédiction" mais je veux pouvoir faire de même avec DBSCAN. Quelque chose comme ça:

dbscan.predict(X, Y)

Pour que la densité puisse être déduite de X, mais les valeurs de retour (affectations/étiquettes de cluster) ne sont que pour Y. D'après ce que je peux dire, cette capacité est disponible dans R, donc je suppose qu'elle est également disponible en Python. Je n'arrive pas à trouver de documentation pour cela.

De plus, j'ai essayé de chercher des raisons pour lesquelles DBSCAN ne peut pas être utilisé pour étiqueter de nouvelles données mais je n'ai trouvé aucune justification.

31
slaw

Le regroupement n'est pas une classification.

Le clustering n'est pas étiqueté. Si vous voulez le presser dans un état d'esprit de prédiction (ce qui n'est pas la meilleure idée), alors il s'agit essentiellement de prédit sans apprentissage. Parce qu'il n'y a pas de données de formation étiquetées disponibles pour le clustering. Il doit créer de nouvelles étiquettes pour les données, en fonction de ce qu'il voit. Mais vous ne pouvez pas faire cela sur une seule instance, vous ne pouvez que "prédire en bloc".

Mais il y a quelque chose qui ne va pas avec scipys DBSCAN:

random_state: numpy.RandomState, facultatif:

Le générateur utilisé pour initialiser les centres. Par défaut, numpy.random.

DBSCAN n'initialise pas les centres, car il n'y a pas de centre dans DBSCAN.

À peu près l'algorithme de clustering seulement où vous pouvez attribuer de nouveaux points aux anciens clusters est k-means (et ses nombreuses variations). Parce qu'il effectue une "classification 1NN" en utilisant les centres de cluster d'itérations précédentes, puis met à jour les centres. Mais la plupart des algorithmes ne fonctionnent pas comme k-means, vous ne pouvez donc pas copier cela.

Si vous souhaitez classer de nouveaux points, il est préférable de former un classificateur sur votre résultat de clustering.

La version R utilise peut-être un classificateur 1NN pour la prédiction; peut-être avec la règle supplémentaire que les points se voient attribuer l'étiquette de bruit, si leur distance 1NN est supérieure à epsilon, mabye utilise également les points centraux uniquement. Peut être pas.

Obtenez le document DBSCAN, il ne traite pas de "prédiction" IIRC.

17
Anony-Mousse

Bien qu'Anony-Mousse ait de bons points (le clustering n'est en effet pas classifié), je pense que la capacité d'attribuer de nouveaux points a son utilité. *

Sur la base de l'article original sur DBSCAN et des idées de robertlaytons sur github.com/scikit-learn , je suggère de parcourir les points principaux et affectation au cluster du premier point central qui se trouve à eps de votre nouveau point. Ensuite, il est garanti que votre point sera au moins un point frontière du cluster affecté selon les définitions utilisées pour le clustering. (Sachez que votre point peut être considéré comme du bruit et non affecté à un cluster)

J'ai fait une implémentation rapide:

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

Les étiquettes obtenues par regroupement (dbscan_model = DBSCAN(...).fit(X) et les étiquettes obtenues à partir du même modèle sur les mêmes données (dbscan_predict(dbscan_model, X)) diffèrent parfois. Je ne suis pas certain s'il s'agit d'un bogue quelque part ou un résultat de l'aléatoire.

EDIT: Je pense que le problème ci-dessus des résultats de prédiction différents pourrait provenir de la possibilité qu'un point frontière puisse être proche de plusieurs grappes. Veuillez mettre à jour si vous testez cela et trouvez une réponse. L'ambiguïté peut être résolue en mélangeant les points centraux à chaque fois ou en choisissant le plus proche au lieu du premier point central.

*) Cas concret: je voudrais évaluer si les grappes obtenues à partir d'un sous-ensemble de mes données ont un sens pour un autre sous-ensemble ou s'il s'agit simplement d'un cas spécial. S'il généralise, il prend en charge la validité des clusters et les étapes précédentes de prétraitement appliquées.

18
kidmose

Voici une implémentation légèrement différente et plus efficace. De plus, au lieu de prendre le premier meilleur point central qui se trouve dans le rayon eps, le point central le plus proche de l'échantillon est pris.

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
4
nkaenzig

Bien que ce ne soit pas exactement le même algorithme, vous pouvez effectuer des prédictions approximatives pour de nouveaux points avec sklearn DBSCAN. Voir ici .

Cela fonctionne comme ceci:

clusterer = hdbscan.HDBSCAN(min_cluster_size=15, prediction_data=True).fit(data)
test_labels, strengths = hdbscan.approximate_predict(clusterer, test_points)
0
Benten