23

J'ai 1 million de points en 5 dimensions que j'ai besoin de regrouper en k clusters avec k < < 1 million. Dans chaque groupe, aucun point ne doit être trop éloigné (par exemple, ils peuvent être des sphères de délimitation avec un rayon spécifié). Cela signifie qu'il doit probablement y avoir plusieurs grappes de taille 1.Algorithme de clustering rapide (<n^2)

Mais! J'ai besoin de temps pour être bien en dessous de n^2. n log n ou alors devrait être bon. La raison pour laquelle je fais cette classification est d'éviter de calculer une matrice de distance de tous les n points (ce qui prend n^2 fois ou plusieurs heures), mais je veux juste calculer les distances entre les clusters.

J'ai essayé l'algorithme k-means de pycluster mais je me suis rapidement rendu compte que c'était trop lent. J'ai également essayé l'approche gourmande suivante:

  1. Coupez l'espace en 20 morceaux dans chaque dimension. (donc il y a 20^5 pièces au total). Je vais stocker des grappes dans ces grilles, en fonction de leurs centroïdes.

  2. Pour chaque point, récupérez les boîtes de grille qui se trouvent à l'intérieur de r (rayon de la sphère limite maximale). S'il existe un cluster assez proche, ajoutez-le à ce cluster, sinon créez un nouveau cluster.

Cependant, cela semble me donner plus de clusters que je veux. J'ai également mis en œuvre deux fois des approches similaires à celle-ci, et ils donnent des réponses très différentes. Y a-t-il des approches standard de clustering plus rapide que n^2? Les algorithmes probabilistes sont ok.

+2

Vous pouvez explorer BIRCH http://people.cs.ubc.ca/~rap/teaching/504/2005/slides/Birch.pdf –

+0

Merci, je vais regarder cette. Il semble qu'il utilise essentiellement une approche multi-phase, ce qui était aussi ce que je pensais faire ensuite. Par exemple. Enlevez d'abord les points éloignés de tous les autres points, puis effectuez une mise en grappe en une passe, puis effectuez un rééquilibrage, etc. Où chaque étape est rapide. Mais c'est beaucoup de problèmes à mettre en œuvre. –

+5

Il n'y a pas de repas gratuit ... –

Répondre

12

considérez comme un voisin le plus proche approximative (ANN) ou algorithme localité sensible hachant (LSH). Ils ne résolvent pas directement le problème de clustering, mais ils peuvent vous dire quels sont les points les plus proches les uns des autres. En modifiant les paramètres, vous pouvez définir proche d'être aussi proche que vous le souhaitez. Et c'est rapide.

Plus précisément, LSH peut fournir une fonction de hachage, h, de sorte que, pour deux points x et y, et la distance métrique d,

d(x,y) <= R1 => P(h(x) = h(y)) >= P1 
d(x,y) >= R2 => P(h(x) = h(y)) <= P2 

R1 < R2 et P1 > P2. Alors oui, c'est probabiliste. Vous pouvez post-traiter les données récupérées pour arriver à de vrais clusters.

Voici des informations sur LSH incluant le E2LSH manual. ANN est similaire dans l'esprit; David Mount a des informations here, ou essayez FLANN (possède des liaisons Matlab et Python).

+0

Hmm, c'est très utile. Donc, étant donné que j'ai un algorithme de plus proches voisins (je pense que scipy kdtree a cette fonctionnalité, même si je ne sais pas si c'est rapide), proposez-vous un algorithme comme: 1. Pour chaque point, calcul approximatif tous les voisins les plus proches de rayon inférieur à r. 2. Triez les points parmi le moins de voisins proches. 3. Créez une liste qui contiendra le numéro de cluster entier pour chaque point. 4. En parcourant la liste triée (des ensembles de voisins), assignez tous les points de l'ensemble à un nouveau groupe. (sorte d'algorithme rétrograde) –

+0

Peut-être que cela peut fonctionner. J'aime imaginer que chaque poubelle de la table de hachage devienne «un point». Ainsi, au lieu de regrouper un million de points individuels, vous regroupez à la place des * bins * qui sont des collections de points, l'hypothèse étant que les points dans la même case sont déjà assez proches. Mais trouver ces bacs est rapide. C'est juste une idée. –

+0

OK Je vois votre version, en utilisant la fonction de hachage. Je vais certainement essayer aussi, si je peux trouver une implémentation en python de LSH. –

2

J'ai un module Perl qui fait exactement ce que vous voulez Algorithm::ClusterPoints. Tout d'abord, il utilise l'algorithme que vous avez décrit dans votre message pour diviser les points dans les secteurs multidimensionnels, puis il utilise la force brute pour trouver des clusters entre les points dans les secteurs adjacents.

La complexité varie de O (N) à O (N ** 2) dans les cas très dégradés.

mise à jour:

@Denis: non, il est bien pire:

Pour d dimensions, le secteur (ou peu) hypercube taille s est déterminé de telle sorte que sa diagonale l est la distance minimale c autorisé entre deux points dans différents groupes. Ensuite, vous avez

l = c 
l = sqrt(d * s * s) 
s = sqrt(c * c/d) = c/sqrt(d) 

à considérer tous les secteurs qui touchent le hypersphère avec un diamètre r = 2c + l centré dans le secteur de pivot.

En gros, nous devons considérer ceil(r/s) lignes de secteurs dans toutes les directions et cela signifie n = pow(2 * ceil(r/s) + 1, d).

Par exemple, pour d=5 et c=1 nous obtenons l=2.236, s=0.447, r=3.236 et n=pow(9, 5)=59049

En fait, nous devons vérifier les secteurs moins voisins comme ici nous considérons ceux qui touchent l'hypercube de taille (2r+1)/s et nous ne devons vérifiez ceux qui touchent l'hypersphère circonscrite. Compte tenu de la nature bijective de la relation "sont sur le même cluster", nous pouvons également réduire la moitié du nombre de secteurs à contrôler.

Plus précisément, Algorithm :: ClusterPoints pour le cas où d=5 vérifie 3903 secteurs.

+0

Merci!Il semble que ce que vous faites soit de diviser l'espace en une grille, puis d'utiliser quelque chose de plus intelligent que mon algorithme glouton pour trouver des clusters localement. Dommage que je sois médiocre à la programmation et que je ne connaisse pas perl donc j'utilise uniquement python. En outre, pour obtenir un crédit supplémentaire, je souhaite réellement utiliser mon algorithme pour un ensemble complet de points 1M, mais plus tard, je pourrai voir rapidement quelles seraient les meilleures grappes si seulement j'avais utilisé différents sous-ensembles (disons 500K). Les approches de style de hachage discutées dans la réponse précédente peuvent accomplir cela car j'ai seulement besoin de refaire le post-traitement. –

+0

donc chaque secteur (petit hypercube) regarde 3^5 - 1 = 242 voisins dans 5d? – denis

+0

wow, monter rapidement avec dim. Les vrais temps d'exécution seraient intéressants. – denis

2

est Ci-dessous un petit banc de test pour voir à quelle vitesse scipy.spatial.cKDTree est sur vos données, et d'avoir une idée approximative de la façon dont les distances entre les points à proximité scatter.

Une bonne façon d'exécuter K-cluster pour divers K est de construire un MST des paires les plus proches, et enlever le plus long K-1; voir Wayne, Greedy Algorithms.

Visualiser les clusters serait amusant - projet à 2d avec PCA?

(curiosité, est votre K 10, 100, 1000?)

Ajouté le 17 Dec: real runtimes: 100000 x 5 10 sec, 500000 x 5 60sec

#!/usr/bin/env python 
# time scipy.spatial.cKDTree build, query 

from __future__ import division 
import random 
import sys 
import time 
import numpy as np 
from scipy.spatial import cKDTree as KDTree 
    # http://docs.scipy.org/doc/scipy/reference/spatial.html 
    # $scipy/spatial/kdtree.py is slow but clean, 0.9 has cython 
__date__ = "2010-12-17 dec denis" 

def clumpiness(X, nbin=10): 
    """ how clumpy is X ? histogramdd av, max """ 
     # effect on kdtree time ? not much 
    N, dim = X.shape 
    histo = np.histogramdd(X, nbin)[0] .astype(int) # 10^dim 
    n0 = histo.size - histo.astype(bool).sum() # uniform: 1/e^lambda 
    print "clumpiness: %d of %d^%d data bins are empty av %.2g max %d" % (
     n0, nbin, dim, histo.mean(), histo.max()) 

#............................................................................... 
N = 100000 
nask = 0 # 0: ask all N 
dim = 5 
rnormal = .9 
    # KDtree params -- 
nnear = 2 # k=nnear+1, self 
leafsize = 10 
eps = 1 # approximate nearest, dist <= (1 + eps) * true nearest 
seed = 1 

exec "\n".join(sys.argv[1:]) # run this.py N= ... 
np.random.seed(seed) 
np.set_printoptions(2, threshold=200, suppress=True) # .2f 
nask = nask or N 
print "\nkdtree: dim=%d N=%d nask=%d nnear=%d rnormal=%.2g leafsize=%d eps=%.2g" % (
    dim, N, nask, nnear, rnormal, leafsize, eps) 

if rnormal > 0: # normal point cloud, .9 => many near 1 1 1 axis 
    cov = rnormal * np.ones((dim,dim)) + (1 - rnormal) * np.eye(dim) 
    data = np.abs(np.random.multivariate_normal(np.zeros(dim), cov, N)) % 1 
     # % 1: wrap to unit cube 
else: 
    data = np.random.uniform(size=(N,dim)) 
clumpiness(data) 
ask = data if nask == N else random.sample(data, sample) 
t = time.time() 

#............................................................................... 
datatree = KDTree(data, leafsize=leafsize) # build the tree 
print "%.1f sec to build KDtree of %d points" % (time.time() - t, N) 

t = time.time() 
distances, ix = datatree.query(ask, k=nnear+1, eps=eps) 
print "%.1f sec to query %d points" % (time.time() - t, nask) 

distances = distances[:,1:] # [:,0] is all 0, point to itself 
avdist = distances.mean(axis=0) 
maxdist = distances.max(axis=0) 
print "distances to %d nearest: av" % nnear, avdist, "max", maxdist 

# kdtree: dim=5 N=100000 nask=100000 nnear=2 rnormal=0.9 leafsize=10 eps=1 
# clumpiness: 42847 of 10^5 data bins are empty av 1 max 21 
# 0.4 sec to build KDtree of 100000 points 
# 10.1 sec to query 100000 points 
# distances to 2 nearest: av [ 0.07 0.08] max [ 0.15 0.18] 

# kdtree: dim=5 N=500000 nask=500000 nnear=2 rnormal=0.9 leafsize=10 eps=1 
# clumpiness: 2562 of 10^5 data bins are empty av 5 max 80 
# 2.5 sec to build KDtree of 500000 points 
# 60.1 sec to query 500000 points 
# distances to 2 nearest: av [ 0.05 0.06] max [ 0.13 0.13] 
# run: 17 Dec 2010 15:23 mac 10.4.11 ppc 
6

Vous pourriez aimer essayez mon projet de recherche appelé K-tree. Il s'adapte bien aux grandes entrées par rapport aux k-means et forme une hiérarchie de clusters. Le compromis est qu'il produit des grappes avec une distorsion plus élevée. Il a un temps d'exécution moyen de O (n log n) et le pire cas de O (n ** 2) qui ne se produit que si vous avez une topologie bizarre. Plus de détails de l'analyse de complexité sont dans mon Masters thesis. Je l'ai utilisé avec des données de texte de très haute dimension et n'ai eu aucun problème. Parfois, de mauvaises divisions peuvent se produire dans l'arborescence, où toutes les données vont d'un côté (cluster). Le tronc dans SVN traite cela différemment que la version actuelle. Il divise aléatoirement les données s'il y a un mauvais partage. La méthode précédente peut forcer l'arbre à devenir trop profond s'il y a de mauvaises divisions.

+0

K-signifie échelles avec 'O (nki)', alors êtes-vous sûr que votre approche est réellement supérieure? –

+0

Il semble également étroitement lié à k-means Bisecting (qui devrait fonctionner dans 'O (ni)', c'est-à-dire échelle mieux à grand 'k'). –

5

Mettre les données dans un index comme un R*-tree (Wikipedia), alors vous pouvez exécuter de nombreux algorithmes de regroupement à base de densité (comme DBSCAN (Wikipedia) ou OPTICS (Wikipedia)) dans O(n log n).

Density-based clustering (Wikipedia) semble être précisément ce que vous voulez (« pas trop loin à part »)

+1

R/R +/R * -Tree et KD-tree semblent être très similaires. Si vous implémentez la structure de données vous-même, un KD-Tree peut être beaucoup plus facile. OTOH, un R-Tree est bon pour ajouter des données successivement, car il est auto-équilibré, alors que le KD-Tree peut devenir inefficace s'il n'est pas complètement recalculé après l'ajout de nouvelles données. Voir [cette question SO] (http://stackoverflow.com/questions/4326332/could-anyone-tell-me-whats-the-difference-between-kd-tree-and-r-tree). – akraf

3

Les gens ont l'impression que k-means est lent, mais la lenteur est vraiment seulement un problème pour l'algorithme EM (Lloyd). Les méthodes de gradient stochastique pour les moyennes k sont plus rapides que celles des EM (voir www.eecs.tufts.edu/~dsculley/papers/fastkmeans.pdf).

Une mise en œuvre est ici: http://code.google.com/p/sofia-ml/wiki/SofiaKMeans

Questions connexes