[mlcourse.ai](https://mlcourse.ai) – открытый курс OpenDataScience по машинному обучению \n",
" \n",
"Авторы материала: Ольга Дайховская (@aiho в Slack ODS), Юрий Кашницкий (@yorko в Slack ODS). Материал распространяется на условиях лицензии [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#
Домашнее задание № 7 (демо)\n",
"##
Обучение без учителя: метод главных компонент и кластеризация"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В этом задании мы разберемся с тем, как работают методы снижения размерности и кластеризации данных. Заодно еще раз попрактикуемся в задаче классификации.\n",
"\n",
"Мы будем работать с набором данных [Samsung Human Activity Recognition](https://archive.ics.uci.edu/ml/datasets/Human+Activity+Recognition+Using+Smartphones). Скачайте данные [отсюда](https://drive.google.com/file/d/14RukQ0ylM2GCdViUHBBjZ2imCaYcjlux/view?usp=sharing). Данные поступают с акселерометров и гироскопов мобильных телефонов Samsung Galaxy S3 (подробнее про признаки – по ссылке на UCI выше), также известен вид активности человека с телефоном в кармане – ходил ли он, стоял, лежал, сидел или шел вверх/вниз по лестнице. \n",
"\n",
"Вначале мы представим, что вид активности нам неизвестнен, и попробуем кластеризовать людей чисто на основе имеющихся признаков. Затем решим задачу определения вида физической активности именно как задачу классификации. \n",
"\n",
"Заполните код в клетках (где написано \"Ваш код здесь\") и ответьте на вопросы в [веб-форме](https://docs.google.com/forms/d/1qzcrfsNFy-e4TW59v2fqMj_OTom2SIOxtq4MWlI92p0)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import seaborn as sns\n",
"from tqdm import tqdm_notebook\n",
"\n",
"%matplotlib inline\n",
"from matplotlib import pyplot as plt\n",
"\n",
"plt.style.use(['seaborn-darkgrid'])\n",
"plt.rcParams['figure.figsize'] = (12, 9)\n",
"plt.rcParams['font.family'] = 'DejaVu Sans'\n",
"\n",
"from sklearn import metrics\n",
"from sklearn.cluster import AgglomerativeClustering, KMeans, SpectralClustering\n",
"from sklearn.decomposition import PCA\n",
"from sklearn.model_selection import GridSearchCV\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.svm import LinearSVC\n",
"\n",
"RANDOM_STATE = 17"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"X_train = np.loadtxt(\"../../data/samsung_HAR/samsung_train.txt\")\n",
"y_train = np.loadtxt(\"../../data/samsung_HAR/samsung_train_labels.txt\").astype(int)\n",
"\n",
"X_test = np.loadtxt(\"../../data/samsung_HAR/samsung_test.txt\")\n",
"y_test = np.loadtxt(\"../../data/samsung_HAR/samsung_test_labels.txt\").astype(int)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Проверим размерности\n",
"assert(X_train.shape == (7352, 561) and y_train.shape == (7352,))\n",
"assert(X_test.shape == (2947, 561) and y_test.shape == (2947,))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Для кластеризации нам не нужен вектор ответов, поэтому будем работать с объединением обучающей и тестовой выборок. Объедините *X_train* с *X_test*, а *y_train* – с *y_test*. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь\n",
"X = \n",
"y = "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Определим число уникальных значений меток целевого класса."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"np.unique(y)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"n_classes = np.unique(y).size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Эти метки соответствуют:](https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.names)\n",
"- 1 - ходьбе\n",
"- 2 - подъему вверх по лестнице\n",
"- 3 - спуску по лестнице\n",
"- 4 - сидению\n",
"- 5 - стоянию\n",
"- 6 - лежанию\n",
"\n",
"*уж простите, если звучание этих существительных кажется корявым :)*"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Отмасштабируйте выборку с помощью `StandardScaler` с параметрами по умолчанию."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь\n",
"scaler = \n",
"X_scaled = "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Понижаем размерность с помощью PCA, оставляя столько компонент, сколько нужно для того, чтобы объяснить как минимум 90% дисперсии исходных (отмасштабированных) данных. Используйте отмасштабированную выборку и зафиксируйте random_state (константа RANDOM_STATE)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь\n",
"pca = \n",
"X_pca = "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Вопрос 1:** \n",
"Какое минимальное число главных компонент нужно выделить, чтобы объяснить 90% дисперсии исходных (отмасштабированных) данных?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Варианты:**\n",
"- 56 \n",
"- 65\n",
"- 66\n",
"- 193"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Вопрос 2:** \n",
"Сколько процентов дисперсии приходится на первую главную компоненту? Округлите до целых процентов. \n",
"\n",
"**Варианты:**\n",
"- 45\n",
"- 51\n",
"- 56\n",
"- 61"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Визуализируйте данные в проекции на первые две главные компоненты."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь\n",
"plt.scatter(, , c=y, s=20, cmap='viridis');"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Вопрос 3:** \n",
"Если все получилось правильно, Вы увидите сколько-то кластеров, почти идеально отделенных друг от друга. Какие виды активности входят в эти кластеры? \n",
"\n",
"**Ответ:**\n",
"- 1 кластер: все 6 активностей\n",
"- 2 кластера: (ходьба, подъем вверх по лестнице, спуск по лестнице) и (сидение, стояние, лежание)\n",
"- 3 кластера: (ходьба), (подъем вверх по лестнице, спуск по лестнице) и (сидение, стояние, лежание)\n",
"- 6 кластеров"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"------------------------------"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сделайте кластеризацию данных методом `KMeans`, обучив модель на данных со сниженной за счет PCA размерностью. В данном случае мы подскажем, что нужно искать именно 6 кластеров, но в общем случае мы не будем знать, сколько кластеров надо искать.\n",
"\n",
"Параметры:\n",
"\n",
"- **n_clusters** = n_classes (число уникальных меток целевого класса)\n",
"- **n_init** = 100\n",
"- **random_state** = RANDOM_STATE (для воспроизводимости результата)\n",
"\n",
"Остальные параметры со значениями по умолчанию."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Визуализируйте данные в проекции на первые две главные компоненты. Раскрасьте точки в соответствии с полученными метками кластеров."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь\n",
"plt.scatter(, , c=cluster_labels, s=20, cmap='viridis');"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Посмотрите на соответствие между метками кластеров и исходными метками классов и на то, какие виды активностей алгоритм `KMeans` путает."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tab = pd.crosstab(y, cluster_labels, margins=True)\n",
"tab.index = ['ходьба', 'подъем вверх по лестнице', \n",
" 'спуск по лестнице', 'сидение', 'стояние', 'лежание', 'все']\n",
"tab.columns = ['cluster' + str(i + 1) for i in range(6)] + ['все']\n",
"tab"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Видим, что каждому классу (т.е. каждой активности) соответствуют несколько кластеров. Давайте посмотрим на максимальную долю объектов в классе, отнесенных к какому-то одному кластеру. Это будет простой метрикой, характеризующей, насколько легко класс отделяется от других при кластеризации. \n",
"\n",
"Пример: если для класса \"спуск по лестнице\", в котором 1406 объектов, распределение кластеров такое:\n",
" - кластер 1 – 900\n",
" - кластер 3 – 500\n",
" - кластер 6 – 6,\n",
" \n",
"то такая доля будет 900 / 1406 $\\approx$ 0.64.\n",
" \n",
"\n",
"**Вопрос 4:** \n",
"Какой вид активности отделился от остальных лучше всего в терминах простой метрики, описанной выше? \n",
"\n",
"**Ответ:**\n",
"- ходьба\n",
"- стояние\n",
"- спуск по лестнице\n",
"- перечисленные варианты не подходят"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Видно, что kMeans не очень хорошо отличает только активности друг от друга. Используйте метод локтя, чтобы выбрать оптимальное количество кластеров. Параметры алгоритма и данные используем те же, что раньше, меняем только `n_clusters`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь\n",
"inertia = []\n",
"for k in tqdm_notebook(range(1, n_classes + 1)):\n",
" #\n",
" #"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Вопрос 5:** \n",
"Какое количество кластеров оптимально выбрать, согласно методу локтя? \n",
"\n",
"**Ответ:**\n",
"- 1\n",
"- 2\n",
"- 3\n",
"- 4"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"------------------------"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Попробуем еще один метод кластеризации, который описывался в статье – агломеративную кластеризацию."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ag = AgglomerativeClustering(n_clusters=n_classes, \n",
" linkage='ward').fit(X_pca)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Посчитайте Adjusted Rand Index (`sklearn.metrics`) для получившегося разбиения на кластеры и для `KMeans` с параметрами из задания к 4 вопросу."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Вопрос 6:** \n",
"Отметьте все верные утверждения. \n",
"\n",
"**Варианты:**\n",
"- Согласно ARI, KMeans справился с кластеризацией хуже, чем Agglomerative Clustering\n",
"- Для ARI не имеет значения какие именно метки присвоены кластерам, имеет значение только разбиение объектов на кластеры\n",
"- В случае случайного разбиения на кластеры ARI будет близок к нулю"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"-------------------------------"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можно заметить, что задача не очень хорошо решается именно как задача кластеризации, если выделять несколько кластеров (> 2). Давайте теперь решим задачу классификации, вспомнив, что данные у нас размечены. \n",
"\n",
"Для классификации используйте метод опорных векторов – класс `sklearn.svm.LinearSVC`. Мы в курсе отдельно не рассматривали этот алгоритм, но он очень известен, почитать про него можно, например, в материалах Евгения Соколова – [тут](https://github.com/esokolov/ml-course-msu/blob/master/ML16/lecture-notes/Sem11_linear.pdf). \n",
"\n",
"Настройте для `LinearSVC` гиперпараметр `C` с помощью `GridSearchCV`. \n",
"\n",
"- Обучите новый `StandardScaler` на обучающей выборке (со всеми исходными признаками), прмиените масштабирование к тестовой выборке\n",
"- В `GridSearchCV` укажите cv=3."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь\n",
"#\n",
"X_train_scaled = \n",
"X_test_scaled = "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"svc = LinearSVC(random_state=RANDOM_STATE)\n",
"svc_params = {'C': [0.001, 0.01, 0.1, 1, 10]}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь\n",
"best_svc = "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ваш код здесь"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Вопрос 7** \n",
"Какое значение гиперпараметра `C` было выбрано лучшим по итогам кросс-валидации? \n",
"\n",
"**Ответ:**\n",
"- 0.001\n",
"- 0.01\n",
"- 0.1\n",
"- 1\n",
"- 10"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"y_predicted = best_svc.predict(X_test_scaled)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tab = pd.crosstab(y_test, y_predicted, margins=True)\n",
"tab.index = ['ходьба', 'подъем вверх по лестнице', 'спуск по лестнице', \n",
" 'сидение', 'стояние', 'лежание', 'все']\n",
"tab.columns = tab.index\n",
"tab"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Вопрос 8:** \n",
"Какой вид активности SVM определяет хуже всего в терминах точности? Полноты? \n",
"\n",
"**Ответ:**\n",
"- по точности – подъем вверх по лестнице, по полноте – лежание\n",
"- по точности – лежание, по полноте – сидение\n",
"- по точности – ходьба, по полноте – ходьба\n",
"- по точности – сидение, по полноте – стояние"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Наконец, проделайте то же самое, что в 7 вопросе, только добавив PCA.\n",
"\n",
"- Используйте выборки `X_train_scaled` и `X_test_scaled`\n",
"- Обучите тот же PCA, что раньше, на отмасшабированной обучающей выборке, примените преобразование к тестовой\n",
"- Настройте гиперпараметр `C` на кросс-валидации по обучающей выборке с PCA-преобразованием. Вы заметите, насколько это проходит быстрее, чем раньше.\n",
"\n",
"**Вопрос 9:** \n",
"Какова разность между лучшим качеством (долей верных ответов) на кросс-валидации в случае всех 561 исходных признаков и во втором случае, когда применялся метод главных компонент? Округлите до целых процентов. \n",
"\n",
"**Варианты:**\n",
"- Качество одинаковое\n",
"- 2%\n",
"- 4% \n",
"- 10%\n",
"- 20%\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Вопрос 10:** \n",
"Выберите все верные утверждения:\n",
"\n",
"**Варианты:**\n",
"- Метод главных компонент в данном случае позволил уменьшить время обучения модели, при этом качество (доля верных ответов на кросс-валидации) очень пострадало, более чем на 10%\n",
"- PCA можно использовать для визуализации данных, однако для этой задачи есть и лучше подходящие методы, например, tSNE. Зато PCA имеет меньшую вычислительную сложность\n",
"- PCA строит линейные комбинации исходных признаков, и в некоторых задачах они могут плохо интерпретироваться человеком"
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}