Вы когда-нибудь задумывались, что происходит, когда вы задаете вопрос в Google и сразу же получаете ответ? Например, если мы спросим кто является президентом Соединенных Штатов в поиске Google, мы получим следующий ответ:

Это показывает, что Google первоначально провел поиск и ранжировал 1,6 миллиарда страниц за 1,08 секунды, а затем выполнил дополнительный шаг обработки, чтобы найти фрагмент ответа со страницы. Первая задача — это основной продукт Google как поисковой системы. Второй этап обработки (поиск ответа на вопрос на веб-странице) — это задача НЛП «вопрос-ответ» (QA). Система QA NLP — это тип системы NLP, предназначенный для ответов на вопросы, заданные на естественных языках, таких как английский или китайский. Эти системы используют комбинацию методов понимания естественного языка и представления знаний для анализа вопроса и предоставления релевантного и точного ответа. Эти системы часто используются в таких приложениях, как поисковые системы, чат-боты обслуживания клиентов и виртуальные помощники.

В системе QA NLP вопрос и контекст передаются модели, и модель извлекает ответ из контекста. Этот подход можно использовать для построения системы обеспечения качества на уровне предприятия. Например, механизм поиска документов, такой как Elastic Search, можно использовать для ранжирования документов с наибольшей вероятностью наличия ответа на вопрос, а модель контроля качества — для поиска ответа в этом документе. Эти системы часто называют системами читатель-поисковик, где поиск документа является задачей ретривера, а поиск ответа на вопрос — задачей чтения. Библиотека Haystack, разработанная Deepset, немецкой компанией, специализирующейся на НЛП, может быть использована для построения системы считывания-извлечения корпоративного уровня. Тем не менее, эта статья будет сосредоточена только на части системы, отвечающей за QA (читатель), где у нас есть контекст и вопрос, и мы хотим получить ответ.

На рис. 3 показан пример архитектуры читатель-извлекатель для систем НЛП, отвечающих на вопросы.

QA NLP с трансформерами

Современные приложения обработки естественного языка (NLP) обычно строятся с использованием архитектур преобразователей, предложенных исследователями Google в 2017 году. области НЛП. Двумя наиболее популярными архитектурами преобразователей, которые используются в большинстве приложений НЛП, являются генеративный предварительно обученный преобразователь (GPT) 2 и представления двунаправленного кодировщика от преобразователей (BERT) 3.

Оригинальный преобразователь бумага основан на архитектуре кодировщика и декодера, обычно используемой для таких задач, как машинный перевод, где последовательность слов — с одного языка на другой (рис. 4). Позже блоки кодировщика и декодера были адаптированы как автономные модели во многих моделях НЛП. Модели только для кодировщика преобразуют входные токены в богатое числовое представление, которое идеально подходит для таких задач, как классификация текста или распознавание именованных объектов. BERT, RoBERTa и DistilBERT — это некоторые из моделей, в которых используются трансформаторные блоки только для энкодера. Модели только для декодера (т. е. модели GPT) обычно используются для задач генерации текста или автозаполнения, где представление каждой задачи зависит от левого контекста.

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

После того, как архитектура преобразователя позволила обучаться преобразователю в НЛП, многие учреждения выпустили свои обученные модели НЛП для использования учеными и практиками. GPT и BERT — это две предварительно обученные модели, которые соответствовали новому уровню техники в различных тестах НЛП и открыли эру трансформеров. Со временем различные исследовательские институты выпустили различные варианты архитектур трансформаторов, некоторые из которых использовали PyTorch, а другие — Tensorflow, что затруднило использование этих моделей практиками. HuggingFace создал набор унифицированных API-интерфейсов и набор предварительно обученных моделей и наборов данных, которые упростили внедрение современных моделей НЛП для практиков.

Библиотека обнимающих лиц для НЛП

Hugging Face Transformers, одна из самых популярных библиотек NLP, предоставляет стандартизированный интерфейс для широкого спектра моделей преобразования, а также код и инструменты для адаптации этих моделей к новым вариантам использования. Он также поддерживает три основные среды глубокого обучения: Pytorch, Tensorflow и JAX. Экосистема Hugging Face состоит в основном из двух частей: семейства библиотек и концентратора, как показано ниже. Библиотеки предоставляют код, а Hub предлагает предварительно обученные веса моделей, наборы данных, сценарии для показателей оценки и многое другое.

Построение модели обеспечения качества

Мы используем набор данных отряда v2 для точной настройки предварительно обученной модели трансформатора из концентратора моделей Hugging Face. Squad_v2 объединяет 100 000 вопросов в SQuAD1.1 с более чем 50 000 вопросов без ответа, написанных краудворкерами, чтобы они выглядели похожими на вопросы, на которые можно ответить. Системы отвечают на вопросы, когда это возможно, определяют, когда абзац не поддерживает ответа, и воздерживаются от ответа. Мы будем использовать MobileBert, сжатую версию популярной модели BERT. В задаче SQuAD v1.1/v2.0 вопрос-ответ MobileBERT получает оценку разработчиков F1 90,0/79,2 (на 1,5/2,1 выше, чем у BERT_BASE) 1.

Мы возьмем предварительно обученную модель и токенизатор из библиотеки обнимающих лиц. Поскольку это будет синхронное бессерверное приложение, мы будем использовать уменьшенную версию модели BERT (mobilebert) для ускорения обработки. Следующий код Python загружает модель mobilebert, настроенную для набора данных Squad_v2, в каталог ./model.

from transformers import AutoModelForQuestionAnswering, AutoTokenizer

def get_model(model):
    """Loads model from Huggin face model hub into
    the ./model directory"""

    try:
        model = AutoModelForQuestionAnswering.from_pretrained(model, use_cdn=True)
        model.save_pretrained("./model")
    except Exception as e:
        raise (e)
get_model("mrm8488/mobilebert-uncased-finetuned-squadv2")

Следующий код Python загружает токенизатор mibilbert в каталог ./model.

def get_tokenizer(tokenizer):
    """Loads tokenizer from Huggin face model hub into
    the ./model directory"""

    try:
        tokenizer = AutoTokenizer.from_pretrained(tokenizer)
        tokenizer.save_pretrained("./model")
    except Exception as e:
        raise (e)

get_tokenizer("mrm8488/mobilebert-uncased-finetuned-squadv2")

Получив токенизатор, мы можем кодировать данные, поступающие в модели, и декодировать ответ от модели. Следующий код предназначен для функции кодировщика, которая принимает вопрос, контекст и токенизатор и возвращает attention_masks и inpud_ids, которые будут переданы в модель.

def encode(tokenizer, question, context):
    """encodes the question and context with a given tokenizer
    that is understandable to the model"""
    encoded = tokenizer.encode_plus(question, context)
    return encoded["input_ids"], encoded["attention_mask"]

Этот фрагмент кода декодирует ответ модели в удобочитаемый строковый формат.

def decode(tokenizer, token):
    """decodes the tokens to the answer with a given tokenizer
    to return human readable response in a string format"""
    answer_tokens = tokenizer.convert_ids_to_tokens(token, skip_special_tokens=True)
    return tokenizer.convert_tokens_to_string(answer_tokens)

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

from transformers import AutoModelForQuestionAnswering, AutoTokenizer, AutoConfig
import torch

def serverless_pipeline(model_path="./model"):
    """Initializes the model and tokenzier and returns a predict
        function that ca be used as pipeline"""
    tokenizer = AutoTokenizer.from_pretrained(model_path)
    model = AutoModelForQuestionAnswering.from_pretrained(model_path)

    def predict(question, context):
        """predicts the answer on an given question and context.
        Uses encode and decode method from above"""
        input_ids, attention_mask = encode(tokenizer, question, context)
        start_scores, end_scores = model(
            torch.tensor([input_ids]), attention_mask=torch.tensor([attention_mask])
        )
        ans_tokens = input_ids[
            torch.argmax(start_scores) : torch.argmax(end_scores) + 1
        ]
        answer = decode(tokenizer, ans_tokens)
        return answer

    return predict

Бессерверная архитектура

На рис. 1 показана бессерверная архитектура для приложения QA NLP с использованием облака AWS. Бессерверная серверная часть приложения использует AWS lambda, DynamoDB, шлюз API и ECR для реестра контейнеров. Функция Lambda — это сервер логических выводов, завернутый в образ докера и загруженный в AWS ECR. Шлюз AWS API отправляет полезные данные запроса POST в лямбда-функцию. AWS DynamoDB хранит данные, отправляемые на сервер логических выводов, для целей мониторинга. Lambda может взаимодействовать с DynamoDB, используя роль IAM, которая разрешает только запись в базу данных.

Хранение журналов в DynamoDB

DynamoDB — это полностью управляемая бессерверная база данных NoSQL, которая идеально подходит для хранения входных и выходных данных модели для мониторинга и оценки модели. Мы используем библиотеку boto3 для размещения журналов в нашей базе данных. Мы сохраняем имя нашей DynamoDB в переменной окружения Lambda DYNAMO_TABLE. Мы хотим сохранить время, контекст полезной нагрузки и вопрос, а также ответ модели в базе данных. Следующий код записывает вопрос, контекст и образец ответа в DyanmoDB.

import boto3
import os
import uuid
import time

dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
table = dynamodb.Table(os.environ["DYNAMODB_TABLE"])

timestamp = str(time.time())
item = {
    "primary_key": str(uuid.uuid1()),
    "createdAt": timestamp,
    "context": body["context"],
    "question": body["question"],
    "answer": answer,
}
table.put_item(Item=item)

Лямбда-функция

Lambda — это сервис бессерверных вычислений от AWS, где будет обслуживаться вывод. Обработчик Lambda — это место, где информация из запроса API проходит через функцию и возвращает результат в API. Мы также включим DynamoDB, который пишет код внутри функции.

def handler(event, context):
    try:
        # loads the incoming event into a dictonary
        body = json.loads(event["body"])
        # uses the pipeline to predict the answer
        answer = question_answering_pipeline(
            question=body["question"], context=body["context"]
        )
        timestamp = str(time.time())
        item = {
            "primary_key": str(uuid.uuid1()),
            "createdAt": timestamp,
            "context": body["context"],
            "question": body["question"],
            "answer": answer,
        }
        table.put_item(Item=item)
        return {
            "statusCode": 200,
            "headers": {
                "Content-Type": "application/json",
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Credentials": True,
            },
            "body": json.dumps({"answer": answer}),
        }
    except Exception as e:
        print(repr(e))
        return {
            "statusCode": 500,
            "headers": {
                "Content-Type": "application/json",
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Credentials": True,
            },
            "body": json.dumps({"error": repr(e)}),
        }

Докеризация лямбда-функции

Поскольку функции Lambda теперь поддерживают образы докеров, мы можем все докеризовать и загружать в репозиторий Amazon Elastic Container Registry (Amazon ECR). Функция Lambda будет обращаться к этому изображению, чтобы делать прогнозы. Dockerfile использует опубликованный базовый образ AWS для функций Lambda.

FROM public.ecr.aws/lambda/python:3.8

# Copy function code and models into our /var/task
COPY ./ ${LAMBDA_TASK_ROOT}/

# install our dependencies
RUN python3 -m pip install -r requirements.txt --target ${LAMBDA_TASK_ROOT}

# run get_model.py to get model weights and tokenizers
RUN python3 get_model.py

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "handler.handler" ]

Нам нужно отправить на сборку, пометить и отправить образ докера в репозиторий ECR. Во-первых, мы должны войти в наш репозиторий ECR с помощью интерфейса командной строки AWS.

aws_region=<your aws region>
aws_account_id=<your aws account>

aws ecr get-login-password --region $aws_region \
| docker login username AWS --password-stdin $aws_account_id.dkr.ecr.$aws_region.amazonaws.com

Затем создайте, пометьте и отправьте образ Docker в репозиторий ECR.

docker build -t nlp-lambda:v1 serverless-bert/.
docker tag nlp-lambda:v1 $aws_account_id.dkr.ecr.$aws_region.amazonaws.com/nlp-lambda:v1
docker push $aws_account_id.dkr.ecr.$aws_region.amazonaws.com/nlp-lambda:v1

Развертывание бессерверного приложения

Мы будем использовать библиотеку Serverless, библиотеку с открытым исходным кодом и не зависящую от облака, которая работает со всеми основными поставщиками общедоступных облаков. Установите serverless с помощью npm, если он еще не установлен на вашем компьютере. Если у вас на машине нет npm, следуйте инструкциям по установке здесь.

npm install -g serverless

Вот пример бессерверного файла конфигурации для развертывания функции Lambda со шлюзом API и DynamoDB, похожей на архитектуру, показанную на рис. 7:

service: serverless-bert-qa-lambda-docker

provider:
  name: aws # provider
  region: us-east-1 # aws region
  memorySize: 5120 # optional, in MB
  timeout: 30 # optional, in seconds
  environment:
    DYNAMODB_TABLE: ${self:service}-Table-${sls:stage}
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "dynamodb:PutItem"
      Resource: arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/${self:service}-Table-${sls:stage}
functions:
  questionanswering:
    image: ${ACOUNT_NUMBER}.dkr.ecr.us-east-1.amazonaws.com/bert-lambda:v1 #ecr url
    events:
      - http:
          path: qa # http path
          method: post # http method
resources:
  Resources:
    CustomerTable:
      Type: AWS::DynamoDB::Table
      Properties:
        AttributeDefinitions:
          - AttributeName: primary_key
            AttributeType: S
        BillingMode: PAY_PER_REQUEST
        KeySchema:
          - AttributeName: primary_key
            KeyType: HASH
        TableName: ${self:service}-Table-${sls:stage}

Бессерверной среде требуются учетные данные AWS для доступа к ресурсам AWS от нашего имени. Следуйте этой инструкции, чтобы создать пользователя IAM, если у вас его нет, и используйте его для настройки своих учетных данных. Для настройки учетных данных AWS рекомендуется использовать AWS-CLI. Чтобы настроить их через aws-cli, сначала установите его, затем запустите aws configure для настройки AWS-CLI и учетных данных:

$ aws configure
AWS Access Key ID [None]: 
AWS Secret Access Key [None]: 
Default region name [None]: 
Default output format [None]: 

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

serverless deploy

После завершения развертывания serverless вернет URL-адрес шлюза API, который можно использовать для тестирования модели. Вы можете использовать Postman, javascript или curl для вызова модели. В репозитории GitHub этого проекта создается простое внешнее приложение с использованием HTML, CSS и javascript для взаимодействия с развернутым шлюзом API.

Краткое содержание

Модели NLP с ответами на вопросы часто используются в таких приложениях, как поисковые системы, чат-боты обслуживания клиентов и виртуальные помощники. В этом сообщении блога описана проблема QA NLP, а также построено и развернуто полнофункциональное бессерверное приложение QA NLP с использованием библиотеки HuggingFace и инфраструктуры AWS. Приложение Serverless ML может быть не лучшим подходом к развертыванию для всех случаев использования, но это отличный первый шаг к запуску ваших моделей в производство, не беспокоясь о базовой инфраструктуре.