Como definir o número de clusters para o seu KMeans

Um dos métodos mais famosinhos pra achar agrupamentos em dados de forma não supervisionada é utilizando o KMeans. Mas e quando você não tem ideia de quantos clusters seus dados possam formar, como faz?

Dizer quantos clusters nós queremos encontrar é um desafio principalmente quando estamos lidando com aprendizado e agrupamento não supervisionado. Para resolver essa questão existe um método conhecido como Método Cotovelo (do inglês Elbow Method).

A ideia é bem básica, definir a melhor quantidade de clusters que podem ser encontrados, mesmo sem saber a reposta antecipadamente. Parece mágica né? Mas eu prometo que não é!


Os códigos que vão aparecer daqui para a frente estão organizados nesse repositório no GitHub.


Os dados

Então pra começar precisamos de dados. Vamos usar o conjunto de dados sobre flores Iris. Existe um gênero de flor chamado Iris, que é um grupo de cerca de 300 espécies de flores com tamanhos variados de pétalas e cepas. Curiosidade biológica a parte, esse conjunto de dados é muito utilizado para demonstrar funcionamento de algoritmos de aprendizado de máquina e de agrupamento de dados.

Esse conjunto traz 150 amostras de três espécies de Iris (Iris setosa, Iris virginica e Iris versicolor) que, apesar de muito semelhantes, são passíveis de distinção usando um modelo desenvolvido pelo biólogo e estatístico Ronald Fisher.

Esse é um conjunto de dados tão queridinho que bibliotecas de ciência de dados o trazem de fábrica, principalmente para utilizá-lo nas suas demonstrações e exemplos. Então vamos dar uma olhadinha nas primeiras linhas dele, para começar precisamos importar ele, eu prefiro a versão que vem junto com a biblioteca seaborn:

import seaborn as sns

iris = sns.load_dataset('iris')

E ao olhar para as primeiras linhas do nosso conjunto você pode perguntar, mas Jess, as respostas já estão ali na coluna species, a gente não ia fazer agrupamento não supervisionado?

Primeiras 5 linhas do conjunto de dados de Iris, resultado do comando iris.head()

Okay, sim! Nós temos as respostas no nosso conjunto de dados, mas para o propósito dessa demonstração vamos esquecer que nós já sabemos a espécie das nossas amostras e vamos tentar agrupá-las usando o KMeans.

O KMeans

Se você não está familiarizado com o KMeans, você precisa saber que para utilizar esse método você precisa informar uma quantidade inicial de clusters. No caso da versão implementada no scikit-learn, se você não informa uma quantidade de clusters pra iniciar, ele por padrão, tentará encontrar 8 agrupamentos distintos.

Quando a gente não sabe quantos clusters nossas amostras podem formar, nós precisamos utilizar uma forma de validar o que encontramos no lugar de um chutômetro.

O Método do Cotovelo

E é aí que entra o Método Cotovelo, a ideia é rodar o KMeans para vários quantidades diferentes de clusters e dizer qual dessas quantidades é o número ótimo de clusters. O que geralmente acontece ao aumentar a quantidade de clusters no KMeans é que as diferenças entre clusters se tornam muito pequenas, e as diferenças das observações intra-clusters vão aumentando. Então é preciso achar um equilíbrio em que as observações que formam cada agrupamento sejam o mais homogêneas possível e que os agrupamentos formados sejam o mais diferentes um dos outros.

Como o KMeans calcula a distância das observações até o centro do agrupamento que ela pertence, o ideal é que essa distância seja a menor viável. Matematicamente falando, nós estamos buscando uma quantidade de agrupamentos em que a soma dos quadrados intra-clusters (ou do inglês within-clusters sum-of-squares, comumente abreviado para wcss) seja a menor possível, sendo zero o resultado ótimo.

Usando o scikit-learn

O KMeans do scikit-learn já calcula o wcss pra gente e dá o nome de inertia. Existe dois pontos negativos que precisamos levar em consideração quanto a inércia

  1. A inércia é uma métrica que assume que seus clusters são convexos e isotrópicos, ou seja, se seus clusters são alongados ou de formato irregular essa é uma métrica ruim;
  2. A inércia não é uma métrica normalizada, então se você tiver um espaço com muitas dimensões você provavelmente vai dar de cara com a “maldição da dimensionalidade” já que as distâncias ficam infladas em espaços com muitas dimensões.

Agora que você está ciente disso, vamos olhar para os nossos dados: temos apenas 4 dimensões — comprimento e largura das cepas e pétalas, vamos lá fazer o bendito cotovelo pros nossos dados. Eu escrevi uma função que recebe um conjunto de dados, calcula o KMeans para 19 quantidades de clusters que vão de 2 a 20 possíveis agrupamentos e, finalmente, retorna uma lista com o nosso wcss:

Quando usamos essa função acima para calcular a soma de quadrados intra-clusters pro nossos dados de Iris e fazemos um gráfico, temos um resultado assim:

Cada ponto laranja é uma quantidade de clusters, note que começamos em 2 e vamos até 20 clusters. E aí pode vir a primeira dúvida: Qual desses pontos é o que simboliza de fato a quantidade ótima de clusters? Será que é o a2, ou a3 ou até mesmo o a4?

Que a matemática nos salve

E se eu te disser que existe uma fórmula matemática pra ajudar a gente nisso?

“Esse é o poder da matemática pessoal!”

Acontece que o ponto que indica o equilíbrio entre maior homogeneidade dentro do cluster e a maior diferença entre clusters, é o ponto da curva mais distante de uma reta traçada entre os pontos a0 e a18. E pasme! Existe uma fórmula que faz justamente o cálculo da distância entre um ponto e uma reta! É essa aqui:

Fórmula para o cálculo entre um ponto e uma reta que passa por P0 e P1

Não se assuste, no nosso caso P0 é o nosso a0 e o P1 é o nosso a18, veja:

e o par (x,y) representa as coordenadas de qualquer ponto que a gente queira calcular a distância. Vamos olhar mais uma vez para o nosso gráfico de cotovelo:

Suponha que queremos calcular a distância entre o ponto a1 e a reta que passa por a0e a18, aqui as informações que precisamos:

Substituindo os valores acima na fórmula do cálculo da distância temos o seguinte:

Matemática e Python, o casalzão que todo mundo ama

Imagino que você concorda comigo quando eu digo que “Ninguém merece ficar fazendo essas continhas na mão”. Então vamos usar um método pra isso, em resumo é só transcrever a fórmula do cálculo da distância entre um ponto e uma reta para código, ficou assim:

Esse método optimal_number_of_clusters() recebe uma lista contendo as somas dos quadrados para cada quantidade de clusters que calculamos usando calculate_wcss() e, como resultado, retorna a quantidade ótima de clusters. Agora que sabemos como calcular a quantidade ótima de clusters podemos finalmente usar o KMeans com tranquilidade:

Comparando antes e depois

Depois de clusterizar os dados devemos dar uma olhada nos descritivos estatísticos de cada cluster:

Descritivo estatístico agrupado por cluster

Nada mal se compararmos com o descritivos dos dados originais que coloquei aqui em baixo né?

Descritivo estatístico agrupado por espécie

Conclusão

Óbvio que nenhuma clusterização vai ter 100% de acurácia principalmente quando nós temos clusters possuem características que são bem similares umas das outras. Eu particularmente gosto bastante da versão visual da clusterização. Vamos colocar as duas features de pétala no gráfico e colorir de acordo com os clusters — e também de acordo com as espécies:

Conseguimos ver no gráfico que aquelas 12 observações que “mudaram” de classificação poderia fazer facilmente parte de qualquer um dos clusters.

Por fim, ainda vale salientar que o método do cotovelo não é a única forma de dizer a quantidade ótima de clusters. Existe ainda uma forma usando, por exemplo, o coeficiente de silhueta. Mas hoje, ficamos por aqui.

Xêro e boa clusterização.


Leitura extra