Привет, Хабр! Меня зовут Кирилл Тобола, я Data Scientist и участник профессионального сообщества NTA. Добро пожаловать в год 2912, где ваши DS навыки понадобятся для решения космической загадки. Мы получили сообщение с корабля на расстоянии 4 световых лет, и ситуация выглядит плохо. Крейсер Титаник — межгалактический пассажирский лайнер отправился в путь около месяца назад. Почти 13 000 пассажиров находились на борту. Судно отправилось в свой маршрут, перевозя эмигрантов из нашей солнечной системы к трем новым недавно освоенным экзопланетам. Следуя через Альфа Центавру на пути к первому пункту назначения жаркой 55 Кансри Е, крейсер Титаник столкнулся с пространственно‑временной аномалией, скрывшейся за облаком космической пыли. К сожалению, крейсер постигла та же участь, что и одноименный корабль ровно 1000 лет назад. В то время как корабль не пострадал, почти половина пассажиров переместились в альтернативное пространство. Чтобы спасти команду и вернуть потерянных пассажиров, вам необходимо спрогнозировать кто из пассажиров переместился в альтернативную реальность используя данные из поврежденного журнала корабля. Задача помочь им спастись и не дать истории повториться вновь… Хотя может и не нужно и появится новый достаточно неплохой фильм. Spaceship Titanic — это классическая задача машинного обучения, а именно задача бинарной классификации. Сегодня я хочу рассказать об опыте применения EDA (Exploratory Data Analysis) для улучшения точности ML‑модели, на примере задачи из соревнования Spaceship Titanic. EDA (или разведочный анализ) — анализ основных свойств данных, используемый для нахождения общих зависимостей. Следствием качественного разведочного анализа является повышение качества модели машинного обучения. Для начала рассмотрю набор исходных данных: Размерность обучающей выборки (8693 x 13) Размерность тестовой выборки (4277 x 13) Данные содержат следующие признаки: PassengerId — уникальный идентификатор для каждого пассажира. Состоит из номера группы и номера в этой группе. HomePlanet — планета, с которой отправился пассажир. CryoSleep — индикатор криосна. Cabin — номер кабины. Состоит из палубы/номера/стороны. Destination — планета, на которую летел пассажир. Age — возраст пассажира. VIP — индикатор, заплатил ли пассажир за ВИП сервисы. RoomService, FoodCourt, ShoppingMall, Spa, VRDeck — сумма, которую пассажир заплатил за удобства космического корабля. Name — имя и фамилия пассажира. Transported — целевая функция. Первым делом декомпозирую составные признаки и уберу неинформативные: Я решил удалить следующие признаки:  Name (не получится с помощью него заполнить пропуски); IdWithinGroup (идентификатор внутри группы, неинформативный); Num (не очень понимаю, по какому принципу заполнять пропуски). Для нахождения неинформативных признаков я воспользовался библиотекой SHAP. Данная библиотека оценивает важность признаков при помощи построения вектора Шепли. Результат оценки признаков в виде графика:  На данном графике признаки упорядочены по важности. Можно заметить, что признак VIP практически не влияет на целевую функцию, поэтому исключу его. Исходный набор данных содержит пропуски:   Кол-во пропущенных значений % пропущенных значений HomePlanet 201 2.312205 CryoSleep 217 2.496261 Destination 182 2.093639 Age 179 2.059128 RoomService 181 2.082135 FoodCourt 183 2.105142 ShoppingMall 208 2.392730 Spa 183 2.105142 VRDeck 188 2.162660 GroupId 0 0.000000 Deck 199 2.289198 Side 199 2.289198 Для начала попробую не обращать внимания на данные пропуски. В качестве модели для решения задачи классификации буду использовать модель градиентного бустинга Catboost. Эта модель способна работать с данными, содержащими пропуски, поэтому для начала можно не заполнять пропущенные значения. Закодирую категориальные переменные с помощью one‑hot кодирования: Обучу модель и проверю результат: Score = 0.79 261 (топ 1262). Улучшить результат можно несколькими способами. Например,  подкрутить параметры модели; проанализировать данные и попробовать увидеть зависимости. Заполнить пустые значения, чтобы не потерять информацию. Рассмотрю подробнее последний способ. Для начала попробую заполнить пустые значения, не проводя анализ признаков. Для категориальных признаков заполню пропуски наиболее встречающимся значением, а для числовых признаков заполню пропуски медианами. Проверю результат: Score = 0.79 401. По сравнению с предыдущим — улучшение метрики на 0.0014. Попробую улучшить результат, проанализировав данные. Для начала посмотрю на распределения числовых признаков: Стоит отметить, что методы градиентного бустинга слабо чувствительны к выбросам, поэтому можно оставить данные как есть. Забегая вперёд, скажу, что для борьбы с аномальными значениями я попытался применить следующие методы: Удаление аномальных значений (в результате потерял информацию, из‑за чего значение метрики ухудшилось); Замена аномальных значений при помощи расчёта 1.5 межквартильного размаха (IQR) и замены аномальных значений на Q1 – 1.5*IQR и Q3 + 1.5*IQR. (метрика также снизилась). По графикам можно заметить, что для всех распределений признаков характерна положительная асимметрия («перекос» влево). Для таких данных можно воспользоваться методом log transform (логарифмирования). С помощью данного метода получилось достичь лучшего результата. На графике показано, как с помощью данного метода можно нивелировать выбросы. После применения логарифмирования распределения признаков стали иметь следующий вид:    Далее посмотрю на значения признаков при CryoSleep = True:      RoomService FoodCourt ShoppingMall Spa VRDeck count 2969.0 2967.0 2941.0 2972.0 2975.0 mean 0.0 0.0 0.0 0.0 0.0 std 0.0 0.0 0.0 0.0 0.0 min 0.0 0.0 0.0 0.0 0.0 25% 0.0 0.0 0.0 0.0 0.0 50% 0.0 0.0 0.0 0.0 0.0 75% 0.0 0.0 0.0 0.0 0.0 max 0.0 0.0 0.0 0.0 0.0 Можно сделать вывод о том, что если CryoSleep = True, то для всех записей значения признаков RoomService, FoodCourt, ShoppingMall, Spa, VRDeck = 0. Соответственно, если хотя бы одно из значений признаков RoomService, FoodCourt, ShoppingMall, Spa, VRDeck не равно нулю, то можно считать, что CryoSleep = False. На основании этих утверждений, заполню пропуски в поле CryoSleep: Посмотрю на данные в разрезе признаков HomePlanet — Deck: Deck A B C D E F G T HomePlanet                 Earth 0.0 0.0 0.0 0.0 395.0 1614.0 2498.0 0.0 Europa 252.0 766.0 734.0 186.0 128.0 0.0 0.0 4.0 Mars 0.0 0.0 0.0 282.0 330.0 1110.0 0.0 0.0 Можно заметить, что на палубах (Deck) A, B, C, T размещены только пассажиры с планеты Европа. А на палубе G только пассажиры с планеты Земля. Для пассажиров с Марса нельзя однозначно сказать, на какой палубе они размещены. На основании этих данных заполню пропуски для признака HomePlanet: Исходя из ранее приведенной таблицы в разрезе HomePlanet – Deck, можно заполнить значения Deck по значению HomePlanet, используя вероятностное распределение: Посмотрю на распределения возрастов на планетах:    Заполню пропущенные значения признака Age медианными значениями по планетам: Проверю результат: Score = 0.79 775. В сравнении с первой попыткой — улучшение на 0.5 процента. В результате только при помощи разведочного анализа и заполнения пустых значений на его основе удалось добиться улучшения на половину процента. Что, довольно, неплохо, учитывая тот факт, что в таких соревнованиях каждая десятая процента метрики может значительно повысить место в рейтинге. Попробую улучшить модель при помощи подбора гиперпараметров модели. Для этого воспользуюсь фреймворком Optuna. Optuna подобрал следующие параметры: objective (Функционал ошибки): Logloss  colsample_bylevel (Процент признаков, используемых при каждом выборе разделения): 0.089  depth (глубина дерева): 11  boosting_type (схема бустинга): Ordered  bootstrap_type (тип бустрапа): Bernoulli  Обучу модель с подобранными гиперпараметрами и проверю результат: Score = 0.80 804 (топ 190).  В результате при помощи подбора параметров получилось улучшить значение метрики более чем на один процент и перешагнуть значение итогового Score в 0.80. Какие идеи для улучшения результата я на данный момент вижу: попробовать выделить новые признаки на основе имеющихся; попытаться найти закономерности в других признаках; попробовать другие ML‑модели. Если у вас есть идеи по улучшению, пожалуйста, поделитесь ими в комментариях. Ссылка на kaggle.  Профессиональное сообщество