¿Cómo determino k cuando uso el agrupamiento k-means?

142

He estado estudiando sobre el agrupamiento k-means , y una cosa que no está clara es cómo elegir el valor de k. ¿Es solo una cuestión de prueba y error, o hay más?

Jason Baker
fuente
34
Ah ah ... Esa es realmente la pregunta (sobre k-mean).
mjv el
¿Puedes compartir el código para la función L (probabilidad de registro)? Dado un centro en X, Y y puntos en (x (i = 1,2,3,4, ..., n), y (i = 1,2,3,4, .., n)), cómo ¿obtengo L?
77
un enlace al artículo de Wikipedia sobre el tema: en.wikipedia.org/wiki/…
Amro
11
He respondido una pregunta similar con media docena de métodos (usando R) aquí: stackoverflow.com/a/15376462/1036500
Ben

Respuestas:

142

Puede maximizar el Criterio de información bayesiano (BIC):

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

donde L(X | C)es la probabilidad de registro del conjunto de datos Xsegún el modelo C, pes el número de parámetros en el modelo Cy nes el número de puntos en el conjunto de datos. Ver "X-significa: extender K -significa con una estimación eficiente del número de grupos" por Dan Pelleg y Andrew Moore en ICML 2000.

Otro enfoque es comenzar con un valor grande para ky seguir eliminando los centroides (reduciendo k) hasta que ya no reduzca la longitud de la descripción. Ver "Principio MDL para la cuantificación robusta de vectores" por Horst Bischof, Ales Leonardis y Alexander Selb en Pattern Analysis and Applications vol. 2, p. 59-72, 1999.

Finalmente, puede comenzar con un grupo, luego seguir dividiendo grupos hasta que los puntos asignados a cada grupo tengan una distribución gaussiana. En "Learning the k in k- significa" (NIPS 2003), Greg Hamerly y Charles Elkan muestran cierta evidencia de que esto funciona mejor que BIC, y que BIC no penaliza la complejidad del modelo con suficiente fuerza.

Vebjorn Ljosa
fuente
¡Gran respuesta! Para X-Means, ¿sabe si la puntuación general de BIC n: = k * 2 (k grupos, cada grupo modelado por Gauss con parámetros de media / varianza). Además, si determina el BIC "padre"> BIC "2 hijos", ¿volvería a dividir ese clúster en la próxima iteración?
Budric
2
@Budric, estas probablemente deberían ser preguntas separadas, y tal vez en stats.stackexchange.com.
Vebjorn Ljosa
37

Básicamente, desea encontrar un equilibrio entre dos variables: el número de grupos ( k ) y la varianza promedio de los grupos. Desea minimizar el primero y al mismo tiempo minimizar el segundo. Por supuesto, a medida que aumenta el número de grupos, la varianza promedio disminuye (hasta el caso trivial de k = ny varianza = 0).

Como siempre en el análisis de datos, no existe un enfoque único que funcione mejor que todos los demás en todos los casos. Al final, debes usar tu propio mejor juicio. Para eso, ayuda a trazar el número de clústeres contra la varianza promedio (lo que supone que ya ha ejecutado el algoritmo para varios valores de k ). Luego puede usar el número de grupos en la rodilla de la curva.

Jan Krüger
fuente
24

Sí, puede encontrar la mejor cantidad de clústeres usando el método Elbow, pero me resultó problemático encontrar el valor de los clústeres del gráfico de codo usando el script. Puede observar el gráfico del codo y encontrar el punto del codo usted mismo, pero fue mucho trabajo encontrarlo desde el script.

Entonces, otra opción es utilizar el Método de silueta para encontrarlo. El resultado de Silhouette cumple completamente con el resultado del método Elbow en R.

Esto es lo que hice.

#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")

¡¡Espero eso ayude!!

Udeep Shakya
fuente
2
Simplemente agregue un enlace al tutorial de Silhouette Analysis para usuarios de python scikit-learn.org/stable/auto_examples/cluster/…
Chaitanya Shivade
10

Puede ser alguien principiante como yo buscando código de ejemplo. la información para silhouette_score está disponible aquí.

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)
bhargav patel
fuente
9

Mire este documento, "Aprender la k en k-significa" por Greg Hamerly, Charles Elkan. Utiliza una prueba gaussiana para determinar el número correcto de grupos. Además, los autores afirman que este método es mejor que BIC, que se menciona en la respuesta aceptada.

Autónomo
fuente
7

Hay algo llamado regla de oro. Dice que el número de grupos puede calcularse por

k = (n/2)^0.5

donde n es el número total de elementos de su muestra. Puede verificar la veracidad de esta información en el siguiente documento:

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

También hay otro método llamado G-means, donde su distribución sigue una Distribución Gaussiana o Distribución Normal. Consiste en aumentar k hasta que todos tus k grupos sigan una distribución gaussiana. Requiere muchas estadísticas pero se puede hacer. Aquí está la fuente:

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

¡Espero que esto ayude!

Arthur Busqueiro
fuente
3

Primero construya un árbol de expansión mínimo de sus datos. La eliminación de los bordes más caros de K-1 divide el árbol en grupos de K,
por lo que puede construir el MST una vez, ver los espacios / métricas de grupo para varias K y tomar la rodilla de la curva.

Esto funciona solo para Single-linkage_clustering , pero para eso es rápido y fácil. Además, los MST hacen buenas imágenes.
Consulte, por ejemplo, el diagrama MST en el software de visualización stats.stackexchange para la agrupación .

denis
fuente
3

Me sorprende que nadie haya mencionado este excelente artículo: http://www.ee.columbia.edu/~dpwe/papers/PhamDN05-kmeans.pdf

Después de seguir varias otras sugerencias, finalmente encontré este artículo mientras leía este blog: https://datasciencelab.wordpress.com/2014/01/21/selection-of-k-in-k-means-clustering-reloaded/

Después de eso lo implementé en Scala, una implementación que para mis casos de uso proporciona resultados realmente buenos. Aquí está el código:

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]]
}
Eirirlar
fuente
Implicado en scala 2.11.7 con brisa 0.12 y nak 1.3
eirirlar
Hola @eirirlar, estoy tratando de implementar el mismo código con Python, pero no pude seguir el código en el sitio web. Vea mi publicación: stackoverflow.com/questions/36729826/python-k-means-clustering
piccolo el
@ImranRashid Lo siento, solo probé con 2 dimensiones, y no soy un experto en Python.
Eirirlar
3

Si usa MATLAB, cualquier versión desde 2013b, es decir, puede hacer uso de la función evalclusterspara averiguar cuál debería ser el óptimo kpara un conjunto de datos determinado.

Esta función le permite elegir entre 3 algoritmos de agrupamiento kmeans, linkagey gmdistribution.

También le permite elegir de entre los criterios de evaluación 4 clustering - CalinskiHarabasz, DaviesBouldin, gapy silhouette.

Kristada673
fuente
3

Si no conoce los números de los grupos k para proporcionar como parámetro a k-means, hay cuatro formas de encontrarlo automáticamente:

  • Algrtitmo G-significa: descubre el número de grupos automáticamente usando una prueba estadística para decidir si dividir un centro k-medias en dos. Este algoritmo adopta un enfoque jerárquico para detectar el número de grupos, basado en una prueba estadística para la hipótesis de que un subconjunto de datos sigue una distribución gaussiana (función continua que se aproxima a la distribución binomial exacta de eventos), y si no divide el grupo . Comienza con un pequeño número de centros, digamos solo un grupo (k = 1), luego el algoritmo lo divide en dos centros (k = 2) y divide cada uno de estos dos centros nuevamente (k = 4), teniendo cuatro centros en total. Si G-means no acepta estos cuatro centros, entonces la respuesta es el paso anterior: dos centros en este caso (k = 2). Este es el número de clústeres en los que se dividirá su conjunto de datos. G-means es muy útil cuando no tiene una estimación del número de clústeres que obtendrá después de agrupar sus instancias. Tenga en cuenta que una elección inconveniente para el parámetro "k" puede dar resultados incorrectos. La versión paralela de g-means se llamap-significa . G-significa fuentes: fuente 1 fuente 2 fuente 3

  • x-significa : un nuevo algoritmo que busca eficientemente el espacio de las ubicaciones de los conglomerados y la cantidad de conglomerados para optimizar el criterio de información bayesiano (BIC) o la medida del criterio de información de Akaike (AIC). Esta versión de k-means encuentra el número k y también acelera k-means.

  • K-means en línea o Streaming k-means: permite ejecutar k-means al escanear todos los datos una vez y encuentra automáticamente el número óptimo de k. Spark lo implementa.

  • Algoritmo MeanShift : es una técnica de agrupación no paramétrica que no requiere un conocimiento previo del número de agrupaciones y no limita la forma de las agrupaciones. La agrupación de turnos medios tiene como objetivo descubrir "manchas" en una densidad uniforme de muestras. Es un algoritmo basado en centroide, que funciona actualizando candidatos para que los centroides sean la media de los puntos dentro de una región determinada. Luego, estos candidatos se filtran en una etapa de procesamiento posterior para eliminar casi duplicados para formar el conjunto final de centroides. Fuentes: Source1 , source2 , source3

curiosidad
fuente
2

Utilicé la solución que encontré aquí: http://efavdb.com/mean-shift/ y funcionó muy bien para mí:

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

ingrese la descripción de la imagen aquí

snoob dogg
fuente
1

Suponiendo que tiene una matriz de datos llamada DATA, puede realizar particiones alrededor de medoides con una estimación del número de grupos (por análisis de silueta) de esta manera:

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

Una posible respuesta es usar Algoritmo Metaheurístico como Algoritmo Genético para encontrar k. Así de simple. puede usar K al azar (en algún rango) y evaluar la función de ajuste del Algoritmo genético con alguna medición como Silhouette And Find best K base on fit function.

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

Masoud
fuente
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})
sumitir
fuente
selecciona datos y agrega la biblioteca y copia km = [] al Porcentaje ': 2}) último y ejecuta su python y ve
sumita el
¡Bienvenido a Stack Overflow! Aunque este código puede ayudar a resolver el problema, no explica por qué y / o cómo responde la pregunta. Proporcionar este contexto adicional mejoraría significativamente su valor educativo a largo plazo. Por favor, editar su respuesta para agregar explicación, incluyendo lo que se aplican limitaciones y supuestos.
Toby Speight
1

Otro enfoque es utilizar los Mapas autoorganizados (SOP) para encontrar la cantidad óptima de clústeres. El SOM (Mapa de autoorganización) es una metodología de red neuronal no supervisada, que solo necesita la entrada utilizada para la agrupación para la resolución de problemas. Este enfoque se utiliza en un documento sobre la segmentación de clientes.

La referencia del artículo es

Abdellah Amine et al., Modelo de segmentación de clientes en comercio electrónico utilizando técnicas de agrupamiento y modelo LRFM: el caso de las tiendas en línea en Marruecos, Academia Mundial de Ciencia, Ingeniería y Tecnología Revista Internacional de Ingeniería Informática e Informática Vol: 9, No: 8 , 2015, 1999 - 2010

boyaronur
fuente
0

Hola, lo haré simple y directo de explicar, me gusta determinar los clústeres utilizando la biblioteca 'NbClust'.

Ahora, cómo usar la función 'NbClust' para determinar el número correcto de clústeres: puede verificar el proyecto real en Github con datos y clústeres reales: la extensión a este algoritmo 'kmeans' también se realizó utilizando el número correcto de 'centros'.

Enlace del proyecto Github: https://github.com/RutvijBhutaiya/Thailand-Customer-Engagement-Facebook

Rutvij
fuente
En lugar de agregar el enlace github, ¿puede agregar un par de líneas clave de código que pueden ayudar a otros incluso si su código no es accesible?
Giulio Caccin
0

Puede elegir el número de clústeres inspeccionando visualmente sus puntos de datos, pero pronto se dará cuenta de que hay mucha ambigüedad en este proceso para todos, excepto para los conjuntos de datos más simples. Esto no siempre es malo, porque está aprendiendo sin supervisión y hay una subjetividad inherente en el proceso de etiquetado. Aquí, tener experiencia previa con ese problema en particular o algo similar lo ayudará a elegir el valor correcto.

Si desea alguna pista sobre la cantidad de grupos que debe usar, puede aplicar el método Elbow:

En primer lugar, calcule la suma del error al cuadrado (SSE) para algunos valores de k (por ejemplo, 2, 4, 6, 8, etc.). El SSE se define como la suma de la distancia al cuadrado entre cada miembro del grupo y su centroide. Matemáticamente:

SSE = ∑Ki = 1∑x∈cidist (x, ci) 2

Si traza k contra el SSE, verá que el error disminuye a medida que k aumenta; Esto se debe a que cuando aumenta el número de grupos, deberían ser más pequeños, por lo que la distorsión también es menor. La idea del método del codo es elegir la k en la cual el SSE disminuye abruptamente. Esto produce un "efecto codo" en el gráfico, como puede ver en la siguiente imagen:

ingrese la descripción de la imagen aquí

En este caso, k = 6 es el valor que ha seleccionado el método Elbow. Tenga en cuenta que el método Elbow es heurístico y, como tal, puede o no funcionar bien en su caso particular. A veces, hay más de un codo, o ningún codo. En esas situaciones, generalmente terminas calculando la mejor k evaluando qué tan bien se desempeña k-means en el contexto del problema de agrupamiento particular que estás tratando de resolver.

Faisal Shahbaz
fuente
0

Trabajé en un paquete de Python arrodillado (algoritmo Kneedle). Encuentra el número de clúster dinámicamente como el punto donde la curva comienza a aplanarse ... Dado un conjunto de valores x e y, arrodillado devolverá el punto de inflexión de la función. El punto de inflexión es el punto de máxima curvatura. Aquí está el código de muestra.

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 = rango (1, len (y) +1)

desde rodillas importación KneeLocator kn = KneeLocator (x, y, curva = 'convexo', dirección = 'decreciente')

imprimir (kn.knee)

madhuri M
fuente
Agregue alguna explicación a su respuesta para que otros puedan aprender de ella
Nico Haase