Теперь, когда мы обучили несколько моделей, какая модель лучше всего представляет данные?

Самый простой подход к определению лучшей модели - визуальный осмотр участков. Из 3 моделей, которые мы обучили, будет выглядеть так, как если бы первая с k = 2 кластерами была лучшей моделью. Сформированные 2 кластера отличаются друг от друга четким разделением между ними.

Давайте проверим это наблюдение с помощью оценки силуэта.

Примечание: для получения дополнительной информации о очках силуэта (и инерции) перейдите в здесь и здесь.

# variables to store silhouette score and inertia for different clusters
inertias = []
scores = []
from sklearn.metrics import silhouette_score
# Calculate silhouette scores and inertia for different number of clusters
for cluster_number in range(2,10):
  kmeans = KMeans(n_clusters=cluster_number, random_state=42).fit(X)
  inertias.append(kmeans.inertia_)  
  scores.append(silhouette_score(X,kmeans.labels_))
#plot the results
plt.clf()
plt.figure(figsize=(15,4))
#plot Silhouette Score
plt.subplot(121)
plt.plot(range(2,10), scores, 'bo-')
plt.plot(2, scores[0], 'ro-')
plt.plot(3, scores[1], 'yo-')
plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Score')
#plot Inertia
plt.subplot(122)
plt.plot(range(2,10), inertias, 'bo-')
plt.annotate(s='Elbow',xy=(3, inertias[1]),
             xytext=(0.75, 0.50),
             textcoords='figure fraction',
             fontsize=16,
             arrowprops=dict(facecolor='black', shrink=0.1)
)
plt.plot(2, inertias[0], 'ro-')
plt.plot(3, inertias[1], 'yo-')
plt.xlabel('Number of Clusters')
plt.ylabel('Inertia')

Как видно из рисунка справа, самый высокий балл по силуэту достигается, когда количество кластеров равно 2. Однако мы уже знаем, что в наборе данных есть 3 типа цветов! Таким образом, лучшая модель не может быть моделью с двумя кластерами. Одной метрики для оценки моделей часто бывает недостаточно, чтобы выбрать лучшую. Однако тот факт, что заранее определенный факт (k = 3 кластера) не соответствовал наилучшей оценке силуэта, не означает, что мы можем просто отбросить эту метрику. Это просто означает, что этот показатель необходимо дополнить другими показателями производительности.

«Одной метрики для оценки моделей часто недостаточно, чтобы выбрать лучшую».

Другой показатель, который мы можем использовать, - это инерция, которая представляет собой сумму квадратов расстояний между выборками до их ближайшего центроида. Высокое значение указывает на то, что образцы разбросаны вокруг центроида, в то время как низкое значение указывает, что образцы плотно сгруппированы вокруг центроида, что мы и хотим видеть в идеальном сценарии. По мере формирования большего количества кластеров значение инерции неуклонно уменьшается до тех пор, пока количество кластеров не станет равным количеству образцов. В этот момент инерция падает до нуля. Однако создание модели, в которой каждая точка представляет собой отдельный кластер, не имеет вообще никакого смысла. Это гарантия избыточного соответствия данных и рецепт для модели, чтобы делать неверные прогнозы с новыми невидимыми данными.

На приведенном ниже графике показано, что инерция быстро уменьшается при увеличении количества кластеров от 2 до 3.

Но начиная с 3 и далее изменение инерции значительно уменьшается. Следовательно, изгиб графика находится в Cluster = 3. Любое количество кластеров, превышающее эту точку, больше не стоит дополнительных затрат на обучение и просто сопряжено с риском чрезмерной подгонки данных. Можно сделать вывод, что наиболее оптимальное количество кластеров - 3. Также обратите внимание, что cluster = 3 также имеет второй лучший результат по силуэту.

Оценка производительности модели

Давайте сравним фактические данные с прогнозными данными модели с 3 кластерами.

plt.figure(figsize=(10,4))
# actual 
plt.subplot(121)
plt.plot(X[y==0,0], X[y==0,1], 'ro', label='Setosa')
plt.plot(X[y==1,0], X[y==1,1], 'b.', label='Versicolor')
plt.plot(X[y==2,0], X[y==2,1], 'g*', label='Virginica')
plt.legend(loc='upper left')
plt.title('Actual')
# predicted
plt.subplot(122)
kmeans = KMeans(n_clusters=3, random_state=42).fit(X)
y_pred = kmeans.predict(X)
plt.plot(X[y_pred==0,0], X[y_pred==0,1], 'ro', label='Setosa')
plt.plot(X[y_pred==1,0], X[y_pred==1,1], 'b.', label='Versicolor')
plt.plot(X[y_pred==2,0], X[y_pred==2,1], 'g*', label='Virginica')
plt.legend(loc='upper left')
plt.title('Predicted')
plt.show()

Точки данных на прогнозируемом графике явно неправильно обозначены, в противном случае графики почти такие же! Давайте исправим ярлыки.

plt.figure(figsize=(10,4))
# actual 
plt.subplot(121)
plt.plot(X[y==0,0], X[y==0,1], 'ro', label='Setosa')
plt.plot(X[y==1,0], X[y==1,1], 'b.', label='Versicolor')
plt.plot(X[y==2,0], X[y==2,1], 'g*', label='Virginica')
plt.legend(loc='upper left')
plt.title('Actual')
# predicted
plt.subplot(122)
kmeans = KMeans(n_clusters=3, random_state=42).fit(X)
y_pred = kmeans.predict(X)
y_pred_new= np.copy(y_pred)
# fixing the labels
for i in range(np.size(y_pred_new)):
  if y_pred_new[i] ==  0:
    y_pred_new[i] = 2      
  elif y_pred_new[i] ==  1:
    y_pred_new[i] = 0
  else: 
    y_pred_new[i] = 1
plt.plot(X[y_pred_new==0,0], X[y_pred_new==0,1], 'ro', label='Setosa')
plt.plot(X[y_pred_new==1,0], X[y_pred_new==1,1], 'b.', label='Versicolor')
plt.plot(X[y_pred_new==2,0], X[y_pred_new==2,1], 'g*', label='Virginica')
plt.legend(loc='upper left')
plt.title('Predicted')
plt.show()

Это больше походит на это. Теперь давайте посчитаем точность предсказанных значений.

sum(y==y_pred_new) * 100 / len(y)

96%! Совсем неплохо!

Сравнение времени обучения разных алгоритмов

Существует как минимум 3 различных алгоритма на выбор при кластеризации данных с помощью kmeans, а именно:

  • full - классический алгоритм kmeans. Используйте, когда данных мало.
  • elkan - предположительно более эффективен, чем полный за счет неравенства треугольника. Используйте, когда данные очень плотные.
  • мини-пакет - обучаются подмножества данных. Используйте, когда объем данных велик.

Выбор правильного алгоритма для ваших данных иногда может сэкономить вам много времени на обучение. Для иллюстрации давайте обучим несколько моделей с разными кластерами и запишем время их обучения.

from sklearn.cluster import MiniBatchKMeans
from timeit import timeit
run_time_full = []
run_time_mini=[]
run_time_elkan = []
max_k = 25
for k in range(1, max_k):
    kmeans_ = KMeans(n_clusters=k, random_state=42,algorithm='full')
    minibatch_kmeans = MiniBatchKMeans(n_clusters=k, random_state=42)
    elkan_kmeans = KMeans(n_clusters=k, random_state=42 ,algorithm='elkan')
    run_time_full.append(timeit("kmeans_.fit(X)", number=10, globals=globals()))
    run_time_mini.append(timeit("minibatch_kmeans.fit(X)", number=10, globals=globals()))
    run_time_elkan.append(timeit("elkan_kmeans.fit(X)", number=10, globals=globals()))

Постройте график времени обучения различных моделей

plt.clf()
plt.plot(range(1,max_k),run_time_full, 'r.-' , label='Full' )
plt.plot(range(1,max_k),run_time_mini, 'b.-' , label = 'Mini Batch' )
plt.plot(range(1,max_k),run_time_elkan, 'g.-', label = 'Elkan' )
plt.xlabel('Number of clusters')
plt.ylabel('Training time (usec)')
plt.legend(loc='upper left')
plt.show()

Обратите внимание, что Mini Batch - явный победитель в этом конкретном сценарии. Elkan лучше Full, но ненамного.