Тестирование универсальной модели на смехотворно сложной задаче

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

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

Я повторил эту процедуру для нескольких наборов данных и для нескольких групп одного и того же набора данных, всего 600 сравнений. Был явный победитель: общая модель.

Однако мой эксперимент не убедил некоторых людей, которые утверждали, что мой подход чрезмерно упрощен. Например, это был один из самых популярных комментариев под моим постом в Linkedin о статье:

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

Рабочая гипотеза

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

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

Если моя интуиция верна, мы могли бы набросать эту связь следующим образом:

Но это всего лишь моя гипотеза. Итак, давайте попробуем проверить это.

Давая нашей модели трудное время

Наша цель - ответить на этот вопрос:

Что происходит, если группы, составляющие набор данных, полностью отличаются друг от друга, а мы все еще используем общую модель?

Итак, вопрос в том, как смоделировать такой сценарий.

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

Возьмем, к примеру, три набора данных:

  • «банк»: каждая строка — клиент банка, задача — предсказать, подпишется ли он на срочный вклад;
  • «сотрудник»: каждая строка — сотрудник, задача — предсказать, покинет ли он компанию;
  • «доход»: каждая строка — это человек, задача состоит в том, чтобы предсказать, превышает ли его доход 50 тыс. $.

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

Вот образец (и строки, и столбцы) из трех наборов данных.

Как видите, наборы данных имеют разные столбцы. Итак, как мы можем объединить их вместе? Первая, самая наивная идея — использовать pd.concat:

pd.concat([X_bank, X_employee, X_income]).reset_index(drop=True)

Но, если бы мы это сделали, мы бы получили фрейм данных следующего вида:

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

Чтобы избежать этого, нам нужен способ «принудительно» объединить столбцы разных наборов данных.

Единственный способ, который я мог придумать, — это переименовать столбцы каждого набора данных с прогрессивным номером: «feature_01», «feature_02» и т. д. Но это не сработает, потому что столбцы имеют разные типы. Поэтому нам нужно различать категориальные и числовые признаки: «cat_feature_01», «cat_feature_02» и т. д. и «num_feature_01», «num_feature_02» и т. д. Более того, я решил отсортировать признаки по убыванию важности.

Это результат:

Возможно, вы думаете, что этого недостаточно. В конце концов, модель все еще может распознавать некоторые категории, принадлежащие данному набору данных (например, «замужем» в столбце «cat_feature_01» существует только в наборе данных «банк»). То же самое касается числовых признаков (например, значения от 0 до 1 в столбце «num_feature_02» существуют только в наборе данных «employee»). Это все еще может быть полезно для модели, и мы хотим этого избежать.

Таким образом, в качестве дополнительного шага я:

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

Итак, конечный результат:

Отказ от ответственности

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

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

Этот эксперимент должен быть задуман как своего рода «стресс-тест» возможностей моделей повышения градиента на основе деревьев.

Полученные результаты

Теперь, когда мы разработали стратегию, пришло время применить ее к некоторым реальным наборам данных. Я использовал 7 наборов данных для бинарной классификации с более чем 5000 строк, которые доступны в Pycaret (библиотека Python под лицензией MIT).

Это наборы данных с соответствующим количеством строк и столбцов:

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

  • Я переименовал каждый категориальный столбец (отсортированный в порядке убывания важности) как «cat_feature_01», «cat_feature_02», … и каждый числовой столбец (отсортированный в порядке убывания важности) как «num_feature_01», «num_feature_02», …;
  • для каждого категориального столбца я сопоставил каждое значение с отдельным целым числом: 0, 1, 2, …;
  • для каждого числового столбца я стандартизировал значения, вычитая их среднее значение и разделив на их стандартное отклонение;
  • Я добавил столбец, содержащий имя набора данных.

Затем я объединил все исходные наборы данных, чтобы получить окончательный набор данных. В этот момент я приступил к эксперименту. Это заключалось в:

  • обучение общей модели (Catboost, без настройки параметров) на полном объединенном наборе данных;
  • обучение 7 специализированных моделей (Catboost, без настройки параметров), по одной на каждом исходном наборе данных;
  • сравните производительность общей модели и специализированной модели для каждого набора данных.

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

Но как насчет производительности?

Вот сравнение оценок ROC общей модели и специализированных моделей:

Средняя разница между оценкой ROC общей модели и оценкой ROC специализированной модели составляет -0,53%. Это означает, что специализированные модели в целом превосходили общую модель.

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

А объяснимость?

Еще одна проблема, которую я слышал об общих моделях, — это их якобы необъяснимость. Фактически, некоторые люди утверждают, что одна общая модель менее прозрачна, чем множество специализированных моделей.

Я не согласен с этим пунктом. Фактически, благодаря значениям SHAP вы можете объяснить каждую группу отдельно от других, даже если модель уникальна. Мы могли бы назвать этот процесс «специализированной объяснимостью».

Позвольте мне привести пример, используя наш предыдущий эксперимент.

Если мы возьмем каждую группу отдельно и вычислим коэффициент корреляции между исходными значениями признаков и соответствующими значениями SHAP, мы получим следующее:

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

  • Для группы «банк» «num_feature_01» соответствует признаку «длительность», то есть как долго это лицо является владельцем счета. Целевой признак — подписался ли клиент на срочный депозит. Разумно ожидать положительного влияния функции на прогноз.
  • Для группы «сотрудник» «num_feature_01» соответствует признаку «satisfaction_level». Поскольку целевым признаком является то, ушел ли сотрудник, отрицательная корреляция легко объяснима.

Выводы

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

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

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

Это был стресс-тест, чтобы увидеть, что произойдет в смехотворно сложной ситуации для общей модели. Тем не менее, я обнаружил, что разница в производительности минимальна: средняя потеря показателя ROC при использовании общей модели вместо специализированных составляет 0,53%.

Кроме того, я использовал эксперимент, чтобы показать, почему объяснимость также не должна вызывать беспокойства. На самом деле, после использования общей модели все еще можно объяснить отдельные группы по отдельности посредством «специализированной объяснимости».

Вы можете найти весь код Python, использованный для этой статьи, в этой записной книжке.

Спасибо, что прочитали! Надеюсь, вам понравилась эта статья. Если хотите, добавьте меня в Linkedin!