Искусственный интеллект
Оптимизация развертывания LLM: vLLM PagedAttention и будущее эффективного обслуживания ИИ

Развертывание больших языковых моделей (LLM) в реальных приложениях представляет собой уникальные проблемы, особенно с точки зрения вычислительных ресурсов, задержек и экономической эффективности. В этом подробном руководстве мы рассмотрим ландшафт обслуживания LLM, уделив особое внимание vLLM (векторной языковой модели) — решению, которое меняет способы развертывания и взаимодействия с этими мощными моделями.
Проблемы обслуживания больших языковых моделей
Прежде чем углубляться в конкретные решения, давайте рассмотрим ключевые проблемы, которые делают LLM решением сложной задачи:
Вычислительные ресурсы
LLM печально известны своим огромным количеством параметров: от миллиардов до сотен миллиардов. Например, GPT-3 может похвастаться 175 миллиардами параметров, а более поздние модели, такие как GPT-4 по оценкам, их еще больше. Этот огромный размер приводит к значительным вычислительным требованиям для вывода.
Пример:
Рассмотрим относительно скромный LLM с 13 миллиардами параметров, например LLaMA-13B. Даже эта модель требует:
– Примерно 26 ГБ памяти только для хранения параметров модели (при 16-битной точности)
– Дополнительная память для активаций, механизмов внимания и промежуточных вычислений.
– Значительная вычислительная мощность графического процессора для вывода в реальном времени.
Задержка
Во многих приложениях, таких как чат-боты или создание контента в реальном времени, низкая задержка имеет решающее значение для хорошего пользовательского опыта. Однако сложность LLM может привести к значительному увеличению времени обработки, особенно для более длинных последовательностей.
Пример:
Представьте себе чат-бота по обслуживанию клиентов, работающего на базе LLM. Если на создание каждого ответа уходит несколько секунд, разговор будет казаться пользователям неестественным и разочаровывающим.
Цена
Аппаратное обеспечение, необходимое для масштабного запуска LLM, может быть чрезвычайно дорогим. Часто необходимы высокопроизводительные графические процессоры или TPU, а энергопотребление этих систем существенно.
Пример:
Использование кластера графических процессоров NVIDIA A100 (часто используемого для вывода LLM) может стоить тысячи долларов в день в виде платы за облачные вычисления.
Традиционные подходы к обслуживанию LLM
Прежде чем изучать более продвинутые решения, давайте кратко рассмотрим некоторые традиционные подходы к обслуживанию LLM:
Простое развертывание с помощью трансформеров с обнимающими лицами
Библиотека Hugging Face Transformers предоставляет простой способ развертывания LLM, но она не оптимизирована для обслуживания с высокой пропускной способностью.
Пример кода:
from transformers import AutoModelForCausalLM, AutoTokenizer import torch model_name = "meta-llama/Llama-2-13b-hf" model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto") tokenizer = AutoTokenizer.from_pretrained(model_name) def generate_text(prompt, max_length=100): inputs = tokenizer(prompt, return_tensors="pt").to(model.device) outputs = model.generate(**inputs, max_length=max_length) return tokenizer.decode(outputs[0], skip_special_tokens=True) print(generate_text("The future of AI is"))
Хотя этот подход работает, он не подходит для приложений с высоким трафиком из-за неэффективного использования ресурсов и отсутствия оптимизации обслуживания.
Использование TorchServe или подобных фреймворков
Такие платформы, как TorchServe, предоставляют более надежные возможности обслуживания, включая балансировку нагрузки и управление версиями модели. Однако они по-прежнему не решают конкретные проблемы обслуживания LLM, такие как эффективное управление памятью для больших моделей.
Понимание управления памятью в обслуживании LLM
Эффективное управление памятью имеет решающее значение для обслуживания больших языковых моделей (LLM) из-за необходимости использования обширных вычислительных ресурсов. Следующие изображения иллюстрируют различные аспекты управления памятью, которые являются неотъемлемой частью оптимизации производительности LLM.
Сегментированная и страничная память
На этих двух диаграммах сравниваются методы управления сегментированной памятью и страничной памятью, обычно используемые в операционных системах (ОС).
- Сегментированная память: этот метод делит память на разные сегменты, каждый из которых соответствует отдельной программе или процессу. Например, в контексте обслуживания LLM разные сегменты могут быть выделены различным компонентам модели, таким как токенизация, внедрение и механизмы внимания. Каждый сегмент может расти или сокращаться независимо, что обеспечивает гибкость, но потенциально может привести к фрагментации, если сегментами не управляют должным образом.
- Страничная память: Здесь память делится на страницы фиксированного размера, которые отображаются в физическую память. Страницы можно заменять и выгружать по мере необходимости, что позволяет эффективно использовать ресурсы памяти. При обслуживании LLM это может иметь решающее значение для управления большими объемами памяти, необходимыми для хранения весов модели и промежуточных вычислений.
Управление памятью в ОС по сравнению с vLLM
На этом изображении традиционное управление памятью ОС сравнивается с подходом к управлению памятью, используемым в vLLM.
- Управление памятью ОС: В традиционных операционных системах процессам (например, процессу A и процессу B) выделяются страницы памяти (страница 0, страница 1 и т. д.) в физической памяти. Такое распределение может со временем привести к фрагментации, поскольку процессы запрашивают и освобождают память.
- Управление памятью vLLM: Платформа vLLM использует кеш «ключ-значение» (KV) для более эффективного управления памятью. Запросы (например, Запрос A и Запрос B) представляют собой выделенные блоки кэша KV (Блок KV 0, Блок KV 1 и т. д.). Этот подход помогает минимизировать фрагментацию и оптимизирует использование памяти, обеспечивая более быстрое и эффективное обслуживание моделей.
Механизм внимания в LLM
Механизм внимания является фундаментальным компонентом моделей трансформеров, которые обычно используются для LLM. Эта диаграмма иллюстрирует формулу внимания и ее компоненты:
- Запрос (Q): новый токен в шаг декодера или последний токен, который увидела модель.
- Ключ (К): предыдущий контекст, на который должна обратить внимание модель.
- Значение (V): Взвешенная сумма по предыдущему контексту.
Формула вычисляет показатели внимания, беря скалярное произведение запроса на ключи, масштабируя квадратный корень ключевого измерения, применяя функцию softmax и, наконец, беря скалярное произведение со значениями. Этот процесс позволяет модели сосредоточиться на соответствующих частях входной последовательности при создании каждого токена.
Сравнение пропускной способности обслуживания
На этом изображении представлено сравнение пропускной способности обслуживания между различными платформами (HF, TGI и vLLM) с использованием модели LLaMA на разных аппаратных настройках.
- ЛЛаМА-13Б, А100-40ГБ: vLLM обеспечивает в 14–24 раза более высокую пропускную способность, чем HuggingFace Transformers (HF), и в 2.2–2.5 раза более высокую пропускную способность, чем HuggingFace Text Generation Inference (TGI).
- ЛЛаМА-7Б, А10Г: Наблюдаются аналогичные тенденции: vLLM значительно превосходит как HF, так и TGI.
vLLM: новая архитектура обслуживания LLM
vLLM, разработанный Исследователи из Калифорнийского университета в Беркли, представляет собой значительный шаг вперед в технологии обслуживания LLM. Давайте рассмотрим его ключевые особенности и нововведения:
PagedAttention
В основе vLLM лежит PagedAttention, новый алгоритм внимания, вдохновленный управлением виртуальной памятью в операционных системах. Вот как это работает:
– Разделение кэша по принципу «ключ-значение» (KV): вместо того, чтобы хранить весь кэш KV в памяти последовательно, PagedAttention делит его на блоки фиксированного размера.
– Непрерывное хранилище: эти блоки могут храниться в памяти несмежно, что обеспечивает более гибкое управление памятью.
– Распределение по требованию: блоки выделяются только при необходимости, что снижает потери памяти.
– Эффективный обмен: несколько последовательностей могут использовать общие блоки, что позволяет оптимизировать такие методы, как параллельная выборка и поиск луча.
Иллюстрация:
«`
Традиционный KV-кэш:
[Жетон 1 КВ][Жетон 2 КВ][Жетон 3 КВ]…[Жетон Н КВ]
(Непрерывное распределение памяти)
Кэш PagedAttention KV:
[Блок 1] -> Физический адрес A
[Блок 2] -> Физический адрес C
[Блок 3] -> Физический адрес B
...
(Несмежное распределение памяти)
«`
Такой подход значительно снижает фрагментацию памяти и позволяет гораздо более эффективно использовать память графического процессора.
Непрерывное дозирование
vLLM реализует непрерывную пакетную обработку, которая динамически обрабатывает запросы по мере их поступления, а не ждет формирования пакетов фиксированного размера. Это приводит к снижению задержки и повышению пропускной способности.
Пример:
Представьте себе поток входящих запросов:
«`
Время 0 мс: поступает запрос A
Время 10 мс: начало обработки запроса A.
Время 15 мс: поступает запрос B.
Время 20 мс: начало обработки запроса B (параллельно с A)
Время 25 мс: поступает запрос C.
...
«`
Благодаря непрерывной пакетной обработке vLLM может начать обработку каждого запроса немедленно, не дожидаясь группировки их в заранее определенные пакеты.
Эффективная параллельная выборка
Для приложений, которым требуется несколько образцов выходных данных для каждого приглашения (например, помощники по творческому письму), возможности совместного использования памяти vLLM превосходны. Он может генерировать несколько выходных данных при повторном использовании кэша KV для общих префиксов.
Пример кода с использованием vLLM:
from vllm import LLM, SamplingParams llm = LLM(model="meta-llama/Llama-2-13b-hf") prompts = ["The future of AI is"] # Generate 3 samples per prompt sampling_params = SamplingParams(n=3, temperature=0.8, max_tokens=100) outputs = llm.generate(prompts, sampling_params) for output in outputs: print(f"Prompt: {output.prompt}") for i, out in enumerate(output.outputs): print(f"Sample {i + 1}: {out.text}")
Этот код эффективно генерирует несколько образцов для заданного запроса, используя оптимизацию vLLM.
Сравнительный анализ производительности vLLM
Чтобы по-настоящему оценить влияние vLLM, давайте посмотрим на некоторые сравнения производительности:
Сравнение пропускной способности
Судя по предоставленной информации, vLLM значительно превосходит другие сервисные решения:
– Пропускная способность до 24 раз выше по сравнению с трансформаторами Hugging Face Transformers.
– Пропускная способность в 2.2–3.5 раза выше, чем при генерации текста обнимающего лица (TGI)
Иллюстрация:
«`
Пропускная способность (токены/секунду)
|
| ****
| ****
| ****
| **** ****
| **** **** ****
| **** **** ****
|————————
КВ ТГИ вЛЛМ
«`
Эффективность памяти
PagedAttention vLLM обеспечивает почти оптимальное использование памяти:
– Всего около 4% потерь памяти по сравнению с 60-80% в традиционных системах
– Такая эффективность позволяет обслуживать более крупные модели или обрабатывать больше одновременных запросов на одном и том же оборудовании.
Начало работы с vLLM
Теперь, когда мы изучили преимущества vLLM, давайте рассмотрим процесс его настройки и использования в ваших проектах.
Установка 6.1
Установить vLLM очень просто, используя pip:
!pip install vllm
6.2 Базовое использование автономного вывода
Вот простой пример использования vLLM для автономной генерации текста:
from vllm import LLM, SamplingParams # Initialize the model llm = LLM(model="meta-llama/Llama-2-13b-hf") # Prepare prompts prompts = [ "Write a short poem about artificial intelligence:", "Explain quantum computing in simple terms:" ] # Set sampling parameters sampling_params = SamplingParams(temperature=0.8, max_tokens=100) # Generate responses outputs = llm.generate(prompts, sampling_params) # Print the results for output in outputs: print(f"Prompt: {output.prompt}") print(f"Generated text: {output.outputs[0].text}\n")
Этот сценарий демонстрирует, как загрузить модель, установить параметры выборки и сгенерировать текст для нескольких подсказок.
6.3 Настройка сервера vLLM
Для онлайн-обслуживания vLLM предоставляет сервер API, совместимый с OpenAI. Вот как это настроить:
1. Запустите сервер:
python -m vllm.entrypoints.openai.api_server --model meta-llama/Llama-2-13b-hf
2. Опросите сервер с помощью Curl:
curl http://localhost:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "meta-llama/Llama-2-13b-hf", "prompt": "The benefits of artificial intelligence include:", "max_tokens": 100, "temperature": 0.7 }'
Эта настройка позволяет вам обслуживать ваш LLM с помощью интерфейса, совместимого с API OpenAI, что упрощает интеграцию в существующие приложения.
Расширенные темы по vLLM
Хотя vLLM предлагает значительные улучшения в обслуживании LLM, есть дополнительные соображения и дополнительные темы для изучения:
7.1 Квантование модели
Для еще более эффективного обслуживания, особенно на оборудовании с ограниченной памятью, можно использовать методы квантования. Хотя сам vLLM в настоящее время не поддерживает квантование, его можно использовать в сочетании с квантованными моделями:
from transformers import AutoModelForCausalLM, AutoTokenizer import torch # Load a quantized model model_name = "meta-llama/Llama-2-13b-hf" model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto", load_in_8bit=True) tokenizer = AutoTokenizer.from_pretrained(model_name) # Use the quantized model with vLLM from vllm import LLM llm = LLM(model=model, tokenizer=tokenizer)
7.2 Распределенный вывод
Для чрезвычайно больших моделей или приложений с высоким трафиком может потребоваться распределенный вывод по нескольким графическим процессорам или машинам. Хотя vLLM изначально не поддерживает это, его можно интегрировать в распределенные системы с помощью таких фреймворков, как Ray:
import ray from vllm import LLM @ray.remote(num_gpus=1) class DistributedLLM: def __init__(self, model_name): self.llm = LLM(model=model_name) def generate(self, prompt, params): return self.llm.generate(prompt, params) # Initialize distributed LLMs llm1 = DistributedLLM.remote("meta-llama/Llama-2-13b-hf") llm2 = DistributedLLM.remote("meta-llama/Llama-2-13b-hf") # Use them in parallel result1 = llm1.generate.remote("Prompt 1", sampling_params) result2 = llm2.generate.remote("Prompt 2", sampling_params) # Retrieve results print(ray.get([result1, result2]))
7.3 Мониторинг и наблюдаемость
При обслуживании LLM на производстве контроль имеет решающее значение. Хотя vLLM не предоставляет встроенного мониторинга, вы можете интегрировать его с такими инструментами, как Prometheus и Grafana:
from prometheus_client import start_http_server, Summary from vllm import LLM # Define metrics REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request') # Initialize vLLM llm = LLM(model="meta-llama/Llama-2-13b-hf") # Expose metrics start_http_server(8000) # Use the model with monitoring @REQUEST_TIME.time() def process_request(prompt): return llm.generate(prompt) # Your serving loop here
Эта настройка позволяет отслеживать такие показатели, как время обработки запроса, которое можно визуализировать на панелях мониторинга Grafana.
Заключение
Эффективное обслуживание больших языковых моделей — сложная, но важная задача в эпоху искусственного интеллекта. vLLM с его инновационным алгоритмом PagedAttention и оптимизированной реализацией представляет собой значительный шаг вперед в том, чтобы сделать развертывание LLM более доступным и экономически эффективным.
Значительно повышая пропускную способность, сокращая потери памяти и обеспечивая более гибкие возможности обслуживания, vLLM открывает новые возможности для интеграции мощных языковых моделей в широкий спектр приложений. Независимо от того, создаете ли вы чат-бота, систему генерации контента или любое другое приложение на базе НЛП, понимание и использование таких инструментов, как vLLM, будет ключом к успеху.