Примечание. Это вторая часть из двух частей, посвященных анализу и пониманию набора данных Titanic. Часть 1 Вы найдете здесь.

Вступление

В прошлом посте я провел статистический анализ набора данных Титаника, чтобы ответить на вопрос, влияет ли социально-экономический класс пассажиров на их вероятность выживания. Тест статистической значимости показал, что переменная Pclass, которая представляет собой класс каждого человека, который я использовал в качестве показателя социально-экономического статуса, оказала значительное влияние на выживание людей с p-значением 2,24e-147. Вы можете прочитать пост здесь.

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

Очистка и предварительная обработка данных

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

df = pd.read_csv('train_titanic.csv')
df.head(10)

Посмотрим, отсутствует ли значительное количество данных:

cols = df.columns 
colours = ['darkblue', 'red'] 
sns.heatmap(df[cols].isnull(), cmap=sns.color_palette(colours))

На приведенной выше тепловой карте по оси X показаны имена столбцов, а по оси Y - индексы строк. Маленькие красные полоски указывают на то, что для этой строки в конкретном столбце отсутствует значение. Кажется, что у признаков «Возраст» и «Кабина» много пропущенных значений. Посмотрим в процентах:

pct_list = []
for col in df.columns:
    pct_missing = np.mean(df[col].isnull())
    if round(pct_missing*100) >0:
        pct_list.append([col, round(pct_missing*100)])
    print('{} - {}%'.format(col, round(pct_missing*100)))

Для функции «Кабина» отсутствует 77% данных. Я собираюсь удалить эту функцию. Однако возраст не включает 20% данных. Возраст должен быть важной переменной в этом приложении, поскольку он должен был повлиять на вероятность выживания (например, пожилым людям или детям мог быть отдан приоритет). Обычно я просто заполняю недостающие значения средним значением возраста других людей. Однако в этом конкретном наборе данных люди были из разных классов, поэтому не рекомендуется рассматривать всех их как одну группу. В наборе данных есть функция «Имя», у имени есть титул людей (например, «Мистер», «Мисс»… и т. Д.). Этот титул должен точно указывать на возраст. Кроме того, я должен иметь в виду, что в то время заболеваемости (в 1912 году) социально-экономический статус влиял на титул людей независимо от возраста (например, более молодые люди, которые богаты, могли получать титулы, которые не могли бы получить обычные бедные люди в том же возрасте) . Итак, я собираюсь сгруппировать людей по их титулам и Pclass, а затем сопоставлю средний возраст каждой группы отсутствующему возрасту в каждой группе.

# extracting the title from the name:
Title = []
for name in  df.Name:
    Title.append(name.split(",")[1].split(".")[0])
    
df["Title"] = Title
#grouping people with pclass and title
df.groupby(["Pclass", 'Title'])['Age'].agg(['mean']).round(0)
# adding the mean of the age of each group to the missing values
df["Age"] = df.groupby(["Title", "Pclass"])["Age"].transform(lambda x: x.fillna(x.mean()))

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

df = df.drop(columns = ["Name"])
df = df.drop(columns = ["PassengerId"])
df = df.drop(columns = ["Ticket"])

В качестве последнего шага я закодирую категориальные признаки в числовые:

df.Sex = pd.Categorical(df.Sex)
df.Embarked = pd.Categorical(df.Embarked)
df["Sex"] = df.Sex.cat.codes
df["Embarked"] = df.Embarked.cat.codes

и я исключу конечную переменную «Выживание» из набора x-данных

target = df.Survived.values
df = df.drop(columns =["Survived"])

Построение линейной модели

Наконец, я собираюсь построить модель. Я буду использовать простую модель логистической регрессии, поскольку цель здесь - увидеть, как функции влияют на результат, а не получить высокий балл в прогнозировании.

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df, target, test_size=0.2, random_state=0)
from sklearn.linear_model import LogisticRegression
LR = LogisticRegression()
LR.fit(x_train, y_train)
LR.score(x_test, y_test)

Модель имеет показатель точности 82%, что является разумным для нашей цели.

Теперь мы можем перейти к самой интересной части. Я буду использовать SHAP, чтобы интерпретировать эту модель, чтобы увидеть, как эти особенности повлияли на падение Титаника.

Интерпретация модели с помощью SHAP

SHAP - отличный инструмент для интерпретации моделей. Несмотря на то, что это сложная модель, ее легко понять. Цель SHAP - обеспечить визуализацию влияния каждой функции на конечную переменную. Для этого SHAP построит модель, которая использует все функции, кроме интересующей, и посмотрит, как модель будет работать без этой функции. Затем он снова построит модель и сделает прогноз с помощью функции. Эффектом этой функции будет тогда разница между двумя значениями. Но порядок, в котором признаки передаются в модель, влияет на результат (особенно в моделях на основе дерева, в которых модель следует схематическому подходу, упорядоченному по признакам). Таким образом, SHAP вычисляет все возможные перестановки, при которых различные функции могут быть переданы модели. Кажется, что это требует огромных вычислительных затрат, но SHAP оптимизировал алгоритмы, которые ускоряют работу с конкретными моделями машинного обучения.

Получим графики SHAP:

import shap
explainer = shap.LinearExplainer(LR, x_train, feature_perturbation="interventional")
shap_values = explainer.shap_values(x_test)
shap.summary_plot(shap_values, x_test)
shap.summary_plot(shap_values, x_train, plot_type="bar")

Как и ожидалось из статистических выводов, которые я провел, Pclass оказывает значительное влияние на выживаемость пассажиров. Это вторая по значимости характеристика после «Секса». Из графика выше видно, что низкие значения (синий) для Pclass, которые соответствуют классу 1 (более богатые люди), положительно влияют на выживание людей, в то время как более высокие значения (красный), которые соответствуют третьему классу, имеют отрицательный эффект. на выживаемость. Мы также можем видеть, что «пол» является наиболее важной характеристикой с указанием того, что принадлежность к «женщине» (синий цвет) положительно влияет на выживаемость. Функция «Возраст» также показывает, что более низкие значения (синий цвет) оказали положительное влияние на выживаемость.

Давайте посмотрим на переменный тариф, который представляет собой сумму, которую каждый человек заплатил за свой билет. Эта переменная должна быть непрерывным описанием благосостояния людей:

shap.dependence_plot("Fare", shap_values, x_test)

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

Наконец, давайте внимательнее посмотрим на нескольких пассажиров:

shap.force_plot(explainer.expected_value, shap_values[0,:], x_test.iloc[0,:], link="logit")

Это сюжет про пассажира, который не выжил. Сюжет показывает, что его «пол» (принадлежность к мужчине) и его «класс» (принадлежность к третьему классу) снижали его выживаемость. График также показывает, что количество братьев и сестер («SibSp»), равное 0, немного увеличивало его шанс. Кажется, что люди, которые были на корабле одни без семьи, могли бежать быстрее, не отвлекаясь.

Давайте посмотрим на кого-то, кто выжил:

shap.force_plot(explainer.expected_value, shap_values[3,:], x_test.iloc[3,:], link="logit")

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

Эти результаты можно обобщить, используя следующий график сил:

На этом графике показаны особенности, которые положительно влияли на выживаемость каждого пассажира (синий) или отрицательно (красный).

Заключение

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

Найдите мою записную книжку здесь. Вам необходимо загрузить и запустить записную книжку для загрузки графиков SHAP, поскольку они зависят от Javascript .

Ссылки