Титаник на Kaggle: вы не дочитаете этот пост до конца

Титаник на Kaggle: вы не дочитаете этот пост до конца

Меня зовут Глеб Морозов, мы с Вами уже знакомы по предыдущим статьям. По многочисленным просьбам продолжаю описывать опыт своего участия в образовательных проектах MLClass.ru (кстати, кто еще не успел — до конца еще можно получить материалы прошедших курсов — это, наверное, самый краткий и максимально практичный курс по анализу данных, который можно себе представить).

Данная работа описывает мою попытку создать модель для предсказания выживших пассажиров «Титаника». Основная задача — тренировка в использовании инструментов применяемых в Data Science для анализа данных и презентации результатов исследования, поэтому данная статья будет очень и очень длинной. Основное внимание уделено исследовательскому анализу (exploratory research) и работе по созданию и выбору предикторов (feature engineering). Модель создаётся в рамках соревнования Titanic: Machine Learning from Disaster проходящего на сайте Kaggle. В своей работе я буду использовать язык «R».

Предпосылки для создания модели

Если доверять Википедии, то Титаник столкнулся с айсбергом в 11:40 вечера корабельного времени, когда подавляющее большинство пассажиров и корабельной команды находились в своих каютах. Соответственно, расположение кают, возможно, имело влияние на вероятность выжить, т.к. пассажиры нижних палуб, во-первых, позднее узнали о столкновении и, соответственно, имели меньше времени добраться до верхней палубы. И, во-вторых, им, естественно, было дольше выбираться из помещений корабля. Ниже изображены схемы Титаника с указанием палуб и помещений.

Титаник являлся британским кораблем, а согласно законам Британии на корабле должно было быть число шлюпок, соответствующее водоизмещению судна, а не пассажировместимости. Титаник формально соответствовал этим требованиям и имел 20 шлюпок (14 со вместимостью 65 человек, 2 — 40 человек, 4 — 47 человек), которые были рассчитаны на погрузку 1178 человек, всего же на Титанике было 2208 человек. Таким образом, зная, что шлюпок на всех не хватит, капитан Титаника Смит отдал, после столкновения с айсбергом, приказ брать на шлюпки только женщин и детей. Однако члены команды не всегда следовали ему.

Получение данных

Kaggle предоставляет данные в виде двух файлов в формате csv:

  • train.csv (содержит выборку пассажиров с известным исходом, т.е. выжил или нет)
  • test.csv (содержит другую выборку пассажиров без зависимой переменной)

Для получения данных в R я использую функцию read_csv из пакета readr. В сравнении с базовыми функциями данный пакет предоставляет ряд преимуществ, в частности: более высокую скорость и понятные названия параметров.

Посмотрим, что у нас получилось:

Анализ данных

Исследовательский анализ данных, как я считаю, является одной из наиболее важных частей работы Data Scientist's, т.к., кроме непосредственно преобразования «сырых» данных в готовые для создания модели, часто во время этого процесса можно увидеть скрытые зависимости, благодаря использованию которых и получаются наиболее точные модели.

Для начала посмотрим на отсутствующие данные. В предоставленных данных часть отсутствующей информации была отмечена символом NA и при загрузке были по умолчанию преобразованы в особый символ NA. Но среди символьных переменных много пассажиров с пропущенными переменными, которые не были отмечены. Проверим их наличие используя возможности пакетов magrittr и dplyr

Заменим пропуски на NA, используя функцию recode из пакета car

Для графического представления удобно использовать функцию missmap из пакета для работы с отсутствующими данными Amelia.

Таким образом пропущенно около 20% данных в переменной Age и почти 80% в Cabin. И если с возрастом пассажиров можно провести обоснованное замещение пропущенных значений, в связи с небольшой их долей, то с каютами маловероятно что-то получится сделать, т.к. пропущенных значений существенно больше нежели заполненных. Пропущенные значения в признаке Embarked

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

Уже можно делать первые выводы:

  • больше пассажиров погибло чем спаслось
  • подавляющее большинство пассажиров находилось в каютах третьего класса
  • мужчин было больше чем женщин

Ненадолго вернёмся к пропущенным значениям. Из графика Распределение пассажиров по пункту отправления очевидно, что большинство пассажиров отправлялось из Southampton, соответственно можно спокойно заменить 2 NA этим значением

Теперь подробнее посмотрим на взаимоотношения между вероятностью выжить и другими факторами. Следующий график подтверждает теорию, что чем выше класс каюты пассажира — тем больше шансы выжить. (Под «выше»" я имею ввиду обратный порядок, т.к. первый класс выше чем второй и, тем более, третий.)

Сравним шансы выжить у мужчин и женщин. Данные подтверждают теорию, высказанную ранее.

Теперь взглянем на шансы выжить у пассажиров из различных портов отправления.

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

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

Как видно, явная зависимость здесь не просматривается.

Теперь при помощи другого вида графика посмотрим на наличие возможных статистических связей между признаками объектов. Можно сделать предварительные выводы, которые подтверждают мысли высказанные ранее. В частности, что шансы выжить уменьшаются с ростом класса и возраст — очень слабый признак для построения модели. Также можно обнаружить и другие закономерности. Между возрастом и классом существует отрицательная корреляция, что, скорее всего, связано с более возрастные пассажиры чаще могли себе позволить более дорогую каюту. Кроме того, стоимость билета и класс тесно связаны (высокий коэффициент корреляции), что вполне ожидаемо.

Вернёмся к пропущенным значениям в данных. Один из обычных способов борьбы с ними — это замена на среднее от доступных значении того же признака. Например, 177 пропущенных из признака Age можно заменить на 29.7

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

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

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

Имя каждого пассажира построено каждый раз по одному паттерну: «Фамилия, Гоноратив. Имя». Обращение Master в 19 веке применялось по отношению к детям мужского пола, соответственно, это можно использовать для выделения более узких и точных групп по возрасту. А Miss применялось по отношению к незамужним женщинам, но в 19 веке незамужними были, в подавляющем большинстве, только молодые девушки и девочки. Для того, чтобы использовать эту зависимость создадим новый признак Title.

Теперь определим титулы, среди владельцев которых есть хотя бы один с отсутствующим возрастом.

И проведём замену. Для этого создадим функцию и применим её к признаку Age.

Если обратить внимание на признак Fare(стоимость билета), то можно увидеть, что есть билеты с нулевой стоимостью.

Первое объяснение, которое приходит в голову — это дети, но, если посмотреть на другие признаки этих пассажиров, то данное предположение оказывается ложным.

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

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

Но многие из значений, как я считаю, можно объединить в 5 групп: Aristocratic, Mr, Mrs, Miss и Master, т.к. объединяемые титулы принадлежать фактически одной или родственным группам.

Давайте введём такой показатель как Процент выживаемости и посмотрим на его зависимость от групп, которые получились на предыдущем этапе.

Для того, чтобы получить составить хорошее представление о взаимосвязях между признаками лучше чем графика, как я думаю ничего ещё не придумано. Например, по следующему графику прекрасно видно, что основные группы выживших — это женщины первого и второго класса всех возрастов. А среди мужчин выжили все мальчики моложе 15 лет кроме третьего класса обслуживания и небольшая доля мужчин более старшего возраста и в основном из первого класса.

Теперь посмотрим на информацию, которую можно получить из количества родственников на корабле.

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

Введём такой признак как Family, т.е. количество родственников на борту корабля и посмотрим на влияние на выживаемость.

И также в разрезе по полам пассажиров.

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

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

Для мужчины во втором классе родственники повышают выживаемость, но для женщины в третьем классе — ситуация обратная.

Из признака Cabin, т.е. номера каюты занимаемой пассажиром, можно было бы извлечь номер палубы (это буква в номере) и на каком борту была каюта (если последняя цифра номера нечётная, то это левый борт, и, соответственно, наоборот), но, т.к. номера кают в данных есть всего лишь у 20% пассажиров, то я не думаю, что это существенно повлияет на точность модели. Гораздо интереснее, по моему мнению, будет информация о наличии этого номера. Номера кают первого класса стали известны из списка, который был найден на теле стюарта Herbert Cave, больше никакой официальной информации не сохранилось, соответственно, можно сделать вывод, что, если известен номер каюты пассажира второго или третьего класса, то он выжил. Поэтому, как и с родственниками, посмотрим на выживаемость в зависимости от наличия номера каюты в целом по всем пассажирам и в разрезе по классам и полу.

Очевидно, что предположение подтвердилось, в особенности для пассажиров мужского пола.

Подведём итог всей исследовательской работе, которая была проделана:

  • Были выявлены определённые закономерности в данных, но для того, чтобы точно сказать, что целевой признак зависит от этого и от этого необходимо провести статистический анализ.
  • Были созданы дополнительные признаки Title, Family, isFamily, isCabin, которые, на мой взгляд, оказывают влияние на целевой признак и могут быть использованы при создании модели. Но окончательный вывод о пользе этих признаков можно будет сделать только в процессе создания предсказательной модели.

Теперь выделим из данных те признаки, которые будем использовать при создании модели.

И последний график в этой части работы.

Подготовим данные для корректного использования в процессе моделями.

Создание модели

В работе я буду использовать пакет caret, который вобрал в себя большинство из известных моделей машинного обучения и предоставляет удобный интерфейс для использования их на практике. Не смотря на то, что у нас есть тестовая выборка предоставляемая сайтом Kaggle, нам всё равно необходимо разбить тренировочную выборку на две части. На одной из которых мы будем тренировать модель, а на другой — оценивать её качество, прежде чем применять к соревновательной выборке. Я выбрал разделение в соотношении 80/20.

Начнём с простейшей классификационной модели — логистической регрессии. Для оценки модели будем использовать статистику residual deviance или девианс остатков, который косвенно соответствует дисперсии в данных, оставшейся необъясненной после применения модели. Null deviance или нуль-девианс — это девианс «пустой» модели, не включающей ни одного параметра кроме beta0. Соответственно чем меньше девианс остатков по отношению к нуль-девианс — тем лучше модель. В дальнейшем, для сравнения различных моделей, будет применяется статистика AUC или площадь под кривой ROC. Для корректной оценки данного параметра он будет оцениваться с использованием десятикратной кросс-проверки (10-fold cross-validation (CV)) с разбиением выборки на 10 частей.

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

Модель уже показывает неплохие показатели в снижении девианса на 950-613=337 пунктов в сравнении с «пустой» моделью. Теперь попробуем улучшить этот показатель путём ввода тех новых признаков, которые были добавлены ранее.

Улучшения не произошло, даже, скорее, наоборот. Хотя, если обратить внимание на показатель ROC, то видно, что он растёт каждый раз как мы удаляем из модели признаки с высоким значением p-value и, соответственно, с большой вероятностью, что коэффициент при этом признаке равен нулю. Мы продолжим удалять избыточные предикторы. Удалим isFamily, т.к. Family содержит в себе всю его информацию. И из классов оставим только третий, как наиболее значимый для модели. Аналогично поступим с Title.

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

И мы получили существенный скачок в качестве модели. На данном этапе остановимся с логистической регрессией и обратимся к другим моделям.

В частности, к очень популярному Random Forest. При тренировки этой модели можно выбрать количество случайно выбираемых, для каждого из множества создаваемых деревьев, признаков — mtry.

В данном случае, наилучшие показатели имеет модель с mtry равным 3.

И последняя модель, которая будет применена в данной работе — это support vector machine (SVM). SVM чувствительна к ненормализованным входным данным, поэтому будет использован параметр preProcess, чтобы перед тренировкой модели была проведена нормализация. У SVM в качестве одного из параметров используется Cost. Модель будет выполнена на его 9 различных значениях и выбрана с наилучшими показателями AUC.

Оценка модели

Для всех трёх созданных моделей проведём оценку при помощи пересечения предсказанных на тестовой выборке и реальных значений целевого признака. Функция confusionMatrix из пакета Caret позволяет легко это сделать.

Random Forest показывает лучший результат в предсказании погибших — показатель Sensitivity. А логистическая регрессия в предсказании выживших — показатель Specificity.

Изобразим на одном графике кривые ROC на тестовых данных для всех созданных моделей.

По статистике AUC лидирует Random Forest, но это результаты однократного применения модели на тестовой выборке. Если же собрать эту статистику путём resampling, то результат будет другим, что и показано на следующем графике.

И, наконец, последний график этой работы. Это суммарная информация по моделям по трём статистикам:ROC,Sensitivity и Specificity.

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

Использование результатов для Kaggle

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

После загрузки результатов применения моделей на Kaggle наилучшие результаты показала модель SVM. Входя на момент написания этой работы в лучшие 10% результатов, но, т.к. до окончания соревнования модель оценивается только по части данных, то финальные результаты могут сильно отличаться, причём как в лучшую, так и в худшую сторону. Ниже приведены результаты оценки моделей на Public data.

📎📎📎📎📎📎📎📎📎📎