Автор: Trent Hauck
Переводчик: Wizardforcel
Лицензия: CC BY-NC-SA 4.0
В этой главе мы будем рассматривать кластеризацию. Кластеризация обычно связана с непараметрическими методами, которые предполагают, что мы не знаем значения целевой переменной. Это делает результаты неопределенными и объективными. Однако кластеризация очень полезна. Мы увидим, как можно использовать кластеризацию для локализации наших прогнозов в условиях надзора. Возможно, это и является причиной эффективности кластеризации, так как она может работать со широким спектром данных, часто давая необычные результаты.
В этой главе мы рассмотрим множество приложений от изображений до регрессии и выявления выбросов. Эти примеры покажут, что кластеризация обычно может быть представлена через вероятностные или оптимизационные структуры. Различные интерпретации приведут к различным компромиссам. Мы увидим, как обучать модель таким образом, чтобы позволить инструментам пробовать различные модели при решении задач кластеризации.
Сначала посмотрим на очень простую кластеризацию, а затем поговорим о том, как работает KMeans для поиска оптимального количества кластеров.
Посмотрим на наши данные:
>>> from sklearn.datasets import make_blobs
>>> blobs, classes = make_blobs(500, centers=3)
Как и прежде, импортируем matplotlib
, чтобы создать графики:
>>> import matplotlib.pyplot as plt
Мы собираемся просмотреть простой пример кластеризации синтетических данных. Затем мы немного обсудим, как KMeans работает для поиска оптимального количества кластеров.
Посмотрим на наши данные:
>>> f, ax = plt.subplots(figsize=(7.5, 7.5))
>>> ax.scatter(blobs[:, 0], blobs[:, 1], color=rgb[classes])
>>> rgb = np.array(['r', 'g', 'b'])
>>> ax.set_title("Блобы")
Результат будет следующим:
Теперь мы можем использовать KMeans для поиска центров этих кластеров. В первом примере мы будем предполагать, что нас интересуют три центра.
>>> from sklearn.cluster import KMeans
>>> kmeans = KMeans(n_clusters=3)
>>> kmeans.fit(blobs)
KMeans(copy_x=True, init='k-means++', max_iter=300, n_clusters=3,
n_init=10, n_jobs=1, precompute_distances=True,
random_state=None, tol=0.0001, verbose=0)
>>> kmeans.cluster_centers_
array([[ 0.47819567, 1.80819197],
[ 0.08627847, 8.24102715],
[ 5.2026125 , 7.86881767]])
>>> f, ax = plt.subplots(figsize=(7.5, 7.5))
>>> ax.scatter(blobs[:, 0], blobs[:, 1], color=rgb[classes])
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
kmeans.cluster_centers_[:, 1], marker='*', s=250,
color='black', label='Центры')
>>> ax.set_title("Блобы")
>>> ax.legend(loc='best')
```Нижеследующее изображение демонстрирует вывод:

Другие атрибуты также полезны. Например, атрибут `labels_` генерирует предполагаемую метку для каждого объекта.
```py
>>> kmean.labels_[:5]
array([1, 1, 2, 2, 1], dtype=int32)
Можно проверить, например, совпадает ли labels_
с категориями, но поскольку KMeans не знает, что такое категории, он не может присвоить одинаковый индекс значению двум категориям:
>>> classes[:5]
array([0, 0, 2, 2, 0])
Преобразуйте 1
в 0
, чтобы проверить соответствие с labels_
.
Функция transform
очень полезна, так как она выдаёт расстояние от каждой точки до центра кластера.
>>> kmean.transform(blobs)[:5]
array([[ 6.47297373, 1.39043536, 6.4936008 ],
[ 6.78947843, 1.51914705, 3.67659072],
[ 7.24414567, 5.42840092, 0.76940367],
[ 8.56306214, 5.78156881, 0.89062961],
[ 7.32149254, 0.89737788, 5.12246797]])
На самом деле алгоритм KMeans довольно простой — он минимизирует сумму квадратов расстояний между точками и центром кластера.
Сначала он устанавливает заранее определённое количество кластеров K
, затем выполняются следующие действия:
Это продолжается до тех пор, пока не будет достигнуто определённое условие.
Оценка модели для беспризнакового обучения представляет собой вызов. Поэтому, когда известно реальное положение дел, sklearn
предлагает множество способов для оценки кластеризации, но при отсутствии знаний — гораздо меньше.
Мы начнём с простой модели кластера и будем оценивать её схожесть. Это больше связано с механизмами, поскольку измерение схожести кластера при поиске реального количества кластеров явно бесполезно.
Для начала создадим несколько наборов данных, которые могут использоваться для моделирования кластеров данных.
>>> from sklearn.datasets import make_blobs
>>> import numpy as np
>>> blobs, classes = make_blobs(n_samples=500, centers=3)
>>> from sklearn.cluster import KMeans
>>> kmeans = KMeans(n_clusters=3)
>>> kmeans.fit(blobs)
KMeans(copy_x=True, init='k-means++', max_iter=300, n_clusters=3,
n_init=10, n_jobs=1, precompute_distances=True,
random_state=None, tol=0.0001, verbose=0)
Сначала мы рассмотрим расстояние силуэта. Расстояние силуэта представляет собой отношение между внутренней неразличимостью кластера и минимальной внешней неразличимостью. Оно может рассматриваться как мера разделения кластеров.Позвольте нам взглянуть на распределение расстояний до центра кластера для каждого объекта, что очень полезно для понимания расстояния силуэта.
>>> from sklearn import metrics
>>> silhouette_samples = metrics.silhouette_samples(blobs,
kmeans.labels_)
>>> np.column_stack((classes[:5], silhouette_samples[:5]))
array([[ 1., 0.87617292],
[ 1., 0.89082363],
[ 1., 0.88544994],
[ 1., 0.91478369],
[ 1., 0.91308287]])
>>> f, ax = plt.subplots(figsize=(10, 5))
>>> ax.set_title("Распределение значений силуэтов")
>>> ax.hist(silhouette_samples)
Выход:
Обратите внимание, что обычно коэффициенты близкие к 1 имеют более высокие значения.
Среднее значение коэффициента силуэта часто используется для оценки качества всего кластерного алгоритма.
>>> silhouette_samples.mean()
0.57130462953339578
Это довольно распространено, в действительности модуль metrics
предоставляет функцию для получения этого значения.
Теперь давайте обучим модель с различным количеством кластеров и посмотрим на среднее значение метрики:
# первый новый набор данных
>>> blobs, classes = make_blobs(n_samples=500, centers=10)
>>> silhouette_avgs = []
# это может занять некоторое время
>>> for k in range(2, 60):
kmeans = KMeans(n_clusters=k).fit(blobs)
silhouette_avgs.append(metrics.silhouette_score(blobs,
kmeans.labels_))
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> ax.plot(silhouette_avgs)
Ниже представлен вывод:
Этот график показывает изменения средних значений контуров в зависимости от количества центроидов. Мы видим, что оптимальное количество равно 3, согласно сгенерированным данным. Однако, оптимальное значение кажется равным 6 или 7. Это типично для задач кластеризации; мы не можем точно определить количество кластеров, а лишь приближенно его оценить.## 3.3 Оценка правильности кластеризации
Ранее мы обсуждали оценку кластеризации в условиях отсутствия истинной информации. Однако мы ещё не говорили об оценке метода KMeans, когда истинные кластеры известны. В большинстве случаев это невозможно, но если есть внешние метки, то мы знаем истинные кластеры или хотя бы их приближение.
Предположим, что существует мир, где у нас есть внешний источник, который предоставляет нам информацию о реальных кластерах.
Мы создадим простой набор данных и будем оценивать корректность относительно реальной ситуации различными способами. Затем мы обсудим эти способы.
Перед тем как начнём измерять, давайте сначала посмотрим на наш набор данных:
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> colors = ['r', 'g', 'b']
>>> for i in range(3):
p = blobs[ground_truth == i]
ax.scatter(p[:,0], p[:,1], c=colors[i],
label="Кластер {}".format(i))
>>> ax.set_title("Кластеры с реальными метками")
>>> ax.legend()
>>> f.savefig("9485OS_03-16")
Ниже представлен вывод:
Так как мы уже обучили модель, давайте посмотрим на центроиды кластеров:
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> colors = ['r', 'g', 'b']
>>> for i in range(3):
p = blobs[ground_truth == i]
ax.scatter(p[:,0], p[:,1], c=colors[i],
label="Кластер {}".format(i))
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
kmeans.cluster_centers_[:, 1], s=100,
color='black',
label='Центры')
>>> ax.set_title("Кластеры с реальными метками")
>>> ax.legend()
>>> f.savefig("9485OS_03-17")
```Ниже представлен вывод:

Так как мы можем рассматривать кластеризацию как задачу классификации, полезные методы здесь применимы:
```py
>>> for i in range(3):
print((kmeans.labels_ == ground_truth)[ground_truth == i])
.astype(int).mean()
0.0778443113772
0.990990990991
0.0570570570571
Очевидно, что у нас есть несколько неправильно классифицированных кластеров. Давайте попробуем исправить ситуацию, после чего проверим точность. Мы правильны в 90% случаев. Второй метрикой схожести является нормированное взаимное информационное сходство (mutual information score).
>>> from sklearn import metrics
>>> metrics.normalized_mutual_info_score(ground_truth, kmeans.labels_)
0.78533737204433651
Значение близкое к нулю указывает на то, что метки могут не быть созданы на основе процесса схожести. Однако значение близкое к единице указывает на высокую согласованность между двумя наборами меток.
Например, давайте рассмотрим само значение взаимной информации:
>>> metrics.normalized_mutual_info_score(ground_truth, ground_truth)
1.0
Из имени можно понять, что существует неконстантированная версия mutual_info_score
:
>>> metrics.mutual_info_score(ground_truth, kmeans.labels_)
0.78945287371677486
Это очень близко. Тем не менее, нормированное взаимное информационное сходство представляет собой отношение взаимной информации к квадратному корню произведения энтропий истинных меток и меток.### Дополнительно
Есть одна метрика, которую мы ещё не обсуждали, и она не зависит от истинных меток — это метрика инерции (inertia). В настоящее время эта метрика недостаточно подробно документирована, но она является самой простой метрикой в KMeans.
Инерция — это сумма квадратов расстояний от каждого объекта до центра кластера, которому он принадлежит. Мы можем немного использовать NumPy для её вычисления:
>>> kmeans.inertia_
Метод KMeans хорош, но не подходит для работы с большим объёмом данных. Это связано с его сложностью. То есть, мы можем использовать алгоритмы с меньшей сложностью для получения приближённых решений.
MiniBatch KMeans — это более быстрая реализация метода KMeans. Сложность KMeans очень велика, и это NP-полная задача.
Однако, используя MiniBatch KMeans, мы можем значительно увеличить скорость выполнения KMeans. Это достигается за счёт обработки нескольких мини-выборок, называемых мини-пакетами. Если эти мини-выборки сходятся и имеют хорошие начальные условия, они дают приближённое решение обычного KMeans.
>>> %time kmeans.fit(blobs) #IPython Magic CPU times: user 8.17 s, sys: 881 ms, total: 9.05 s Wall time: 9.97 s
>>> %time minibatch.fit(blobs) CPU times: user 4.04 s, sys: 90.1 ms, total: 4.13 s Wall time: 4.69 s
Заметна значительная разница во времени выполнения. Различия в производительности кластеризации показаны ниже:
>>> kmeans.cluster_centers_[0] array([ 1.10522173, -5.59610761, -8.35565134])
>>> minibatch.cluster_centers_[0] array([ 1.12071187, -5.61215116, -8.32015587])
Следующий вопрос, который может возникнуть, заключается в том, насколько близко расположены центры кластеров.
>>> from sklearn.metrics import pairwise
>>> pairwise.pairwise_distances(kmeans.cluster_centers_[0],
minibatch.cluster_centers_[0])
array([[ 0.03305309]])
Похоже, расстояние между ними очень мало. Диагональ содержит разницу между центрами кластеров:
>>> np.diag(pairwise.pairwise_distances(kmeans.cluster_centers_,
minibatch.cluster_centers_))
array([ 0.04191979, 0.03133651, 0.04342707])
Ключевой момент здесь — это использование пакетов данных. Пакеты используются для итерационного поиска средних значений пакетов. Для следующей итерации среднее значение предыдущего пакета обновляется с учетом текущей итерации. Есть несколько параметров, которые контролируют общее поведение KMeans и определяют параметры MiniBatch KMeans.
Параметр batch_size
определяет размер пакета. При игре можно запустить MiniBatch KMeans, но если мы хотим использовать все данные, то размер пакета будет равен количеству записей в наборе данных.```py
minibatch = MiniBatchKMeans(batch_size=len(blobs)) %time minibatch.fit(blobs) CPU times: user 34.6 s, sys: 3.17 s, total: 37.8 s Wall time: 44.6 s
Очевидно, это противоречит основной задаче, но демонстрирует важные аспекты. Неправильные начальные условия могут влиять на модель, особенно при работе с кластеризацией. Использование MiniBatch KMeans не гарантирует достижение глобального оптимума.
## 3.5 Кластеризация изображения с помощью KMeans
Обработка изображений является важной темой, в которой кластеризация имеет ряд применений. Стоит отметить, что в Python существует несколько отличных библиотек для работы с изображениями. Проект scikit-image является "родственным" проектом scikit-learn. Если вы планируете выполнять сложные задачи, стоит обратить внимание на него.
### Подготовка
Мы проведём некоторое время, работая с этим рецептом. Цель — использовать кластеризацию для размытия изображения.
Сначала мы будем использовать SciPy для чтения изображения. Изображение представляется трёхмерным массивом, где координаты x и y описывают высоту и ширину, а третья ось представляет значения цветов RGB каждого пикселя.
$ wget http://blog.trenthauck.com/assets/headshot.jpg
### Операция
Теперь давайте прочтем изображение в Python:
```py
>>> from scipy import ndimage
>>> img = ndimage.imread("headshot.jpg")
>>> plt.imshow(img)
Это и есть изображение:
Здравствуйте, это автор в молодости.
Так как у нас уже есть изображение, проверим его размерность:
```py
>>> img.shape
(420, 420, 3)
Для реальной квантизации изображения нам потребуется преобразовать его в двумерный массив, имеющий длину 420x420
и ширину значений RGB. Лучшим образом представить это можно как множество точек в трёхмерном пространстве, которое мы будем кластеризировать для снижения количества различных цветов в изображении — это простая форма квантизации.
Сначала применим метод reshape к массиву, который является объектом NumPy, поэтому это очень просто сделать:
>>> x, y, z = img.shape
>>> long_img = img.reshape(x * y, z)
>>> long_img.shape
(176400, 3)
Теперь начнём процесс кластеризации. Сначала импортируем модуль кластеризации и создадим объект KMeans. Мы передаем n_clusters=5
, чтобы иметь 5 кластеров, или фактически 5 различных цветов.
Это хороший рецепт, используя предложенное ранее расстояние контура:
>>> from sklearn import cluster
>>> k_means = cluster.KMeans(n_clusters=5)
>>> k_means.fit(long_img)
Теперь, когда мы обучили объект KMeans, давайте посмотрим на центры кластеров:
>>> centers = k_means.cluster_centers_
>>> centers
array([[ 142.58775848, 206.12712986, 226.04416873],
[ 86.29356543, 68.86312505, 54.04770507],
[ 194.36182899, 172.19845258, 149.65603813],
[ 24.67768412, 20.45778933, 16.19698314],
[ 149.27801776, 132.19850659, 115.32729167]])
Имея центроиды, следующим шагом является получение меток. Они указывают, к какому кластеру относится каждая точка.```py
labels = k_means.labels_ labels[:5] array([1, 1, 1, 1, 1], dtype=int32)
Далее нам потребуются базовые операции с массивами NumPy, после чего мы преобразуем данные в новое изображение:
```py
>>> plt.imshow(centers[labels].reshape(x, y, z))
Рассмотрим полученное изображение:
Часто самой простой задачей является вычисление расстояния между двумя объектами. Для этого требуется выбрать подходящую метрику расстояния, рассчитать попарные расстояния и сравнить результаты с ожидаемыми значениями.
Библиотека Scikit-learn предоставляет внутренний инструмент sklearn.metrics.pairwise
. Он содержит служебные функции для вычисления расстояний между векторами в матрице X или между векторами в матрицах X и Y.
Это полезно для информационного поиска. Например, имея набор данных клиентов с атрибутами X, можно найти наиболее подходящего представителя клиента и определить самого близкого к нему клиента. Кроме того, возможно отсортировать клиентов по концепциям схожести, используя функцию расстояния.
Мы будем использовать функцию pairwise_distances
, чтобы определить степень близости объектов. Важно помнить, что эта функция работает аналогично функциям расстояния для кластеризации/классификации.
Сначала импортируем функцию pairwise_distances
из модуля metrics
и создадим набор данных для операций:
>>> from sklearn.metrics import pairwise
>>> from sklearn.datasets import make_blobs
>>> points, labels = make_blobs()
```
Простейший способ проверки расстояний — это использование `pairwise_distances`:
```py
>>> distances = pairwise.pairwise_distances(points)
```
`distances` представляет собой матрицу размером NxN, где диагональ равна нулю. В самом простом случае, давайте посмотрим на расстояние каждой точки до первой точки:
```py
>>> np.diag(distances)[:5]
array([ 0., 0., 0., 0., 0.])
```
Теперь найдём ближайшую к первой точке точку:
```py
>>> distances[0][:5]
array([ 0., 11.82643041, 1.23751545, 1.17612135, 14.61927874])
```
Отсортируем точки по степени близости, используя `np.argsort`:
```py
>>> ranks = np.argsort(distances[0])
>>> ranks[:5]
array([ 0, 27, 98, 23, 67])
```
Преимущество использования `argsort` заключается в том, что теперь мы можем сортировать нашу матрицу `points`, чтобы получить реальные точки.
```py
>>> points[ranks][:5]
array([[ 8.96147382, -1.90405304],
[ 8.75417014, -1.76289919],
[ 8.78902665, -2.27859923],
[ 8.59694131, -2.10057667],
[ 8.70949958, -2.30040991]])
```
Просмотр ближайших точек может оказаться полезным. Результат вполне предсказуем:

### Как это работает
Заданной некоторой функцией расстояния каждый пункт измеряет друг друга парно. Обычно это Евклидово расстояние, которое вычисляется следующим образом:

```Другими словами, он вычисляет разницу каждого компонента двух векторов, возведение этих разностей в квадрат, суммирование и последующее взятие корня. Это звучит знакомо, поскольку то же самое используется при расчете среднеквадратичной ошибки. Если бы мы взяли квадратный корень, они были бы одинаковыми. В действительности часто используемой метрикой является среднее квадратическое отклонение (RMSE), которая представляет собой применение функции расстояния.В Python это выглядит так:
```py
>>> def euclid_distances(x, y):
... return np.power(np.power(x - y, 2).sum(), .5)
>>> euclid_distances(points[0], points[1])
11.826430406213145
```
Scikit-learn также предоставляет несколько других функций, но использует функции расстояний из SciPy. На момент написания этой книги функции расстояний Scikit-learn поддерживают разреженные матрицы. Дополнительную информацию о функциях расстояний можно найти в документации SciPy.
+ `cityblock`
+ `cosine`
+ `euclidean`
+ `l1`
+ `l2`
+ `manhattan`
Теперь мы можем решать задачи. Например, если мы стоим на клетке в начале координат и линия — это улица, то как далеко нам придётся пройти до точки `(5,5)`?
```py
>>> pairwise.pairwise_distances([[0, 0], [5, 5]], metric='cityblock')[0]
array([ 0., 10.])
```
### Дополнительно
Используя парное расстояние, мы можем находить схожесть между векторами. Это относится к Гамильтоновому расстоянию, которое определяется следующим образом:

С помощью следующих команд:
```py
>>> X = np.random.binomial(1, .5, size=(2, 4)).astype(np.bool)
>>> X
array([[False, True, False, False],
[False, False, False, True]], dtype=bool)
>>> pairwise.pairwise_distances(X, metric='hamming')
array([[ 0. , 0.25],
[ 0.25, 0.]])
```
## 3.7 Вероятностное кластеризование с использованием модели гауссовских смесей
В KMeans мы предполагаем, что дисперсия кластеров равна. Это приводит к разделению пространства, которое определяет, как будут распределены кластеры. Но что, если есть ситуация, где дисперсия не равна, а каждая точка внутри кластера имеет связанную вероятность?### ПодготовкаЕсть более вероятностный способ рассмотреть кластеры K-Means. Кластеры K-Means эквивалентны применению ковариационной матрицы `S` к гауссовому смесительному моделированию, где эта матрица может быть декомпозирована в единичную матрицу с учетом ошибок. Для каждого кластера структура ковариации одинакова, что приводит к формированию сферических кластеров.
Однако, если позволить матрице `S` меняться, можно оценить модель GMM и использовать её для прогнозирования. Мы рассмотрим это с точки зрения одномерного пространства, а затем расширим до многомерного.
### Шаги выполнения
Сначала нам нужно создать некоторые данные. Например, давайте сгенерируем случайные значения для роста мужчин и женщин. Мы будем использовать этот пример в течение всего руководства. Это простой пример, но он демонстрирует то, что мы хотим сделать в N-мерном пространстве, что легче всего визуализировать:
```py
>>> import numpy as np
>>> N = 1000
>>> in_m = 72
>>> in_w = 66
>>> s_m = 2
>>> s_w = s_m
>>> m = np.random.normal(in_m, s_m, N)
>>> w = np.random.normal(in_w, s_w, N)
>>> from matplotlib import pyplot as plt
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> ax.set_title("Гистограмма роста")
>>> ax.hist(m, alpha=.5, label="Мужчины");
>>> ax.hist(w, alpha=.5, label="Женщины");
>>> ax.legend()
```
Ниже представлен вывод:

Далее наш интерес заключается в том, чтобы повторно отобрать группы, обучить распределение на основе этих групп, а затем использовать его для прогнозирования остальных групп.```py
>>> random_sample = np.random.choice([True, False], size=m.size)
>>> m_test = m[random_sample]
>>> m_train = m[~random_sample]
>>> w_test = w[random_sample]
>>> w_train = w[~random_sample]
```
Теперь нам нужно получить эмпирическое распределение высот мужчин и женщин на основе тренировочного набора данных:
```py
>>> from scipy import stats
>>> m_pdf = stats.norm(loc=m_train.mean(), scale=m_train.std())
>>> w_pdf = stats.norm(loc=w_train.mean(), scale=w_train.std())
```
Для тестового набора данных мы должны вычислить вероятность того, что каждая точка данных была сгенерирована из каждого распределения, и наиболее вероятное распределение будет использоваться для назначения подходящей метки. Конечно, мы увидим, насколько точно это работает.
```py
>>> m_pdf.pdf(m[0])
0.043532673457165431
>>> w_pdf.pdf(m[0])
9.2341848872766183e-07
```
Обратите внимание на различия между вероятностями.
Предположим, что если вероятность мужчины выше, мы сделаем догадку, что это мужчина, но если вероятность женщины выше, мы будем считать это женщиной.
```py
>>> guesses_m = np.ones_like(m_test)
>>> guesses_m[m_pdf.pdf(m_test) < w_pdf.pdf(m_test)] = 0
```
Конечно, вопрос состоит в том, насколько точно мы можем предсказывать. Поскольку `guesses_m` равен 1 при правильном предсказании и 0 при неправильном, мы можем вычислить среднее значение этого вектора, чтобы получить точность.```py
>>> guesses_m.mean()
0.93775100401606426
```
Не так уж плохо. Теперь давайте посмотрим, как хорошо мы справились с группировкой женщин, используя следующие команды:
``````py
>>> guesses_w = np.ones_like(w_test)
>>> guesses_w[m_pdf.pdf(w_test) > w_pdf.pdf(w_test)] = 0
>>> guesses_w.mean()
0.93172690763052213
```
Давайте позволим различиям в дисперсиях между двумя группами отличаться. Сначала создадим несколько новых массивов:
```py
>>> s_m = 1
>>> s_w = 4
>>> m = np.random.normal(in_m, s_m, N)
>>> w = np.random.normal(in_w, s_w, N)
```
Затем создадим тренировочный набор данных:
```py
>>> m_test = m[random_sample]
>>> m_train = m[~random_sample]
>>> w_test = w[random_sample]
>>> w_train = w[~random_sample]
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> ax.set_title("Гистограмма высот")
>>> ax.hist(m_train, alpha=.5, label="Мужчины")
>>> ax.hist(w_train, alpha=.5, label="Женщины")
>>> ax.legend()
```
Посмотрим, как различаются дисперсии между мужчинами и женщинами:

Теперь мы можем создать такие же PDF:
```py
>>> m_pdf = stats.norm(m_train.mean(), m_train.std())
>>> w_pdf = stats.norm(w_train.mean(), w_train.std())
```
Вот вывод:

Вы можете представить это себе в многомерном пространстве:
```py
>>> class_A = np.random.normal(0, 1, size=(100, 2))
>>> class_B = np.random.normal(4, 1.5, size=(100, 2))
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> ax.scatter(class_A[:,0], class_A[:,1], label='A', c='r')
>>> ax.scatter(class_B[:,0], class_B[:,1], label='B')
```
Вот вывод:

### Как это работает
Теперь, после того как мы уже видели, как мы классифицировали точки на основе распределений, давайте посмотрим, как это сделать в Scikit-learn:
```py
>>> from sklearn.mixture import GaussianMixture as GMM
>>> gmm = GMM(n_components=2)
>>> X = np.row_stack((class_A, class_B))
>>> y = np.hstack((np.ones(100), np.zeros(100)))
```
Итак, поскольку мы маленькие данные-научники, мы создаем тренировочные наборы данных:
``````py
>>> train = np.random.choice([True, False], 200)
>>> gmm.fit(X[train])
GaussianMixture(covariance_type='diag', init_params='wc', max_iter=100,
means_init=None, n_components=2, n_init=1,
random_state=None, reg_covar=0.01, tol=0.001,
verbose=0, warm_start=False)
```
Способ обучения и прогнозирования аналогичен другим объектам Scikit-learn.
```py
>>> gmm.fit(X[train])
>>> gmm.predict(X[train])[:5]
array([0, 0, 0, 0, 0])
```
Когда модель уже обучена, есть несколько методов, которые стоит рассмотреть.
Например, используя `score_samples`, мы можем фактически получить вероятность каждого примера для каждой метки.
## 3.8 Использование KMeans для выявления выбросов
В этом разделе мы рассмотрим механизм и преимущества использования KMeans для выявления выбросов. Это полезно для выявления некоторых типов ошибок, но следует использовать с осторожностью.
### Подготовка
В этой рецепте мы будем использовать KMeans для выполнения анализа выбросов в кластерах. Важно отметить, что при упоминании выбросов и анализа выбросов существует множество подходов. С одной стороны, мы можем удалить выбросы, чтобы удалить точки, созданные процессом генерации данных. С другой стороны, выбросы могут происходить из погрешностей измерений или других внешних факторов.
Это и является спорной темой. Оставшаяся часть этого рецепта будет сосредоточена на поиске выбросов. Наше предположение заключается в том, что выбор наших удалённых выбросов является обоснованным.
```Процесс анализа выбросов состоит в том, чтобы найти центр кластера и затем идентифицировать потенциальные выбросы на основе расстояния от точки до центра кластера.
### Шаги операции
Сначала мы создадим набор данных из 100 точек, а затем определим 5 самых удалённых точек относительно центра. Эти точки будут считаться потенциальными выбросами.
```py
>>> from sklearn.datasets import make_blobs
>>> X, labels = make_blobs(100, centers=1)
>>> import numpy as np
```
Очень важно помнить, что KMeans имеет только один центр кластера. Эта идея аналогична одноклассному SVM, используемому для анализа выбросов.
```py
>>> from sklearn.cluster import KMeans
>>> kmeans = KMeans(n_clusters=1)
>>> kmeans.fit(X)
```
Теперь давайте посмотрим на график. Для тех точек, которые находятся далеко от центра, попробуйте догадаться, какую точку можно будет идентифицировать как одну из пяти выбросов:
```py
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> ax.set_title("Blob")
>>> ax.scatter(X[:, 0], X[:, 1], label='Points')
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
kmeans.cluster_centers_[:, 1],
label='Centroid',
color='r')
>>> ax.legend()
```
Вот результат:

Теперь давайте определим пять наиболее удалённых точек:
```py
>>> distances = kmeans.transform(X)
# argsort возвращает массив индексов, который отсортирует массив в порядке возрастания
# поэтому мы обращаем его через [::-1] и берем первые пять с помощью [:5]
>>> sorted_idx = np.argsort(distances.ravel())[::-1][:5]
```
Теперь давайте посмотрим, какая из этих точек находится дальше всего:```py
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> ax.set_title("Одиночный кластер")
>>> ax.scatter(X[:, 0], X[:, 1], label='Точки')
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
... kmeans.cluster_centers_[:, 1],
... label='Центроид', color='r')
>>> ax.scatter(X[sorted_idx][:, 0], X[sorted_idx][:, 1],
... label='Экстремальное значение', edgecolors='g',
... facecolors='none', s=100)
>>> ax.legend(loc='best')
```
Вот что мы получаем в результате:

Если этот результат нас устраивает, удаление этих точек очень простое.
```py
>>> new_X = np.delete(X, sorted_idx, axis=0)
```
Аналогично, после удаления этих точек центроид заметно сместился.
```py
>>> new_kmeans = KMeans(n_clusters=1)
>>> new_kmeans.fit(new_X)
```
Давайте теперь визуализируем старый и новый центроид:
```py
>>> f, ax = plt.subplots(figsize=(7, 5))
>>> ax.set_title("Удалены экстремальные значения")
>>> ax.scatter(new_X[:, 0], new_X[:, 1], label='Обрезанные точки')
>>> ax.scatter(kmeans.cluster_centers_[:, 0],
... kmeans.cluster_centers_[:, 1], label='Старый центроид',
... color='r', s=80, alpha=.5)
>>> ax.scatter(new_kmeans.cluster_centers_[:, 0],
... new_kmeans.cluster_centers_[:, 1], label='Новый центроид',
... color='m', s=80, alpha=.5)
>>> ax.legend(loc='best')
```
Вот что мы получаем в результате:

Конечно, центроид смещается немного, но именно так мы и ожидали при удалении пяти экстремальных точек. Этот процесс можно повторять до тех пор, пока вы не будете удовлетворены представлением данных.
### Как это работает
Мы уже видели, что между распределением Гаусса и методом KMeans кластеризации существует существенная связь. Давайте создадим эмпирическое нормальное распределение на основе центроида и ковариационной матрицы образцов, а затем посмотрим на вероятность каждого отдельного значения — теоретически, это будут наши пять самых экстремальных значений. Это показывает, что фактически мы удаляем значения с наименьшей вероятностью. Концепция расстояния и вероятности важна и часто встречается при обучении машинному обучению.Используйте следующие команды для создания эмпирического нормального распределения:
```py
>>> from scipy import stats
>>> emp_dist = stats.multivariate_normal(
... kmeans.cluster_centers_.ravel())
>>> lowest_prob_idx = np.argsort(emp_dist.pdf(X))[:5]
>>> np.all(X[sorted_idx] == X[lowest_prob_idx])
True
```
## 3.9 Использование KNN для регрессии
Регрессия была рассмотрена в других частях этой книги, но мы можем захотеть использовать её в контексте «кармана» пространства признаков. Мы можем предположить, что наш набор данных прошел через несколько этапов предварительной обработки данных. В этом случае идеей будет обучение только на близко расположенных данных.
### Подготовка
Наш старый друг — регрессия — может использоваться в контексте кластеризации. Регрессия явно является методом с надзорным обучением, поэтому мы используем KNN вместо KMeans.
Для KNN регрессии мы используем K ближайших точек в пространстве признаков для построения модели регрессии, а не все пространство признаков как в обычной регрессии.
### Операция
Для этого рецепта мы будем использовать набор данных `iris`. Если нам нужно прогнозировать что-то, например, ширину лепестков каждого цветка, то кластеризация по видам `iris` может дать лучший результат. KNN регрессия не делает кластеризацию по видам, но наша гипотеза заключается в том, что одинаковые виды будут иметь близкие значения X, или в данном случае, длину лепестков.Для этого рецепта мы будем использовать набор данных `iris`:
```py
>>> from sklearn import datasets
>>> iris = datasets.load_iris()
>>> iris.feature_names # ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
```
Мы попробуем предсказывать длину лепестка на основе длины и ширины чашелистика. Также мы будем обучать линейную регрессию для сравнения качества KNN регрессии.
```py
>>> from sklearn.linear_model import LinearRegression
>>> lr = LinearRegression()
>>> lr.fit(X, y)
>>> print("Среднеквадратичное отклонение:", "{:.2f}".format(np.mean((y - lr.predict(X)) ** 2)))
Среднеквадратичное отклонение: 0.15
```
Теперь для KNN регрессии используем следующий код:
```py
>>> from sklearn.neighbors import KNeighborsRegressor
>>> knnr = KNeighborsRegressor(n_neighbors=10)
>>> knnr.fit(X, y)
>>> print("Среднеквадратичное отклонение:", "{:.2f}".format(np.mean((y - knnr.predict(X)) ** 2)))
Среднеквадратичное отклонение: 0.069
```
Посмотрим, что произойдет, когда мы позволим ему использовать 10 ближайших точек для регрессии?
```py
>>> f, ax = plt.subplots(nrows=2, figsize=(7, 10))
>>> ax[0].set_title("Прогнозы")
>>> ax[0].scatter(X[:, 0], X[:, 1], s=lr.predict(X)*80, label='Прогнозы LR', color='c', edgecolors='black')
>>> ax[1].scatter(X[:, 0], X[:, 1], s=knnr.predict(X)*80, label='Прогнозы k-NN', color='m', edgecolors='black')
>>> ax[0].legend()
>>> ax[1].legend()
```
Приведено ниже:

Очевидно, что большинство прогнозов близки друг к другу. Однако давайте сравним с реальными данными и посмотрим на прогноз для вида Setosa:```py
>>> setosa_idx = np.where(iris.target_names=='setosa')
>>> setosa_mask = iris.target == setosa_idx[0]
>>> y[setosa_mask][:5]
array([ 0.2, 0.2, 0.2, 0.2, 0.2])
>>> knnr.predict(X)[setosa_mask][:5]
array([ 0.28, 0.17, 0.21, 0.2 , 0.31])
>>> lr.predict(X)[setosa_mask][:5]
array([ 0.44636645, 0.53893889, 0.29846368, 0.27338255, 0.32612885])
```
Просматривая график, можно заметить, что для вида Setosa (кластер слева сверху) линейная регрессия переоценивает значения, тогда как KNN очень близко к истинным.
### Как это работает
Алгоритм KNN регрессии очень прост: он вычисляет среднее значение K наиболее близких точек к тестовой точке.
Давайте попробуем сделать прогноз для одной точки вручную:
```py
>>> example_point = X[0]
```
Теперь нам нужно найти 10 ближайших точек к нашей `example_point`:
```py
>>> from sklearn.metrics import pairwise
>>> distances_to_example = pairwise.pairwise_distances(X)[0]
>>> ten_closest_points = X[np.argsort(distances_to_example)][:10]
>>> ten_closest_y = y[np.argsort(distances_to_example)][:10]
>>> ten_closest_y.mean()
0.28000
```
Можно заметить, что полученный результат очень близок к ожидаемому.
```
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )