Если вы когда-либо имели дело с классическими моделями, такими как XGBClassifier и LGBMClassifier, вы знаете, что их так же легко настроить с помощью перекрестной проверки, как и круговую диаграмму. функция, то пусть тюнер творит чудеса! Но когда дело доходит до тонкой настройки XGBRanker (или любой модели ранжирования, если на то пошло) с перекрестной проверкой, это совсем другая игра. Итак, пристегнитесь и давайте погрузимся в это захватывающее приключение!

С вашими старыми добрыми классификационными моделями метрики оценки (такие как точность@k и отзыв@k) вычислить несложно. Почему? Потому что эти модели производят оценки вероятности, которые хорошо работают и не зависят друг от друга. Но ранжирование моделей? О ф! Они не производят оценки вероятности. Вместо этого они выдают беспорядочную мешанину случайных оценок, и единственное, что имеет смысл, — это их относительный порядок в банде, к которой они принадлежат. Итак, пришло время засучить рукава и изменить такие показатели, как точность@k и отзыв@k в области ранжирования.

Давайте поговорим о Recall@k. В диком мире ранжирования его аналогом является Average Recall@K. Чтобы взломать этот орех, нам нужно вычислить отзыв@k для каждой группы запросов, а затем усреднить этих плохих парней, отсюда и название «Среднее» в прозвище метрики. Но чтобы это осуществить, наша функция подсчета очков должна быть такой же точной, как гвоздь! Он должен распознавать группы запросов, скрытые в данных, вычислять RectR@k в каждой группе, а затем сглаживать результаты путем усреднения по всем запросам. Кусок пирога, верно?

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

1 — создание синтетических данных

Этот фрагмент кода демонстрирует, как создать синтетический набор данных для ранжирования задач с использованием pandas, numpy и других основных библиотек Python. Набор данных состоит из 10 000 образцов, одной функции и 200 групп запросов. DataFrame data содержит целевые метки, одну функцию с именем feat1 и идентификаторы групп запросов qid.

Вот как выглядят данные:

data.head(10)

Эй, давайте удостоверимся, что в каждом запросе есть 50 записей!

data.groupby('qid')['target'].count()

Давайте также проверим, что доля target=1 составляет примерно 20% в каждом запросе, как мы определили в строке 16 файла data_generation.py:

data.groupby('qid')['target'].mean().plot.hist()

2 — определение стратегии перекрестной проверки

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

Как мы видим, каждый запрос, а значит, и связанные с ним записи, выделяются как обучающие или тестовые:

3 — Создать оценщик

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

Этот код определяет пользовательский оценщик scikit-learn под названием XgboostRankerWrapper, который включает в себя модель XGBRanker из библиотеки XGBoost. XGBRanker — это алгоритм повышения градиента, разработанный специально для ранжирования задач. XgboostRankerWrapper может использоваться в конвейере scikit-learn и предоставляет метод подгонки и прогнозирования для обучения и прогнозирования с помощью XGBRanker.

Метод fit класса XgboostRankerWrapper подгоняет модель XGBRanker к обучающим данным. Входные данные должны быть пандами DataFrame, где последний столбец содержит идентификатор запроса, а другие столбцы содержат функции. Целевые метки должны быть предоставлены в виде массива numpy. Метод fit извлекает идентификатор запроса из последнего столбца входных данных, отбрасывает его из объектов и подгоняет модель XGBRanker к оставшимся функциям и целевым меткам. Параметр qid установлен на извлеченный идентификатор запроса, что позволяет модели учитывать структуру запроса во время обучения.

Метод predict класса XgboostRankerWrapper генерирует прогнозы для новых входных данных. Входные данные должны быть пандами DataFrame, где последний столбец содержит идентификатор запроса, а другие столбцы содержат функции. Метод predict извлекает идентификатор запроса из последнего столбца входных данных, отбрасывает его и использует обученную модель XGBRanker для создания прогнозируемых оценок для оставшихся функций. Затем он возвращает pandas DataFrame с идентификатором запроса и прогнозируемыми оценками для каждой записи во входных данных.

4 — определение пользовательского счетчика

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

Код определяет несколько функций для вычисления и оценки среднего отзыва при k балле для ранжирования задач. Функция recall_at_k вычисляет показатель отзыва при k для заданного списка двоичных меток релевантности. Функция mean_recall_at_k вычисляет среднее значение отзыва с показателем k по всем запросам. Функция generate_sorted_labels_by_score_by_session создает отсортированные двоичные метки релевантности по количеству баллов для всех запросов. Функция mean_recall_score вычисляет среднюю оценку отзыва по всем сеансам, используя recall_at_k

5 — Определение тюнера

После разделения перекрестной проверки, пользовательского оценщика и пользовательского счетчика пришло время объединить все ради тюнера!

Код выполняет настройку гиперпараметров для модели XgboostRankerWrapper с использованием байесовской оптимизации с классом BayesSearchCV из библиотеки scikit-optimize. Вот краткое объяснение кода:

  • Сэмплер Grid из библиотеки scikit-optimize используется для определения области поиска гиперпараметров. Словарь param_space определяет пространство поиска для гиперпараметров, включая скорость обучения, максимальную глубину деревьев и количество деревьев.
  • Модель XgboostRankerWrapper инициализируется с параметром objective, установленным на 'rank:pairwise'.
  • Словарь opt_config определяет конфигурацию байесовского оптимизатора, включая тип базовой оценки и генератора начальных точек.
  • Класс BayesSearchCV создается с параметром estimator, установленным для модели XgboostRankerWrapper, параметром search_spaces, установленным для пространства поиска для гиперпараметров, числом итераций, установленным на 20, параметром cv, установленным для объекта перекрестной проверки GroupKFold, и scoring Параметр установлен на среднее значение отзыва при 10 баллах.
  • Функции и целевые метки извлекаются из data DataFrame, а настройка гиперпараметров выполняется с использованием bayes_search.fit(X, y).
  • Лучшие гиперпараметры и соответствующий лучший результат печатаются с использованием bayes_search.best_params_ и bayes_search.best_score_ соответственно.
  • Доступ к средним результатам тестов для всех разделений перекрестной проверки и комбинаций параметров осуществляется с помощью bayes_search.cv_results_['mean_test_score'].

Вот результат, который представляет собой среднее значение оценок тестовых сгибов с использованием метрики Average Recall@K для каждой из 20 итераций настройки:

array([0.67797807, 0.69785539, 0.70211674, 0.67955076, 0.69737492,
       0.68768379, 0.68819851, 0.66789982, 0.70261091, 0.70821937,
       0.70730222, 0.70786932, 0.70318874, 0.70676681, 0.70880812,
       0.64094843, 0.69300529, 0.70416447, 0.69691877, 0.7095061 ])

Соображения

В этом примере мы помещаем qid во фрейм данных в качестве последней функции. Мы используем эту информацию для извлечения столбца qid в методах подгонки и прогнозирования нашего пользовательского оценщика. Если мы отправим чистый фрейм данных для подбора и прогнозирования метода пользовательской оценки, то мы сможем получить доступ к qid по его имени, а не по его индексу. Но если вы отправляете numpy.ndarray вместо кадра данных pandas, то использование индекса может быть единственным вариантом.

Этот пост охватывает только средний отзыв @K, но его можно легко расширить до MAP@K, MRR@K и NDCG@K, просто изменив часть счетчика.