Aprendizado não supervisionado com K-means
Tags: machinelearning, python
Published: 2019-10-28 01:59:16

Este artigo trata sobre aprendizado não supervisionado, o método de aprendizado de máquina sem precisar de dados pré-rotulados. No aprendizado não supervisionado existe uma técnica chamada Clustering (ou Aglomeração) que serve exatamente para agrupar dados que tenham algumas características parecidas. Muitas vezes essas técnicas são usadas justamente para rotular dados para que seja possível utilizá-los em um aprendizado supervisionado. O agrupamento dos dados podem ser calculados de muitos modos. Aqui explico um deles, o algoritmo de K-means.

# K-means

Este é um dos mais simples e mais rápidos métodos de Clustering. Ele tenta separar os dados em K (um número predefinido) clusters, de acordo com a distância de cada ponto até algo chamado de centroide. Um centroide é como um protótipo para um cluster. Na maioria das técnicas, K pontos aleatórios do dataset são escolhidos para serem as coordenadas dos centroides iniciais. Então cada instância desses dados é atribuída ao centroide mais próximo. Nas demais iterações a posição dos centroides é calculada através da distancia média entre todos os pontos atribuídos àquele centroide na última iteração. O algoritmo termina quando a posição dos centroides não é mais modificada ou a distancia dessa mudança é menor que um threshold predefinido.

![](https://miro.medium.com/max/30/1*8eKtGLTpuThr6Q-pSiGn4g.png?q=20)

![](https://miro.medium.com/max/367/1*8eKtGLTpuThr6Q-pSiGn4g.png)

![](https://miro.medium.com/max/30/1*fTMJ0hlH6SByilzPQ3wHpA.png?q=20)

![](https://miro.medium.com/max/369/1*fTMJ0hlH6SByilzPQ3wHpA.png)

![](https://miro.medium.com/max/30/1*WF3AdvRSenOtuvUHTaphEA.png?q=20)

![](https://miro.medium.com/max/372/1*WF3AdvRSenOtuvUHTaphEA.png)

<figcaption> Mudança de centroides </figcaption> 

Criar clusters com K-means em Python é muito simples também. Vejamos o código:
```
import numpy as np
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=5)
kmeans.fit(data)
```

Mas, como escolher o número correto de clusters? A resposta para essa pergunta é bastante subjetiva, entretanto, existem muitos métodos para calcular o melhor número e dois deles explico abaixo, Elbow e Average Silhouette métodos.

# Elbow

Neste método é preciso rodar seu algoritmo de clustering com alguns valores, por exemplo, de 1 a 10. Calcular a função de custo, a soma dos quadrados das distâncias internas dos clusters, e traçá-la em um gráfico. O melhor número para a quantidade de clusters é quando a adição de um novo cluster não muda significativamente a função de custo. Isso geralmente acontece no "cotovelo" da linha.

![](https://miro.medium.com/proxy/0*jWe7Ns_ubBpOaemM.png)

<figcaption> Exemplo de um gráfico com a soma dos quadrados das distancias </figcaption>

# Average Silhouette

A análise por Silhouette mede o quão bem um ponto se encaixa em um cluster. Neste método um gráfico é feito medindo quão perto os pontos de um cluster estão dos pontos de outro cluster mais próximo. O coeficiente de Silhouette quando próximo de +1, indica que os pontos estão muito longe dos pontos do outro cluster, e quando próximo de 0, indica que os pontos então muito perto ou até interseccionando um outro cluster.

Para calcular o coeficiente de Silhouette é preciso definir a distância média de um ponto para todos os outros pontos em seu cluster  _(a(i))_ e também definir a distância média até os pontos do cluster mais próximo  _(b(i))_. Então, o coeficiente de Silhouette é calculado assim:

`s(i) = (b(i) - a(i)) / max(b(i), a(i)) `

Para visualizar a análise feita com Silhouette, vamos olhar para o cluster inteiro. Para isso podemos calcular o score médio do Silhouette pegando a média de todos os exemplos no dataset.

![](https://miro.medium.com/max/30/0*43jiGnUr-eDyQ1OO.png?q=20)

![](https://miro.medium.com/max/1400/0*43jiGnUr-eDyQ1OO.png)

<figcaption> Exemplo de análise com Silhouette </figcaption>

----------

Agora, vamos ver o K-means e os métodos para avaliar o número de clusters utilizados em um dataset real. Os dados usado aqui são sobre o consumo em e-commerces de clientes brasileiros. Abaixo está o código para normalizar os dados e criar os gráficos:
```
mms = MinMaxScaler()
mms.fit(costumers_dataset)
data_transformed = mms.transform(costumers_dataset)
def plot_clustering(data, labels, title=None):
    x_min, x_max = np.min(data, axis=0), np.max(data, axis=0)
    data = (data - x_min) / (x_max - x_min)
    fig = plt.figure(1, figsize=(4, 3))
    plt.figure(figsize=(6, 4))
    plt.scatter(data[:, 0], data[:, 1],
                 c=labels.astype(np.float))
    plt.xticks([])
    plt.yticks([])
    if title is not None:
        plt.title(title, size=17)
    plt.axis('off')
    plt.tight_layout(rect=[0, 0.03, 1, 0.95]) 
```

Agora, vamos tentar usar o K-means com 5 clusters:

```
kmeans = KMeans(n_clusters=5)
kmeans.fit(data_transformed)
labels = kmeans.labels_
plot_clustering(data_transformed, labels)
plt.show()
```

![](https://miro.medium.com/max/30/1*3XrPBt6EHJWiEbQ0c-kRcQ.png?q=20)

![](https://miro.medium.com/max/424/1*3XrPBt6EHJWiEbQ0c-kRcQ.png)

Então, essa separação parece um pouco estranha, não? Temos 5 clusters mas temos muita ocorrência de intersecções. Vamos avaliar o dataset com o método Elbow:
```
Sum_of_squared_distances = []
K = range(1,15)
for k in K:
    km = KMeans(n_clusters=k)
    km = km.fit(data_transformed)
    Sum_of_squared_distances.append(km.inertia_)
plt.plot(K, Sum_of_squared_distances, 'bx-')
plt.xlabel('k')
plt.ylabel('Sum_of_squared_distances')
plt.title('Elbow Method For Optimal k')
plt.show()
```

![](https://miro.medium.com/max/30/1*MZyDw-_eKeltZ-4j7BXd8w.png?q=20)

![](https://miro.medium.com/max/408/1*MZyDw-_eKeltZ-4j7BXd8w.png)

O gráfico mostra que esse dataset não possui exatamente um "cotovelo". Talvez o número correto de clusters esteja entre 4 e 6. Mas talvez este dataset que construímos não possa ser separado com essa configuração… Vamos ver o coeficiente de Silhouette sobre alguns valores para a quantidade de clusters.


```
range_n_clusters = [2, 3, 4, 5, 6]
for n_clusters in range_n_clusters:
    # Create a subplot with 1 row and 2 columns
    fig, (ax1, ax2) = plt.subplots(1, 2)
    fig.set_size_inches(18, 7)
    # The 1st subplot is the silhouette plot
    # The silhouette coefficient can range from -1, 1 but in this example all
    # lie within [-0.1, 1]
    ax1.set_xlim([-0.1, 1])
    # The (n_clusters+1)*10 is for inserting blank space between silhouette
    # plots of individual clusters, to demarcate them clearly.
    ax1.set_ylim([0, len(data_transformed) + (n_clusters + 1) * 10])
    # Initialize the clusterer with n_clusters value and a random generator
    # seed of 10 for reproducibility.
    clusterer = KMeans(n_clusters=n_clusters, random_state=10)
    cluster_labels = clusterer.fit_predict(data_transformed)
# The silhouette_score gives the average value for all the samples.
    # This gives a perspective into the density and separation of the formed
    # clusters
    silhouette_avg = silhouette_score(data_transformed, cluster_labels)
    print("Para n_clusters =", n_clusters,
          "O score_silhouette médio é :", silhouette_avg)
    # Compute the silhouette scores for each sample
    sample_silhouette_values = silhouette_samples(data_transformed, cluster_labels)
    y_lower = 10
    for i in range(n_clusters):
        # Aggregate the silhouette scores for samples belonging to
        # cluster i, and sort them
        ith_cluster_silhouette_values = \
            sample_silhouette_values[cluster_labels == i]
        ith_cluster_silhouette_values.sort()
        size_cluster_i = ith_cluster_silhouette_values.shape[0]
        y_upper = y_lower + size_cluster_i
        color = cm.nipy_spectral(float(i) / n_clusters)
        ax1.fill_betweenx(np.arange(y_lower, y_upper),
                          0, ith_cluster_silhouette_values,
                          facecolor=color, edgecolor=color,       alpha=0.7)
        # Label the silhouette plots with their cluster numbers at the middle
        ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
        # Compute the new y_lower for next plot
        y_lower = y_upper + 10  # 10 for the 0 samples
    ax1.set_title("The silhouette plot for the various clusters.")
    ax1.set_xlabel("The silhouette coefficient values")
    ax1.set_ylabel("Cluster label")
    # The vertical line for average silhouette score of all the values
    ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
    ax1.set_yticks([])  # Clear the yaxis labels / ticks
    ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
    # 2nd Plot showing the actual clusters formed
    colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
    ax2.scatter(data_transformed[:, 0], data_transformed[:, 1], marker='.', s=30, lw=0, alpha=0.7,
                c=colors, edgecolor='k')
    # Labeling the clusters
    centers = clusterer.cluster_centers_
    # Draw white circles at cluster centers
    ax2.scatter(centers[:, 0], centers[:, 1], marker='o',
                c="white", alpha=1, s=200, edgecolor='k')
    for i, c in enumerate(centers):
        ax2.scatter(c[0], c[1], marker='$%d$' % i, alpha=1,
                    s=50, edgecolor='k')
    ax2.set_title("The visualization of the clustered data.")
    ax2.set_xlabel("Feature space for the 1st feature")
    ax2.set_ylabel("Feature space for the 2nd feature")
    plt.suptitle(("Silhouette analysis for KMeans clustering on sample data "
                  "with n_clusters = %d" % n_clusters),
                 fontsize=14, fontweight='bold')
plt.show()
```

Abaixo temos a resposta desse algoritmo. Os gráficos estão separados por quantidade de clusters utilizados. Os gráficos da esquerda nos mostram o score da análise com Silhouette e da direita uma visualização dos clusters.

Para n_clusters = 2 O score_silhouette médio é: 0.24604273339845253  
Para n_clusters = 3 O score_silhouette médio é : 0.20670607133321856  
Para n_clusters = 4 O score_silhouette médio é : 0.188444914764597  
Para n_clusters = 5 O score_silhouette médio é : 0.19090629903451375  
Para n_clusters = 6 O score_silhouette médio é : 0.18047082769472864

![](https://miro.medium.com/max/30/1*phs26z7ottYAzh_suClTkg.png?q=20)

![](https://miro.medium.com/max/1039/1*phs26z7ottYAzh_suClTkg.png)

![](https://miro.medium.com/max/30/1*0mNvvtuU8KZPV73I0lO58Q.png?q=20)

![](https://miro.medium.com/max/1039/1*0mNvvtuU8KZPV73I0lO58Q.png)

![](https://miro.medium.com/max/30/1*oGB5GT7AxTR6nuJUZkRPvA.png?q=20)

![](https://miro.medium.com/max/1039/1*oGB5GT7AxTR6nuJUZkRPvA.png)

![](https://miro.medium.com/max/30/1*SBpQ5TLybsTDVUUaA01MWg.png?q=20)

![](https://miro.medium.com/max/1039/1*SBpQ5TLybsTDVUUaA01MWg.png)

![](https://miro.medium.com/max/30/1*CGWTuCPIa8FiUoEL81Q1hA.png?q=20)

![](https://miro.medium.com/max/1039/1*CGWTuCPIa8FiUoEL81Q1hA.png)

Como podem ver, os coeficientes de Silhouette não nos mostraram números muito bons sobre nenhum dos cenários apresentados. O melhor que podemos fazer aqui é remontar o dataset com outras informações. Talvez remover algumas das informações pode melhorar os resultados.

----------

# Conclusão

O aprendizado não supervisionado usando análise com Cluster pode ser muito fácil de produzir e ao mesmo tempo muito útil e importante para muitas áreas do conhecimento, por exemplo, quais tipos de cliente uma empresa tem, para ter conhecimento sobre qual o jeito certo para fazer propagandas ou para criar produtos melhores para cada tipo de cliente, além disso, outro exemplo é entender sobre algumas espécies na biologia, o pesquisador pode agrupar animais, células, ou qualquer outra coisa de acordo com suas características.

É importante lembrar que K-means não é a única técnica de Clustering. Existem outras métodos muito úteis como o Hierárquico, que também é muito simples de aprender, ou Expectation-maximization, que é muito utilizado pelos Cientístas de Dados.

----------

# Referências

O os códigos completos usados para as experiências nesse artigo estão  [nesse repositório do Github.](https://github.com/giselyalves13/brazillian-ecommerce-analysis)

> [Methods to determining the optimal number of clusters](https://www.datanovia.com/en/lessons/determining-the-optimal-number-of-clusters-3-must-know-methods/)  
> [How to determine the optimal number of clusters for K-means](https://blog.cambridgespark.com/how-to-determine-the-optimal-number-of-clusters-for-k-means-clustering-14f27070048f)  
> [Plot silhouette analysis](https://scikit-learn.org/stable/auto_examples/cluster/plot_kmeans_silhouette_analysis.html)  
> [Silhouette Analysis](https://kapilddatascience.wordpress.com/2015/11/10/using-silhouette-analysis-for-selecting-the-number-of-cluster-for-k-means-clustering/)  
> [Dataset usado no artigo](https://www.kaggle.com/olistbr/brazilian-ecommerce)