# ONNX Runtime GPU

> **Кросс-платформенное, аппаратно-ускоренное ML-инференс — развертывайте любую модель из любого фреймворка**

ONNX Runtime (ORT) — это открытый движок инференса от Microsoft для моделей ONNX (Open Neural Network Exchange). Он обеспечивает аппаратно-ускоренный инференс на CPU, GPU и специализированных ускорителях через унифицированный API. Независимо от того, в PyTorch, TensorFlow, Scikit-learn или XGBoost была обучена ваша модель — если вы можете экспортировать её в формат ONNX, ORT сможет запустить её быстрее.

**GitHub:** [microsoft/onnxruntime](https://github.com/microsoft/onnxruntime) — 14K+ ⭐

***

## Почему ONNX Runtime?

| Функция                                     | ONNX Runtime      | TorchScript      | TensorFlow Serving |
| ------------------------------------------- | ----------------- | ---------------- | ------------------ |
| Независимость от фреймворка                 | ✅                 | ❌ Только PyTorch | ❌ Только TF        |
| Ускорение на GPU                            | ✅ CUDA/TensorRT   | ✅                | ✅                  |
| Квантизация INT8/FP16                       | ✅                 | Частично         | Частично           |
| Развертывание на мобильных/edge-устройствах | ✅                 | Ограничено       | Ограничено         |
| Слияние операторов                          | ✅                 | Частично         | ✅                  |
| Лёгкая интеграция                           | ✅ Python/C++/Java | Python           | Python/gRPC        |

{% hint style="success" %}
**Ключевое преимущество:** ONNX Runtime с провайдером выполнения CUDA обычно обеспечивает **ускорение 1.5–3×** по сравнению с нативным инференсом PyTorch для моделей компьютерного зрения и NLP.
{% endhint %}

***

## Поддерживаемые провайдеры выполнения

ONNX Runtime поддерживает несколько аппаратных бэкендов (провайдеров выполнения):

| Провайдер                   | Оборудование  | Сценарий использования              |
| --------------------------- | ------------- | ----------------------------------- |
| `CUDAExecutionProvider`     | NVIDIA GPUs   | Общий GPU-инференс                  |
| `TensorrtExecutionProvider` | NVIDIA GPUs   | Максимальная пропускная способность |
| `CPUExecutionProvider`      | CPU           | Резерв / edge                       |
| `ROCMExecutionProvider`     | AMD GPUs      | Аппаратное обеспечение AMD          |
| `CoreMLExecutionProvider`   | Apple Silicon | macOS/iOS                           |
| `OpenVINOExecutionProvider` | Intel         | Intel CPU/GPU                       |

***

## Требования

* Аккаунт Clore.ai с арендой GPU
* Базовые знания Python
* Обученная модель (PyTorch, TensorFlow или заранее экспортированная в ONNX)

***

## Шаг 1 — Арендуйте GPU на Clore.ai

1. Перейдите на [clore.ai](https://clore.ai) → **Маркетплейс**
2. Любая NVIDIA GPU подходит — от RTX 3070 для небольших моделей до A100 для больших трансформеров
3. **Для моделей-трансформеров:** Рекомендуются RTX 4090 или A100
4. **Для задач компьютерного зрения:** Достаточно RTX 3090 или RTX 4090

***

## Шаг 2 — Разверните ваш контейнер

У ONNX Runtime нет официального предсобранного контейнера, но базовый образ NVIDIA CUDA идеален:

**Docker-образ:**

```
nvcr.io/nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04
```

**Порты:**

```
22
```

**Переменные окружения:**

```
NVIDIA_VISIBLE_DEVICES=all
NVIDIA_DRIVER_CAPABILITIES=compute,utility
```

{% hint style="info" %}
В качестве альтернативы используйте `pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime` который включает CUDA и Python-окружение, готовое для установки ORT.
{% endhint %}

***

## Шаг 3 — Установите ONNX Runtime с поддержкой GPU

```bash
ssh root@<server-ip> -p <ssh-port>

# Обновите пакеты
apt-get update && apt-get install -y \
    python3-pip \
    python3-dev \
    wget \
    git \
    libgomp1

# Установите ONNX Runtime с поддержкой CUDA
pip install onnxruntime-gpu

# Установите сопутствующие пакеты
pip install \
    onnx \
    numpy \
    Pillow \
    transformers \
    torch \
    torchvision \
    fastapi \
    uvicorn

# Проверка установки
python3 << 'EOF'
import onnxruntime as ort
print(f"ORT Version: {ort.__version__}")
print(f"Available providers: {ort.get_available_providers()}")
# Должны быть: CUDAExecutionProvider, TensorrtExecutionProvider, CPUExecutionProvider
EOF
```

***

## Шаг 4 — Экспортируйте вашу модель в ONNX

### Экспорт модели PyTorch

```python
import torch
import torch.nn as nn
import onnx

# Пример: экспорт ResNet50
model = torch.hub.load('pytorch/vision:v0.10.0', 'resnet50', pretrained=True)
model.eval()

# Создайте фиктивный ввод (batch=1, RGB изображение 224x224)
dummy_input = torch.randn(1, 3, 224, 224)

# Экспорт в ONNX
torch.onnx.export(
    model,
    dummy_input,
    "resnet50.onnx",
    export_params=True,
    opset_version=17,              # Используйте последний стабильный opset
    do_constant_folding=True,      # Оптимизация констант
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={
        "input": {0: "batch_size"},    # Динамический batch
        "output": {0: "batch_size"}
    }
)
print("Модель успешно экспортирована!")

# Проверьте экспортированную модель
onnx_model = onnx.load("resnet50.onnx")
onnx.checker.check_model(onnx_model)
print("ONNX-модель валидна!")
```

### Экспорт HuggingFace Transformers

```bash
# Установите optimum для экспорта HuggingFace в ONNX
pip install optimum[exporters]

# Экспорт BERT для классификации текста
optimum-cli export onnx \
    --model bert-base-uncased \
    --task text-classification \
    ./bert_onnx/

# Экспорт с оптимизацией
optimum-cli export onnx \
    --model microsoft/phi-2 \
    --task text-generation \
    --optimize O2 \
    ./phi2_onnx/
```

### Экспорт с оптимизацией ORT

```python
from optimum.onnxruntime import ORTModelForSequenceClassification
from optimum.onnxruntime.configuration import OptimizationConfig, ORTConfig
from optimum.onnxruntime import ORTOptimizer

# Загрузить и оптимизировать
model = ORTModelForSequenceClassification.from_pretrained(
    "distilbert-base-uncased-finetuned-sst-2-english",
    export=True
)

optimizer = ORTOptimizer.from_pretrained(model)
optimization_config = OptimizationConfig(
    optimization_level=2,
    optimize_for_gpu=True,
    fp16=True
)

optimizer.optimize(
    save_dir="./distilbert_optimized",
    optimization_config=optimization_config
)
```

***

## Шаг 5 — Запуск инференса с ONNX Runtime

### Базовый GPU-инференс

```python
import onnxruntime as ort
import numpy as np
from PIL import Image
import torchvision.transforms as transforms

# Настройка сессии с провайдерами выполнения для GPU
# Провайдеры пробуются в порядке — сначала CUDA, затем резерв на CPU
providers = [
    ("CUDAExecutionProvider", {
        "device_id": 0,
        "arena_extend_strategy": "kNextPowerOfTwo",
        "gpu_mem_limit": 4 * 1024 * 1024 * 1024,  # Ограничение 4 ГБ
        "cudnn_conv_algo_search": "EXHAUSTIVE",
        "do_copy_in_default_stream": True,
    }),
    "CPUExecutionProvider"
]

# Параметры сессии для производительности
opts = ort.SessionOptions()
opts.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
opts.intra_op_num_threads = 8
opts.execution_mode = ort.ExecutionMode.ORT_PARALLEL

# Загрузите модель
session = ort.InferenceSession(
    "resnet50.onnx",
    sess_options=opts,
    providers=providers
)

print(f"Запуск на: {session.get_providers()}")

# Подготовка ввода
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

img = Image.open("test_image.jpg").convert("RGB")
img_tensor = transform(img).unsqueeze(0).numpy()

# Запустить инференс
outputs = session.run(None, {"input": img_tensor})
probabilities = outputs[0][0]
top5_idx = probabilities.argsort()[-5:][::-1]
print("Топ-5 предсказаний:", top5_idx, probabilities[top5_idx])
```

### Пакетный инференс для пропускной способности

```python
import onnxruntime as ort
import numpy as np
import time

session = ort.InferenceSession(
    "resnet50.onnx",
    providers=["CUDAExecutionProvider"]
)

# Разогрев GPU
dummy = np.random.randn(1, 3, 224, 224).astype(np.float32)
for _ in range(10):
    session.run(None, {"input": dummy})

# Бенчмарк для размеров батчей
for batch_size in [1, 4, 8, 16, 32, 64]:
    inputs = np.random.randn(batch_size, 3, 224, 224).astype(np.float32)
    
    start = time.time()
    n_iter = 100
    for _ in range(n_iter):
        session.run(None, {"input": inputs})
    elapsed = time.time() - start
    
    throughput = (batch_size * n_iter) / elapsed
    latency = (elapsed / n_iter) * 1000  # мс
    
    print(f"Batch {batch_size:3d}: {throughput:7.1f} img/sec, {latency:.1f}ms/batch")
```

***

## Шаг 6 — Провайдер выполнения TensorRT (максимальная производительность)

Для GPU NVIDIA провайдер TensorRT обеспечивает ещё лучшую производительность:

```python
import onnxruntime as ort
import numpy as np

# Конфигурация провайдера выполнения TensorRT
tensorrt_provider_options = {
    "trt_max_workspace_size": 4 * 1024 * 1024 * 1024,  # 4 ГБ
    "trt_fp16_enable": True,          # Включить FP16 для более быстрого инференса
    "trt_int8_enable": False,
    "trt_engine_cache_enable": True,   # Кешировать скомпилированные движки
    "trt_engine_cache_path": "/tmp/trt_cache",
    "trt_max_partition_iterations": 1000,
    "trt_min_subgraph_size": 1,
    "trt_timing_cache_enable": True,
}

providers = [
    ("TensorrtExecutionProvider", tensorrt_provider_options),
    ("CUDAExecutionProvider", {"device_id": 0}),
    "CPUExecutionProvider"
]

session = ort.InferenceSession("resnet50.onnx", providers=providers)
print("Активный провайдер:", session.get_providers()[0])

# Первый запуск компилирует движок TensorRT (может занять 1–3 минуты)
# Последующие запуски используют кешированный движок и очень быстры
```

{% hint style="warning" %}
**Компиляция движка TensorRT** происходит при первом инференсе и может занять 1–5 минут. Включите кеширование (`trt_engine_cache_enable: True`), чтобы скомпилированный движок повторно использовался между сессиями.
{% endhint %}

***

## Шаг 7 — Квантизация до INT8 для максимальной скорости

```python
from onnxruntime.quantization import quantize_dynamic, quantize_static, QuantType
import onnxruntime as ort
import numpy as np

# Динамическая INT8-квантизация (не требуется калибровочных данных)
quantize_dynamic(
    model_input="resnet50.onnx",
    model_output="resnet50_int8_dynamic.onnx",
    weight_type=QuantType.QInt8
)

# Статическая INT8-квантизация (требует калибровочных данных)
from onnxruntime.quantization import CalibrationDataReader

class ImageCalibrationReader(CalibrationDataReader):
    def __init__(self, data_dir, input_name="input"):
        self.data_dir = data_dir
        self.input_name = input_name
        self.images = self._load_images()
        self.idx = 0
    
    def _load_images(self):
        # Загрузите 100 изображений для калибровки
        import glob, torchvision.transforms as T
        from PIL import Image
        transform = T.Compose([T.Resize(256), T.CenterCrop(224), T.ToTensor()])
        images = []
        for path in glob.glob(f"{self.data_dir}/*.jpg")[:100]:
            img = Image.open(path).convert("RGB")
            images.append(transform(img).numpy())
        return images
    
    def get_next(self):
        if self.idx >= len(self.images):
            return None
        data = {self.input_name: self.images[self.idx:self.idx+1]}
        self.idx += 1
        return data

from onnxruntime.quantization import quantize_static, QuantFormat
quantize_static(
    model_input="resnet50.onnx",
    model_output="resnet50_int8_static.onnx",
    calibration_data_reader=ImageCalibrationReader("/data/calibration_images"),
    quant_format=QuantFormat.QDQ,
    weight_type=QuantType.QInt8
)
```

***

## Шаг 8 — Постройте API инференса

```bash
cat > /workspace/onnx_api.py << 'EOF'
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse
import onnxruntime as ort
import numpy as np
from PIL import Image
import io
import torchvision.transforms as transforms
import json

app = FastAPI(title="ONNX Runtime Inference API")

# Загрузка модели при старте
session = ort.InferenceSession(
    "resnet50.onnx",
    providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
)

# Загрузите метки классов ImageNet
with open("imagenet_classes.json") as f:
    classes = json.load(f)

transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

@app.get("/health")
async def health():
    return {"status": "ok", "providers": session.get_providers()}

@app.post("/predict")
async def predict(file: UploadFile = File(...), topk: int = 5):
    image_data = await file.read()
    img = Image.open(io.BytesIO(image_data)).convert("RGB")
    tensor = transform(img).unsqueeze(0).numpy()
    
    outputs = session.run(None, {"input": tensor})[0][0]
    top_indices = outputs.argsort()[-topk:][::-1]
    
    results = [
        {"label": classes[str(i)], "score": float(outputs[i])}
        for i in top_indices
    ]
    return JSONResponse({"predictions": results})

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8080)
EOF

python3 /workspace/onnx_api.py &

# Протестировать API
curl -X POST "http://localhost:8080/predict" \
    -H "accept: application/json" \
    -F "file=@test_image.jpg"
```

***

## Шаг 9 — Мониторинг использования GPU

```bash
# Мониторинг GPU в реальном времени во время инференса
watch -n 0.5 nvidia-smi

# Или используйте nvitop для лучшего интерфейса
pip install nvitop
nvitop
```

***

## Тесты производительности

| Модель    | GPU      | Провайдер     | Пропускная способность (инференс/сек) |
| --------- | -------- | ------------- | ------------------------------------- |
| ResNet50  | RTX 4090 | CUDA          | \~4,200                               |
| ResNet50  | RTX 4090 | TensorRT FP16 | \~8,500                               |
| BERT Base | RTX 4090 | CUDA          | \~380                                 |
| BERT Base | RTX 4090 | TensorRT FP16 | \~720                                 |
| YOLOv8n   | RTX 3090 | CUDA          | \~1,800                               |
| YOLOv8x   | A100     | TensorRT FP16 | \~920                                 |

***

## Устранение неполадок

### Провайдер CUDA недоступен

```bash
# Проверьте, установлен ли CUDA ORT (не только версия для CPU)
pip uninstall onnxruntime
pip install onnxruntime-gpu

python3 -c "import onnxruntime as ort; print(ort.get_available_providers())"
```

### Ошибки компиляции TensorRT

```bash
# Проверьте совместимость версии TensorRT
python3 -c "import tensorrt; print(tensorrt.__version__)"

# Вместо этого используйте CUDA EP
providers = ["CUDAExecutionProvider"]  # Пропустить TensorRT EP
```

### Ошибки несоответствия формы

```python
# Проверьте формы входов/выходов модели
for input in session.get_inputs():
    print(f"Input: {input.name}, shape: {input.shape}, type: {input.type}")

for output in session.get_outputs():
    print(f"Output: {output.name}, shape: {output.shape}, type: {output.type}")
```

***

## Продвинуто: конвейер с несколькими моделями

```python
import onnxruntime as ort
import numpy as np

class MultiModelPipeline:
    def __init__(self):
        providers = ["CUDAExecutionProvider"]
        self.detector = ort.InferenceSession("detector.onnx", providers=providers)
        self.classifier = ort.InferenceSession("classifier.onnx", providers=providers)
    
    def run(self, image: np.ndarray) -> list:
        # Этап 1: обнаружение объектов
        boxes = self.detector.run(None, {"image": image})[0]
        
        results = []
        for box in boxes:
            # Обрезать обнаруженный регион
            crop = self._crop(image, box)
            
            # Этап 2: Классификация каждого региона
            label = self.classifier.run(None, {"input": crop})[0]
            results.append({"box": box.tolist(), "label": int(label.argmax())})
        
        return results
    
    def _crop(self, image, box):
        x1, y1, x2, y2 = box.astype(int)
        return image[:, :, y1:y2, x1:x2]

pipeline = MultiModelPipeline()
```

***

## Дополнительные ресурсы

* [GitHub ONNX Runtime](https://github.com/microsoft/onnxruntime)
* [Документация ONNX Runtime](https://onnxruntime.ai/docs/)
* [Hugging Face Optimum](https://huggingface.co/docs/optimum/)
* [ONNX Model Zoo](https://github.com/onnx/models) — Предварительно экспортированные модели
* [Netron](https://netron.app/) — Визуализатор ONNX-моделей
* [Python API ONNX Runtime](https://onnxruntime.ai/docs/api/python/)

***

*ONNX Runtime на Clore.ai — идеальный выбор для production-инференс-сервисов, которым нужно обслуживать модели из разных фреймворков с максимальной эффективностью использования GPU.*

***

## Рекомендации Clore.ai по GPU

| Сценарий использования           | Рекомендуемый GPU | Примерная стоимость на Clore.ai |
| -------------------------------- | ----------------- | ------------------------------- |
| Разработка/Тестирование          | RTX 3090 (24GB)   | \~$0.12/gpu/hr                  |
| Производственный инференс        | RTX 4090 (24GB)   | \~$0.70/gpu/hr                  |
| Развертывание в крупном масштабе | A100 80GB         | \~$1.20/gpu/hr                  |

> 💡 Все примеры в этом руководстве можно развернуть на [Clore.ai](https://clore.ai/marketplace) GPU-серверах. Просматривайте доступные GPU и арендуйте по часам — без обязательств, с полным root-доступом.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-ru/gpu-devops/onnx-runtime.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
