Повышение опыта использования генеративного искусственного интеллекта Введение поддержки потоковой передачи в Amazon SageMaker хостинг

Улучшение опыта использования генеративного искусственного интеллекта с поддержкой потоковой передачи в Amazon SageMaker хостинге

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

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

Обзор решения

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

Реализация потоковой передачи ответов в реальных конечных точках SageMaker достигается с помощью HTTP 1.1 с кодировкой в виде блоков, которая является механизмом для отправки нескольких ответов. Это стандарт HTTP, который поддерживает двоичное содержимое и поддерживается большинством клиентских и серверных фреймворков. Кодировка в виде блоков HTTP поддерживает как потоковую передачу текстовых данных, так и изображений, что означает, что модели, размещенные на конечных точках SageMaker, могут отправлять потоковые ответы в виде текста или изображения, такие как Falcon, Llama 2 и модели Stable Diffusion. Что касается безопасности, как ввод, так и вывод защищены с использованием TLS с использованием AWS Sigv4 Auth. Другие техники потоковой передачи, такие как Server-Sent Events (SSE), также реализованы с использованием того же механизма кодировки в виде блоков HTTP. Чтобы воспользоваться новым потоковым API, вам необходимо убедиться, что контейнер модели возвращает потоковой передаче ответа в виде блоков закодированных данных.

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

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

В этом посте мы показываем два варианта контейнеров для создания конечной точки SageMaker с потоковой передачей ответов: использование контейнера AWS Large Model Inference (LMI) и контейнера Hugging Face Text Generation Inference (TGI). В следующих разделах мы расскажем вам о подробных шагах реализации для развертывания и тестирования модели Falcon-7B-Instruct с использованием контейнеров LMI и TGI на SageMaker. Мы выбрали Falcon 7B в качестве примера, но любая модель может воспользоваться этой новой функцией потоковой передачи.

Предварительные требования

Вам понадобится учетная запись AWS с ролью AWS Identity and Access Management (IAM) с разрешениями на управление созданными ресурсами в рамках решения. Подробности см. в разделе Создание учетной записи AWS. Если вы впервые работаете с Amazon SageMaker Studio, вам нужно сначала создать домен SageMaker. Кроме того, вам может потребоваться увеличение квоты службы для соответствующих экземпляров размещения SageMaker. Для модели Falcon-7B-Instruct мы используем экземпляр размещения SageMaker ml.g5.2xlarge. Для размещения модели Falcon-40B-Instruct мы используем экземпляр размещения SageMaker ml.g5.48xlarge. Вы можете запросить увеличение квоты с помощью пользовательского интерфейса Service Quotas. Дополнительные сведения см. в разделе Запрос увеличения квоты.

Вариант 1: Развертывание конечной точки потоковой передачи в реальном времени с использованием контейнера LMI

Контейнер LMI является одним из контейнеров Deep Learning Containers для размещения больших моделей вывода, размещенных SageMaker, чтобы облегчить размещение больших языковых моделей (LLM) на инфраструктуре AWS для случаев низкой задержки вывода. Контейнер LMI использует Deep Java Library (DJL) Serving, которая является открытой высокоуровневой Java-фреймворкой, не зависящей от движка, для глубокого обучения. С помощью этих контейнеров вы можете использовать соответствующие библиотеки с открытым исходным кодом, такие как DeepSpeed, Accelerate, Transformers-neuronx и FasterTransformer, для разделения параметров модели с использованием техник параллельного моделирования для использования памяти нескольких графических процессоров или ускорителей для вывода. Дополнительные сведения о преимуществах использования контейнера LMI для развертывания больших моделей на SageMaker см. в разделе Развертывание больших моделей с высокой производительностью с использованием FasterTransformer на Amazon SageMaker и Развертывание больших моделей на Amazon SageMaker с использованием DJLServing и DeepSpeed модельного параллельного вывода. Вы также можете найти больше примеров размещения моделей с открытым исходным кодом LLM на SageMaker с использованием контейнеров LMI в этом репозитории GitHub.

Для контейнера LMI мы ожидаем следующие артефакты, которые помогут настроить модель для вывода:

  • serving.properties (обязательно) – Определяет настройки сервера моделей
  • model.py (необязательно) – Файл на языке Python для определения основной логики вывода
  • requirements.txt (необязательно) – Любые дополнительные пакеты pip, которые необходимо установить

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

  • В этом примере мы размещаем модель Falcon-7B-Instruct. Нам нужно создать файл конфигурации serving.properties с выбранными параметрами размещения и упаковать его в артефакт tar.gz. Для включения потоковой передачи ответов в DJL Serving установите параметр enable_streaming в файле serving.properties. Для получения списка поддерживаемых параметров обратитесь к документации Streaming Python.
  • В этом примере мы используем стандартные обработчики в DJL Serving для потоковой передачи ответов, поэтому нам важно только отправлять запросы и разбирать выходной ответ. Вы также можете предоставить собственный обработчик с помощью кода entrypoint в файле model.py, чтобы настроить обработчики ввода и вывода. Дополнительные сведения о пользовательском обработчике см. в документации Custom model.py handler.
  • Так как мы размещаем модель Falcon-7B-Instruct на одном экземпляре GPU (ml.g5.2xlarge), мы устанавливаем option.tensor_parallel_degree равным 1. Если вы планируете использовать несколько GPU, используйте этот параметр для указания количества GPU на каждого рабочего.
  • Мы используем option.output_formatter для управления типом выводимого контента. По умолчанию тип контента вывода – application/json, поэтому, если вашему приложению требуется другой тип вывода, вы можете перезаписать это значение. Дополнительные сведения о доступных параметрах см. в документации Configurations and settings и All DJL configuration options.
%%writefile serving.properties
engine=MPI 
option.model_id=tiiuae/falcon-7b-instruct
option.trust_remote_code=true
option.tensor_parallel_degree=1
option.max_rolling_batch_size=32
option.rolling_batch=auto
option.output_formatter=jsonlines
option.paged_attention=false
option.enable_streaming=true

Чтобы создать модель SageMaker, получите URI контейнера изображения:

image_uri = image_uris.retrieve(
    framework="djl-deepspeed",
    region=sess.boto_session.region_name,
    version="0.23.0"
)

Используйте SageMaker Python SDK, чтобы создать модель SageMaker и развернуть ее на реальный конечный точке SageMaker с помощью метода deploy:

instance_type = "ml.g5.2xlarge"
endpoint_name = sagemaker.utils.name_from_base("lmi-model-falcon-7b")

model = Model(sagemaker_session=sess, 
                image_uri=image_uri, 
                model_data=code_artifact, 
                role=role)

model.deploy(
    initial_instance_count=1,
    instance_type=instance_type,
    endpoint_name=endpoint_name,
    container_startup_health_check_timeout=900
)

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

Тип контента ответа, указанный в x-amzn-sagemaker-content-type для контейнера LMI, является application/jsonline, как указано в конфигурации свойств модели. Поскольку он является частью общих форматов данных, поддерживаемых для вывода, мы можем использовать предоставленный SageMaker Python SDK для десериализации данных JSON lines. Мы создаем класс-помощник LineIterator для разбора потока ответов, полученного из запроса вывода:

class LineIterator:
    """
    Вспомогательный класс для разбора входного байтового потока. 
    
    Выход модели будет иметь следующий формат:
    ```
    b'{"outputs": [" a"]}\n'
    b'{"outputs": [" challenging"]}\n'
    b'{"outputs": [" problem"]}\n'
    ...
    ```
    
    Обычно каждое событие PayloadPart из потока событий содержит массив байтов 
    с полным json, но это не гарантируется, и некоторые объекты json могут быть разделены
    между событиями PayloadPart. Например:
    ```
    {'PayloadPart': {'Bytes': b'{"outputs": '}}
    {'PayloadPart': {'Bytes': b'[" problem"]}\n'}}
    ```
    
    Этот класс учитывает это, объединяя байты, записанные с помощью функции 'write',
    и затем предоставляет метод, который возвращает строки (заканчивающиеся символом '\n') из
    буфера с помощью функции 'scan_lines'. Он сохраняет позицию последнего чтения 
    для обеспечения того, что предыдущие байты не будут предоставлены снова. 
    """
    
    def __init__(self, stream):
        self.byte_iterator = iter(stream)
        self.buffer = io.BytesIO()
        self.read_pos = 0

    def __iter__(self):
        return self

    def __next__(self):
        while True:
            self.buffer.seek(self.read_pos)
            line = self.buffer.readline()
            if line and line[-1] == ord('\n'):
                self.read_pos += len(line)
                return line[:-1]
            try:
                chunk = next(self.byte_iterator)
            except StopIteration:
                if self.read_pos < self.buffer.getbuffer().nbytes:
                    continue
                raise
            if 'PayloadPart' not in chunk:
                print('Неизвестный тип события:' + chunk)
                continue
            self.buffer.seek(0, io.SEEK_END)
            self.buffer.write(chunk['PayloadPart']['Bytes'])

С классом в предыдущем коде каждый раз, когда происходит потоковый ответ, будет возвращаться двоичная строка (например, b'{"outputs": [" a"]}\n'), которую можно снова десериализовать в словарь Python с использованием пакета JSON. Мы можем использовать следующий код для перебора каждой потоковой строки и возврата текстового ответа:

body = {"inputs": "what is life", "parameters": {"max_new_tokens":400}}
resp = smr.invoke_endpoint_with_response_stream(EndpointName=endpoint_name, Body=json.dumps(body), ContentType="application/json")
event_stream = resp['Body']

for line in LineIterator(event_stream):
    resp = json.loads(line)
    print(resp.get("outputs")[0], end='')

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

Вариант 2: Реализация чатбота с использованием контейнера Hugging Face TGI

В предыдущем разделе вы увидели, как развернуть модель Falcon-7B-Instruct с использованием контейнера LMI. В этом разделе мы покажем, как сделать то же самое с использованием контейнера Hugging Face Text Generation Inference (TGI) на SageMaker. TGI – это открытое решение, специально разработанное для развертывания LLM. Оно включает оптимизации, включая тензорный параллелизм для ускоренного многократного GPU-вывода, динамическую пакетизацию для повышения общей пропускной способности и оптимизированный код трансформаторов с использованием flash-attention для популярных архитектур моделей, включая BLOOM, T5, GPT-NeoX, StarCoder и LLaMa.

Контейнеры глубокого обучения TGI поддерживают потоковую токенизацию с использованием серверных событий (SSE). С потоковой токенизацией сервер может начать отвечать сразу после первого прохода prefill, не дожидаясь завершения всей генерации. Для очень длинных запросов это означает, что клиенты могут начать видеть что-то происходящее на порядки раньше, чем работа окончена. На следующей диаграмме показана общая рабочий процесс запроса/ответа с использованием контейнера TGI для размещения LLM на точке доступа SageMaker.

Для развертывания модели Falcon-7B-Instruct на точке доступа SageMaker мы используем класс HuggingFaceModel из SageMaker Python SDK. Мы начинаем с установки следующих параметров:

hf_model_id = "tiiuae/falcon-7b-instruct" # идентификатор модели с huggingface.co/models
number_of_gpus = 1 # количество использованных GPU для вывода и тензорного параллелизма
health_check_timeout = 300 # увеличение времени ожидания проверки состояния для загрузки модели до 5 минут
instance_type = "ml.g5.2xlarge" # тип экземпляра для развертывания

По сравнению с развертыванием обычных моделей Hugging Face, нам сначала нужно получить URI контейнера и указать его в классе модели HuggingFaceModel с помощью image_uri. Чтобы получить новый контейнер Hugging Face LLM в SageMaker, мы можем использовать метод get_huggingface_llm_image_uri, предоставляемый SDK SageMaker. Этот метод позволяет нам получить URI для нужного контейнера Hugging Face LLM на основе указанного бэкенда, сессии, региона и версии. Для получения дополнительной информации о доступных версиях обратитесь к контейнерам HuggingFace Text Generation Inference.

llm_image = get_huggingface_llm_image_uri(
    "huggingface",
    version="0.9.3"
)

Затем мы создаем HuggingFaceModel и развертываем его на SageMaker с помощью метода deploy:

endpoint_name = sagemaker.utils.name_from_base("tgi-model-falcon-7b")
llm_model = HuggingFaceModel(
    role=role,
    image_uri=llm_image,
    env={
        'HF_MODEL_ID': hf_model_id,
        # 'HF_MODEL_QUANTIZE': "bitsandbytes", # раскомментируйте, чтобы использовать квантование
        'SM_NUM_GPUS': str(number_of_gpus),
        'MAX_INPUT_LENGTH': "1900",  # Максимальная длина вводного текста
        'MAX_TOTAL_TOKENS': "2048",  # Максимальная длина генерации (включая вводной текст)
    }
)

llm = llm_model.deploy(
    initial_instance_count=1,
    instance_type=instance_type,
    container_startup_health_check_timeout=health_check_timeout,
    endpoint_name=endpoint_name,
)

Основное отличие по сравнению с контейнером LMI заключается в том, что вы включаете потоковую передачу данных при вызове конечной точки, указывая stream=true в качестве части запроса вызова. Вот пример кода запроса, используемого для вызова контейнера TGI с потоковой передачей данных:

body = {
    "inputs":"скажи мне одно предложение",
    "parameters":{
        "max_new_tokens":400,
        "return_full_text": False
    },
    "stream": True
}

Затем вы можете вызвать конечную точку и получить ответ в виде потока данных с помощью следующей команды:

from sagemaker.base_deserializers import StreamDeserializer

llm.deserializer=StreamDeserializer()
resp = smr.invoke_endpoint_with_response_stream(EndpointName=llm.endpoint_name, Body=json.dumps(body), ContentType='application/json')

Тип содержимого ответа, отображаемый в x-amzn-sagemaker-content-type для контейнера TGI, равен text/event-stream. Мы используем StreamDeserializer для десериализации ответа в класс EventStream и разбора тела ответа с использованием того же класса LineIterator, что и в разделе о контейнере LMI.

Обратите внимание, что потоковый ответ от контейнеров TGI возвращается в виде двоичной строки (например, b`data:{"token": {"text": " sometext"}}`), которую можно повторно десериализовать в словарь Python с помощью пакета JSON. Мы можем использовать следующий код для итерации по каждой строке текста из потока данных и возврата текстового ответа:

event_stream = resp['Body']
start_json = b'{'
for line in LineIterator(event_stream):
    if line != b'' and start_json in line:
        data = json.loads(line[line.find(start_json):].decode('utf-8'))
        if data['token']['text'] != stop_token:
            print(data['token']['text'],end='')

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

Запуск приложения бота в SageMaker Studio

В этом примере использования мы создаем динамического бота в SageMaker Studio с использованием Streamlit, который вызывает модель Falcon-7B-Instruct, размещенную на конечной точке реального времени в SageMaker, чтобы предоставлять потоковые ответы. Сначала вы можете проверить, что потоковые ответы работают в блокноте, как показано в предыдущем разделе. Затем вы можете настроить приложение Streamlit в терминале JupyterServer SageMaker Studio и получить доступ к пользовательскому интерфейсу бота из вашего браузера, выполнив следующие действия:

  1. Откройте системный терминал в SageMaker Studio.

  2. В верхнем меню консоли SageMaker Studio выберите пункт File, затем New, затем Terminal.

  3. Установите необходимые пакеты Python, указанные в файле requirements.txt:

    $ pip install -r requirements.txt
  4. Настройте переменную среды с именем конечной точки, развернутой в вашей учетной записи:

    $ export endpoint_name=<имя конечной точки Falcon-7B-instruct, развернутой в вашей учетной записи>
  5. Запустите приложение Streamlit из файла streamlit_chatbot_LMI.py, которое автоматически обновит имена конечных точек в скрипте на основе ранее настроенной переменной среды:

    $ streamlit run streamlit_chatbot_LMI.py --server.port 6006
  6. Для доступа к пользовательскому интерфейсу Streamlit скопируйте свой URL SageMaker Studio в другую вкладку вашего браузера и замените lab? на proxy/[НОМЕР ПОРТА]/. Поскольку мы указали порт сервера 6006, URL должен выглядеть следующим образом:

    https://<ID домена>.studio.<регион>.sagemaker.aws/jupyter/default/proxy/6006/

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

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

Очистка

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

# - Удалить конечную точку
sm_client.delete_endpoint(EndpointName=endpoint_name)

Заключение

В этой статье мы предоставили обзор создания приложений с генеративным искусственным интеллектом, столкновений и того, как ответы в режиме реального времени в SageMaker помогают вам решать эти проблемы. Мы продемонстрировали, как создать чатбот-приложение для развертывания модели Falcon-7B-Instruct с использованием потоковой передачи ответов как в контейнерах SageMaker LMI, так и в контейнерах HuggingFace TGI с использованием примера, доступного на GitHub.

Начните создавать собственные передовые потоковые приложения с LLM и SageMaker уже сегодня! Обратитесь к нам за экспертным руководством и разблокируйте потенциал потоковой передачи больших моделей для ваших проектов.