{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "## Специализация \"Машинное обучение и анализ данных\"\n", "
Автор материала: программист-исследователь Mail.Ru Group, старший преподаватель Факультета Компьютерных Наук ВШЭ [Юрий Кашницкий](https://yorko.github.io/)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#
Capstone проект №1
Идентификация пользователей по посещенным веб-страницам\n", "\n", "\n", "#
Неделя 4. Сравнение алгоритмов классификации\n", "\n", "Теперь мы наконец подойдем к обучению моделей классификации, сравним на кросс-валидации несколько алгоритмов, разберемся, какие параметры длины сессии (*session_length* и *window_size*) лучше использовать. Также для выбранного алгоритма построим кривые валидации (как качество классификации зависит от одного из гиперпараметров алгоритма) и кривые обучения (как качество классификации зависит от объема выборки).\n", "\n", "**План 4 недели:**\n", "- Часть 1. Сравнение нескольких алгоритмов на сессиях из 10 сайтов\n", "- Часть 2. Выбор параметров – длины сессии и ширины окна\n", "- Часть 3. Идентификация конкретного пользователя и кривые обучения\n", " \n", "\n", "\n", "**В этой части проекта Вам могут быть полезны видеозаписи следующих лекций курса \"Обучение на размеченных данных\":**\n", " - [Линейная классификация](https://www.coursera.org/learn/supervised-learning/lecture/jqLcO/linieinaia-klassifikatsiia)\n", " - [Сравнение алгоритмов и выбор гиперпараметров](https://www.coursera.org/learn/supervised-learning/lecture/aF79U/sravnieniie-alghoritmov-i-vybor-ghipierparamietrov)\n", " - [Кросс-валидация. Sklearn.cross_validation](https://www.coursera.org/learn/supervised-learning/lecture/XbHEk/kross-validatsiia-sklearn-cross-validation)\n", " - [Линейные модели. Sklearn.linear_model. Классификация](https://www.coursera.org/learn/supervised-learning/lecture/EBg9t/linieinyie-modieli-sklearn-linear-model-klassifikatsiia)\n", " - и многие другие\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Задание\n", "1. Заполните код в этой тетрадке \n", "2. Если вы проходите специализацию Яндеса и МФТИ, пошлите файл с ответами в соответствующем Programming Assignment.
Если вы проходите курс ODS, выберите ответы в [веб-форме](https://docs.google.com/forms/d/12VB7kmzDoSVzSpQNaJp0tR-2t8K8PynQopP3dypf7i4). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# pip install watermark\n", "%load_ext watermark" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%watermark -v -m -p numpy,scipy,pandas,matplotlib,statsmodels,sklearn -g" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from __future__ import division, print_function\n", "# отключим всякие предупреждения Anaconda\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "from time import time\n", "import itertools\n", "import os\n", "import numpy as np\n", "import pandas as pd\n", "import seaborn as sns\n", "%matplotlib inline\n", "from matplotlib import pyplot as plt\n", "import pickle\n", "from scipy.sparse import csr_matrix\n", "from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold, GridSearchCV\n", "from sklearn.metrics import accuracy_score, f1_score" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Поменяйте на свой путь к данным\n", "PATH_TO_DATA = 'capstone_user_identification'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Часть 1. Сравнение нескольких алгоритмов на сессиях из 10 сайтов" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Загрузим сериализованные ранее объекты *X_sparse_10users* и *y_10users*, соответствующие обучающей выборке для 10 пользователей.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(os.path.join(PATH_TO_DATA, \n", " 'X_sparse_10users.pkl'), 'rb') as X_sparse_10users_pkl:\n", " X_sparse_10users = pickle.load(X_sparse_10users_pkl)\n", "with open(os.path.join(PATH_TO_DATA, \n", " 'y_10users.pkl'), 'rb') as y_10users_pkl:\n", " y_10users = pickle.load(y_10users_pkl)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Здесь более 14 тысяч сессий и почти 5 тысяч уникальных посещенных сайтов.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X_sparse_10users.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Разобьем выборку на 2 части. На одной будем проводить кросс-валидацию, на второй – оценивать модель, обученную после кросс-валидации.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X_train, X_valid, y_train, y_valid = train_test_split(X_sparse_10users, y_10users, \n", " test_size=0.3, \n", " random_state=17, stratify=y_10users)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Зададим заранее тип кросс-валидации: 3-кратная, с перемешиванием, параметр random_state=17 – для воспроизводимости.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "skf = StratifiedKFold(n_splits=3, shuffle=True, random_state=17)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Вспомогательная функция для отрисовки кривых валидации после запуска GridSearchCV (или RandomizedCV).**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot_validation_curves(param_values, grid_cv_results_):\n", " train_mu, train_std = grid_cv_results_['mean_train_score'], grid_cv_results_['std_train_score']\n", " valid_mu, valid_std = grid_cv_results_['mean_test_score'], grid_cv_results_['std_test_score']\n", " train_line = plt.plot(param_values, train_mu, '-', label='train', color='green')\n", " valid_line = plt.plot(param_values, valid_mu, '-', label='test', color='red')\n", " plt.fill_between(param_values, train_mu - train_std, train_mu + train_std, edgecolor='none',\n", " facecolor=train_line[0].get_color(), alpha=0.2)\n", " plt.fill_between(param_values, valid_mu - valid_std, valid_mu + valid_std, edgecolor='none',\n", " facecolor=valid_line[0].get_color(), alpha=0.2)\n", " plt.legend()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**1. Обучите `KNeighborsClassifier` со 100 ближайшими соседями (остальные параметры оставьте по умолчанию, только `n_jobs`=-1 для распараллеливания) и посмотрите на долю правильных ответов на 3-кратной кросс-валидации (ради воспроизводимости используйте для этого объект `StratifiedKFold` `skf`) по выборке `(X_train, y_train)` и отдельно на выборке `(X_valid, y_valid)`.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.neighbors import KNeighborsClassifier" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "knn = KNeighborsClassifier ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Вопрос 1. Посчитайте доли правильных ответов для KNeighborsClassifier на кросс-валидации и отложенной выборке. Округлите каждое до 3 знаков после запятой и введите через пробел.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**2. Обучите случайный лес (`RandomForestClassifier`) из 100 деревьев (для воспроизводимости `random_state`=17). Посмотрите на OOB-оценку (для этого надо сразу установить `oob_score`=True) и на долю правильных ответов на выборке `(X_valid, y_valid)`. Для распараллеливания задайте `n_jobs`=-1.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.ensemble import RandomForestClassifier" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "forest = RandomForestClassifier ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Вопрос 2. Посчитайте доли правильных ответов для `RandomForestClassifier` при Out-of-Bag оценке и на отложенной выборке. Округлите каждое до 3 знаков после запятой и введите через пробел.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "write_answer_to_file(''' ВАШ КОД ЗДЕСЬ ''',\n", " 'answer4_2.txt')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!cat answer4_2.txt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**3. Обучите логистическую регрессию (`LogisticRegression`) с параметром `C` по умолчанию и `random_state`=17 (для воспроизводимости). Посмотрите на долю правильных ответов на кросс-валидации (используйте объект `skf`, созданный ранее) и на выборке `(X_valid, y_valid)`. Для распараллеливания задайте `n_jobs=-1`.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.linear_model import LogisticRegression, LogisticRegressionCV" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "logit = LogisticRegression ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Почитайте документацию к [LogisticRegressionCV](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegressionCV.html). Логистическая регрессия хорошо изучена, и для нее существуют алгоритмы быстрого подбора параметра регуляризации `C` (быстрее, чем с `GridSearchCV`).**\n", "\n", "**С помощью `LogisticRegressionCV` подберите параметр `C` для `LogisticRegression` сначала в широком диапазоне: 10 значений от 1e-4 до 1e2, используйте `logspace` из `NumPy`. Укажите у `LogisticRegressionCV` параметры `multi_class`='multinomial' и `random_state`=17. Для кросс-валидации используйте объект `skf`, созданный ранее. Для распараллеливания задайте `n_jobs=-1`.**\n", "\n", "**Нарисуйте кривые валидации по параметру `C`.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "logit_c_values1 = np.logspace(-4, 2, 10)\n", "\n", "logit_grid_searcher1 = LogisticRegressionCV ''' ВАШ КОД ЗДЕСЬ ''' \n", "logit_grid_searcher1.fit(X_train, y_train)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Средние значения доли правильных ответов на кросс-валидации по каждому из 10 параметров `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "logit_mean_cv_scores1 = ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выведите лучшее значение доли правильных ответов на кросс-валидации и соответствующее значение `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Нарисуйте график зависимости доли правильных ответов на кросс-валидации от `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(logit_c_values1, logit_mean_cv_scores1);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Теперь то же самое, только значения параметра `C` перебирайте в диапазоне `np.linspace`(0.1, 7, 20). Опять нарисуйте кривые валидации, определите максимальное значение доли правильных ответов на кросс-валидации.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "logit_c_values2 = np.linspace(0.1, 7, 20)\n", "\n", "logit_grid_searcher2 = LogisticRegressionCV ''' ВАШ КОД ЗДЕСЬ '''\n", "logit_grid_searcher2.fit(X_train, y_train)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Средние значения доли правильных ответов на кросс-валидации по каждому из 10 параметров `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выведите лучшее значение доли правильных ответов на кросс-валидации и соответствующее значение `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Нарисуйте график зависимости доли правильных ответов на кросс-валидации от `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(logit_c_values2, logit_mean_cv_scores2);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выведите долю правильных ответов на выборке `(X_valid, y_valid)` для логистической регрессии с лучшим найденным значением `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "logit_cv_acc = accuracy_score ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Вопрос 3. Посчитайте доли правильных ответов для `logit_grid_searcher2` на кросс-валидации для лучшего значения параметра `C` и на отложенной выборке. Округлите каждое до 3 знаков после запятой и выведите через пробел.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**4. Обучите линейный SVM (`LinearSVC`) с параметром `C`=1 и `random_state`=17 (для воспроизводимости). Посмотрите на долю правильных ответов на кросс-валидации (используйте объект `skf`, созданный ранее) и на выборке `(X_valid, y_valid)`.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.svm import LinearSVC" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "svm = LinearSVC ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**С помощью `GridSearchCV` подберите параметр `C` для SVM сначала в широком диапазоне: 10 значений от 1e-4 до 1e4, используйте `linspace` из NumPy. Нарисуйте кривые валидации.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "svm_params1 = {'C': np.linspace(1e-4, 1e4, 10)}\n", "\n", "svm_grid_searcher1 = GridSearchCV ''' ВАШ КОД ЗДЕСЬ '''\n", "svm_grid_searcher1.fit(X_train, y_train)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выведите лучшее значение доли правильных ответов на кросс-валидации и соответствующее значение `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Нарисуйте график зависимости доли правильных ответов на кросс-валидации от `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_validation_curves(svm_params1['C'], svm_grid_searcher1.cv_results_)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Но мы помним, что с параметром регуляризации по умолчанию (С=1) на кросс-валидации доля правильных ответов выше. Это тот случай (не редкий), когда можно ошибиться и перебирать параметры не в том диапазоне (причина в том, что мы взяли равномерную сетку на большом интервале и упустили действительно хороший интервал значений `C`). Здесь намного осмысленней подбирать `C` в районе 1, к тому же, так модель быстрее обучается, чем при больших `C`. **\n", "\n", "**С помощью `GridSearchCV` подберите параметр `C` для SVM в диапазоне (1e-3, 1), 30 значений, используйте `linspace` из NumPy. Нарисуйте кривые валидации.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "svm_params2 = {'C': np.linspace(1e-3, 1, 30)}\n", "\n", "svm_grid_searcher2 = GridSearchCV ''' ВАШ КОД ЗДЕСЬ '''\n", "svm_grid_searcher2.fit(X_train, y_train)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выведите лучшее значение доли правильных ответов на кросс-валидации и соответствующее значение `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Нарисуйте график зависимости доли правильных ответов на кросс-валидации от С." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_validation_curves(svm_params2['C'], svm_grid_searcher2.cv_results_)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выведите долю правильных ответов на выборке `(X_valid, y_valid)` для `LinearSVC` с лучшим найденным значением `C`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "svm_cv_acc = accuracy_score ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Вопрос 4. Посчитайте доли правильных ответов для `svm_grid_searcher2` на кросс-валидации для лучшего значения параметра `C` и на отложенной выборке. Округлите каждое до 3 знаков после запятой и выведите через пробел.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Часть 2. Выбор параметров – длины сессии и ширины окна" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Возьмем `LinearSVC`, показавший лучшее качество на кросс-валидации в 1 части, и проверим его работу еще на 8 выборках для 10 пользователей (с разными сочетаниями параметров *session_length* и *window_size*). Поскольку тут уже вычислений побольше, мы не будем каждый раз заново подбирать параметр регуляризации `C`.**\n", "\n", "**Определите функцию `model_assessment`, ее документация описана ниже. Обратите внимание на все детали. Например, на то, что разбиение выборки с `train_test_split` должно быть стратифицированным. Не теряйте нигде `random_state`.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def model_assessment(estimator, path_to_X_pickle, path_to_y_pickle, cv, random_state=17, test_size=0.3):\n", " '''\n", " Estimates CV-accuracy for (1 - test_size) share of (X_sparse, y) \n", " loaded from path_to_X_pickle and path_to_y_pickle and holdout accuracy for (test_size) share of (X_sparse, y).\n", " The split is made with stratified train_test_split with params random_state and test_size.\n", " \n", " :param estimator – Scikit-learn estimator (classifier or regressor)\n", " :param path_to_X_pickle – path to pickled sparse X (instances and their features)\n", " :param path_to_y_pickle – path to pickled y (responses)\n", " :param cv – cross-validation as in cross_val_score (use StratifiedKFold here)\n", " :param random_state – for train_test_split\n", " :param test_size – for train_test_split\n", " \n", " :returns mean CV-accuracy for (X_train, y_train) and accuracy for (X_valid, y_valid) where (X_train, y_train)\n", " and (X_valid, y_valid) are (1 - test_size) and (testsize) shares of (X_sparse, y).\n", " '''\n", " \n", " ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Убедитесь, что функция работает.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_assessment(svm_grid_searcher2.best_estimator_, \n", " os.path.join(PATH_TO_DATA, 'X_sparse_10users.pkl'),\n", " os.path.join(PATH_TO_DATA, 'y_10users.pkl'), skf, random_state=17, test_size=0.3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Примените функцию *model_assessment* для лучшего алгоритма из предыдущей части (а именно, `svm_grid_searcher2.best_estimator_`) и 9 выборок вида с разными сочетаниями параметров *session_length* и *window_size* для 10 пользователей. Выведите в цикле параметры *session_length* и *window_size*, а также результат вывода функции *model_assessment*. \n", "Удобно сделать так, чтоб *model_assessment* возвращала 3-им элементом время, за которое она выполнилась. На моем ноуте этот участок кода выполнился за 20 секунд. Но со 150 пользователями каждая итерация занимает уже несколько минут.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Здесь для удобства стоит создать копии ранее созданных pickle-файлов X_sparse_10users.pkl, X_sparse_150users.pkl, y_10users.pkl и y_150users.pkl, добавив к их названиям s10_w10, что означает длину сессии 10 и ширину окна 10. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!cp $PATH_TO_DATA/X_sparse_10users.pkl $PATH_TO_DATA/X_sparse_10users_s10_w10.pkl \n", "!cp $PATH_TO_DATA/X_sparse_150users.pkl $PATH_TO_DATA/X_sparse_150users_s10_w10.pkl \n", "!cp $PATH_TO_DATA/y_10users.pkl $PATH_TO_DATA/y_10users_s10_w10.pkl \n", "!cp $PATH_TO_DATA/y_150users.pkl $PATH_TO_DATA/y_150users_s10_w10.pkl " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "estimator = svm_grid_searcher2.best_estimator_\n", "\n", "for window_size, session_length in itertools.product([10, 7, 5], [15, 10, 7, 5]):\n", " if window_size <= session_length:\n", " path_to_X_pkl = ''' ВАШ КОД ЗДЕСЬ '''\n", " path_to_y_pkl = ''' ВАШ КОД ЗДЕСЬ '''\n", " print ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Вопрос 5. Посчитайте доли правильных ответов для `LinearSVC` с настроенным параметром `C` и выборки `X_sparse_10users_s15_w5`. Укажите доли правильных ответов на кросс-валидации и на отложенной выборке. Округлите каждое до 3 знаков после запятой и выведите через пробел.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Прокомментируйте полученные результаты. Сравните для 150 пользователей доли правильных ответов на кросс-валидации и оставленной выборке для сочетаний параметров (*session_length, window_size*): (5,5), (7,7) и (10,10). На среднем ноуте это может занять до часа – запаситесь терпением, это Data Science :) **\n", "\n", "**Сделайте вывод о том, как качество классификации зависит от длины сессии и ширины окна.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "estimator = svm_grid_searcher2.best_estimator_\n", "\n", "for window_size, session_length in [(5,5), (7,7), (10,10)]:\n", " path_to_X_pkl = ''' ВАШ КОД ЗДЕСЬ '''\n", " path_to_y_pkl = ''' ВАШ КОД ЗДЕСЬ '''\n", " print ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Вопрос 6. Посчитайте доли правильных ответов для `LinearSVC` с настроенным параметром `C` и выборки `X_sparse_150users`. Укажите доли правильных ответов на кросс-валидации и на отложенной выборке. Округлите каждое до 3 знаков после запятой и выведите через пробел.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Часть 3. Идентификация конкретного пользователя и кривые обучения" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Поскольку может разочаровать, что многоклассовая доля правильных ответов на выборке из 150 пользовалей невелика, порадуемся тому, что конкретного пользователя можно идентифицировать достаточно хорошо. **" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Загрузим сериализованные ранее объекты *X_sparse_150users* и *y_150users*, соответствующие обучающей выборке для 150 пользователей с параметрами (*session_length, window_size*) = (10,10). Так же точно разобьем их на 70% и 30%.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(os.path.join(PATH_TO_DATA, 'X_sparse_150users.pkl'), 'rb') as X_sparse_150users_pkl:\n", " X_sparse_150users = pickle.load(X_sparse_150users_pkl)\n", "with open(os.path.join(PATH_TO_DATA, 'y_150users.pkl'), 'rb') as y_150users_pkl:\n", " y_150users = pickle.load(y_150users_pkl)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X_train_150, X_valid_150, y_train_150, y_valid_150 = train_test_split(X_sparse_150users, \n", " y_150users, test_size=0.3, \n", " random_state=17, stratify=y_150users)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Обучите `LogisticRegressionCV` для одного значения параметра `C` (лучшего на кросс-валидации в 1 части, используйте точное значение, не на глаз). Теперь будем решать 150 задач \"Один-против-Всех\", поэтому укажите аргумент `multi_class`='ovr'. Как всегда, где возможно, указывайте `n_jobs=-1` и `random_state`=17.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "logit_cv_150users = LogisticRegressionCV ''' ВАШ КОД ЗДЕСЬ '''\n", "logit_cv_150users.fit(X_train_150, y_train_150)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Посмотрите на средние доли правильных ответов на кросс-валидации в задаче идентификации каждого пользователя по отдельности.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cv_scores_by_user = {}\n", "for user_id in logit_cv_150users.scores_:\n", " print('User {}, CV score: {}'.format ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Результаты кажутся впечатляющими, но возможно, мы забываем про дисбаланс классов, и высокую долю правильных ответов можно получить константным прогнозом. Посчитайте для каждого пользователя разницу между долей правильных ответов на кросс-валидации (только что посчитанную с помощью `LogisticRegressionCV`) и долей меток в *y_train_150*, отличных от ID \n", " этого пользователя (именно такую долю правильных ответов можно получить, если классификатор всегда \"говорит\", что это не пользователь с номером $i$ в задаче классификации $i$-vs-All).**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class_distr = np.bincount(y_train_150.astype('int'))\n", "\n", "for user_id in np.unique(y_train_150):\n", " ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "num_better_than_default = (np.array(list(acc_diff_vs_constant.values())) > 0).sum()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Вопрос 7. Посчитайте долю пользователей, для которых логистическая регрессия на кросс-валидации дает прогноз лучше константного. Округлите до 3 знаков после запятой.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Дальше будем строить кривые обучения для конкретного пользователя, допустим, для 128-го. Составьте новый бинарный вектор на основе *y_150users*, его значения будут 1 или 0 в зависимости от того, равен ли ID-шник пользователя 128.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y_binary_128 = ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.model_selection import learning_curve\n", "\n", "def plot_learning_curve(val_train, val_test, train_sizes, \n", " xlabel='Training Set Size', ylabel='score'):\n", " def plot_with_err(x, data, **kwargs):\n", " mu, std = data.mean(1), data.std(1)\n", " lines = plt.plot(x, mu, '-', **kwargs)\n", " plt.fill_between(x, mu - std, mu + std, edgecolor='none',\n", " facecolor=lines[0].get_color(), alpha=0.2)\n", " plot_with_err(train_sizes, val_train, label='train')\n", " plot_with_err(train_sizes, val_test, label='valid')\n", " plt.xlabel(xlabel); plt.ylabel(ylabel)\n", " plt.legend(loc='lower right');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Посчитайте доли правильных ответов на кросс-валидации в задаче классификации \"user128-vs-All\" в зависимости от размера выборки. Не помешает посмотреть встроенную документацию для *learning_curve*.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "train_sizes = np.linspace(0.25, 1, 20)\n", "estimator = svm_grid_searcher2.best_estimator_\n", "n_train, val_train, val_test = learning_curve ''' ВАШ КОД ЗДЕСЬ '''" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_learning_curve(val_train, val_test, n_train, \n", " xlabel='train_size', ylabel='accuracy')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Сделайте выводы о том, помогут ли алгоритму новые размеченные данные при той же постановке задачи.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Пути улучшения\n", "- конечно, можно проверить еще кучу алгоритмов, например, Xgboost, но в такой задаче очень маловероятно, что что-то справится лучше линейных методов\n", "- интересно проверить качество алгоритма на данных, где сессии выделялись не по количеству посещенных сайтов, а по времени, например, 5, 7, 10 и 15 минут. Отдельно стоит отметить данные нашего [соревнования](https://inclass.kaggle.com/c/catch-me-if-you-can-intruder-detection-through-webpage-session-tracking2) \n", "- опять же, если ресурсы позволяют, можно проверить, насколько хорошо можно решить задачу для 3000 пользователей\n", "\n", "\n", "На следующей неделе мы вспомним про линейные модели, обучаемые стохастическим градиентным спуском, и порадуемся тому, насколько быстрее они работают. Также сделаем первые (или не первые) посылки в [соревновании](https://inclass.kaggle.com/c/catch-me-if-you-can-intruder-detection-through-webpage-session-tracking2) Kaggle Inclass." ] } ], "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.6.4" } }, "nbformat": 4, "nbformat_minor": 1 }