В этом блоге я поделюсь своим опытом определения пользовательской метрики в Catboost для соревнования Kaggle.

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

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

Цель: построить модель, которая прогнозирует группу оценивания (4 класса) детей с учетом их предыдущих данных об использовании приложения. См. Kaggle’s Data Science Bowl 2019.

Оценочная метрика: Quadratic Weighted Kappa (QWK) - метрика, измеряющая согласие между двумя результатами; наши прогнозы ПРОТИВ наземных меток правды.

Стратегия. Постройте регрессионную модель, примените некоторые граничные ограничения, которые преобразуют непрерывные результаты регрессионной модели в дискретные результаты (4 класса), которые можно использовать в метрике QWK.

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

Один из способов решения этой проблемы - создать собственную метрику, которая преобразует непрерывный результат регрессионной модели в 4 класса, а затем применяет метрику QWK, переданную на аутсорсинг. (См. пример 2)

Пользовательская метрика в примере 2 может использоваться в качестве детектора переобучения регрессионной модели!

Чтобы создать собственную метрику в Catboost, необходимо следовать формату, указанному ниже. См. Страницу документации Catboost.

class CustomMetric(object):
    def get_final_error(self, error, weight):
        return 0.0
def is_max_optimal(self):
        return True
def evaluate(self, approxes, target, weight):
        # approxes - list of list-like objects (one object per approx dimension)
        # target - list-like object
        # weight - list-like object, can be None
        return 0.0, 0.0

Вы можете изменить имя класса CustomMetric на любое имя, которое хотите использовать, но ...

ВНИМАНИЕ! Не редактируйте следующие имена функций
get_final_error, is_max_optimal, evaluate

  1. Приступим к обсуждению функции evaluate.

Переменные
approxes: это ваши прогнозы, предоставленные моделью CatBoostRegressor во время метода fit при использовании параметра eval_metric. approxes будет иметь формат класса DoubleArrayWrapper, который представляет собой индексированные контейнеры (например, tuple, list, set, dict; это встроенные контейнеры). Чтобы получить доступ к содержимому контейнера, нам нужно вызвать его по индексу и присвоить переменной. Таким образом, мы пишем approx=approxes[0], как показано в двух примерах ниже.

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

В моем случае, Пример 2, мой целевой столбец имеет форму N x 1, поэтому доступ к каждой записи осуществляется простым вызовом их индекса, т.е. approx[i] for i in range(len(approx))

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

target: Это ваши метки истины. Форма будет зависеть от типа метрики, которую вы создаете для своей модели.

weight: веса, которые будут использоваться в вашей специальной метрике. Это может быть None. Форма будет зависеть от типа метрики, которую вы создаете для своей модели.

Оператор возврата
Функция evaluate требует от нас возврата упорядоченной пары, например return 0.0, 0.0.

Первая и вторая записи этой упорядоченной пары будут использоваться как переменная error и переменная weight для функции get_final_error соответственно.

2. Далее идет функция is_max_optimal.

Settingreturn True означает, что более высокие возвращаемые значения лучше, чем более низкие возвращаемые значения.

Settingreturn Falsemeans более низкие возвращаемые значения лучше, чем более высокие возвращаемые значения.

3. Наконец, наша последняя функция - это get_final_error.

Переменные
error и weight: как упоминалось выше, эти две переменные являются выходными данными функции evaluate. Теперь вы можете выполнить соответствующие операции, чтобы получить окончательный вывод об ошибке (см. Пример 1). Иногда делать больше нечего, поэтому достаточно просто вернуть error, как в примере 2.

Оператор возврата
Catboost ожидает одноэлементное действительное число.

Наш первый пример представлен в документации Catboost. Положу сюда для удобства.

Пример 1

class LoglossMetric(object):
    def get_final_error(self, error, weight):
        return error / (weight + 1e-38)
    def is_max_optimal(self):
        return True
    def evaluate(self, approxes, target, weight):
        # approxes is list of indexed containers
        # (containers with only __len__ and __getitem__ defined), one container
        # per approx dimension. Each container contains floats.
        # weight is one dimensional indexed container.
        # target is float.   
        # weight parameter can be None.
        # Returns pair (error, weights sum)
        assert len(approxes) == 1
        assert len(target) == len(approxes[0])
        approx = approxes[0]
        error_sum = 0.0
        weight_sum = 0.0
        for i in xrange(len(approx)):
            w = 1.0 if weight is None else weight[i]
            weight_sum += w
            error_sum += w * (target[i] * approx[i] - math.log(1 + math.exp(approx[i])))
        return error_sum, weight_sum

Следующий ниже пример - это тот, который я реализовал для конкурса Data Science Bowl 2019 Kaggle Competition.

Пример 2

Примечание. Я определил переменную pred потому, что approx, являющийся объектом ‘_catboost._DoubleArrayWrapper’, не поддерживает назначение элементов.

Результаты Kaggle

Результат выше - это оценка одного из моих ядер с использованием настроенной мной метрики Каппа для Catboost. К сожалению, я не выбрал это ядро ​​в качестве одного из последних представленных, поэтому я попрощался с серебряной медалью.

Преимущество использования индивидуализированной метрики Каппа (как я понял после окончания конкурса) состоит в том, что я смог добиться гораздо более низкой среднеквадратичной ошибки в несколько раз для модели регрессора во время перекрестная проверка. Это был бы более стабильный показатель, на который стоит обратить внимание, а не чувствительный показатель Каппа в таблице лидеров.

Урок выучен!

Я планирую поделиться очищенной версией моего ядра Kaggle через мой GitHub, как только у меня будет время. Спасибо, что дочитали до этого момента!