mlcourse.ai – открытый курс OpenDataScience по машинному обучению

Авторы материала: Юрий Кашницкий, программист-исследователь Mail.Ru Group, и Мария Сумарокова, старший эксперт по аналитике VimpelCom. Материал распространяется на условиях лицензии Creative Commons CC BY-NC-SA 4.0. Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала

Домашнее задание № 3 (демо).
Деревья решений в игрушечной задаче и на данных Adult репозитория UCI

В задании Вам предлагается разобраться с тем, как работает дерево решений, на игрушечном примере, затем обучить и настроить деревья и (при желании) случайный лес в задаче классификации на данных Adult репозитория UCI. Ответьте на все вопросы в этой тетрадке и заполните ответы в гугл-форме.

Подключаем необходимые библиотеки

In [1]:
%matplotlib inline
from matplotlib import pyplot as plt

plt.rcParams['figure.figsize'] = (10, 8)
import collections

import numpy as np
import pandas as pd
import seaborn as sns
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import LabelEncoder
from sklearn.tree import DecisionTreeClassifier, export_graphviz

Часть 1. Игрушечный набор данных "девушка в баре"

Цель – "на пальцах", с помощью игрушечной задачи классификации разобраться в том, как работают деревья решений. Само по себе дерево решений – довольно слабый алгоритм, но основанные на нем алгоритмы случайного леса и градиентного бустинга - пожалуй, лучшее, что есть на сегодняшний день (в задачах, где можно обойтись без нейронных сетей). Поэтому разобраться в том, как работает дерево решений, полезно.

Рассмотрим игрушечную задачу бинарной классификации: поедет ли с Вами девушка из бара? Это будет зависеть от Вашей внешности и красноречия, крепости предлагаемых напитков и, как это ни меркантильно, от количества потраченных в баре денег.

Создание набора данных

In [2]:
# Создание датафрейма с dummy variables
def create_df(dic, feature_list):
    out = pd.DataFrame(dic)
    out = pd.concat([out, pd.get_dummies(out[feature_list])], axis = 1)
    out.drop(feature_list, axis = 1, inplace = True)
    return out

# Некоторые значения признаков есть в тесте, но нет в трейне и наоборот
def intersect_features(train, test):
    common_feat = list( set(train.keys()) & set(test.keys()))
    return train[common_feat], test[common_feat]
In [3]:
features = ['Внешность', 'Алкоголь_в_напитке',
            'Уровень_красноречия', 'Потраченные_деньги']

Обучающая выборка

In [4]:
df_train = {}
df_train['Внешность'] = ['приятная', 'приятная', 'приятная', 'отталкивающая',
                         'отталкивающая', 'отталкивающая', 'приятная'] 
df_train['Алкоголь_в_напитке'] = ['да', 'да', 'нет', 'нет', 'да', 'да', 'да']
df_train['Уровень_красноречия'] = ['высокий', 'низкий', 'средний', 'средний', 'низкий',
                                   'высокий', 'средний']
df_train['Потраченные_деньги'] = ['много', 'мало', 'много', 'мало', 'много',
                                  'много', 'много']
df_train['Поедет'] = LabelEncoder().fit_transform(['+', '-', '+', '-', '-', '+', '+'])

df_train = create_df(df_train, features)
df_train
Out[4]:
Поедет Внешность_отталкивающая Внешность_приятная Алкоголь_в_напитке_да Алкоголь_в_напитке_нет Уровень_красноречия_высокий Уровень_красноречия_низкий Уровень_красноречия_средний Потраченные_деньги_мало Потраченные_деньги_много
0 0 0 1 1 0 1 0 0 0 1
1 1 0 1 1 0 0 1 0 1 0
2 0 0 1 0 1 0 0 1 0 1
3 1 1 0 0 1 0 0 1 1 0
4 1 1 0 1 0 0 1 0 0 1
5 0 1 0 1 0 1 0 0 0 1
6 0 0 1 1 0 0 0 1 0 1

Тестовая выборка

In [5]:
df_test = {}
df_test['Внешность'] = ['приятная', 'приятная', 'отталкивающая'] 
df_test['Алкоголь_в_напитке'] = ['нет', 'да', 'да']
df_test['Уровень_красноречия'] = ['средний', 'высокий', 'средний']
df_test['Потраченные_деньги'] = ['много', 'мало', 'много']
df_test = create_df(df_test, features)
df_test
Out[5]:
Внешность_отталкивающая Внешность_приятная Алкоголь_в_напитке_да Алкоголь_в_напитке_нет Уровень_красноречия_высокий Уровень_красноречия_средний Потраченные_деньги_мало Потраченные_деньги_много
0 0 1 0 1 0 1 0 1
1 0 1 1 0 1 0 1 0
2 1 0 1 0 0 1 0 1
In [6]:
# Некоторые значения признаков есть в тесте, но нет в трейне и наоборот
y = df_train['Поедет']
df_train, df_test = intersect_features(train=df_train, test=df_test)
df_train
Out[6]:
Внешность_отталкивающая Уровень_красноречия_высокий Внешность_приятная Потраченные_деньги_много Алкоголь_в_напитке_нет Уровень_красноречия_средний Алкоголь_в_напитке_да Потраченные_деньги_мало
0 0 1 1 1 0 0 1 0
1 0 0 1 0 0 0 1 1
2 0 0 1 1 1 1 0 0
3 1 0 0 0 1 1 0 1
4 1 0 0 1 0 0 1 0
5 1 1 0 1 0 0 1 0
6 0 0 1 1 0 1 1 0
In [7]:
df_test
Out[7]:
Потраченные_деньги_мало Алкоголь_в_напитке_нет Потраченные_деньги_много Уровень_красноречия_высокий Алкоголь_в_напитке_да Уровень_красноречия_средний Внешность_приятная Внешность_отталкивающая
0 0 1 1 0 0 1 1 0
1 1 0 0 1 1 0 1 0
2 0 0 1 0 1 1 0 1

Постройте от руки (или в графическом редакторе) дерево решений для этого набора данных. Дополнительно (для желающих) – можете сделать отрисовку дерева и написать код для построения всего дерева.

Вопрос 1. Какова энтропия начальной системы ($S_0$)? Под состояниями системы понимаем значения признака "Поедет" – 0 или 1 (то есть всего 2 состояния).

Вопрос 2. Рассмотрим разбиение обучающей выборки по признаку "Внешность_приятная". Какова энтропия $S_1$ левой группы, тех, у кого внешность приятная, и правой группы – $S_2$? Каков прирост информации при данном разбиении (IG)?

In [8]:
# Ваш код здесь

Постройте с помощью sklearn дерево решений, обучив его на обучающей выборке. Глубину можно не ограничивать.

In [9]:
# Ваш код здесь

Дополнительно: отобразите дерево с помощью graphviz. Можно использовать pydot или, например, онлайн-сервис dot2png.

In [10]:
# Ваш код здесь

Часть 2. Функции для расчета энтропии и прироста информации

Примерчик для проверки: 9 синих шариков и 11 желтых. Пусть шарик находится в состоянии "1", если он синий и "0" – если он желтый.

In [11]:
balls = [1 for i in range(9)] + [0 for i in range(11)]

png

Дальше пусть шарики разбиваются на 2 группы png

In [12]:
# две группы
balls_left  = [1 for i in range(8)] + [0 for i in range(5)] # 8 синих и 5 желтых
balls_right = [1 for i in range(1)] + [0 for i in range(6)] # 1 синий и 6 желтых

Реализуйте функцию для расчета энтропии Шеннона.

In [13]:
def entropy(a_list):
    
    # Ваш код здесь
    pass

Проверка

In [ ]:
print(entropy(balls)) # 9 синих и 11 желтых
print(entropy(balls_left)) # 8 синих и 5 желтых
print(entropy(balls_right)) # 1 синий и 6 желтых
print(entropy([1,2,3,4,5,6])) # энтропия игральной кости с несмещенным центром тяжести

Вопрос 3. Чему равна энтропия состояния, заданного списком balls_left?

Вопрос 4. Чему равна энтропия игральной кости с несмещенным центром тяжести?

In [15]:
# расчет прироста информации

def information_gain(root, left, right):
    ''' root - изначальный набор данных, left и right два разбиения изначального набора'''
    
    # Ваш код здесь
    pass

Вопрос 5. Каков прирост информации при разделении выборки на balls_left и balls_right?

In [16]:
# Ваш код здесь
In [17]:
def best_feature_to_split(X, y):
    ''' Выводит прирост информации при разбиении по каждому признаку'''
    
    # Ваш код здесь
    pass

Опционально:

  • реализуйте алгоритм построения дерева за счет рекурсивного вызова функции best_feature_to_split
  • нарисуйте полученное дерево

Часть 3. Набор данных "Adult"

Описание набора:

Набор данных UCI Adult (качать не надо, все есть в репозитории): классификация людей с помощью демографических данных для прогнозирования, зарабатывает ли человек более \$ 50 000 в год.

Описание признаков:

Age – возраст, количественный признак
Workclass – тип работодателя, количественный признак
fnlwgt – итоговый вес обьекта, количественный признак
Education – уровень образования, качественный признак
Education_Num – количество лет обучения, количественный признак
Martial_Status – семейное положение, категориальный признак
Occupation – профессия, категориальный признак
Relationship – тип семейных отношений, категориальный признак
Race – раса, категориальный признак
Sex – пол, качественный признак
Capital_Gain – прирост капитала, количественный признак
Capital_Loss – потери капитала, количественный признак
Hours_per_week – количество часов работы в неделю, количественный признак
Country – страна, категориальный признак

Целевая переменная: Target – уровень заработка, категориальный (бинарный) признак

Считываем обучающую и тестовую выборки.

In [18]:
data_train = pd.read_csv('../../data/adult_train.csv', sep=';') 
In [19]:
data_train.tail()
Out[19]:
Age Workclass fnlwgt Education Education_Num Martial_Status Occupation Relationship Race Sex Capital_Gain Capital_Loss Hours_per_week Country Target
32556 27 Private 257302 Assoc-acdm 12 Married-civ-spouse Tech-support Wife White Female 0 0 38 United-States <=50K
32557 40 Private 154374 HS-grad 9 Married-civ-spouse Machine-op-inspct Husband White Male 0 0 40 United-States >50K
32558 58 Private 151910 HS-grad 9 Widowed Adm-clerical Unmarried White Female 0 0 40 United-States <=50K
32559 22 Private 201490 HS-grad 9 Never-married Adm-clerical Own-child White Male 0 0 20 United-States <=50K
32560 52 Self-emp-inc 287927 HS-grad 9 Married-civ-spouse Exec-managerial Wife White Female 15024 0 40 United-States >50K
In [20]:
data_test = pd.read_csv('../../data/adult_test.csv', sep=';') 
In [21]:
data_test.tail()
Out[21]:
Age Workclass fnlwgt Education Education_Num Martial_Status Occupation Relationship Race Sex Capital_Gain Capital_Loss Hours_per_week Country Target
16277 39 Private 215419.0 Bachelors 13.0 Divorced Prof-specialty Not-in-family White Female 0.0 0.0 36.0 United-States <=50K.
16278 64 NaN 321403.0 HS-grad 9.0 Widowed NaN Other-relative Black Male 0.0 0.0 40.0 United-States <=50K.
16279 38 Private 374983.0 Bachelors 13.0 Married-civ-spouse Prof-specialty Husband White Male 0.0 0.0 50.0 United-States <=50K.
16280 44 Private 83891.0 Bachelors 13.0 Divorced Adm-clerical Own-child Asian-Pac-Islander Male 5455.0 0.0 40.0 United-States <=50K.
16281 35 Self-emp-inc 182148.0 Bachelors 13.0 Married-civ-spouse Exec-managerial Husband White Male 0.0 0.0 60.0 United-States >50K.
In [22]:
# необходимо убрать строки с неправильными метками в тестовой выборке
data_test = data_test[(data_test['Target'] == ' >50K.') 
                      | (data_test['Target']==' <=50K.')]

# перекодируем target в числовое поле
data_train.at[data_train['Target'] == ' <=50K', 'Target'] = 0
data_train.at[data_train['Target'] == ' >50K', 'Target'] = 1

data_test.at[data_test['Target'] == ' <=50K.', 'Target'] = 0
data_test.at[data_test['Target'] == ' >50K.', 'Target'] = 1

Первичный анализ данных.

In [23]:
data_test.describe(include='all').T
Out[23]:
count unique top freq mean std min 25% 50% 75% max
Age 16281 73 35 461 NaN NaN NaN NaN NaN NaN NaN
Workclass 15318 8 Private 11210 NaN NaN NaN NaN NaN NaN NaN
fnlwgt 16281 NaN NaN NaN 189436 105715 13492 116736 177831 238384 1.4904e+06
Education 16281 16 HS-grad 5283 NaN NaN NaN NaN NaN NaN NaN
Education_Num 16281 NaN NaN NaN 10.0729 2.56755 1 9 10 12 16
Martial_Status 16281 7 Married-civ-spouse 7403 NaN NaN NaN NaN NaN NaN NaN
Occupation 15315 14 Prof-specialty 2032 NaN NaN NaN NaN NaN NaN NaN
Relationship 16281 6 Husband 6523 NaN NaN NaN NaN NaN NaN NaN
Race 16281 5 White 13946 NaN NaN NaN NaN NaN NaN NaN
Sex 16281 2 Male 10860 NaN NaN NaN NaN NaN NaN NaN
Capital_Gain 16281 NaN NaN NaN 1081.91 7583.94 0 0 0 0 99999
Capital_Loss 16281 NaN NaN NaN 87.8993 403.105 0 0 0 0 3770
Hours_per_week 16281 NaN NaN NaN 40.3922 12.4793 1 40 40 45 99
Country 16007 40 United-States 14662 NaN NaN NaN NaN NaN NaN NaN
Target 16281 2 0 12435 NaN NaN NaN NaN NaN NaN NaN
In [24]:
data_train['Target'].value_counts()
Out[24]:
0    24720
1     7841
Name: Target, dtype: int64
In [25]:
fig = plt.figure(figsize=(25, 15))
cols = 5
rows = np.ceil(float(data_train.shape[1]) / cols)
for i, column in enumerate(data_train.columns):
    ax = fig.add_subplot(rows, cols, i + 1)
    ax.set_title(column)
    if data_train.dtypes[column] == np.object:
        data_train[column].value_counts().plot(kind="bar", axes=ax)
    else:
        data_train[column].hist(axes=ax)
        plt.xticks(rotation="vertical")
plt.subplots_adjust(hspace=0.7, wspace=0.2)

Проверяем типы данных

In [26]:
data_train.dtypes
Out[26]:
Age                int64
Workclass         object
fnlwgt             int64
Education         object
Education_Num      int64
Martial_Status    object
Occupation        object
Relationship      object
Race              object
Sex               object
Capital_Gain       int64
Capital_Loss       int64
Hours_per_week     int64
Country           object
Target            object
dtype: object
In [27]:
data_test.dtypes
Out[27]:
Age                object
Workclass          object
fnlwgt            float64
Education          object
Education_Num     float64
Martial_Status     object
Occupation         object
Relationship       object
Race               object
Sex                object
Capital_Gain      float64
Capital_Loss      float64
Hours_per_week    float64
Country            object
Target             object
dtype: object

Выяснилось, что в тесте возраст отнесен к типу object, необходимо это исправить.

In [28]:
data_test['Age'] = data_test['Age'].astype(int)

Также приведем показатели типа float в int для соответствия train и test выборок.

In [29]:
data_test['fnlwgt'] = data_test['fnlwgt'].astype(int)
data_test['Education_Num'] = data_test['Education_Num'].astype(int)
data_test['Capital_Gain'] = data_test['Capital_Gain'].astype(int)
data_test['Capital_Loss'] = data_test['Capital_Loss'].astype(int)
data_test['Hours_per_week'] = data_test['Hours_per_week'].astype(int)

Заполним пропуски в количественных полях медианными значениями, а в категориальных – наиболее часто встречающимся значением

In [30]:
# выделим в выборках категориальные и числовые поля

categorical_columns_train = [c for c in data_train.columns 
                             if data_train[c].dtype.name == 'object']
numerical_columns_train = [c for c in data_train.columns 
                           if data_train[c].dtype.name != 'object']

categorical_columns_test = [c for c in data_test.columns 
                            if data_test[c].dtype.name == 'object']
numerical_columns_test = [c for c in data_test.columns 
                          if data_test[c].dtype.name != 'object']

print('categorical_columns_test:', categorical_columns_test)
print('categorical_columns_train:', categorical_columns_train)
print('numerical_columns_test:', numerical_columns_test)
print('numerical_columns_train:', numerical_columns_train)
categorical_columns_test: ['Workclass', 'Education', 'Martial_Status', 'Occupation', 'Relationship', 'Race', 'Sex', 'Country', 'Target']
categorical_columns_train: ['Workclass', 'Education', 'Martial_Status', 'Occupation', 'Relationship', 'Race', 'Sex', 'Country', 'Target']
numerical_columns_test: ['Age', 'fnlwgt', 'Education_Num', 'Capital_Gain', 'Capital_Loss', 'Hours_per_week']
numerical_columns_train: ['Age', 'fnlwgt', 'Education_Num', 'Capital_Gain', 'Capital_Loss', 'Hours_per_week']
In [31]:
# заполним пропуски

for c in categorical_columns_train:
    data_train[c] = data_train[c].fillna(data_train[c].mode())
for c in categorical_columns_test:
    data_test[c] = data_test[c].fillna(data_train[c].mode())
    
for c in numerical_columns_train:
    data_train[c] = data_train[c].fillna(data_train[c].median())
for c in numerical_columns_test:
    data_test[c] = data_test[c].fillna(data_train[c].median())    

Кодируем категориальные признаки 'Workclass', 'Education', 'Martial_Status', 'Occupation', 'Relationship', 'Race', 'Sex', 'Country'. Это можно сделать с помощью метода pandas get_dummies.

In [32]:
data_train = pd.concat([data_train, pd.get_dummies(data_train['Workclass'], 
                                                   prefix="Workclass"),
                      pd.get_dummies(data_train['Education'], prefix="Education"),
                      pd.get_dummies(data_train['Martial_Status'], prefix="Martial_Status"),
                      pd.get_dummies(data_train['Occupation'], prefix="Occupation"),
                      pd.get_dummies(data_train['Relationship'], prefix="Relationship"),
                      pd.get_dummies(data_train['Race'], prefix="Race"),
                      pd.get_dummies(data_train['Sex'], prefix="Sex"),
                      pd.get_dummies(data_train['Country'], prefix="Country")],
                     axis=1)

data_test = pd.concat([data_test, pd.get_dummies(data_test['Workclass'], prefix="Workclass"),
                      pd.get_dummies(data_test['Education'], prefix="Education"),
                      pd.get_dummies(data_test['Martial_Status'], prefix="Martial_Status"),
                      pd.get_dummies(data_test['Occupation'], prefix="Occupation"),
                      pd.get_dummies(data_test['Relationship'], prefix="Relationship"),
                      pd.get_dummies(data_test['Race'], prefix="Race"),
                      pd.get_dummies(data_test['Sex'], prefix="Sex"),
                      pd.get_dummies(data_test['Country'], prefix="Country")],
                     axis=1)
In [33]:
data_train.drop(['Workclass', 'Education', 'Martial_Status',
                 'Occupation', 'Relationship', 'Race', 'Sex', 'Country'],
                axis=1, inplace=True)
data_test.drop(['Workclass', 'Education', 'Martial_Status', 'Occupation', 
                'Relationship', 'Race', 'Sex', 'Country'],
               axis=1, inplace=True)
In [34]:
data_test.describe(include='all').T
Out[34]:
count mean std min 25% 50% 75% max
Age 16281.0 38.767459 13.849187 17.0 28.0 37.0 48.0 90.0
fnlwgt 16281.0 189435.677784 105714.907671 13492.0 116736.0 177831.0 238384.0 1490400.0
Education_Num 16281.0 10.072907 2.567545 1.0 9.0 10.0 12.0 16.0
Capital_Gain 16281.0 1081.905104 7583.935968 0.0 0.0 0.0 0.0 99999.0
Capital_Loss 16281.0 87.899269 403.105286 0.0 0.0 0.0 0.0 3770.0
Hours_per_week 16281.0 40.392236 12.479332 1.0 40.0 40.0 45.0 99.0
Target 16281.0 0.236226 0.424776 0.0 0.0 0.0 0.0 1.0
Workclass_ Federal-gov 16281.0 0.028991 0.167786 0.0 0.0 0.0 0.0 1.0
Workclass_ Local-gov 16281.0 0.064062 0.244872 0.0 0.0 0.0 0.0 1.0
Workclass_ Never-worked 16281.0 0.000184 0.013574 0.0 0.0 0.0 0.0 1.0
Workclass_ Private 16281.0 0.747681 0.434356 0.0 0.0 1.0 1.0 1.0
Workclass_ Self-emp-inc 16281.0 0.035563 0.185203 0.0 0.0 0.0 0.0 1.0
Workclass_ Self-emp-not-inc 16281.0 0.081138 0.273055 0.0 0.0 0.0 0.0 1.0
Workclass_ State-gov 16281.0 0.041951 0.200483 0.0 0.0 0.0 0.0 1.0
Workclass_ Without-pay 16281.0 0.000430 0.020731 0.0 0.0 0.0 0.0 1.0
Education_ 10th 16281.0 0.028008 0.165001 0.0 0.0 0.0 0.0 1.0
Education_ 11th 16281.0 0.039125 0.193899 0.0 0.0 0.0 0.0 1.0
Education_ 12th 16281.0 0.013758 0.116490 0.0 0.0 0.0 0.0 1.0
Education_ 1st-4th 16281.0 0.004852 0.069491 0.0 0.0 0.0 0.0 1.0
Education_ 5th-6th 16281.0 0.010810 0.103412 0.0 0.0 0.0 0.0 1.0
Education_ 7th-8th 16281.0 0.018979 0.136456 0.0 0.0 0.0 0.0 1.0
Education_ 9th 16281.0 0.014864 0.121012 0.0 0.0 0.0 0.0 1.0
Education_ Assoc-acdm 16281.0 0.032799 0.178116 0.0 0.0 0.0 0.0 1.0
Education_ Assoc-voc 16281.0 0.041705 0.199920 0.0 0.0 0.0 0.0 1.0
Education_ Bachelors 16281.0 0.163995 0.370282 0.0 0.0 0.0 0.0 1.0
Education_ Doctorate 16281.0 0.011117 0.104854 0.0 0.0 0.0 0.0 1.0
Education_ HS-grad 16281.0 0.324489 0.468198 0.0 0.0 0.0 1.0 1.0
Education_ Masters 16281.0 0.057367 0.232551 0.0 0.0 0.0 0.0 1.0
Education_ Preschool 16281.0 0.001965 0.044292 0.0 0.0 0.0 0.0 1.0
Education_ Prof-school 16281.0 0.015847 0.124886 0.0 0.0 0.0 0.0 1.0
... ... ... ... ... ... ... ... ...
Country_ Germany 16281.0 0.004238 0.064964 0.0 0.0 0.0 0.0 1.0
Country_ Greece 16281.0 0.001228 0.035028 0.0 0.0 0.0 0.0 1.0
Country_ Guatemala 16281.0 0.001474 0.038367 0.0 0.0 0.0 0.0 1.0
Country_ Haiti 16281.0 0.001904 0.043595 0.0 0.0 0.0 0.0 1.0
Country_ Honduras 16281.0 0.000430 0.020731 0.0 0.0 0.0 0.0 1.0
Country_ Hong 16281.0 0.000614 0.024776 0.0 0.0 0.0 0.0 1.0
Country_ Hungary 16281.0 0.000369 0.019194 0.0 0.0 0.0 0.0 1.0
Country_ India 16281.0 0.003132 0.055883 0.0 0.0 0.0 0.0 1.0
Country_ Iran 16281.0 0.000983 0.031334 0.0 0.0 0.0 0.0 1.0
Country_ Ireland 16281.0 0.000798 0.028247 0.0 0.0 0.0 0.0 1.0
Country_ Italy 16281.0 0.001965 0.044292 0.0 0.0 0.0 0.0 1.0
Country_ Jamaica 16281.0 0.001536 0.039157 0.0 0.0 0.0 0.0 1.0
Country_ Japan 16281.0 0.001843 0.042888 0.0 0.0 0.0 0.0 1.0
Country_ Laos 16281.0 0.000307 0.017522 0.0 0.0 0.0 0.0 1.0
Country_ Mexico 16281.0 0.018918 0.136239 0.0 0.0 0.0 0.0 1.0
Country_ Nicaragua 16281.0 0.000921 0.030340 0.0 0.0 0.0 0.0 1.0
Country_ Outlying-US(Guam-USVI-etc) 16281.0 0.000553 0.023506 0.0 0.0 0.0 0.0 1.0
Country_ Peru 16281.0 0.000921 0.030340 0.0 0.0 0.0 0.0 1.0
Country_ Philippines 16281.0 0.005958 0.076959 0.0 0.0 0.0 0.0 1.0
Country_ Poland 16281.0 0.001658 0.040691 0.0 0.0 0.0 0.0 1.0
Country_ Portugal 16281.0 0.001843 0.042888 0.0 0.0 0.0 0.0 1.0
Country_ Puerto-Rico 16281.0 0.004299 0.065431 0.0 0.0 0.0 0.0 1.0
Country_ Scotland 16281.0 0.000553 0.023506 0.0 0.0 0.0 0.0 1.0
Country_ South 16281.0 0.002150 0.046317 0.0 0.0 0.0 0.0 1.0
Country_ Taiwan 16281.0 0.000860 0.029312 0.0 0.0 0.0 0.0 1.0
Country_ Thailand 16281.0 0.000737 0.027140 0.0 0.0 0.0 0.0 1.0
Country_ Trinadad&Tobago 16281.0 0.000491 0.022162 0.0 0.0 0.0 0.0 1.0
Country_ United-States 16281.0 0.917388 0.275303 0.0 1.0 1.0 1.0 1.0
Country_ Vietnam 16281.0 0.001167 0.034143 0.0 0.0 0.0 0.0 1.0
Country_ Yugoslavia 16281.0 0.000430 0.020731 0.0 0.0 0.0 0.0 1.0

105 rows × 8 columns

In [35]:
set(data_train.columns) - set(data_test.columns)
Out[35]:
{'Country_ Holand-Netherlands'}
In [36]:
data_train.shape, data_test.shape
Out[36]:
((32561, 106), (16281, 105))

В тестовой выборке не оказалось Голландии. Заведем необходимый признак из нулей.

In [37]:
data_test['Country_ Holand-Netherlands'] = np.zeros([data_test.shape[0], 1])
In [38]:
set(data_train.columns) - set(data_test.columns)
Out[38]:
set()
In [39]:
data_train.head(2)
Out[39]:
Age fnlwgt Education_Num Capital_Gain Capital_Loss Hours_per_week Target Workclass_ Federal-gov Workclass_ Local-gov Workclass_ Never-worked ... Country_ Portugal Country_ Puerto-Rico Country_ Scotland Country_ South Country_ Taiwan Country_ Thailand Country_ Trinadad&Tobago Country_ United-States Country_ Vietnam Country_ Yugoslavia
0 39 77516 13 2174 0 40 0 0 0 0 ... 0 0 0 0 0 0 0 1 0 0
1 50 83311 13 0 0 13 0 0 0 0 ... 0 0 0 0 0 0 0 1 0 0

2 rows × 106 columns

In [40]:
data_test.head(2)
Out[40]:
Age fnlwgt Education_Num Capital_Gain Capital_Loss Hours_per_week Target Workclass_ Federal-gov Workclass_ Local-gov Workclass_ Never-worked ... Country_ Puerto-Rico Country_ Scotland Country_ South Country_ Taiwan Country_ Thailand Country_ Trinadad&Tobago Country_ United-States Country_ Vietnam Country_ Yugoslavia Country_ Holand-Netherlands
1 25 226802 7 0 0 40 0 0 0 0 ... 0 0 0 0 0 0 1 0 0 0.0
2 38 89814 9 0 0 50 0 0 0 0 ... 0 0 0 0 0 0 1 0 0 0.0

2 rows × 106 columns

In [41]:
X_train=data_train.drop(['Target'], axis=1)
y_train = data_train['Target']

X_test=data_test.drop(['Target'], axis=1)
y_test = data_test['Target']

3.1. Дерево решений без настройки параметров

Обучите на имеющейся выборке дерево решений (DecisionTreeClassifier) максимальной глубины 3 и получите качество на тесте. Используйте параметр random_state = 17 для воспроизводимости результатов.

In [ ]:
tree = # Ваш код здесь
tree.fit # Ваш код здесь

Сделайте с помощью полученной модели прогноз для тестовой выборки.

In [ ]:
tree_predictions = tree.predict # Ваш код здесь
In [ ]:
accuracy_score # Ваш код здесь

Вопрос 6. Какова доля правильных ответов дерева решений на тестовой выборке при максимальной глубине дерева = 3 и random_state = 17?

3.2. Дерево решений с настройкой параметров

Обучите на имеющейся выборке дерево решений (DecisionTreeClassifier, опять random_state = 17 ). Максимальную глубину настройте на кросс-валидации с помощью GridSearchCV. Проведите 5-кратную кросс-валидацию

In [ ]:
tree_params = {'max_depth': range(2,11)}

locally_best_tree = GridSearchCV # Ваш код здесь                      

locally_best_tree.fit # Ваш код здесь
In [ ]:
print("Best params:", locally_best_tree.best_params_)
print("Best cross validaton score", locally_best_tree.best_score_)

Обучите на имеющейся выборке дерево решений максимальной глубины 9 (это лучшее значение max_depth в моем случае) и оцените долю правильных ответов на тесте. Используйте параметр random_state = 17 для воспроизводимости результатов.

In [ ]:
tuned_tree = # Ваш код здесь
tuned_tree.fit # Ваш код здесь
tuned_tree_predictions = tuned_tree.predict # Ваш код здесь
accuracy_score # Ваш код здесь

Вопрос 7. Какова доля правильных ответов дерева решений на тестовой выборке при максимальной глубине дерева = 9 и random_state = 17?

3.3. Случайный лес без настройки параметров (опционально)¶

Немного забежим вперед и попробуем в нашей задаче случайный лес. Пока можно его себе представлять, как куча деревьев решений, обученных на немного разных подвыборках исходной обучающей выборки, причем эта куча деревьев обычно работает существенно лучше, чем отдельные деревья.

Обучите на имеющейся выборке случайный лес (RandomForestClassifier), число деревьев сделайте равным ста, а random_state = 17.

In [ ]:
rf = # Ваш код здесь
rf.fit # Ваш код здесь

Сделайте с помощью полученной модели прогноз для тестовой выборки.

In [ ]:
forest_predictions = rf.predict # Ваш код здесь
In [ ]:
accuracy_score # Ваш код здесь

3.4. Случайный лес с настройкой параметров (опционально)

Обучите на имеющейся выборке случайный лес (RandomForestClassifier). Максимальную глубину и максимальное число признаков для каждого дерева настройте с помощью GridSearchCV.

In [ ]:
forest_params = {'max_depth': range(10, 21),
                'max_features': range(5, 105, 10)}

locally_best_forest = GridSearchCV # Ваш код здесь

locally_best_forest.fit # Ваш код здесь
In [ ]:
print("Best params:", locally_best_forest.best_params_)
print("Best cross validaton score", locally_best_forest.best_score_)

Сделайте с помощью полученной модели прогноз для тестовой выборки.

In [ ]:
tuned_forest_predictions = locally_best_forest.predict # Ваш код здесь
accuracy_score # Ваш код здесь