Ускорение генерации текста с помощью vLLM
Введение
Большие языковые модели (LLM) играют ключевую роль в современных задачах обработки естественного языка, обеспечивая высокую точность и качество генерации текста. Однако процесс инференса, особенно на больших наборах данных, часто становится узким местом из-за значительных временных и вычислительных затрат. В данной статье рассматривается использование библиотеки vLLM для ускорения генерации текста в LLM вплоть до 20 раз за счет применения методов оптимизации, таких как PagedAttention и непрерывный батчинг.
Библиотека vLLM
Библиотека vLLM предоставляет инструменты для эффективного развертывания языковых моделей, поддерживающих OpenAI-совместимый API. Она включает поддержку квантизированных моделей (GPTQ, AWQ и SqueezeLLM), оптимизацию памяти с помощью PagedAttention, оптимизированные CUDA ядра, непрерывный батчинг и другие функции. vLLM совместима с большинством популярных моделей из HuggingFace.
Эндпоинты API
OpenAI-совместимый сервер в vLLM реализует следующие эндпоинты:
- list models
- ChatCompletion
- Completion
Развертывание API может быть выполнено следующим образом:
from openai import OpenAI
client = OpenAI()
num_stories = 10
prompt = "Я пошел в магазин и купил"
for _ in range(num_stories):
response = client.completions.create(
model=model_name,
prompt=prompt,
max_tokens=100,
)
print(prompt + response.choices[0].text)
Для статического батчинга:
from openai import OpenAI
client = OpenAI()
num_stories = 10
prompts = ["Я пошел в магазин и купил"] * num_stories
response = client.completions.create(
model=model_name,
prompt=prompts,
max_tokens=100,
)
stories = [""] * len(prompts)
for choice in response.choices:
stories[choice.index] = prompts[choice.index] + choice.text
for story in stories:
print(story)
Ускорение генерации: Инференс в LLM
Инференс в LLM происходит следующим образом: на вход подается последовательность токенов, затем итеративно генерируются следующие токены до достижения eos-токена или максимальной длины последовательности. Время обработки входного промпта примерно равно времени генерации выходной последовательности, поскольку вычисления для механизма внимания остаются постоянными на протяжении всей генерации.
Влияние батчинга
Пропускная способность инференса LLM определяется размером батча, который можно поместить в память графического процессора (GPU). Два типа батчинга — статический и непрерывный — помогают значительно ускорить генерацию данных.
Статический батчинг
Метод статического батчинга предполагает постоянный размер батча до полной генерации каждой последовательности. Это повышает пропускную способность, но память GPU используется неэффективно из-за времени генерации самой длинной последовательности.
Непрерывный батчинг
Непрерывный батчинг (реализация метода ORCA в vLLM) позволяет определять размер батча на каждой итерации отдельно. Как только генерация одной последовательности завершена, на ее место становится новая, что обеспечивает более высокую нагрузку на GPU.
PagedAttention
Механизм PagedAttention также способствует значительному ускорению инференса. Во время авторегрессионного декодирования для каждого входного токена генерируются тензоры K и V для слоя внимания, которые затем сохраняются в памяти GPU (KV-кэш). Память, используемая для KV-кэша, может достигать значительных объемов (до 1,7 ГБ для LLaMA-13B).
PagedAttention оптимизирует использование памяти, разбивая KV-кэш каждой последовательности на блоки, которые хранятся в несмежных пространствах памяти. Это позволяет обрабатывать большие батчи последовательностей, увеличивать загрузку GPU и существенно повышать скорость инференса.
Пример асинхронной генерации
Для использования непрерывного батчинга и ускорения генерации данных с помощью vLLM был применен асинхронный подход с использованием библиотеки asyncio:
import openai
import asyncio
async def generate_answers(prompt):
completion = await openai.ChatCompletion.acreate(
model="model_name",
messages=[{"role": "user", "content": prompt}],
max_tokens=1024,
request_timeout=10000
)
return completion["choices"][0].get("message").get("content")
async def main(data):
tasks = []
for idx, prompt in enumerate(data):
task = asyncio.create_task(generate_answers(prompt))
tasks.append(task)
answers = await asyncio.gather(*tasks)
return answers
results = asyncio.run(main(data))
Результаты
Использование vLLM привело к многократному сокращению времени генерации данных. На видеокарте A100 40 ГБ время генерации 100 инструкций сократилось с 6 минут 10 секунд до 1 минуты, а для 2000 инструкций — с 2 часов 50 минут до 5-10 минут.
Заключение
Методы непрерывного батчинга и PagedAttention, реализованные в vLLM, позволяют значительно ускорить процесс инференса в больших языковых моделях. Несмотря на возможное падение качества генерации некоторых ответов и необходимость выставления большого времени ожидания, эти методы обеспечивают существенное ускорение экспериментов с LLM.
Надеемся, что статья окажется полезной в вашей работе с LLM.