Авторы материала: Ольга Дайховская (@aiho в Slack ODS), Юрий Кашницкий (@yorko в Slack ODS). Материал распространяется на условиях лицензии Creative Commons CC BY-NC-SA 4.0. Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала.
В этом задании мы разберемся с тем, как работают методы снижения размерности и кластеризации данных. Заодно еще раз попрактикуемся в задаче классификации.
Мы будем работать с набором данных Samsung Human Activity Recognition. Скачайте данные отсюда. Данные поступают с акселерометров и гироскопов мобильных телефонов Samsung Galaxy S3 (подробнее про признаки – по ссылке на UCI выше), также известен вид активности человека с телефоном в кармане – ходил ли он, стоял, лежал, сидел или шел вверх/вниз по лестнице.
Вначале мы представим, что вид активности нам неизвестнен, и попробуем кластеризовать людей чисто на основе имеющихся признаков. Затем решим задачу определения вида физической активности именно как задачу классификации.
Заполните код в клетках (где написано "Ваш код здесь") и ответьте на вопросы в веб-форме.
import numpy as np
import pandas as pd
import seaborn as sns
from tqdm import tqdm_notebook
%matplotlib inline
from matplotlib import pyplot as plt
plt.style.use(['seaborn-darkgrid'])
plt.rcParams['figure.figsize'] = (12, 9)
plt.rcParams['font.family'] = 'DejaVu Sans'
from sklearn import metrics
from sklearn.cluster import AgglomerativeClustering, KMeans, SpectralClustering
from sklearn.decomposition import PCA
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
RANDOM_STATE = 17
X_train = np.loadtxt("../../data/samsung_HAR/samsung_train.txt")
y_train = np.loadtxt("../../data/samsung_HAR/samsung_train_labels.txt").astype(int)
X_test = np.loadtxt("../../data/samsung_HAR/samsung_test.txt")
y_test = np.loadtxt("../../data/samsung_HAR/samsung_test_labels.txt").astype(int)
# Проверим размерности
assert(X_train.shape == (7352, 561) and y_train.shape == (7352,))
assert(X_test.shape == (2947, 561) and y_test.shape == (2947,))
Для кластеризации нам не нужен вектор ответов, поэтому будем работать с объединением обучающей и тестовой выборок. Объедините X_train с X_test, а y_train – с y_test.
# Ваш код здесь
X =
y =
Определим число уникальных значений меток целевого класса.
np.unique(y)
n_classes = np.unique(y).size
уж простите, если звучание этих существительных кажется корявым :)
Отмасштабируйте выборку с помощью StandardScaler
с параметрами по умолчанию.
# Ваш код здесь
scaler =
X_scaled =
Понижаем размерность с помощью PCA, оставляя столько компонент, сколько нужно для того, чтобы объяснить как минимум 90% дисперсии исходных (отмасштабированных) данных. Используйте отмасштабированную выборку и зафиксируйте random_state (константа RANDOM_STATE).
# Ваш код здесь
pca =
X_pca =
Вопрос 1:
Какое минимальное число главных компонент нужно выделить, чтобы объяснить 90% дисперсии исходных (отмасштабированных) данных?
# Ваш код здесь
Варианты:
Вопрос 2:
Сколько процентов дисперсии приходится на первую главную компоненту? Округлите до целых процентов.
Варианты:
# Ваш код здесь
Визуализируйте данные в проекции на первые две главные компоненты.
# Ваш код здесь
plt.scatter(, , c=y, s=20, cmap='viridis');
Вопрос 3:
Если все получилось правильно, Вы увидите сколько-то кластеров, почти идеально отделенных друг от друга. Какие виды активности входят в эти кластеры?
Ответ:
Сделайте кластеризацию данных методом KMeans
, обучив модель на данных со сниженной за счет PCA размерностью. В данном случае мы подскажем, что нужно искать именно 6 кластеров, но в общем случае мы не будем знать, сколько кластеров надо искать.
Параметры:
Остальные параметры со значениями по умолчанию.
# Ваш код здесь
Визуализируйте данные в проекции на первые две главные компоненты. Раскрасьте точки в соответствии с полученными метками кластеров.
# Ваш код здесь
plt.scatter(, , c=cluster_labels, s=20, cmap='viridis');
Посмотрите на соответствие между метками кластеров и исходными метками классов и на то, какие виды активностей алгоритм KMeans
путает.
tab = pd.crosstab(y, cluster_labels, margins=True)
tab.index = ['ходьба', 'подъем вверх по лестнице',
'спуск по лестнице', 'сидение', 'стояние', 'лежание', 'все']
tab.columns = ['cluster' + str(i + 1) for i in range(6)] + ['все']
tab
Видим, что каждому классу (т.е. каждой активности) соответствуют несколько кластеров. Давайте посмотрим на максимальную долю объектов в классе, отнесенных к какому-то одному кластеру. Это будет простой метрикой, характеризующей, насколько легко класс отделяется от других при кластеризации.
Пример: если для класса "спуск по лестнице", в котором 1406 объектов, распределение кластеров такое:
то такая доля будет 900 / 1406 $\approx$ 0.64.
Вопрос 4:
Какой вид активности отделился от остальных лучше всего в терминах простой метрики, описанной выше?
Ответ:
Видно, что kMeans не очень хорошо отличает только активности друг от друга. Используйте метод локтя, чтобы выбрать оптимальное количество кластеров. Параметры алгоритма и данные используем те же, что раньше, меняем только n_clusters
.
# Ваш код здесь
inertia = []
for k in tqdm_notebook(range(1, n_classes + 1)):
#
#
Вопрос 5:
Какое количество кластеров оптимально выбрать, согласно методу локтя?
Ответ:
Попробуем еще один метод кластеризации, который описывался в статье – агломеративную кластеризацию.
ag = AgglomerativeClustering(n_clusters=n_classes,
linkage='ward').fit(X_pca)
Посчитайте Adjusted Rand Index (sklearn.metrics
) для получившегося разбиения на кластеры и для KMeans
с параметрами из задания к 4 вопросу.
# Ваш код здесь
Вопрос 6:
Отметьте все верные утверждения.
Варианты:
Можно заметить, что задача не очень хорошо решается именно как задача кластеризации, если выделять несколько кластеров (> 2). Давайте теперь решим задачу классификации, вспомнив, что данные у нас размечены.
Для классификации используйте метод опорных векторов – класс sklearn.svm.LinearSVC
. Мы в курсе отдельно не рассматривали этот алгоритм, но он очень известен, почитать про него можно, например, в материалах Евгения Соколова – тут.
Настройте для LinearSVC
гиперпараметр C
с помощью GridSearchCV
.
StandardScaler
на обучающей выборке (со всеми исходными признаками), прмиените масштабирование к тестовой выборкеGridSearchCV
укажите cv=3.# Ваш код здесь
#
X_train_scaled =
X_test_scaled =
svc = LinearSVC(random_state=RANDOM_STATE)
svc_params = {'C': [0.001, 0.01, 0.1, 1, 10]}
# Ваш код здесь
best_svc =
# Ваш код здесь
Вопрос 7
Какое значение гиперпараметра C
было выбрано лучшим по итогам кросс-валидации?
Ответ:
y_predicted = best_svc.predict(X_test_scaled)
tab = pd.crosstab(y_test, y_predicted, margins=True)
tab.index = ['ходьба', 'подъем вверх по лестнице', 'спуск по лестнице',
'сидение', 'стояние', 'лежание', 'все']
tab.columns = tab.index
tab
Вопрос 8:
Какой вид активности SVM определяет хуже всего в терминах точности? Полноты?
Ответ:
Наконец, проделайте то же самое, что в 7 вопросе, только добавив PCA.
X_train_scaled
и X_test_scaled
C
на кросс-валидации по обучающей выборке с PCA-преобразованием. Вы заметите, насколько это проходит быстрее, чем раньше.Вопрос 9:
Какова разность между лучшим качеством (долей верных ответов) на кросс-валидации в случае всех 561 исходных признаков и во втором случае, когда применялся метод главных компонент? Округлите до целых процентов.
Варианты:
Вопрос 10:
Выберите все верные утверждения:
Варианты: