{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Python | Урок 3: построение моделей" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Загрузка необходимых библиотек\n", "\n", "Для работы нам понадобятся две библиотеки:\n", "1. `pandas`: при помощи нее мы считаем из файла таблицу с данными\n", "3. `sklearn`: библиотека инструментов для анализа данных и машинного обучения" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Подключаем нужные библиотеки\n", "import pandas as pd\n", "import sklearn" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Прочитаем данные уже известным нам методом\n", "table = pd.read_csv('dota2_skill_train.csv', index_col='id')\n", "\n", "# и поделим данные на признаки и целевую переменную\n", "x = table.drop(['skilled'], axis = 1)\n", "y = table['skilled']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "По традиции проверим, что все считалось корректно" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
player_teamwinner_teamdurationpre_game_durationfirst_blood_timefirst_blood_claimedhero_idhero_pick_orderleaver_statusparty_players...avg_deaths_x16avg_assists_x16avg_gpm_x16avg_xpm_x16best_kills_x16best_assists_x16best_gpm_x16best_xpm_x16win_streakbest_win_streak
id
7diredire214090129090903...815352430103055174525
8radiantradiant21389017405501...9192944251337445717212
12radiantradiant354790360081701...79493543231869176233
13direradiant18789028074902...712515583253486993506
14direradiant223290129014602...1016337452344367279709
\n", "

5 rows × 56 columns

\n", "
" ], "text/plain": [ " player_team winner_team duration pre_game_duration first_blood_time \\\n", "id \n", "7 dire dire 2140 90 129 \n", "8 radiant radiant 2138 90 174 \n", "12 radiant radiant 3547 90 360 \n", "13 dire radiant 1878 90 28 \n", "14 dire radiant 2232 90 129 \n", "\n", " first_blood_claimed hero_id hero_pick_order leaver_status \\\n", "id \n", "7 0 90 9 0 \n", "8 0 5 5 0 \n", "12 0 81 7 0 \n", "13 0 74 9 0 \n", "14 0 14 6 0 \n", "\n", " party_players ... avg_deaths_x16 avg_assists_x16 \\\n", "id ... \n", "7 3 ... 8 15 \n", "8 1 ... 9 19 \n", "12 1 ... 7 9 \n", "13 2 ... 7 12 \n", "14 2 ... 10 16 \n", "\n", " avg_gpm_x16 avg_xpm_x16 best_kills_x16 best_assists_x16 best_gpm_x16 \\\n", "id \n", "7 352 430 10 30 551 \n", "8 294 425 13 37 445 \n", "12 493 543 23 18 691 \n", "13 515 583 25 34 869 \n", "14 337 452 34 43 672 \n", "\n", " best_xpm_x16 win_streak best_win_streak \n", "id \n", "7 745 2 5 \n", "8 717 2 12 \n", "12 762 3 3 \n", "13 935 0 6 \n", "14 797 0 9 \n", "\n", "[5 rows x 56 columns]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Выводим первые 5 строк таблицы признаков\n", "x.head()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "id\n", "7 1\n", "8 0\n", "12 0\n", "13 1\n", "14 1\n", "Name: skilled, dtype: int64" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# и первые 5 строк столбца целевой переменной\n", "y.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для задач классификации, как наша, очень важно знать - сбалансированы классы или нет. Другими словами: какое соотношение объектов разных классов в обучающей выборке?" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 60085\n", "1 39786\n", "Name: skilled, dtype: int64" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Для того, чтобы это проверить, используем метод value_counts() объекта y \n", "y.value_counts()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как видите, объектов класса \"0\" (неопытные игроки) в полтора раза больше, чем объектов класса \"1\" (опытные игроки). Это не самый большой дисбаланс классов, поэтому не волнуйтесь :)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Series([], dtype: int64)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Также важно проверить, нет ли пропусков в данных\n", "count_missings = x.isnull().sum()\n", "count_missings[count_missings > 0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Какое везение! Все данные на месте, пропусков нет. В реальных задача такое бывает редко, но здесь организаторы соревнования о вас позаботились и подготовили очень хороший набор данных." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# Подготовим данные для алгоритма\n", "# Заменим строковые значения в колонках 'player_team' и 'winner_team' на числовые\n", "x['player_team'] = x['player_team'].apply(lambda x: 1 if x == 'dire' else 0)\n", "x['winner_team'] = x['winner_team'].apply(lambda x: 1 if x == 'dire' else 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Построение модели" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# Для начала разбиваем выборку на обучающую и валидационную (проверочную)\n", "\n", "# Загружаем нужный модуль: train_test_split \n", "from sklearn.model_selection import train_test_split\n", "\n", "# Создаем 4 новых переменных разделив выборки x и y по строкам в соотношении 2 к 1 (размер тестовой выборки = 0.33)\n", "# 2 для обучения модели (с префиксом _train) \n", "# и 2 для проверки (с префиксом _valid) \n", "\n", "x_train, x_validation, y_train, y_validation = train_test_split(x, y, test_size=.33, random_state=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как вы уже знаете, у каждого алгоритма машинного обучения есть масса параметров. Мы будем использовать алгоритм RandomForestClassifier - этот \"лес\" является ансамблем большого количества деревьев решений, о которых мы говорили в видеоуроках. \n", "\n", "Чтобы не подбирать его параметры вручную, воспользуемся готовой библиотекой GridSearcCV, которой нужно передать набор значений какого-либо параметра (или нескольких) и дождаться результата." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fitting 3 folds for each of 3 candidates, totalling 9 fits\n", "[CV] n_estimators=10 .................................................\n", "[CV] n_estimators=10 .................................................\n", "[CV] n_estimators=10 .................................................\n", "[CV] n_estimators=50 .................................................\n", "[CV] ........ n_estimators=10, score=0.6686841515355302, total= 4.4s\n", "[CV] ........ n_estimators=10, score=0.6657550215208035, total= 4.3s\n", "[CV] n_estimators=50 .................................................\n", "[CV] n_estimators=50 .................................................\n", "[CV] ......... n_estimators=10, score=0.669521162123386, total= 4.2s\n", "[CV] n_estimators=100 ................................................\n", "[CV] ........ n_estimators=50, score=0.7026675633266084, total= 20.6s\n", "[CV] n_estimators=100 ................................................\n", "[CV] ........ n_estimators=50, score=0.7057926829268293, total= 20.0s\n", "[CV] ........ n_estimators=50, score=0.7019817073170732, total= 20.0s\n", "[CV] n_estimators=100 ................................................\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[Parallel(n_jobs=-1)]: Done 6 out of 9 | elapsed: 26.4s remaining: 13.2s\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "[CV] ....... n_estimators=100, score=0.7099305088545169, total= 35.6s\n", "[CV] ....... n_estimators=100, score=0.7076757532281205, total= 28.8s\n", "[CV] ....... n_estimators=100, score=0.7117109038737446, total= 27.7s\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[Parallel(n_jobs=-1)]: Done 9 out of 9 | elapsed: 55.9s finished\n" ] }, { "data": { "text/plain": [ "GridSearchCV(cv=None, error_score='raise',\n", " estimator=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',\n", " max_depth=None, max_features='auto', max_leaf_nodes=None,\n", " min_impurity_decrease=0.0, min_impurity_split=None,\n", " min_samples_leaf=1, min_samples_split=2,\n", " min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,\n", " oob_score=False, random_state=322, verbose=0, warm_start=False),\n", " fit_params=None, iid=True, n_jobs=-1,\n", " param_grid={'n_estimators': [10, 50, 100]}, pre_dispatch='2*n_jobs',\n", " refit=True, return_train_score='warn', scoring=None, verbose=3)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Загружаем классификатор RandomForestClassifier\n", "from sklearn.ensemble import RandomForestClassifier\n", "\n", "# Загружаем GridSearchCV для подбора параметров\n", "from sklearn.model_selection import GridSearchCV\n", "\n", "# Задаем 'сетку' возможных значений - количество деревьев решений в \"лесу\"\n", "param_grid = {'n_estimators': [10, 50, 100]}\n", "\n", "# Создаем объект, в котором для нашего классификатора будет идти поиск лучших параметров\n", "clf = GridSearchCV(RandomForestClassifier(random_state=322), param_grid, verbose=3, n_jobs=-1)\n", "\n", "# Обучаем этот объект на подготовленных данных\n", "clf.fit(x_train, y_train)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',\n", " max_depth=None, max_features='auto', max_leaf_nodes=None,\n", " min_impurity_decrease=0.0, min_impurity_split=None,\n", " min_samples_leaf=1, min_samples_split=2,\n", " min_weight_fraction_leaf=0.0, n_estimators=100, n_jobs=1,\n", " oob_score=False, random_state=322, verbose=0, warm_start=False)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Итак, подбор параметров окончен, посмотрим на \"лучший\" по мнению GridSearchCV классификатор\n", "best_clf = clf.best_estimator_\n", "best_clf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Обратите внимание на параметр 'n_estimators=100' - получилось, что 100 деревьев самый лучшей вариант среди `[10, 50, 100]`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Оценим качество модели" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Точность на валидации: 0.712452211906\n" ] } ], "source": [ "# Теперь сделаем предсказание переменной 'skilled' на валидационной выборке и оценим качество модели\n", "\n", "# Загружаем модуль accuracy_score - который посчитает нам долю правильных ответов, \n", "# сравнив истинные значения y и предсказанные\n", "from sklearn.metrics import accuracy_score\n", "\n", "# Считаем долю правильных ответов\n", "validation_acc = accuracy_score(y_validation, best_clf.predict(x_validation))\n", "\n", "# Выводим на экран\n", "print('Точность на валидации:', validation_acc)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Это значение говорит о том, что данный классификатор \"угадал\" 71% правильных ответов - согласитесь, не очень хороший предсказатель. Что же делать?\n", "\n", "Сейчас, когда вы познакомились с основными библиотеками python для машинного обучения, самое время переходить к прорешиванию [базового решения соревнования](https://github.com/sberbank-ai/ai-academy-2019/blob/master/Dota2SkillPrediction_Tutorial.ipynb)! \n", "\n", "C его помощью вы сможете сделать свой первый сабмит, а также, немного усовершенствов, занять достойное место в рейтинге и попасть в финал!" ] } ], "metadata": { "kernelspec": { "display_name": "Python [Root]", "language": "python", "name": "Python [Root]" }, "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.5.2" } }, "nbformat": 4, "nbformat_minor": 2 }