Skip to content

Instantly share code, notes, and snippets.

@izmailovpavel
Created October 20, 2015 00:15
Show Gist options
  • Save izmailovpavel/477b54c4e5375e6b99e7 to your computer and use it in GitHub Desktop.
Save izmailovpavel/477b54c4e5375e6b99e7 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Лабораторная работа 2. Метод ближайших соседей и решающие деревья."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"ФИО: Измаилов Павел Алексеевич\n",
"\n",
"Группа: 317"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import time\n",
"from sklearn import cross_validation\n",
"from sklearn.neighbors import KNeighborsClassifier\n",
"from sklearn.metrics import roc_auc_score"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Все эксперименты в этой лабораторной работе предлагается проводить на данных соревнования Amazon Employee Access Challenge: https://www.kaggle.com/c/amazon-employee-access-challenge\n",
"\n",
"В данной задаче предлагается предсказать, будет ли одобрен запрос сотрудника на получение доступа к тому или иному ресурсу. Все признаки являются категориальными.\n",
"\n",
"Для удобства данные можно загрузить по ссылке: https://www.dropbox.com/s/q6fbs1vvhd5kvek/amazon.csv\n",
"\n",
"Сразу прочитаем данные и создадим разбиение на обучение и контроль:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>ACTION</th>\n",
" <th>RESOURCE</th>\n",
" <th>MGR_ID</th>\n",
" <th>ROLE_ROLLUP_1</th>\n",
" <th>ROLE_ROLLUP_2</th>\n",
" <th>ROLE_DEPTNAME</th>\n",
" <th>ROLE_TITLE</th>\n",
" <th>ROLE_FAMILY_DESC</th>\n",
" <th>ROLE_FAMILY</th>\n",
" <th>ROLE_CODE</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>39353</td>\n",
" <td>85475</td>\n",
" <td>117961</td>\n",
" <td>118300</td>\n",
" <td>123472</td>\n",
" <td>117905</td>\n",
" <td>117906</td>\n",
" <td>290919</td>\n",
" <td>117908</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1</td>\n",
" <td>17183</td>\n",
" <td>1540</td>\n",
" <td>117961</td>\n",
" <td>118343</td>\n",
" <td>123125</td>\n",
" <td>118536</td>\n",
" <td>118536</td>\n",
" <td>308574</td>\n",
" <td>118539</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1</td>\n",
" <td>36724</td>\n",
" <td>14457</td>\n",
" <td>118219</td>\n",
" <td>118220</td>\n",
" <td>117884</td>\n",
" <td>117879</td>\n",
" <td>267952</td>\n",
" <td>19721</td>\n",
" <td>117880</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1</td>\n",
" <td>36135</td>\n",
" <td>5396</td>\n",
" <td>117961</td>\n",
" <td>118343</td>\n",
" <td>119993</td>\n",
" <td>118321</td>\n",
" <td>240983</td>\n",
" <td>290919</td>\n",
" <td>118322</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1</td>\n",
" <td>42680</td>\n",
" <td>5905</td>\n",
" <td>117929</td>\n",
" <td>117930</td>\n",
" <td>119569</td>\n",
" <td>119323</td>\n",
" <td>123932</td>\n",
" <td>19793</td>\n",
" <td>119325</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" ACTION RESOURCE MGR_ID ROLE_ROLLUP_1 ROLE_ROLLUP_2 ROLE_DEPTNAME \\\n",
"0 1 39353 85475 117961 118300 123472 \n",
"1 1 17183 1540 117961 118343 123125 \n",
"2 1 36724 14457 118219 118220 117884 \n",
"3 1 36135 5396 117961 118343 119993 \n",
"4 1 42680 5905 117929 117930 119569 \n",
"\n",
" ROLE_TITLE ROLE_FAMILY_DESC ROLE_FAMILY ROLE_CODE \n",
"0 117905 117906 290919 117908 \n",
"1 118536 118536 308574 118539 \n",
"2 117879 267952 19721 117880 \n",
"3 118321 240983 290919 118322 \n",
"4 119323 123932 19793 119325 "
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data = pd.read_csv('amazon.csv')\n",
"data.head()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(32769, 10)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data.shape"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0.94210992096188473"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# доля положительных примеров\n",
"data.ACTION.mean()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ACTION 2\n",
"RESOURCE 7518\n",
"MGR_ID 4243\n",
"ROLE_ROLLUP_1 128\n",
"ROLE_ROLLUP_2 177\n",
"ROLE_DEPTNAME 449\n",
"ROLE_TITLE 343\n",
"ROLE_FAMILY_DESC 2358\n",
"ROLE_FAMILY 67\n",
"ROLE_CODE 343\n"
]
}
],
"source": [
"# число значений у признаков\n",
"for col_name in data.columns:\n",
" print(col_name, len(data[col_name].unique()))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from sklearn.cross_validation import train_test_split\n",
"X_train, X_test, y_train, y_test = train_test_split(data.iloc[:, 1:], data.iloc[:, 0],\n",
" test_size=0.3, random_state=241)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Часть 1: kNN и категориальные признаки"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 1. Реализуйте три функции расстояния на категориальных признаках, которые обсуждались на втором семинаре. Реализуйте самостоятельно метод k ближайших соседей, который будет уметь работать с этими функциями расстояния (учитите, что он должен возвращать вероятность — отношение объектов первого класса среди соседей к числу соседей). Как вариант, можно реализовать метрики как [user-defined distance](http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.DistanceMetric.html), после чего воспользоваться реализацией kNN из sklearn (в этом случае используйте функцию predict_proba).\n",
"\n",
"#### Подсчитайте для каждой из метрик качество на тестовой выборке `X_test` при числе соседей $k = 10$. Мера качества — AUC-ROC.\n",
"\n",
"Какая функция расстояния оказалась лучшей?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Readme\n",
"\n",
"В этом задании для ускорения работы knn я постарался максимально векторизовать вычисления. Для вычисления попарных расстояний я добавлял к данным третью ось, после чего вычислял попарные расстояния с использованием numpy broadcasting."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from sklearn.neighbors import KNeighborsClassifier\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.metrics import accuracy_score"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Переведем все данные в numpy array"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"x_train_m = X_train.as_matrix()\n",
"x_test_m = X_test.as_matrix()\n",
"y_train_m = y_train.as_matrix()\n",
"y_test_m = y_test.as_matrix()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"####Первая функция расстояния"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Функция расстояния $\\rho(x_i, y_i) = [x_i \\ne y_i]$"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def dist_1(x, y):\n",
" return np.sum(x != y, axis=2).astype(float)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"####Вторая функция расстояния"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Отсортируем для каждой координаты признаки по частоте и построим список, состоящий из массивов — по массиву на каждый признак. В первом столбце массива находятся всевозможные значения $x$ данного признака $j$, а во втором — сумма $\\sum\\limits_{q: p_j(q) < p_j(x)} p_j^2(q)$, где $p_j^2(x) = f_j(x) (f_j(x) - 1) / l (l - 1)$, где $f_j(x)$ — количество примеров, в которых значение признака $j$ равно $x$ , а $l$ — число всевозможных значений признака. В третьем столбце находится $\\log(f_j(x))$. При этом сверху находятся элементы с наименьшимим, а снизу — с наибольшими вероятностями"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"labels_sorted_by_freq = []\n",
"for column in data.columns:\n",
" frequencies = np.array(((data[column].value_counts()).tolist()))[::-1]\n",
" l = len(data[column])\n",
" sqrd_prbs = (np.cumsum(frequencies * (frequencies-1)) / (l * (l-1))).tolist()\n",
" freq_logs = (np.log(frequencies)).tolist()\n",
" labels_sorted_by_freq.append(np.array(\n",
" [((data[column].value_counts()).index.tolist())[::-1], sqrd_prbs, freq_logs]).T)\n",
"labels_sorted_by_freq = labels_sorted_by_freq[1:]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Функция corrections получает на вход вектор-столбец из значений признака column в точках x, и возвращает массив значений сумм квадратов вероятностей значений этого признака, меньших, чем значения этого признака у x. С ее Помощью в dist_2 вычисляется метрика $\\rho(x_i, y_i) = [x_i \\ne y_i] + [x_i = y_i] \\sum\\limits_{q: p_i(q) \\le p_i(x_i)} p_i^2(q)$."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def corrections(x, column):\n",
" correction_matrix = labels_sorted_by_freq[column]\n",
" label_values_stacked = np.vstack([correction_matrix[:, 0]]*x.shape[0])\n",
" probs_stacked = np.vstack([correction_matrix[:, 1]]*x.shape[0])\n",
" return (probs_stacked)[label_values_stacked == x].reshape(x.shape)\n",
"\n",
"def dist_2(x, y):\n",
" dist = dist_1(x, y)\n",
" for i in range(x.shape[2]):\n",
" correction_matrix = np.hstack([corrections(x[:, :, i], i)] * dist.shape[1])\n",
" correction_matrix[(x[:, :, i] != y[:, :, i])] = 0\n",
" dist += correction_matrix\n",
"\n",
" return dist\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"####Третья функция расстояния\n",
"$\\rho_j(x_j, y_j) = [x_j \\ne y_j] \\log(f_j(x_j)) \\log(f_j(y_j))$"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def log_corrections(x, column):\n",
" correction_matrix = labels_sorted_by_freq[column]\n",
" label_values_stacked = np.vstack([correction_matrix[:, 0]]*x.shape[0])\n",
" log_freqs_stacked = np.vstack([correction_matrix[:, 2]]*x.shape[0])\n",
" return (log_freqs_stacked)[label_values_stacked == x].reshape(x.shape)\n",
"\n",
"def dist_3(x, y):\n",
" dist = np.zeros((x.shape[0], y.shape[1]))\n",
" for i in range(x.shape[2]):\n",
" correction_matrix = log_corrections(y[:, :, i].T, i).T * log_corrections(x[:, :, i], i)\n",
" correction_matrix[(x[:, :, i] == y[:, :, i])] = 1\n",
" dist += correction_matrix*(x[:, :, i] != y[:, :, i])\n",
" return dist\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class k_neighbour_classifier:\n",
" def __init__(self, distance, num_neigbours):\n",
" self.dist = distance\n",
" self.k = num_neigbours\n",
" \n",
" def predict(self, train_x, train_y, test_x):\n",
" \"\"\"Returns predicted labels at test data points\"\"\"\n",
" distance_matrix = self.dist(test_x[:, None, :], train_x[None, :, :])\n",
" args = np.argsort(distance_matrix, axis=1)[:, :self.k]\n",
" args = args.flatten()\n",
" y_indices_list = np.hstack([np.arange(test_x.shape[0])[:, None]] * self.k).ravel()\n",
" labels_matrix = np.vstack([train_y]*test_x.shape[0])\n",
" sorted_dist_labels_matrix = np.array(np.hsplit(labels_matrix[y_indices_list, args.flatten()], test_x.shape[0]))\n",
" return np.sum(sorted_dist_labels_matrix, axis=1)/self.k"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Метрика roc-auc для первой функции расстояния: 0.83088009598\n"
]
}
],
"source": [
"knn = k_neighbour_classifier(dist_1, 10)\n",
"predicted_y = knn.predict(x_train_m, y_train_m, x_test_m)\n",
"print('Метрика roc-auc для первой функции расстояния: ', roc_auc_score(y_test_m, predicted_y))"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Метрика roc-auc для второй функции расстояния: 0.83294982967\n"
]
}
],
"source": [
"knn = k_neighbour_classifier(dist_2, 10)\n",
"predicted_y = knn.predict(x_train_m, y_train_m, x_test_m)\n",
"print('Метрика roc-auc для второй функции расстояния: ', roc_auc_score(y_test_m, predicted_y))"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Метрика roc-auc для третьей функции расстояния: 0.813206669298\n"
]
}
],
"source": [
"knn = k_neighbour_classifier(dist_3, 10)\n",
"predicted_y = knn.predict(x_train_m, y_train_m, x_test_m)\n",
"print('Метрика roc-auc для третьей функции расстояния: ', roc_auc_score(y_test_m, predicted_y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"###Ответ на 1.1 \n",
"Лучшей оказалась вторая функция расстояния"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 2 (бонус). Подберите лучшее (на тестовой выборке) число соседей $k$ для каждой из функций расстояния. Какое наилучшее качество удалось достичь?"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"####Первая функция расстояния"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"3\n",
"5\n",
"10\n",
"15\n",
"50\n",
"100\n",
"Наилучшее качество 0.83088009598 достигается при числе соседей k = 10\n"
]
}
],
"source": [
"k_vals = [1, 3, 5, 10, 15, 50, 100]\n",
"max_quality = 0\n",
"k_opt = 0\n",
"for k in k_vals:\n",
" print(k)\n",
" knn = k_neighbour_classifier(dist_1, k)\n",
" predicted_y = knn.predict(x_train_m, y_train_m, x_test_m)\n",
" quality = roc_auc_score(y_test_m, predicted_y)\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" k_opt = k\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе соседей k = \", k_opt)"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"3\n",
"5\n",
"10\n",
"15\n",
"50\n",
"100\n",
"Наилучшее качество 0.83294982967 достигается при числе соседей k = 10\n"
]
}
],
"source": [
"k_vals = [1, 3, 5, 10, 15, 50, 100]\n",
"max_quality = 0\n",
"k_opt = 0\n",
"for k in k_vals:\n",
" print(k)\n",
" knn = k_neighbour_classifier(dist_2, k)\n",
" predicted_y = knn.predict(x_train_m, y_train_m, x_test_m)\n",
" quality = roc_auc_score(y_test_m, predicted_y)\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" k_opt = k\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе соседей k = \", k_opt)"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"3\n",
"5\n",
"10\n",
"15\n",
"50\n",
"100\n",
"Наилучшее качество 0.813206669298 достигается при числе соседей k = 10\n"
]
}
],
"source": [
"k_vals = [1, 3, 5, 10, 15, 50, 100]\n",
"max_quality = 0\n",
"k_opt = 0\n",
"for k in k_vals:\n",
" print(k)\n",
" knn = k_neighbour_classifier(dist_3, k)\n",
" predicted_y = knn.predict(x_train_m, y_train_m, x_test_m)\n",
" quality = roc_auc_score(y_test_m, predicted_y)\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" k_opt = k\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе соседей k = \", k_opt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 3. Реализуйте счетчики (http://blogs.technet.com/b/machinelearning/archive/2015/02/17/big-learning-made-easy-with-counts.aspx), которые заменят категориальные признаки на вещественные.\n",
"\n",
"А именно, каждый категориальный признак нужно заменить на три: \n",
"1. Число `counts` объектов в обучающей выборке с таким же значением признака.\n",
"2. Число `clicks` объектов первого класса ($y = 1$) в обучающей выборке с таким же значением признака.\n",
"3. Сглаженное отношение двух предыдущих величин: (`clicks` + 1) / (`counts` + 2).\n",
"\n",
"Поскольку признаки, содержащие информацию о целевой переменной, могут привести к переобучению, может оказаться полезным сделать *фолдинг*: разбить обучающую выборку на $n$ частей, и для $i$-й части считать `counts` и `clicks` по всем остальным частям. Для тестовой выборки используются счетчики, посчитанный по всей обучающей выборке. Реализуйте и такой вариант. Можно использовать $n = 3$.\n",
"\n",
"#### Посчитайте на тесте AUC-ROC метода $k$ ближайших соседей с евклидовой метрикой для выборки, где категориальные признаки заменены на счетчики. Сравните по AUC-ROC два варианта формирования выборки — с фолдингом и без. Не забудьте подобрать наилучшее число соседей $k$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"####Без фолдинга"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Выделим необходимые признаки"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def calculate_counts(data_frame):\n",
" new_data = pd.DataFrame()\n",
" x_tr, x_test, y_tr, y_test = train_test_split(data_frame.iloc[:, 1:], data_frame.iloc[:, 0],\n",
" test_size=0.3, random_state=241)\n",
" new_data['ACTION'] = data_frame['ACTION']\n",
" for col in data_frame.columns[1:]:\n",
" new_data[col+'_COUNTS'] = (x_tr[col].value_counts())[data_frame[col]].tolist()\n",
" new_data[col+'_CLICKS'] = ((x_tr[y_tr == 1])[col].value_counts())[data_frame[col]].tolist()\n",
" new_data = new_data.fillna(0)\n",
" new_data[col+'_FRACTION'] = (new_data[col+'_CLICKS'] + 1) / (new_data[col+'_COUNTS'] + 2)\n",
" new_data = new_data.fillna(0)\n",
" return new_data"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"new_data = calculate_counts(data)\n",
"counts_X_train, counts_X_test, counts_y_train, counts_y_test = \\\n",
" train_test_split(new_data.iloc[:, 1:], new_data.iloc[:, 0], test_size=0.3, random_state=241)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Наилучшее качество 0.794659936123 достигается при числе соседей k = 15\n"
]
}
],
"source": [
"k_vals = [1, 3, 5, 10, 15, 50, 100]\n",
"max_quality = 0\n",
"k_opt = 0\n",
"for k in k_vals:\n",
" clf = KNeighborsClassifier(n_neighbors=k)\n",
" clf.fit(counts_X_train, counts_y_train)\n",
" count_predicted_y = clf.predict_proba(counts_X_test)\n",
" quality = roc_auc_score(counts_y_test.astype(float), count_predicted_y[:, 1])\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" k_opt = k\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе соседей k = \", k_opt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### С фолдингом"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def calculate_folded_counts(data_frame, num_folds):\n",
" x_tr, x_test, y_tr, y_test = train_test_split(data_frame.iloc[:, 1:], data_frame.iloc[:, 0],\n",
" test_size=0.3, random_state=241)\n",
" kf = cross_validation.KFold(x_tr.shape[0], n_folds=num_folds)\n",
" new_data = pd.DataFrame()\n",
" new_data['ACTION'] = data['ACTION']\n",
" for col in data_frame.columns[1:]:\n",
" new_data[col+'_COUNTS'] = [0]*len(data_frame[col])\n",
" new_data[col+'_CLICKS'] = [0]*len(data_frame[col])\n",
" new_data[col+'_FRACTION'] = [0]*len(data_frame[col])\n",
" for train_index, test_index in kf:\n",
" x_in, x_left_out = x_tr.iloc[train_index], x_tr.iloc[test_index]\n",
" y_in, y_left_out = y_tr.iloc[train_index], y_tr.iloc[test_index]\n",
" for col in data.columns[1:]:\n",
" new_data.loc[x_left_out.index, [col+'_COUNTS']] = (x_in[col].value_counts())[x_left_out[col]].tolist()\n",
" new_data.loc[x_left_out.index, [col+'_CLICKS']] = \\\n",
" ((x_in[y_in == 1])[col].value_counts())[x_left_out[col]].tolist()\n",
" new_data = new_data.fillna(0) \n",
" for col in data_frame.columns[1:]:\n",
" new_data.loc[x_test.index, [col+'_COUNTS']] = (x_tr[col].value_counts())[x_test[col]].tolist()\n",
" new_data.loc[x_test.index, [col+'_CLICKS']] = ((x_tr[y_tr==1])[col].value_counts())[x_test[col]].tolist()\n",
" new_data = new_data.fillna(0)\n",
" for col in data_frame.columns[1:]:\n",
" new_data[col+'_FRACTION'] = ((new_data[col+'_CLICKS'] + 1.) / \n",
" (new_data[col+'_COUNTS'] + 2.))\n",
" return new_data"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"new_data = calculate_folded_counts(data, 30)\n",
"new_data\n",
"fold_x_train, fold_x_test, fold_y_train, fold_y_test = \\\n",
" train_test_split(new_data.iloc[:, 1:], data.iloc[:, 0], test_size=0.3, random_state=241)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Наилучшее качество 0.728717164071 достигается при числе соседей k = 1\n"
]
}
],
"source": [
"k_vals = [1, 3, 5, 10, 15, 50, 100]\n",
"max_quality = 0\n",
"k_opt = 0\n",
"for k in k_vals:\n",
" clf = KNeighborsClassifier(n_neighbors=k)\n",
" clf.fit(fold_x_train, fold_y_train)\n",
" count_predicted_y = clf.predict_proba(fold_x_test)\n",
" quality = roc_auc_score(fold_y_test.astype(float), fold_predicted_y[:, 1])\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" k_opt = k\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе соседей k = \", k_opt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 4. Добавьте в исходную выборку парные признаки — то есть для каждой пары $f_i$, $f_j$ исходных категориальных признаков добавьте новый категориальный признак $f_{ij}$, значение которого является конкатенацией значений $f_i$ и $f_j$. Посчитайте счетчики для этой выборки, найдите качество метода $k$ ближайших соседей с наилучшим $k$ (с фолдингом и без)."
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"paired_data = data.copy()\n",
"for i in range(len(data.columns[1:])):\n",
" for j in range(i+1, len(data.columns[1:])):\n",
" column1 = data.columns[i+1]\n",
" column2 = data.columns[j+1]\n",
" paired_data[column1 + '_+_' + column2] = list(zip(data[column1], data[column2]))\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"####Без Фолдинга"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"paired_data_counts = calculate_counts(paired_data)"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"pc_x_tr, pc_x_test, pc_y_tr, pc_y_test = train_test_split(paired_data_counts.iloc[:, 1:], \n",
" paired_data_counts.iloc[:, 0], test_size=0.3, random_state=241)"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Наилучшее качество 0.800464876887 достигается при числе соседей k = 15\n"
]
}
],
"source": [
"k_vals = [1, 3, 5, 10, 15, 50, 100]\n",
"max_quality = 0\n",
"k_opt = 0\n",
"for k in k_vals:\n",
" clf = KNeighborsClassifier(n_neighbors=k)\n",
" clf.fit(pc_x_tr, pc_y_tr)\n",
" pc_predicted_y = clf.predict_proba(pc_x_test)\n",
" quality = roc_auc_score(pc_y_test.astype(float), pc_predicted_y[:, 1])\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" k_opt = k\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе соседей k = \", k_opt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"####С Фолдингом"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"folded_paired_data_counts = calculate_folded_counts(paired_data, 30)"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"pfc_x_tr, pfc_x_test, pfc_y_tr, pfc_y_test = train_test_split(folded_paired_data_counts.iloc[:, 1:], \n",
" folded_paired_data_counts.iloc[:, 0], test_size=0.3, random_state=241)"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Наилучшее качество 0.745394900947 достигается при числе соседей k = 50\n"
]
}
],
"source": [
"k_vals = [1, 3, 5, 10, 15, 50, 100]\n",
"max_quality = 0\n",
"k_opt = 0\n",
"for k in k_vals:\n",
" clf = KNeighborsClassifier(n_neighbors=k)\n",
" clf.fit(pfc_x_tr, pfc_y_tr)\n",
" pfc_predicted_y = clf.predict_proba(pfc_x_test)\n",
" quality = roc_auc_score(pfc_y_test.astype(float), pfc_predicted_y[:, 1])\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" k_opt = k\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе соседей k = \", k_opt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Часть 2: Решающие деревья и леса"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 1. Возьмите из предыдущей части выборку с парными признаками, преобразованную с помощью счетчиков без фолдинга. Настройте решающее дерево, подобрав оптимальные значения параметров `max_depth` и `min_samples_leaf`. Какой наилучший AUC-ROC на контроле удалось получить?"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from sklearn.tree import DecisionTreeClassifier"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Наилучшее качество 0.578835701567 достигается при глубине 5 и min_samples_leaf = 10\n"
]
}
],
"source": [
"depth_vals = [3, 5, 10, 20, 100]\n",
"min_samples_vals = [1, 2, 5, 10, 100, 1000]\n",
"max_quality = 0\n",
"opt_depth = 0\n",
"opt_min_sample = 0\n",
"for depth in depth_vals:\n",
" for min_sample in min_samples_vals: \n",
" clf = DecisionTreeClassifier(random_state=0, max_depth=depth, min_samples_leaf=min_sample)\n",
" clf.fit(pc_x_tr, pc_y_tr)\n",
" tree_pc_prediction = clf.predict_proba(pc_x_test)\n",
" quality = roc_auc_score(pc_y_test.astype(float), tree_pc_prediction[:, 1])\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" opt_depth = depth\n",
" opt_min_sample = min_sample\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при глубине \", opt_depth, \n",
" \" и min_samples_leaf = \", opt_min_sample)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 2. Настройте случайный лес, подобрав оптимальное число деревьев `n_estimators`. Какое качество на тестовой выборке он дает?"
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from sklearn.ensemble import RandomForestClassifier"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Наилучшее качество 0.739282833772 достигается при числе деревьев 100\n"
]
}
],
"source": [
"n_vals = [10, 20, 50, 100, 200, 500]\n",
"max_quality = 0\n",
"opt_n = 0\n",
"for n in n_vals:\n",
" clf = RandomForestClassifier(n_estimators=n)\n",
" clf.fit(pc_x_tr, pc_y_tr)\n",
" forest_pc_prediction = clf.predict_proba(pc_x_test)\n",
" quality = roc_auc_score(pc_y_test.astype(float), forest_pc_prediction[:, 1])\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" opt_n = n\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе деревьев \", opt_n)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 3. Возьмите выборку с парными признаками, для которой счетчики посчитаны с фолдингом. Обучите на ней случайный лес, подобрав число деревьев. Какое качество на тестовой выборке он дает? Чем вы можете объяснить изменение результата по сравнению с предыдущим пунктом?"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Наилучшее качество 0.849579224237 достигается при числе деревьев 50\n"
]
}
],
"source": [
"n_vals = [10, 20, 50, 100, 200, 500]\n",
"max_quality = 0\n",
"opt_n = 0\n",
"for n in n_vals:\n",
" clf = RandomForestClassifier(n_estimators=100)\n",
" clf.fit(pfc_x_tr, pfc_y_tr)\n",
" forest_pfc_prediction = clf.predict_proba(pfc_x_test)\n",
" quality = roc_auc_score(pfc_y_test.astype(float), forest_pfc_prediction[:, 1])\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" opt_n = n\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при числе деревьев \", opt_n)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"Для ответа на теоретический вопрос обучим на признаках, почитанных фолдингом, обычное решающее дерево."
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Наилучшее качество 0.796974616155 достигается при глубине 10 и min_samples_leaf = 1000\n"
]
}
],
"source": [
"depth_vals = [3, 5, 10, 20, 100]\n",
"min_samples_vals = [1, 2, 5, 10, 100, 1000]\n",
"max_quality = 0\n",
"opt_depth = 0\n",
"opt_min_sample = 0\n",
"for depth in depth_vals:\n",
" for min_sample in min_samples_vals: \n",
" clf = DecisionTreeClassifier(random_state=0, max_depth=depth, min_samples_leaf=min_sample)\n",
" clf.fit(pfc_x_tr, pfc_y_tr)\n",
" tree_pfc_prediction = clf.predict_proba(pfc_x_test)\n",
" quality = roc_auc_score(pfc_y_test.astype(float), tree_pfc_prediction[:, 1])\n",
" if max_quality < quality:\n",
" max_quality = quality\n",
" opt_depth = depth\n",
" opt_min_sample = min_sample\n",
"print(\"Наилучшее качество \", max_quality, \" достигается при глубине \", opt_depth, \n",
" \" и min_samples_leaf = \", opt_min_sample)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Видно, что его качество заметно выше, чем для признаков, посчитанных без фолдинга. Одной из причин этого эффекта является то, что в обучающей выборке многие признаки (а особенно парные) принимают какие-то из значений ровно по одному разу. Решающее дерево легко может переобучиться на таких признаках. При использовании фолдинга такие признаки не принимаются во внимание (для них counts и clicks равны 0). Да и вообще фолдинг понижает вероятность переобучения, так как при этой стратегии подсчета счетчиков мы не вносим в выборку информацию о целевой переменной."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"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.4.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment