Новый список RecList обеспечивает большую гибкость и лучшую поддержку оценки.

Введение

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

А потом? ну, вам нужно где-то сохранять результаты и, возможно, регистрировать выходные данные онлайн, чтобы вы могли их отслеживать.

Поскольку это всегда сложная процедура, недавно мы попытались предоставить более структурированный способ тестирования. В этом сообщении блога мы представляем и показываем, как использовать бета-версию RecList, наш пакет с открытым исходным кодом для оценки; RecList — это общий подход plug-and-play к масштабируемому тестированию с простым расширяемым интерфейсом для пользовательских сценариев использования. RecList — это проект с открытым исходным кодом, свободно доступный на GitHub.

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

Мы начали работать над RecList пару лет назад, а альфа-версия RecList вышла чуть больше года назад. С тех пор RecList собрал более 400 звезд GitHub.

Мы использовали RecList и провели его стресс-тестирование для запуска испытания RecSys на CIKM в 2022 году и в настоящее время готовимся к предстоящему испытанию на KDD 2023. RecList позволил нам систематизировать оценку для всех участников. Идея состоит в том, что, как только всем будет предоставлен один и тот же RecList, сравнение различных оценок станет простым. Краткое изложение нашего опыта представлено в нашем комментарии Природный машинный интеллект.

Первоначально RecList был представлен в академической статье, но у нас также был общий обзор, который был представлен в публикации Towards Data Science, которую вы можете прочитать здесь:



Чиа, П.Дж., Тальябу, Дж., Бьянки, Ф., Хе, К., и Ко, Б. (2022, апрель). Помимо nDCG: поведенческое тестирование рекомендательных систем с Reclist. В Companion Proceedings of the Web Conference 2022 (стр. 99–104).

Хотя изначально мы разработали RecList для тестирования рекомендательных систем, ничто не мешает использовать RecList для тестирования других моделей машинного обучения. Итак, почему появился новый пост в блоге? ну, после разработки первой версии мы поняли, что она нуждается в некоторых обновлениях.

Чему мы научились: переосмысление API

Часто только после того, как вы что-то построили, вы понимаете, как это улучшить.

Для тех, кто использовал RecList 1.0, мы внесли серьезные обновления в RecList API. Изначально у нас были более жесткие ограничения на структуру кода и пары ввода/вывода.

Действительно, когда мы внедряли Reclist, мы намеревались предоставить более общий API для оценки рекомендательных систем, который предлагал несколько готовых функций. Однако для этого нам пришлось создать несколько абстрактных интерфейсов, которые должны были реализовать пользователи.

Например, исходный Reclist 1.0 требовал от пользователей оборачивать свои собственные модели и наборы данных в предопределенные абстрактные классы (т. е. RecModel и RecDataset). Это позволило нам реализовать общий набор поведений, связанных этими абстракциями. Однако вскоре мы поняли, что это часто может усложнять потоки и требует много дополнительной работы, что может не понравиться некоторым людям.

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

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

RecList 2.0 в действии

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

Простой вариант использования

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

n = 10000

target = [randint(0, 1) for _ in range(n)]
metadata = {"categories": [choice(["red", "blue", "yellow"]) 
                          for _ in range(n)]}

Наш очень простой набор данных должен выглядеть примерно так:

>>> target

[0, 1, 0, 1, 1, 0]

>>> metadata

{"categories" : ["red", "blue", "yellow", "blue", "yellow", "yellow"]}

Простая модель

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

class DummyModel:
  def __init__(self, n):
          self.n = n

      def predict(self):
          from random import randint
          return [randint(0, 1) for _ in range(self.n)]

simple_model = DummyModel(n)

# let's run some predictions
predictions = simple_model.predict()

Теперь, как мы проводим оценки?

Простой RecList

RecList — это класс Python, который наследует функции от нашего абстрактного класса RecList. RecList реализует RecTests, простые абстракции, позволяющие систематизировать оценку. Например, это может быть возможная проверка точности.

@rec_test(test_type="Accuracy", display_type=CHART_TYPE.SCALAR)
def accuracy(self):
    """
    Compute the accuracy
    """
    from sklearn.metrics import accuracy_score

    return accuracy_score(self.target, self.predictions)

Мы берем метрику точности sklearn и оборачиваем ее в другой метод. Чем это отличается от простой функции точности? хорошо, декоратор позволяет нам добавить некоторые дополнительные функции: например, прямое тестирование теперь будет автоматически сохранять информацию в локальной папке. Кроме того, определение типа диаграммыпозволяет нам создавать некоторые визуализации для этих результатов.

Что, если мы захотим более сложный тест? Например, что, если мы хотим увидеть, насколько стабильна наша точность в разных категориях (например, выше ли точность, рассчитанная для красных объектов, чем для желтых объектов?)

@rec_test(test_type="SlicedAccuracy", display_type=CHART_TYPE.SCALAR)
def sliced_accuracy_deviation(self):
    """
    Compute the accuracy by slice
    """
    from reclist.metrics.standard_metrics import accuracy_per_slice
    
    return accuracy_per_slice(
        self.target, self.predictions, self.metadata["categories"])

Давайте теперь посмотрим на пример полного RecList!

class BasicRecList(RecList):

    def __init__(self, target, metadata, predictions, model_name, **kwargs):
        super().__init__(model_name, **kwargs)
        self.target = target
        self.metadata = metadata
        self.predictions = predictions

    @rec_test(test_type="SlicedAccuracy", display_type=CHART_TYPE.SCALAR)
    def sliced_accuracy_deviation(self):
        """
        Compute the accuracy by slice
        """
        from reclist.metrics.standard_metrics import accuracy_per_slice

        return accuracy_per_slice(
            self.target, self.predictions, self.metadata["categories"]
        )

    @rec_test(test_type="Accuracy", display_type=CHART_TYPE.SCALAR)
    def accuracy(self):
        """
        Compute the accuracy
        """
        from sklearn.metrics import accuracy_score

        return accuracy_score(self.target, self.predictions)

    @rec_test(test_type="AccuracyByCountry", display_type=CHART_TYPE.BARS)
    def accuracy_by_country(self):
        """
        Compute the accuracy by country
        """
        # TODO: note that is a static test, 
        # used to showcase the bin display

        from random import randint
        return {"US": randint(0, 100), 
                "CA": randint(0, 100), 
                "FR": randint(0, 100)}

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

Пока ваши метрики возвращают какие-то значения, вы можете реализовать их любым способом. Например, этот BasicRecList оценивает конкретную модель в конкретном контексте. Но ничто не мешает вам создавать дополнительные списки reclist для конкретных моделей (например, GPT-RecList) или наборов данных (например, IMDB-Reclist). Если вы хотите увидеть пример глубокой модели в RecList, вы можете заценить этот колаб.

Запуск и получение выходных данных

Запустим RecList. Нам нужны наши целевые данные, метаданные и прогнозы. Мы также можем указать регистратор и хранилище метаданных.

rlist = BasicRecList(
    target=target,
    metadata=metadata,
    predictions=predictions,
    model_name="myRandomModel",
)

# run reclist
rlist(verbose=True)

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

Метрики также отображаются автоматически. Например, AccuracyByCountry должен показать что-то вроде этого:

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

{
  "metadata": {
    "model_name": "myRandomModel",
    "reclist": "BasicRecList",
    "tests": [
      "sliced_accuracy",
      "accuracy",
      "accuracy_by_country"
    ]
  },
  "data": [
    {
      "name": "SlicedAccuracy",
      "description": "Compute the accuracy by slice",
      "result": 0.00107123176804103,
      "display_type": "CHART_TYPE.SCALAR"
    },
...
}

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

Использование онлайн-логгеров и хранилищ метаданных

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

logger=LOGGER.LOCAL,
metadata_store= METADATA_STORE.LOCAL,

Однако ничто не мешает нам использовать онлайн и облачные решения. Например, мы объединяем API CometML и Neptune, чтобы вы могли напрямую использовать их в конвейере оценки. Мы также предлагаем поддержку хранения данных S3.

Например, добавление пары параметров в BasicReclist позволит нам логировать информацию о Neptune (мы предлагаем аналогичную поддержку для Comet.ml)!

rlist = BasicRecList(
    target=target,
    model_name="myRandomModel",
    predictions=predictions,
    metadata=metadata,
    logger=LOGGER.NEPTUNE,
    metadata_store= METADATA_STORE.LOCAL,
    NEPTUNE_KEY=os.environ["NEPTUNE_KEY"],
    NEPTUNE_PROJECT_NAME=os.environ["NEPTUNE_PROJECT_NAME"],
)

# run reclist
rlist(verbose=True)

Очень похожим образом, добавив следующее:

bucket=os.environ["S3_BUCKET"]

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

Заключение

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

Благодарности

В период с июня по декабрь 2022 года разработка нашей бета-версии поддерживалась замечательными людьми из Comet, Neptune, Gantry и была разработана с помощью Уннати Пателя.