Como determino k ao usar o cluster de k-means?

142

Eu estudei sobre o agrupamento k-means , e uma coisa que não está clara é como você escolhe o valor de k. É apenas uma questão de tentativa e erro, ou há mais?

Jason Baker
fonte
34
Ah ah ... Essa é realmente a questão (sobre k-mean).
Mjv
você pode compartilhar o código da função L (probabilidade de log)? Dado um centro em X, Y e pontos em (x (i = 1,2,3,4, ..., n), y (i = 1,2,3,4, .., n)), como eu recebo L?
7
um link para o artigo da Wikipedia sobre o assunto: en.wikipedia.org/wiki/…
Amro
11
Eu respondi a um Q semelhante com meia dúzia de métodos (usando R) aqui: stackoverflow.com/a/15376462/1036500
Ben

Respostas:

142

Você pode maximizar o Critério de Informação Bayesiano (BIC):

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

onde L(X | C)é a probabilidade de log do conjunto de dados de Xacordo com o modelo C, pé o número de parâmetros no modelo Ce no número de pontos no conjunto de dados. Veja "meios X: estendendo os meios K com estimativa eficiente do número de clusters" por Dan Pelleg e Andrew Moore no ICML 2000.

Outra abordagem é começar com um grande valor ke continuar removendo os centróides (reduzindo k) até que não reduz mais o tamanho da descrição. Ver "Princípio MDL para quantização robusta de vetores" de Horst Bischof, Ales Leonardis e Alexander Selb em Pattern Analysis and Applications vol. 2, p. 59-72, 1999.

Finalmente, você pode começar com um cluster e continuar dividindo os clusters até que os pontos atribuídos a cada cluster tenham uma distribuição gaussiana. Em "Aprender a k em k -means" (PIN 2003), Greg Hamerly e Charles Elkan mostrar alguma evidência de que isso funciona melhor do que BIC, e que BIC não penalize a complexidade do modelo fortemente o suficiente.

Vebjorn Ljosa
fonte
Ótima resposta! Para X-Means, você sabe se a pontuação geral do BIC n: = k * 2 (k clusters, cada cluster modelado por Gaussian com parâmetros de média / variância). Além disso, se você determinar o BIC "pai"> "2 filhos", você dividiria esse cluster novamente na próxima iteração?
Budric
2
@Budric, essas provavelmente devem ser perguntas separadas e talvez no stats.stackexchange.com.
Vebjorn Ljosa
37

Basicamente, você deseja encontrar um equilíbrio entre duas variáveis: o número de clusters ( k ) e a variação média dos clusters. Você deseja minimizar o primeiro enquanto também minimiza o último. Obviamente, à medida que o número de clusters aumenta, a variação média diminui (até o caso trivial de k = n e variação = 0).

Como sempre na análise de dados, não existe uma abordagem verdadeira que funcione melhor do que todas as outras em todos os casos. No final, você deve usar seu próprio julgamento. Para isso, ajuda a plotar o número de clusters em relação à variação média (que pressupõe que você já executou o algoritmo para vários valores de k ). Então você pode usar o número de clusters no joelho da curva.

Jan Krüger
fonte
24

Sim, você pode encontrar o melhor número de clusters usando o método Elbow, mas achei difícil encontrar o valor de clusters no gráfico do cotovelo usando o script. Você pode observar o gráfico do cotovelo e descobrir o ponto do cotovelo, mas foi muito trabalho encontrá-lo a partir do script.

Portanto, outra opção é usar o Silhouette Method para encontrá-lo. O resultado do Silhouette é totalmente compatível com o resultado do método Elbow em R.

Aqui está o que eu fiz.

#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 que ajude!!

Udeep Shakya
fonte
2
Basta adicionar um link para o tutorial Análise da silhueta por usuários de Python scikit-learn.org/stable/auto_examples/cluster/...
Chaitanya Shivade
10

Pode ser alguém iniciante como eu procurando um exemplo de código. informações para silhouette_score estão disponíveis aqui.

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
fonte
9

Veja este artigo, "Aprendendo o k em k-means", de Greg Hamerly, Charles Elkan. Ele usa um teste gaussiano para determinar o número certo de clusters. Além disso, os autores afirmam que esse método é melhor que o BIC, mencionado na resposta aceita.

Autônomo
fonte
7

Existe algo chamado Regra de Polegar. Diz que o número de clusters pode ser calculado por

k = (n/2)^0.5

onde n é o número total de elementos da sua amostra. Você pode verificar a veracidade dessas informações no seguinte documento:

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

Há também outro método chamado G-means, em que sua distribuição segue uma distribuição gaussiana ou distribuição normal. Consiste em aumentar k até que todos os seus grupos k sigam uma distribuição gaussiana. Requer muitas estatísticas, mas pode ser feito. Aqui está a fonte:

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

Eu espero que isso ajude!

Arthur Busqueiro
fonte
3

Primeiro, crie uma árvore de abrangência mínima dos seus dados. A remoção das arestas mais caras do K-1 divide a árvore em clusters K,
para que você possa construir o MST uma vez, examinar espaçamentos / métricas de cluster para vários K e assumir o controle da curva.

Isso funciona apenas para Single-linkage_clustering , mas para isso é rápido e fácil. Além disso, os MSTs produzem bons visuais.
Veja, por exemplo, o gráfico MST no software de visualização stats.stackexchange para cluster .

denis
fonte
3

Estou surpreso que ninguém tenha mencionado este excelente artigo: http://www.ee.columbia.edu/~dpwe/papers/PhamDN05-kmeans.pdf

Depois de seguir várias outras sugestões, finalmente encontrei este artigo enquanto lia este blog: https://datasciencelab.wordpress.com/2014/01/21/selection-of-k-in-k-means-clustering-reloaded/

Depois disso, eu o implementei no Scala, uma implementação que, para meus casos de uso, fornece resultados realmente bons. Aqui está o 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
fonte
Implmented em scala 2.11.7 com brisa 0,12 e nak 1.3
eirirlar
Oi @eirirlar Estou tentando implementar o mesmo código com Python - mas não consegui seguir o código no site. Veja meu post: stackoverflow.com/questions/36729826/python-k-means-clustering
piccolo
@ImranRashid Desculpe, eu testei apenas com 2 dimensões e não sou especialista em Python.
eirirlar
3

Se você usa o MATLAB, qualquer versão desde 2013b, é possível usar a função evalclusterspara descobrir qual deve kser o melhor para um determinado conjunto de dados.

Esta função permite escolher entre três algoritmos de agrupamento - kmeans, linkagee gmdistribution.

Ele também permite que você escolha entre critérios de avaliação 4 de agrupamento - CalinskiHarabasz, DaviesBouldin, gape silhouette.

Kristada673
fonte
3

Se você não souber os números dos clusters k a fornecer como parâmetro para k-means, então existem quatro maneiras de encontrá-lo automaticamente:

  • Algortitmo G-significa: ele descobre o número de grupos automaticamente usando um teste estatístico para decidir se deve dividir um centro de K-médias em dois. Esse algoritmo adota uma abordagem hierárquica para detectar o número de clusters, com base em um teste estatístico para a hipótese de que um subconjunto de dados segue uma distribuição gaussiana (função contínua que aproxima a distribuição binomial exata de eventos) e, se não, divide o cluster . Ele começa com um pequeno número de centros, digamos, apenas um cluster (k = 1), então o algoritmo o divide em dois centros (k = 2) e divide cada um desses dois centros novamente (k = 4), tendo quatro centros em total. Se G-means não aceitar esses quatro centros, então a resposta é a etapa anterior: neste caso, dois centros (k = 2). Esse é o número de clusters nos quais o conjunto de dados será dividido. G-means é muito útil quando você não tem uma estimativa do número de clusters que receberá após o agrupamento de suas instâncias. Observe que uma escolha inconveniente para o parâmetro "k" pode gerar resultados incorretos. A versão paralela do g-means é chamadap-significa . Fontes G-significa: fonte 1 fonte 2 fonte 3

  • x-means : um novo algoritmo que pesquisa com eficiência o espaço das localizações dos clusters e o número de clusters para otimizar a medida do Critério de Informação Bayesiano (BIC) ou do Akaike Information Criterion (AIC). Esta versão do k-means encontra o número k e também acelera o k-means.

  • K-means on-line ou Streaming k-means: permite executar o k-means digitalizando todos os dados uma vez e encontra automaticamente o número ideal de k. O Spark o implementa.

  • Algoritmo MeanShift : é uma técnica de agrupamento não paramétrica que não requer conhecimento prévio do número de clusters e não restringe a forma dos clusters. O agrupamento por turnos médios visa descobrir "bolhas" em uma densidade suave de amostras. É um algoritmo baseado em centróide, que funciona atualizando os candidatos aos centróides como a média dos pontos em uma determinada região. Esses candidatos são então filtrados em um estágio de pós-processamento para eliminar quase duplicatas para formar o conjunto final de centróides. Fontes: source1 , source2 , source3

curiosus
fonte
2

Usei a solução que encontrei aqui: http://efavdb.com/mean-shift/ e funcionou muito bem para mim:

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

insira a descrição da imagem aqui

snoob dogg
fonte
1

Minha idéia é usar o coeficiente de silhueta para encontrar o número ideal de cluster (K). Detalhes explicação está aqui .

qmaruf
fonte
1

Supondo que você tenha uma matriz de dados chamada DATA, é possível executar o particionamento em torno dos medoids com estimativa do número de clusters (por análise de silhueta) como este:

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

Uma resposta possível é usar o algoritmo meta-heurístico, como o algoritmo genético, para encontrar k. Isso é simples. você pode usar K aleatório (em algum intervalo) e avaliar a função de ajuste do algoritmo genético com algumas medidas como Silhouette And Find Best K base on fit function.

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

Masoud
fonte
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})
sumit
fonte
você seleciona dados e biblioteca adiciona e copia km = [] para Percentage ': 2}) por último e executa seu python e vê
soma
Bem-vindo ao Stack Overflow! Embora esse código possa ajudar a resolver o problema, ele não explica por que e / ou como responde à pergunta. Fornecer esse contexto adicional melhoraria significativamente seu valor educacional a longo prazo. Por favor edite sua resposta para adicionar explicação, incluindo o que limitações e premissas se aplicam.
precisa saber é o seguinte
1

Outra abordagem é usar o SOP (Self Organizing Maps) para encontrar o número ideal de clusters. O SOM (Mapa Auto-Organizável) é uma metodologia de rede neural não supervisionada, que precisa apenas da entrada usada para agrupar na solução de problemas. Essa abordagem foi usada em um artigo sobre segmentação de clientes.

A referência do artigo é

Abdellah Amine et al., Modelo de segmentação de clientes em comércio eletrônico usando técnicas de cluster e modelo LRFM: o caso de lojas on-line em Marrocos, Academia Mundial de Ciências, Engenharia e Tecnologia Revista Internacional de Engenharia de Computação e Informação Vol: 9, No: 8 , 2015, 1999 - 2010

boyaronur
fonte
0

Oi, vou simplificar e esclarecer, gosto de determinar os clusters usando a biblioteca 'NbClust'.

Agora, como usar a função 'NbClust' para determinar o número certo de clusters: Você pode verificar o projeto real no Github com dados e clusters reais - A extensão desse algoritmo 'kmeans' também foi executada usando o número certo de 'centros'.

Link do projeto Github: https://github.com/RutvijBhutaiya/Thailand-Customer-Engagement-Facebook

Rutvij
fonte
Em vez de adicionar o link do github, você pode adicionar algumas linhas principais de código que podem ajudar outras pessoas, mesmo que seu código não esteja acessível?
Giulio Caccin
0

Você pode escolher o número de clusters inspecionando visualmente seus pontos de dados, mas em breve perceberá que há muita ambiguidade nesse processo para todos, exceto os conjuntos de dados mais simples. Isso nem sempre é ruim, porque você está aprendendo sem supervisão e há alguma subjetividade inerente no processo de rotulagem. Aqui, ter experiência anterior com esse problema específico ou algo semelhante o ajudará a escolher o valor certo.

Se você quiser alguma dica sobre o número de clusters que deve usar, pode aplicar o método Elbow:

Primeiro, calcule a soma do erro quadrático (SSE) para alguns valores de k (por exemplo 2, 4, 6, 8, etc.). O SSE é definido como a soma da distância ao quadrado entre cada membro do cluster e seu centróide. Matematicamente:

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

Se você plotar k no SSE, verá que o erro diminui à medida que k aumenta; isso ocorre porque quando o número de clusters aumenta, eles devem ser menores, portanto a distorção também é menor. A idéia do método do cotovelo é escolher o k no qual o SSE diminui abruptamente. Isso produz um "efeito cotovelo" no gráfico, como você pode ver na figura a seguir:

insira a descrição da imagem aqui

Nesse caso, k = 6 é o valor que o método Elbow selecionou. Leve em consideração que o método Elbow é uma heurística e, como tal, pode ou não funcionar bem no seu caso particular. Às vezes, há mais de um cotovelo, ou nenhum cotovelo. Nessas situações, você geralmente calcula o melhor k avaliando o desempenho de k-means no contexto do problema específico de cluster que você está tentando resolver.

Faisal Shahbaz
fonte
0

Eu trabalhei em um pacote Python kneed (algoritmo Kneedle). Ele encontra o número do cluster dinamicamente como o ponto em que a curva começa a achatar. Dado um conjunto de valores x e y, o kneed retornará o ponto do joelho da função. O ponto do joelho é o ponto de curvatura máxima. Aqui está o código de exemplo.

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

da importação kneed KneeLocator kn = KneeLocator (x, y, curva = 'convexa', direção = 'decrescente')

print (kn.knee)

madhuri M
fonte
Por favor, adicione alguma explicação à sua resposta para que outras pessoas possam aprender com ela
Nico Haase