Home » Оптимизация вывода больших языковых моделей (LLM)
Оптимизация вывода больших языковых моделей (LLM)

Оптимизация вывода больших языковых моделей (LLM)

Большие языковые модели (LLM) — это круто, но они жрут память и вычислительные ресурсы как голодная база данных в пятницу вечером. Если вы хотите развернуть собственный ChatGPT-подобный сервис или просто поиграться с моделями типа Llama, Mistral или Falcon, вам нужно понимать, как выжать максимум производительности из вашего железа. Эта статья поможет вам не только запустить LLM, но и настроить их так, чтобы они работали быстро и эффективно, не превращая ваш сервер в обогреватель.

Мы разберём три ключевых вопроса: как работает инференс LLM под капотом, как быстро настроить оптимизированную среду развёртывания и какие существуют практические подходы с реальными примерами — как успешными, так и провальными. Плюс покажу команды, скрипты и нестандартные способы использования, которые помогут вам автоматизировать процессы.

Как это работает под капотом

LLM — это по сути гигантская нейросеть, которая предсказывает следующий токен в последовательности. Процесс inference состоит из двух фаз:

  • Prefill — обработка входного промпта (параллельно)
  • Decode — генерация токенов один за другим (последовательно)

Именно вторая фаза становится узким местом. Каждый новый токен требует полного прохода через всю модель, что создаёт memory-bound нагрузку. Чем больше модель, тем больше времени тратится на чтение весов из памяти.

Ключевые метрики производительности:

  • Latency — время до первого токена
  • Throughput — токенов в секунду
  • Memory usage — потребление RAM/VRAM

Быстрая настройка оптимизированной среды

Для начала нужен мощный сервер. Рекомендую VPS с минимум 32GB RAM для моделей 7B-13B или выделенный сервер для более крупных моделей.

Установка базового окружения

# Обновляем систему
sudo apt update && sudo apt upgrade -y

# Устанавливаем NVIDIA драйвера (если есть GPU)
sudo apt install nvidia-driver-535 nvidia-cuda-toolkit -y

# Устанавливаем Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Настраиваем NVIDIA Container Toolkit
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt update && sudo apt install -y nvidia-docker2
sudo systemctl restart docker

# Проверяем GPU
nvidia-smi
docker run --rm --gpus all nvidia/cuda:11.8-base-ubuntu22.04 nvidia-smi

Настройка vLLM — топовый inference engine

vLLM — это пожалуй лучший open-source движок для inference LLM. Он использует PagedAttention для оптимизации памяти и поддерживает continuous batching.

# Создаём рабочую директорию
mkdir llm-inference && cd llm-inference

# Создаём Dockerfile
cat > Dockerfile << 'EOF'
FROM nvidia/cuda:11.8-devel-ubuntu22.04

RUN apt-get update && apt-get install -y \
    python3.10 python3.10-pip python3.10-dev \
    git curl wget && \
    rm -rf /var/lib/apt/lists/*

RUN pip3 install --upgrade pip
RUN pip3 install vllm torch transformers accelerate

WORKDIR /app
COPY . .

EXPOSE 8000
CMD ["python3", "-m", "vllm.entrypoints.openai.api_server", "--host", "0.0.0.0", "--port", "8000"]
EOF

# Собираем образ
docker build -t llm-inference .

# Запускаем с моделью Llama-2-7B
docker run -d --gpus all -p 8000:8000 \
  --name llm-server \
  llm-inference \
  python3 -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-2-7b-chat-hf \
  --host 0.0.0.0 \
  --port 8000 \
  --tensor-parallel-size 1

Техники оптимизации

Квантизация — сжимаем модель без потери качества

Квантизация позволяет уменьшить размер модели в 2-4 раза с минимальной потерей точности. Поддерживаются форматы GPTQ, AWQ и GGUF.

Метод Сжатие Качество Скорость Применение
GPTQ 4x Высокое Быстрый GPU inference
AWQ 3-4x Очень высокое Очень быстрый GPU inference
GGUF 2-16x Переменное Средний CPU inference
# Запуск с квантизованной моделью
docker run -d --gpus all -p 8000:8000 \
  --name llm-quantized \
  llm-inference \
  python3 -m vllm.entrypoints.openai.api_server \
  --model TheBloke/Llama-2-7B-Chat-GPTQ \
  --quantization gptq \
  --host 0.0.0.0 \
  --port 8000

Батчинг и параллелизм

Continuous batching позволяет обрабатывать несколько запросов одновременно, динамически добавляя и удаляя запросы из батча.

# Настройка для высокой нагрузки
docker run -d --gpus all -p 8000:8000 \
  --name llm-optimized \
  llm-inference \
  python3 -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-2-13b-chat-hf \
  --tensor-parallel-size 2 \
  --max-num-batched-tokens 8192 \
  --max-num-seqs 256 \
  --host 0.0.0.0 \
  --port 8000

Альтернативные решения

Сравнение inference engines

Engine Производительность Простота Функции Лучше для
vLLM ★★★★★ ★★★★☆ OpenAI API, batching Production inference
Text Generation Inference ★★★★☆ ★★★★★ Streaming, metrics HuggingFace экосистема
FastChat ★★★☆☆ ★★★★★ Web UI, чат Прототипирование
llama.cpp ★★★☆☆ ★★★★☆ CPU inference Локальное использование

Настройка Text Generation Inference

# Альтернативный вариант с TGI от HuggingFace
docker run -d --gpus all -p 8080:80 \
  --name tgi-server \
  -v /data:/data \
  ghcr.io/huggingface/text-generation-inference:1.3 \
  --model-id meta-llama/Llama-2-7b-chat-hf \
  --num-shard 1 \
  --max-concurrent-requests 128 \
  --max-input-length 4096 \
  --max-total-tokens 8192

Monitoring и логирование

Без мониторинга вы слепой пилот в тумане. Настроим Prometheus + Grafana для отслеживания метрик.

# docker-compose.yml для мониторинга
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-storage:/var/lib/grafana

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro

volumes:
  grafana-storage:
EOF

# Конфиг Prometheus
cat > prometheus.yml << 'EOF'
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']
  
  - job_name: 'llm-metrics'
    static_configs:
      - targets: ['host.docker.internal:8000']
EOF

# Запускаем мониторинг
docker-compose up -d

Практические кейсы и подводные камни

Успешный кейс: Чат-бот для поддержки

Развернули Llama-2-7B с vLLM для внутреннего чат-бота. Результат: 200ms латентность, 15 токенов/сек, обслуживание 50 одновременных пользователей на одной RTX 4090.

# Оптимизированная конфигурация для чат-бота
docker run -d --gpus all -p 8000:8000 \
  --name chatbot-llm \
  -e CUDA_VISIBLE_DEVICES=0 \
  --shm-size=16g \
  llm-inference \
  python3 -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-2-7b-chat-hf \
  --gpu-memory-utilization 0.9 \
  --max-num-seqs 64 \
  --max-model-len 4096 \
  --host 0.0.0.0 \
  --port 8000

Неудачный кейс: OOM при больших батчах

Попытка запустить Llama-2-13B с батчем 512 на 24GB GPU привела к Out Of Memory. Решение: уменьшить batch size и использовать градиентный чекпоинтинг.

# Безопасная конфигурация для больших моделей
docker run -d --gpus all -p 8000:8000 \
  --name safe-llm \
  llm-inference \
  python3 -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-2-13b-chat-hf \
  --gpu-memory-utilization 0.85 \
  --max-num-seqs 32 \
  --max-num-batched-tokens 4096 \
  --host 0.0.0.0 \
  --port 8000

Автоматизация и скрипты

Автоматический деплой с проверкой здоровья

#!/bin/bash
# deploy-llm.sh

set -e

MODEL_NAME=${1:-"meta-llama/Llama-2-7b-chat-hf"}
CONTAINER_NAME="llm-server"
PORT=8000

echo "Деплоим модель: $MODEL_NAME"

# Останавливаем старый контейнер
docker stop $CONTAINER_NAME 2>/dev/null || true
docker rm $CONTAINER_NAME 2>/dev/null || true

# Запускаем новый
docker run -d --gpus all -p $PORT:$PORT \
  --name $CONTAINER_NAME \
  --restart unless-stopped \
  llm-inference \
  python3 -m vllm.entrypoints.openai.api_server \
  --model $MODEL_NAME \
  --host 0.0.0.0 \
  --port $PORT

# Ждём запуска
echo "Ожидаем запуска сервера..."
for i in {1..30}; do
  if curl -s http://localhost:$PORT/health > /dev/null; then
    echo "Сервер запущен успешно!"
    break
  fi
  sleep 10
done

# Тестируем
curl -X POST "http://localhost:$PORT/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'$MODEL_NAME'",
    "messages": [{"role": "user", "content": "Hello, how are you?"}],
    "max_tokens": 100
  }'

Скрипт мониторинга ресурсов

#!/bin/bash
# monitor-llm.sh

CONTAINER_NAME="llm-server"
LOG_FILE="/var/log/llm-metrics.log"

while true; do
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
  
  # Получаем метрики контейнера
  STATS=$(docker stats $CONTAINER_NAME --no-stream --format "table {{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}")
  
  # Получаем метрики GPU
  GPU_STATS=$(nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits)
  
  # Логируем
  echo "$TIMESTAMP | Container: $STATS | GPU: $GPU_STATS" >> $LOG_FILE
  
  # Отправляем в Telegram при высокой нагрузке
  GPU_MEM=$(echo $GPU_STATS | cut -d',' -f2 | tr -d ' ')
  if [ $GPU_MEM -gt 20000 ]; then
    curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
      -d chat_id=$CHAT_ID \
      -d text="⚠️ Высокое потребление GPU памяти: ${GPU_MEM}MB"
  fi
  
  sleep 60
done

Интересные факты и нестандартные способы

Использование с Redis для кэширования

Многие запросы повторяются. Можно кэшировать ответы в Redis для ускорения.

# docker-compose.yml с Redis кэшем
version: '3.8'

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru
  
  llm-server:
    image: llm-inference
    depends_on:
      - redis
    environment:
      - REDIS_URL=redis://redis:6379
    ports:
      - "8000:8000"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

Роутинг запросов между моделями

Настроим nginx для роутинга простых запросов на быструю модель, а сложных — на мощную.

# nginx.conf
upstream llm_fast {
    server llm-7b:8000;
}

upstream llm_smart {
    server llm-70b:8000;
}

server {
    listen 80;
    
    location /v1/chat/completions {
        # Простая эвристика: если запрос короткий, используем быструю модель
        if ($request_body ~ "(.{1,100})") {
            proxy_pass http://llm_fast;
        }
        proxy_pass http://llm_smart;
    }
    
    location /health {
        return 200 "OK";
    }
}

Новые возможности для автоматизации

Интеграция с CI/CD

Модель можно автоматически обновлять при релизе новой версии:

# .github/workflows/deploy-llm.yml
name: Deploy LLM

on:
  schedule:
    - cron: '0 2 * * 1'  # Каждый понедельник в 2:00
  workflow_dispatch:

jobs:
  deploy:
    runs-on: self-hosted
    steps:
    - name: Update model
      run: |
        docker pull huggingface/text-generation-inference:latest
        ./deploy-llm.sh microsoft/DialoGPT-large
    
    - name: Test deployment
      run: |
        sleep 60
        curl -f http://localhost:8000/health || exit 1
        
    - name: Notify success
      run: |
        curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
          -H 'Content-type: application/json' \
          -d '{"text":"✅ LLM model updated successfully"}'

Автоматическое масштабирование

Kubernetes HPA для автоматического масштабирования по нагрузке:

# llm-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: llm-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: llm-deployment
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

Выводы и рекомендации

Оптимизация LLM inference — это искусство балансировки между производительностью, качеством и ресурсами. Ключевые принципы:

  • Используйте vLLM для production — он показывает лучшую производительность благодаря PagedAttention и continuous batching
  • Квантизация обязательна — AWQ/GPTQ дают 3-4x экономию памяти с минимальной потерей качества
  • Мониторинг критически важен — без метрик вы не поймёте, где узкие места
  • Начинайте с малого — модели 7B-13B покрывают 80% задач и требуют меньше ресурсов

Для начала рекомендую взять VPS с 32GB RAM и GPU, развернуть Llama-2-7B с vLLM и постепенно оптимизировать под вашу нагрузку. Если планируете серьёзную нагрузку, сразу берите выделенный сервер с несколькими GPU.

Полезные ссылки для дальнейшего изучения:

Удачи в оптимизации! Если что-то не работает — проверьте логи, они обычно говорят правду 😉


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

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

Leave a reply

Your email address will not be published. Required fields are marked