Представляем новый, более простой способ оценки экстрактивных пайплайнов контроля качества в Haystack

Оценка — неотъемлемая часть любого проекта НЛП. Он позволяет количественно оценить общую производительность системы, обнаружить любое ухудшение с течением времени и сравнить ее с конкурирующими конфигурациями. Оценить конвейеры ответов на вопросы (QA) и поиска документов в Haystack теперь проще, чем когда-либо. Вот что нового:

  • Больше нет необходимости в специальных узлах оценки. Теперь вы можете просто запустить конвейер контроля качества в режиме оценки, вызвав его с помощью eval().
  • Вызов метода eval() возвращает объект EvaluationResult. Для каждого оцениваемого узла объект содержит кадр данных pandas с результатами оценки узла.
  • Объект EvaluationResult содержит метод calculate_metrics(). Он генерирует обзор всех соответствующих показателей для всего конвейера.

Резюме: зачем нужна оценка

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

После того, как вы запустили конвейер контроля качества, самым насущным вопросом, который вы и другие зададите, будет: «Насколько хорошо работает эта система?» Производительность системы обычно складывается из двух факторов: задержки, то есть скорости, с которой она обрабатывает запросы, и качества ее прогнозов. В этом сообщении блога мы в основном будем рассматривать качество прогнозов.

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

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

Как работает оценка?

При контролируемом машинном обучении модель обучается на размеченных данных, которые должны максимально точно отражать конечный вариант использования. Чтобы оценить такую ​​систему, вам нужно отложить часть данных перед обучением. Этот «оценочный набор данных» позволяет вам позже проверить качество прогнозирования вашей обученной системы на данных, которые она раньше не видела. Затем вы можете использовать набор показателей для количественной оценки того, насколько хорошо прогнозы вашей системы соответствуют реальным «золотым» меткам.

Отряд

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

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

Как оценить конвейер контроля качества в Haystack

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

Мы будем работать с небольшим подмножеством набора данных Естественные вопросы от Google Research. Мы преобразовали исходный набор данных в формат SQuAD, чтобы каждая запись содержала вопрос, отрывок из контекста и несколько (потенциально идентичных или перекрывающихся) ответов. Полный пример кода можно найти в нашем оценочном руководстве, которое можно запустить как записную книжку в Colab.

Получение данных

Подмножество можно загрузить из нашей корзины S3 на AWS:

from haystack.utils import fetch_archive_from_http
doc_dir = "../data/nq"
s3_url = "https://s3.eu-central-1.amazonaws.com/deepset.ai-farm-qa/datasets/nq_dev_subset_v2.json.zip"
fetch_archive_from_http(url=s3_url, output_dir=doc_dir)

Последняя функция, fetch_archive_from_http(), сохраняет данные в папке doc_dir во временном хранилище сеанса Colab.

Настройка хранилища документов и препроцессора

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

from haystack.document_stores import ElasticsearchDocumentStore
document_store = ElasticsearchDocumentStore(
  host="localhost",
  username="",
  password="",
  index=doc_index,
  label_index=label_index,
  embedding_field="emb",
  embedding_dim=768,
  excluded_meta_data=["emb"],
)

Нам также нужно определить препроцессор, который разбивает данные на проходы. Здесь мы определяем длину 200 слов на отрывок.

from haystack.nodes import PreProcessor
preprocessor = PreProcessor(
  split_length=200,
  split_overlap=0,
  split_respect_sentence_boundary=False,
  clean_empty_lines=False,
  clean_whitespace=False,
)

Добавление данных в хранилище документов

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

doc_index = "tutorial5_docs"
label_index = "tutorial5_labels"
document_store.delete_documents(index=doc_index)
document_store.delete_documents(index=label_index)

Теперь пришло время передать наши документы и этикетки в хранилище документов. Следующий вызов метода преобразует данные, содержащиеся в нашем файле JSON, в документ Haystack и объекты этикетки. Его можно использовать для любого оценочного набора данных при условии, что он представлен в формате SQuAD.

document_store.add_eval_data(
  filename="../data/nq/nq_dev_subset_v2.json", doc_index=doc_index, label_index=label_index, preprocessor=preprocessor
)

Внедрение экстрактивного конвейера контроля качества

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

from haystack.nodes import BM25Retriever
retriever = BM25Retriever(document_store=document_store)

Далее мы импортируем и инициализируем считыватель:

from haystack.nodes import FARMReader
reader = FARMReader("deepset/roberta-base-squad2", return_no_answer=True)

Теперь настроим конвейер.

from haystack.pipelines import ExtractiveQAPipeline
pipeline = ExtractiveQAPipeline(reader=reader, retriever=retriever)

Тада! Наш трубопровод готов.

Запуск конвейера

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

eval_labels = document_store.get_all_labels_aggregated(drop_negative_labels=True, drop_no_answers=True)

На самом деле метки представляют собой объекты MultiLabel, которые содержат список возможных меток для каждого запроса с ответами, диапазонами ответов и текстом, содержащим ответ.

Мы готовы запустить наш пайплайн в оценочном режиме. Как и в случае с методом run(), мы можем передавать параметры top_k. Для ретривера top_k указывает количество возвращаемых документов; для читателя это количество ответов:

eval_result = pipeline.eval(labels=eval_labels, params={"Retriever": {"top_k": 5}, "Reader": {"top_k": 4}})

Мы подробно рассмотрим содержимое eval_result в следующем разделе.

Изучение результатов оценки

Приведенный выше eval_result представляет собой объект EvaluationResult, похожий на словарь, с двумя фреймами данных pandas. Один содержит результаты ретривера, другой — результаты ридера. Вот как получить к ним доступ:

retriever_result = eval_result["Retriever"]
reader_result = eval_result["Reader"]

Помимо прогнозов узла и ожидаемых документов и ответов, два фрейма данных содержат много дополнительной информации, например:

  • типи узел: тип зависит от узла; это «ответ» для узла чтения и «документ» для узла извлечения
  • rank: позиция результата в списке результатов. Например, верхний документ/ответ имеет ранг 1.
  • document_id: идентификатор полученного документа.
  • gold_document_ids: список документов, содержащих золотые ответы.

Кроме того, есть несколько столбцов с метриками, специфичными для узла. Для узла извлечения это:

  • gold_id_match: может принимать значение 0 или 1 и указывает, соответствует ли полученный документ золотому документу.
  • answer_match: может принимать значение 0 или 1 и указывает, содержит ли документ какие-либо из золотых ответов.

Для читателя актуальными показателями являются:

  • exact_match: может принимать значение 0 или 1 и указывает, соответствует ли прогнозируемый ответ одному из золотых ответов.
  • f1: эта метрика описывает степень совпадения прогнозируемых и золотых ответов с точки зрения токенов. Он может иметь любое значение от 0 до 1. Обратите внимание, что при наличии нескольких золотых ответов учитывается только пара прогноз-ответ с наивысшей оценкой. То же самое относится и к следующему показателю.
  • sas: метрика сходство семантического ответа показывает, насколько прогнозируемый ответ семантически соответствует золотому ответу. Он может иметь любое значение от 0 до 1. Этот показатель является необязательным и требует предварительно обученной языковой модели.

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

Фильтрация запросов

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

query = "who is written in the book of life"
retriever_book_of_life = retriever_result[retriever_result["query"] == query]

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

reader_book_of_life = reader_result[reader_result["query"] == query]

Другие полезные методы

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

Сохранить и загрузить

В зависимости от размера набора данных для оценки оценка может занять много времени. Чтобы не пересчитывать их каждый раз, результаты в объекте EvaluationResult можно хранить в виде CSV-файлов (по одному на узел), из которых их можно перезагружать:

eval_result.save("../")
saved_eval_result = EvaluationResult.load("../")

Вычислить совокупные показатели

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

metrics = saved_eval_result.calculate_metrics()

Объект metrics содержит разные оценки для узлов чтения и извлечения. Для читателя это просто метрики, обсуждавшиеся в предыдущем разделе — точное соответствие (EM), F1 и, при необходимости, SAS — агрегированные и усредненные по всему набору оценочных данных.

Для оценки средства извлечения на этом шаге вводится несколько дополнительных показателей, некоторые из которых учитывают понятие ранг,т. е. положение правильного документа в упорядоченном списке извлеченных документов. . Например, метрика обратного ранга использует понятие мультипликативной инверсии: 1, если правильный документ является первым результатом в полученном списке, ½, если он второй, ⅓, если он третий, и так далее. Тогда средний взаимный ранг представляет собой взаимный ранг, усредненный по всему набору данных. Чтобы узнать больше о метриках ретривера, загляните в документацию.

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

metrics["Retriever"]["mrr"]

Имитация нижнего top_k

Если ваши значения top_k больше 1, вы можете поэкспериментировать с более низкими значениями как для средства извлечения, так и для средства чтения. Это избавляет вас от дорогостоящего этапа повторного запуска pipeline.eval() с другими значениями top_k. Используйте параметры simulated_top_k_reader и simulated_top_k_retriever, чтобы передать нужное значение функции calculate_metrics. Например, чтобы получить только три документа и вернуть только один ответ, вы можете сделать:

metrics = eval_result.calculate_metrics(simulated_top_k_retriever=3, simulated_top_k_reader=1)

С этими настройками вы ожидаете более высокие или более низкие оценки?

Отображение неверных прогнозов

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

Чтобы возвращать только ошибки вашей системы, вызовите wrong_examples() с интересующим вас узлом в качестве аргумента:

eval_result.wrong_examples('Reader')

Создать отчет об оценке

Отчет об оценке — это удобный обзор всего вашего конвейера. Таким образом, он принадлежит не EvaluationResult, а самому классу Pipeline. Вот как это использовать:

pipeline.print_eval_report(eval_result)

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

Комплексная или изолированная оценка?

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

Кроме того, вы можете запустить узел изолированно, чтобы исследовать его производительность независимо от предыдущего узла. Это аналогично ответам на закрытые вопросы, где есть только модуль чтения. В изолированном режиме ввод считывателя не зависит от вывода (и, следовательно, качества) извлекателя. Вместо этого читатель получает «золотой» документ и сканирует его в поисках правильного ответа:

eval_result_with_upper_bounds = pipeline.eval(
  labels=eval_labels, params={"Retriever": {"top_k": 5}, "Reader": {"top_k": 5}}, add_isolated_node_eval=True
)

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

Начать

Пришло время приступить к оценке ваших конвейеров контроля качества с помощью нашего нового метода eval()! (Не стесняйтесь сначала попробовать оценочное руководство.) Если вы хотите поделиться своими результатами с сообществом Haystack, почему бы не присоединиться к нам в Discord? Discord также является идеальным местом для того, чтобы задавать вопросы и получать поддержку от других пользователей или непосредственно от членов нашей команды.

Если вы хотите узнать больше о фреймворке Haystack, зайдите в наш репозиторий GitHub (не забудьте поставить нам звездочку, если вам нравится то, что вы видите) или ознакомьтесь с нашей документацией.