Руководство по машинному обучению для начинающих: модель прогноза выживших на «Титанике»

Начало работы

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

Формат работы: вы начнёте с введения в машинное обучение на основе написания алгоритма, который будет предсказывать, сколько человек выживет при крушении «Титаника». Затем последуют две тренировочные сессии. Руководство будет направлять вас в дальнейшей работе, но код вы должны будете писать самостоятельно.

Требуемые знания: предполагается, что вы уже имеете опыт работы с Python или знаете его на уровне выше базового. Также предполагается, что вы знакомы с Pandas на базовом уровне. Если вы желаете подтянуть знания по Pandas, предлагаем ознакомиться со специальной статьёй. Также предлагаем вам посмотреть курс по основам Pandas.

Разработка будет выполняться с использованием Ipython (если вы раньше не работали с этой оболочкой, можете посмотреть вводный видеоурок и ознакомиться с командной оболочкой для интерактивных вычислений Jupyter Notebook в нашей статье). По определённым причинам рекомендуется использовать пакет Anaconda с Python 3.

Введение в машинное обучение

Презентация к уроку

В презентации вам дано небольшое задание на проверку логики (чем чаще вы будете уделять время на подобные упражнения, тем лучше вы сможете организовать рабочий процесс). Следует открыть файл titanic_train.csv и определить, какие поля, на ваш взгляд, имеют наибольшее значение для обучения. Вы можете сделать это сейчас, чтобы в середине статьи сравнить свой результат и результат автора.

Машинное обучение с Python

Исходный код, с которым вы будете работать, доступен на платформе Github. Для его загрузки регистрация не требуется. Titanic_Machine_Learning.ipynb — название файла, с которым вам предстоит работать.

Начнём с импорта основных библиотек: Pandas и Numpy.

import numpy as np
import pandas as pd

Для машинного обучения будет использоваться алгоритм Random Forest. На данный момент вам не нужно знать, как он работает (но в будущем обязательно изучите), но вам нужно знать, как его следует применять в деле.

#Алгоритм машинного обучения
from sklearn.ensemble import RandomForestClassifier

Если вы посмотрели презентацию (что действительно стоило сделать, иначе вы не сможете следовать половине кода), вы знаете, что нужно разделять данные для теста и для тренировки, чтобы не произошло переобучение. Поэтому нужно импортировать функцию train_test_split().

#Импорт функции
from sklearn.cross_validation import train_test_split

Теперь давайте отключим предупреждения от Pandas.

# Отключение предупреждений Pandas
pd.options.mode.chained_assignment = None

Пришло время импортировать функцию joblib. Она будет использоваться для написания модели в файле для повторного использования.

#Используется для записи модели в файл
from sklearn.externals import joblib

Теперь нужно открыть файл с расширением csv в Pandas. (Вы можете сделать это с помощью Excel или OpenOffice.)

data = pd.read_csv("titanic_train.csv")
data.head()

Вы увидите что-то вроде таблицы сверху (обращаем внимание, что некоторые колонки не отображены из-за чрезмерной ширины таблицы).

Посмотрите на колонку age. В таблице на изображении выше нет ячеек со значением NaN, что означает, что нет данных. В нашем случае есть просто пустые ячейки. Но обратите внимание, что NaN и пустая ячейка — одно и то же.

В презентации вам было дано задание: нужно было найти полезные, на ваш взгляд, входные данные для алгоритма.

Ожидаемые выходные данные — данные, которые мы получим в колонке survived (выжившие). Как насчёт входных данных?

Вы ведь смотрели фильм «Титаник»? (Если нет, то сейчас будет спойлер: он утонет.) Когда пришло время распределения людей по шлюпкам, больше шансов на спасение имели обладатели билетов первого класса. Также предпочтение отдавалось женщинам и детям.

Таким образом мы можем определить, что наиболее важные моменты, определяющие шанс спасения, это класс билета, возраст и пол.

Если вы женщина солидного возраста с детьми и с билетом первого класса, то вам повезло — вероятность выжить увеличивается.

Если же речь идёт о мужчине среднего возраста с билетом третьего или второго класса, то шансов на выживание становится в разы меньше. Всё как в фильме. Несмотря на то, что в таблице также указана цена билета, нам важнее знать, к какому классу относится этот билет. Если к первому, то шансы на выживание заметно возрастают.

Давайте вернёмся к пустым ячейкам. Мы не можем их оставить пустыми, потому что нам нужна информация в них для выполнения вычислений.

Решение проблемы: заменяем пустые поля медианой.

Перед тем, как продолжить чтение, убедитесь, что вы понимаете следующие термины:

  • Среднее арифметическое — сумма чисел вы выборке, делённая на количество чисел в выборке. Пример: вы написали 5 строк кода, ваш друг написал 9 строк кода, ваша подруга написала 7 строк кода. В среднем вы все написали по 7 строк: 5 + 9 + 7 = 21, 21 / 3 = 7.
  • Медиана (серединное число) — это такое число выборки, при котором половина значений выборки больше него, а другая половина меньше него. Пример: 1, 2, 5, 10, 21, 33, 57. Медианой в данном случае будет 10, так как числа слева меньше, а числа справа больше и с обеих сторон расположено одинаковое количество чисел.
  • Мода — самое повторяющееся число из всех повторяющихся.

В нашем случае сейчас будем использовать медиану для определения возраста.

median_age = data['age'].median()
print("Median age is {}".format(median_age))
Median age is 29.0

Можно подсчитать медиану с помощью функции median() в Pandas, из примера выше видно, что медианой является число 29.

Теперь нужно заменить пустые значения в колонке age на значение медианы. Для этого следует использовать функцию fillna().

data['age'].fillna(median_age, inplace = True)
data['age'].head()
0    29
1    65
2    59
3    46
4    29

Помните пустые (NaN) значения? Теперь они заменены на 29.

Теперь нужно извлечь три нужных нам поля: class (класс), age (возраст), sex (пол).

Для чего это нужно делать?

Это делается для того, чтобы не запутать алгоритм и не создавать лишний шум.

data_inputs = data[["pclass", "age", "sex"]]
data_inputs.head()

Ожидаемые выходные данные:

expected_output = data[["survived"]]
expected_output.head()

Как видите, у нас возникла проблема. Алгоритмы в библиотеке Scikit, которая используется, работают только с числами. Иначе говоря, алгоритм не понимает, что 1st — первый класс.

Давайте исправим проблему. Просто заменим 1st на 1, 2nd на 2, 3d на 3. Элементарно!

data_inputs["pclass"].replace("3rd", 3, inplace = True)
data_inputs["pclass"].replace("2nd", 2, inplace = True)
data_inputs["pclass"].replace("1st", 1, inplace = True)
data_inputs.head()

Теперь проблема с классом исправлена. Возраст выставлен правильно. Остаётся поправить проблему с полом.

Будем использовать функцию np.where(), которая не является интуитивной.

data_inputs["sex"] = np.where(data_inputs["sex"] == "female", 0, 1)
data_inputs.head()

Функция заменила female на 0 и male на 1.

Тестирование для предотвращения переобучения

Как вы могли запомнить из презентации, данные были разделены на набор для тестирования и на набор для обучения. Учебный набор используется для тренировки алгоритма, в то время как набор для теста используется для выявления точности алгоритма. (Так как у нас есть предполагаемые выходные данные, мы можем сравнить их с результатами алгоритма и вычислить процент неточности.)

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

inputs_train, inputs_test, expected_output_train, expected_output_test = train_test_split(data_inputs, expected_output, test_size = 0.33, random_state = 42)

Функция возвращает учебные входные и выходные данные, а также выходной набор данных.

test_size=0.33 значит, что 33% набора данных будет использоваться для тестирования, остальное — для обучения. random_state используется для запуска встроенного генератора случайных выборок.

Давайте выведем несколько значений:

print(inputs_train.head())
print(expected_output_train.head())
     pclass  age  sex
618       3   19    1
169       3   29    1
830       1   54    1
140       3   29    1
173       2   28    1
     survived
618         0
169         0
830         1
140         0
173         0

Теперь нужно запустить машинное обучение.

rf = RandomForestClassifier (n_estimators=100)

Создаём образец алгоритма Random forest.

rf.fit(inputs_train, expected_output_train)

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

В идеале образец алгоритма научится предсказывать количество выживших корабле. Давайте проверим точность алгоритма.

accuracy = rf.score(inputs_test, expected_output_test)
print("Accuracy = {}%".format(accuracy * 100))
Accuracy = 79.60526315789474%

Функция score() работает с тестовыми входными данными и выясняет, насколько точным является прогноз на основе известных выходных тестовых данных.

В примере выше показана точность, которая составила 79%. Мы узнаем, на сколько это хорошо, только когда сможем сравним с чем-нибудь.

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

Мы можем записать модель машинного обучения в файл, чтобы в будущем иметь возможность использовать её повторно.

joblib.dump(rf, "titanic_model1", compress=9)

Раньше для этого использовалась библиотека Pickle, но функция joblib.dump намного проще. Применение compress=9 является необходимым условием, в противном случае будут созданы десятки файлов.

Практика

В этой секции созданный алгоритм машинного обучения будет запущен в новом файле.

Работать предстоит с новым файлом, который раннее не использовался — titanic_test.csv. Он был создан с использованием оригинальных данных, от которых были отсечены 30% данных.

Извлеките из набора данные класса и пола, как мы это сделали для первой практики.

rf = joblib.load("titanic_model2")

Теперь нужно взять входные данные и пропустить их через прогнозирующую функцию:

pred = rf.predict()# поместите сюда ваши переменные данные

Сверху вы видите пустую функцию predict(). Вам нужно сделать predict(data), чтобы прогнозирование запустить.

На этом урок закончен. Спасибо за внимание!

Перевод статьи «Machine Learning For Complete Beginners»