Come posso determinare k quando utilizzo il clustering k-mean?


142

Ho studiato il clustering di k-mean e una cosa che non è chiara è come si sceglie il valore di k. È solo una questione di tentativi ed errori, o c'è dell'altro?


34
Ah ah ... Questa è davvero la domanda (su k-mean).
mjv,

puoi condividere il codice per la funzione L (log verosimiglianza)? Dato un centro in X, Y e punti in (x (i = 1,2,3,4, ..., n), y (i = 1,2,3,4, .., n)), come ottengo L?

7
un link all'articolo di Wikipedia sull'argomento: en.wikipedia.org/wiki/…
Amro

11
Ho risposto a una Q simile con una mezza dozzina di metodi (usando R) qui: stackoverflow.com/a/15376462/1036500
Ben

Risposte:


142

È possibile massimizzare il criterio di informazione bayesiana (BIC):

BIC(C | X) = L(X | C) - (p / 2) * log n

dove L(X | C)è la probabilità di log del set di dati in Xbase al modello C, pè il numero di parametri nel modello Ced nè il numero di punti nel set di dati. Vedi "X-significa: estendere i mezzi K con una stima efficiente del numero di cluster" di Dan Pelleg e Andrew Moore in ICML 2000.

Un altro approccio è iniziare con un valore elevato per ke continuare a rimuovere i centroidi (riducendo k) fino a quando non riduce più la lunghezza della descrizione. Vedi "Principio MDL per una solida quantizzazione vettoriale" di Horst Bischof, Ales Leonardis e Alexander Selb in Pattern Analysis and Applications vol. 2, p. 59-72, 1999.

Infine, puoi iniziare con un cluster, quindi continuare a dividere i cluster fino a quando i punti assegnati a ciascun cluster non hanno una distribuzione gaussiana. In "Imparare i k in k -means" (NIPS 2003), Greg Hamerly e Charles Elkan mostrano alcune prove che questo funziona meglio di BIC e che BIC non penalizza abbastanza fortemente la complessità del modello.


Bella risposta! Per X-Means, sai se il punteggio BIC complessivo n: = k * 2 (k cluster, ogni cluster modellato da gaussiano con parametri media / varianza). Inoltre, se si determina il BIC "parent"> "2 children", si dividerà di nuovo quel cluster nella prossima iterazione?
Budric,

2
@Budric, queste dovrebbero probabilmente essere domande separate, e forse su stats.stackexchange.com.
Vebjorn Ljosa,

37

Fondamentalmente, si desidera trovare un equilibrio tra due variabili: il numero di cluster ( k ) e la varianza media dei cluster. Vuoi ridurre al minimo il primo riducendo al minimo anche il secondo. Naturalmente, all'aumentare del numero di cluster, la varianza media diminuisce (fino al caso banale di k = n e varianza = 0).

Come sempre nell'analisi dei dati, non esiste un vero approccio che funzioni meglio di tutti gli altri in tutti i casi. Alla fine, devi usare il tuo miglior giudizio. Per questo, aiuta a tracciare il numero di cluster rispetto alla varianza media (il che presuppone che tu abbia già eseguito l'algoritmo per diversi valori di k ). Quindi è possibile utilizzare il numero di cluster al ginocchio della curva.


24

Sì, puoi trovare il miglior numero di cluster usando il metodo Elbow, ma ho trovato problematico trovare il valore dei cluster dal grafico del gomito usando lo script. Puoi osservare il grafico del gomito e trovare tu stesso il punto del gomito, ma è stato molto lavoro trovarlo dalla sceneggiatura.

Quindi un'altra opzione è quella di utilizzare il metodo Silhouette per trovarlo. Il risultato di Silhouette è completamente conforme al risultato del metodo Elbow in R.

Ecco cosa ho fatto.

#Dataset for Clustering
n = 150
g = 6 
set.seed(g)
d <- data.frame(x = unlist(lapply(1:g, function(i) rnorm(n/g, runif(1)*i^2))), 
                y = unlist(lapply(1:g, function(i) rnorm(n/g, runif(1)*i^2))))
mydata<-d
#Plot 3X2 plots
attach(mtcars)
par(mfrow=c(3,2))

#Plot the original dataset
plot(mydata$x,mydata$y,main="Original Dataset")

#Scree plot to deterine the number of clusters
wss <- (nrow(mydata)-1)*sum(apply(mydata,2,var))
  for (i in 2:15) {
    wss[i] <- sum(kmeans(mydata,centers=i)$withinss)
}   
plot(1:15, wss, type="b", xlab="Number of Clusters",ylab="Within groups sum of squares")

# Ward Hierarchical Clustering
d <- dist(mydata, method = "euclidean") # distance matrix
fit <- hclust(d, method="ward") 
plot(fit) # display dendogram
groups <- cutree(fit, k=5) # cut tree into 5 clusters
# draw dendogram with red borders around the 5 clusters 
rect.hclust(fit, k=5, border="red")

#Silhouette analysis for determining the number of clusters
library(fpc)
asw <- numeric(20)
for (k in 2:20)
  asw[[k]] <- pam(mydata, k) $ silinfo $ avg.width
k.best <- which.max(asw)

cat("silhouette-optimal number of clusters:", k.best, "\n")
plot(pam(d, k.best))

# K-Means Cluster Analysis
fit <- kmeans(mydata,k.best)
mydata 
# get cluster means 
aggregate(mydata,by=list(fit$cluster),FUN=mean)
# append cluster assignment
mydata <- data.frame(mydata, clusterid=fit$cluster)
plot(mydata$x,mydata$y, col = fit$cluster, main="K-means Clustering results")

Spero che sia d'aiuto!!


2
Basta aggiungere un link al tutorial di Silhouette Analysis per gli utenti di Python scikit-learn.org/stable/auto_examples/cluster/…
Chaitanya Shivade

10

Potrebbe essere un principiante come me in cerca di un esempio di codice. le informazioni per silhouette_score sono disponibili qui.

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

range_n_clusters = [2, 3, 4]            # clusters range you want to select
dataToFit = [[12,23],[112,46],[45,23]]  # sample data
best_clusters = 0                       # best cluster number which you will get
previous_silh_avg = 0.0

for n_clusters in range_n_clusters:
    clusterer = KMeans(n_clusters=n_clusters)
    cluster_labels = clusterer.fit_predict(dataToFit)
    silhouette_avg = silhouette_score(dataToFit, cluster_labels)
    if silhouette_avg > previous_silh_avg:
        previous_silh_avg = silhouette_avg
        best_clusters = n_clusters

# Final Kmeans for best_clusters
kmeans = KMeans(n_clusters=best_clusters, random_state=0).fit(dataToFit)

9

Guarda questo articolo, "Imparare la k in k-significa" di Greg Hamerly, Charles Elkan. Usa un test gaussiano per determinare il giusto numero di cluster. Inoltre, gli autori sostengono che questo metodo è migliore del BIC che è menzionato nella risposta accettata.


7

C'è qualcosa chiamato Regola del pollice. Dice che il numero di cluster può essere calcolato da

k = (n/2)^0.5

dove n è il numero totale di elementi dal tuo campione. Puoi verificare la veridicità di queste informazioni sul seguente documento:

http://www.ijarcsms.com/docs/paper/volume1/issue6/V1I6-0015.pdf

C'è anche un altro metodo chiamato G-medie, in cui la tua distribuzione segue una distribuzione gaussiana o una distribuzione normale. Consiste nell'aumentare k fino a quando tutti i tuoi gruppi k seguono una distribuzione gaussiana. Richiede molte statistiche ma può essere fatto. Ecco la fonte:

http://papers.nips.cc/paper/2526-learning-the-k-in-k-means.pdf

Spero che aiuti!


3

Innanzitutto crea un albero di spanning minimo dei tuoi dati. La rimozione dei bordi più costosi di K-1 divide l'albero in cluster K, in
modo da poter costruire l'MST una volta, osservare le spaziature / metriche del cluster per vari K e prendere il ginocchio della curva.

Funziona solo con Single-linkage_clustering , ma per questo è semplice e veloce. Inoltre, gli MST creano una buona visuale.
Vedere ad esempio il diagramma MST nel software di visualizzazione stats.stackexchange per il clustering .


3

Sono sorpreso che nessuno abbia menzionato questo eccellente articolo: http://www.ee.columbia.edu/~dpwe/papers/PhamDN05-kmeans.pdf

Dopo aver seguito diversi altri suggerimenti, mi sono finalmente imbattuto in questo articolo mentre leggevo questo blog: https://datasciencelab.wordpress.com/2014/01/21/selection-of-k-in-k-means-clustering-reloaded/

Dopo di che l'ho implementato in Scala, un'implementazione che per i miei casi d'uso fornisce risultati davvero buoni. Ecco il codice:

import breeze.linalg.DenseVector
import Kmeans.{Features, _}
import nak.cluster.{Kmeans => NakKmeans}

import scala.collection.immutable.IndexedSeq
import scala.collection.mutable.ListBuffer

/*
https://datasciencelab.wordpress.com/2014/01/21/selection-of-k-in-k-means-clustering-reloaded/
 */
class Kmeans(features: Features) {
  def fkAlphaDispersionCentroids(k: Int, dispersionOfKMinus1: Double = 0d, alphaOfKMinus1: Double = 1d): (Double, Double, Double, Features) = {
    if (1 == k || 0d == dispersionOfKMinus1) (1d, 1d, 1d, Vector.empty)
    else {
      val featureDimensions = features.headOption.map(_.size).getOrElse(1)
      val (dispersion, centroids: Features) = new NakKmeans[DenseVector[Double]](features).run(k)
      val alpha =
        if (2 == k) 1d - 3d / (4d * featureDimensions)
        else alphaOfKMinus1 + (1d - alphaOfKMinus1) / 6d
      val fk = dispersion / (alpha * dispersionOfKMinus1)
      (fk, alpha, dispersion, centroids)
    }
  }

  def fks(maxK: Int = maxK): List[(Double, Double, Double, Features)] = {
    val fadcs = ListBuffer[(Double, Double, Double, Features)](fkAlphaDispersionCentroids(1))
    var k = 2
    while (k <= maxK) {
      val (fk, alpha, dispersion, features) = fadcs(k - 2)
      fadcs += fkAlphaDispersionCentroids(k, dispersion, alpha)
      k += 1
    }
    fadcs.toList
  }

  def detK: (Double, Features) = {
    val vals = fks().minBy(_._1)
    (vals._3, vals._4)
  }
}

object Kmeans {
  val maxK = 10
  type Features = IndexedSeq[DenseVector[Double]]
}

Implmented in scala 2.11.7 con brezza 0.12 e nak 1.3
eirirlar

Ciao @eirirlar Sto cercando di implementare lo stesso codice con Python, ma non sono riuscito a seguire il codice nel sito Web. Vedi il mio post: stackoverflow.com/questions/36729826/python-k-means-clustering
piccolo

@ImranRashid Siamo spiacenti, ho provato solo con 2 dimensioni e non sono un esperto di Python.
Eirirlar,

3

Se si utilizza MATLAB, qualsiasi versione a partire dal 2013b, è possibile utilizzare la funzione evalclustersper scoprire quale dovrebbe kessere ottimale per un determinato set di dati.

Questa funzione consente di scegliere tra 3 algoritmi di clustering - kmeans, linkagee gmdistribution.

Essa consente inoltre di scegliere tra i criteri di valutazione 4 raggruppamento - CalinskiHarabasz, DaviesBouldin, gape silhouette.


3

Se non conosci i numeri dei cluster k da fornire come parametro a k-significa quindi ci sono quattro modi per trovarlo automaticamente:

  • Algortitmo G-significa: scopre automaticamente il numero di cluster usando un test statistico per decidere se dividere un centro k-mean in due. Questo algoritmo adotta un approccio gerarchico per rilevare il numero di cluster, basato su un test statistico per l'ipotesi che un sottoinsieme di dati segua una distribuzione gaussiana (funzione continua che approssima l'esatta distribuzione binomiale degli eventi) e, in caso contrario, divide il cluster . Inizia con un piccolo numero di centri, ad esempio un solo cluster (k = 1), quindi l'algoritmo lo divide in due centri (k = 2) e divide nuovamente ciascuno di questi due centri (k = 4), con quattro centri in totale. Se G-mean non accetta questi quattro centri, la risposta è il passaggio precedente: due centri in questo caso (k = 2). Questo è il numero di cluster in cui verrà diviso il set di dati. G-mean è molto utile quando non hai una stima del numero di cluster che otterrai dopo aver raggruppato le tue istanze. Si noti che una scelta scomoda per il parametro "k" potrebbe dare risultati errati. Viene chiamata la versione parallela di g-meanp-significa . Sorgenti dei mezzi G: fonte 1 fonte 2 fonte 3

  • x-significa : un nuovo algoritmo che ricerca in modo efficiente lo spazio delle posizioni dei cluster e il numero di cluster per ottimizzare il criterio Bayesian Information Criterion (BIC) o Akaike Information Criterion (AIC). Questa versione di k-mean trova il numero k e accelera anche k-mean.

  • K-medie online o Streaming k-medie: consente di eseguire k-medie scansionando tutti i dati una volta e trova automaticamente il numero ottimale di k. Spark lo implementa.

  • Algoritmo MeanShift : è una tecnica di clustering non parametrico che non richiede una conoscenza preliminare del numero di cluster e non limita la forma dei cluster. Il clustering a spostamento medio mira a scoprire "blob" in una densità uniforme di campioni. È un algoritmo basato su centroidi, che funziona aggiornando i candidati per i centroidi in modo che siano la media dei punti all'interno di una data regione. Questi candidati vengono quindi filtrati in una fase di post-elaborazione per eliminare i quasi duplicati per formare il set finale di centroidi. Fonti: fonte1 , fonte2 , fonte3


2

Ho usato la soluzione che ho trovato qui: http://efavdb.com/mean-shift/ e ha funzionato molto bene per me:

import numpy as np
from sklearn.cluster import MeanShift, estimate_bandwidth
from sklearn.datasets.samples_generator import make_blobs
import matplotlib.pyplot as plt
from itertools import cycle
from PIL import Image

#%% Generate sample data
centers = [[1, 1], [-.75, -1], [1, -1], [-3, 2]]
X, _ = make_blobs(n_samples=10000, centers=centers, cluster_std=0.6)

#%% Compute clustering with MeanShift

# The bandwidth can be automatically estimated
bandwidth = estimate_bandwidth(X, quantile=.1,
                               n_samples=500)
ms = MeanShift(bandwidth=bandwidth, bin_seeding=True)
ms.fit(X)
labels = ms.labels_
cluster_centers = ms.cluster_centers_

n_clusters_ = labels.max()+1

#%% Plot result
plt.figure(1)
plt.clf()

colors = cycle('bgrcmykbgrcmykbgrcmykbgrcmyk')
for k, col in zip(range(n_clusters_), colors):
    my_members = labels == k
    cluster_center = cluster_centers[k]
    plt.plot(X[my_members, 0], X[my_members, 1], col + '.')
    plt.plot(cluster_center[0], cluster_center[1],
             'o', markerfacecolor=col,
             markeredgecolor='k', markersize=14)
plt.title('Estimated number of clusters: %d' % n_clusters_)
plt.show()

inserisci qui la descrizione dell'immagine



1

Supponendo di avere una matrice di dati chiamati DATA, è possibile eseguire il partizionamento attorno ai medoidi con una stima del numero di cluster (mediante analisi di silhouette) in questo modo:

library(fpc)
maxk <- 20  # arbitrary here, you can set this to whatever you like
estimatedK <- pamk(dist(DATA), krange=1:maxk)$nc

1

Una possibile risposta è usare l'algoritmo meta-euristico come l'algoritmo genetico per trovare k. È semplice. puoi usare K casuale (in un certo intervallo) e valutare la funzione di adattamento dell'algoritmo genetico con alcune misurazioni come Silhouette e Trova la migliore base K sulla funzione di adattamento.

https://en.wikipedia.org/wiki/Silhouette_(clustering)


1
km=[]
for i in range(num_data.shape[1]):
    kmeans = KMeans(n_clusters=ncluster[i])#we take number of cluster bandwidth theory
    ndata=num_data[[i]].dropna()
    ndata['labels']=kmeans.fit_predict(ndata.values)
    cluster=ndata
    co=cluster.groupby(['labels'])[cluster.columns[0]].count()#count for frequency
    me=cluster.groupby(['labels'])[cluster.columns[0]].median()#median
    ma=cluster.groupby(['labels'])[cluster.columns[0]].max()#Maximum
    mi=cluster.groupby(['labels'])[cluster.columns[0]].min()#Minimum
    stat=pd.concat([mi,ma,me,co],axis=1)#Add all column
    stat['variable']=stat.columns[1]#Column name change
    stat.columns=['Minimum','Maximum','Median','count','variable']
    l=[]
    for j in range(ncluster[i]):
        n=[mi.loc[j],ma.loc[j]] 
        l.append(n)

    stat['Class']=l
    stat=stat.sort(['Minimum'])
    stat=stat[['variable','Class','Minimum','Maximum','Median','count']]
    if missing_num.iloc[i]>0:
        stat.loc[ncluster[i]]=0
        if stat.iloc[ncluster[i],5]==0:
            stat.iloc[ncluster[i],5]=missing_num.iloc[i]
            stat.iloc[ncluster[i],0]=stat.iloc[0,0]
    stat['Percentage']=(stat[[5]])*100/count_row#Freq PERCENTAGE
    stat['Cumulative Percentage']=stat['Percentage'].cumsum()
    km.append(stat)
cluster=pd.concat(km,axis=0)## see documentation for more info
cluster=cluster.round({'Minimum': 2, 'Maximum': 2,'Median':2,'Percentage':2,'Cumulative Percentage':2})

selezioni i dati e aggiungi la libreria e copi km = [] in Percentuale ': 2}) per ultimo ed esegui il tuo pitone e vedi
sumit

Benvenuto in Stack Overflow! Sebbene questo codice possa aiutare a risolvere il problema, non spiega perché e / o come risponde alla domanda. Fornire questo contesto aggiuntivo migliorerebbe significativamente il suo valore educativo a lungo termine. Si prega di modificare la risposta di aggiungere spiegazioni, tra cui quello che si applicano le limitazioni e le assunzioni.
Toby Speight,

1

Un altro approccio consiste nell'utilizzare le mappe auto-organizzate (SOP) per trovare il numero ottimale di cluster. La SOM (Self-Organizing Map) è una metodologia di rete neurale non supervisionata, che richiede solo l'input utilizzato per il clustering per la risoluzione dei problemi. Questo approccio è stato utilizzato in un documento sulla segmentazione dei clienti.

Il riferimento del documento è

Abdellah Amine et al., Modello di segmentazione della clientela nell'e-commerce utilizzando tecniche di clustering e modello LRFM: il caso dei negozi online in Marocco, Accademia mondiale delle scienze, ingegneria e tecnologia International Journal of Computer and Information Engineering Vol: 9, No: 8 , 2015, 1999-2010


0

Ciao, lo spiegherò in modo semplice e diretto, mi piace determinare i cluster usando la libreria "NbClust".

Ora, come usare la funzione 'NbClust' per determinare il giusto numero di cluster: puoi controllare il progetto reale in Github con dati e cluster effettivi - L'estensione a questo algoritmo 'kmeans' è stata eseguita anche usando il giusto numero di 'centri'.

Link al progetto Github: https://github.com/RutvijBhutaiya/Thailand-Customer-Engagement-Facebook


Invece di aggiungere il link github, puoi aggiungere un paio di righe chiave di codice che possono aiutare gli altri anche se il tuo codice non è raggiungibile?
Giulio Caccin,

0

Puoi scegliere il numero di cluster ispezionando visivamente i tuoi punti dati, ma ti accorgerai presto che c'è molta ambiguità in questo processo per tutti tranne che per i set di dati più semplici. Questo non è sempre negativo, perché stai imparando senza supervisione e c'è una soggettività intrinseca nel processo di etichettatura. Qui, avere un'esperienza precedente con quel particolare problema o qualcosa di simile ti aiuterà a scegliere il giusto valore.

Se vuoi un suggerimento sul numero di cluster che dovresti usare, puoi applicare il metodo Elbow:

Prima di tutto, calcola la somma dell'errore al quadrato (SSE) per alcuni valori di k (ad esempio 2, 4, 6, 8, ecc.). Il SSE è definito come la somma della distanza quadrata tra ciascun membro del cluster e il suo centroide. Matematicamente:

SSE = ΣKi = 1Σx∈cidist (x, CI) 2

Se tracciate k contro SSE, vedrete che l'errore diminuisce man mano che k diventa più grande; questo perché quando il numero di cluster aumenta, dovrebbero essere più piccoli, quindi anche la distorsione è minore. L'idea del metodo del gomito è quella di scegliere la k in cui l'SSE diminuisce bruscamente. Questo produce un "effetto gomito" nel grafico, come puoi vedere nella figura seguente:

inserisci qui la descrizione dell'immagine

In questo caso, k = 6 è il valore selezionato dal metodo Elbow. Tieni presente che il metodo Elbow è euristico e, in quanto tale, può o meno funzionare bene nel tuo caso particolare. A volte, ci sono più di un gomito o nessun gomito. In quelle situazioni di solito si finisce per calcolare il miglior k valutando il rendimento di k-mean nel contesto del particolare problema di cluster che si sta tentando di risolvere.


0

Ho lavorato su un pacchetto Python in ginocchio (algoritmo Kneedle). Trova dinamicamente il numero di cluster come il punto in cui la curva inizia ad appiattirsi .. Dato un insieme di valori xey, il ginocchio restituirà il punto del ginocchio della funzione. La punta del ginocchio è il punto di massima curvatura. Ecco il codice di esempio.

y = [7.342,1301373073857, 6.881,7109460930769, 6.531,1657905495022,
6.356,2255554679778, 6.209,8382535595829, 6.094,9052166741121, 5.980,0191582610196, 5.880,1869867848218, 5.779,8957906367368, 5.691,1879324562778, 5.617,5153566271356, 5.532,2613232619951, 5.467,352265375117, 5.395,4493783888756, 5.345,3459908298091, 5.290,6769823693812, 5.243,5271656371888, 5.207,2501206569532, 5.164,9617535255456]

x = intervallo (1, len (y) +1)

da import in ginocchio KneeLocator kn = KneeLocator (x, y, curve = 'convex', direction = 'decreasing')

stampare (kn.knee)


Per favore aggiungi qualche spiegazione alla tua risposta in modo che altri possano imparare da essa
Nico Haase,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.