Евгений Туренко

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

Руководство написано для тех, кто раннее не был знаком с машинным обучением. Это введение в машинное обучение на основе написания алгоритма, который будет предсказывать, сколько человек выживет при крушении «Титаника». Предполагается, что вы уже имеете опыт работы с Python и что вы знакомы с Pandas на базовом уровне.

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

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

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

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

Требуемые знания: предполагается, что вы уже имеете опыт работы с 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), чтобы прогнозирование запустить.

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

25499