Оптимизация инференса LLM через ONNX Runtime

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
Оптимизация инференса LLM через ONNX Runtime
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1218
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    853
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1047
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    825

Оптимизация инференса LLM с помощью ONNX Runtime

ONNX Runtime (ORT) — универсальный инференс-движок от Microsoft, работающий на CPU, GPU и специализированных ускорителях (DirectML, TensorRT EP, ROCm). Оптимальный выбор для сред без NVIDIA GPU или при необходимости кросс-платформенного деплоя.

Когда ONNX Runtime — правильный выбор

  • CPU inference: для малых и средних моделей (≤ 7B) без GPU. ORT значительно быстрее HF transformers на CPU через AVX512 оптимизации
  • Edge и embedded: ARM устройства, Azure IoT, Windows on ARM
  • Mixed cloud: деплой на разных облачных провайдерах с разными GPU
  • Compliance: сред без NVIDIA GPU (AMD, Intel GPU, CPU-only)
  • Маленькие энкодер-модели: BERT, RoBERTa для классификации и NER — ORT даёт 2–4x ускорение

Конвертация модели в ONNX

from transformers import AutoTokenizer, AutoModelForSequenceClassification
from optimum.exporters.onnx import main_export

# Экспорт через Optimum (рекомендуется)
main_export(
    model_name_or_path="cardiffnlp/twitter-roberta-base-sentiment",
    output="./onnx_model/",
    task="text-classification",
    opset=17,
    device="cuda",    # экспорт с GPU для лучшей оптимизации
    fp16=True         # половинная точность
)
# Альтернатива: torch.onnx.export для кастомных моделей
import torch

model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-Instruct-v0.3")
dummy_input = {
    "input_ids": torch.ones(1, 128, dtype=torch.long),
    "attention_mask": torch.ones(1, 128, dtype=torch.long),
}

torch.onnx.export(
    model,
    dummy_input,
    "model.onnx",
    opset_version=17,
    input_names=["input_ids", "attention_mask"],
    output_names=["logits"],
    dynamic_axes={
        "input_ids": {0: "batch", 1: "seq_len"},
        "attention_mask": {0: "batch", 1: "seq_len"},
    }
)

INT8 Quantization для CPU

from onnxruntime.quantization import quantize_dynamic, QuantType

# Dynamic quantization — самый простой способ, не требует calibration data
quantize_dynamic(
    model_input="model.onnx",
    model_output="model_int8.onnx",
    weight_type=QuantType.QInt8,
    per_channel=True,
    reduce_range=True    # для старых Intel CPU
)

Результат на Intel Xeon: BERT-base inference 8ms → 3ms при <1% деградации accuracy на GLUE.

Static INT8 с calibration

from onnxruntime.quantization import quantize_static, CalibrationDataReader

class SentimentCalibrationDataReader(CalibrationDataReader):
    def __init__(self, calibration_texts: list[str], tokenizer):
        self.tokenizer = tokenizer
        self.data = iter(calibration_texts)

    def get_next(self) -> dict | None:
        text = next(self.data, None)
        if text is None:
            return None
        inputs = self.tokenizer(text, return_tensors="np", padding="max_length",
                                max_length=128, truncation=True)
        return dict(inputs)

calibration_reader = SentimentCalibrationDataReader(
    calibration_texts=load_calibration_data(),  # 100-500 representativesamples
    tokenizer=tokenizer
)

quantize_static(
    model_input="model.onnx",
    model_output="model_int8_static.onnx",
    calibration_data_reader=calibration_reader,
    quant_format=QuantFormat.QDQ,
    per_channel=True
)

Оптимизированный инференс

import onnxruntime as ort
import numpy as np

# Конфигурация сессии
session_options = ort.SessionOptions()
session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
session_options.intra_op_num_threads = 8       # параллелизм внутри оператора
session_options.inter_op_num_threads = 2       # параллелизм между операторами
session_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL

# Провайдеры: порядок важен (используется первый доступный)
providers = [
    ("CUDAExecutionProvider", {
        "device_id": 0,
        "arena_extend_strategy": "kNextPowerOfTwo",
        "gpu_mem_limit": 4 * 1024 ** 3,  # 4 GB
        "cudnn_conv_algo_search": "EXHAUSTIVE",
    }),
    "CPUExecutionProvider"
]

session = ort.InferenceSession(
    "model_int8.onnx",
    sess_options=session_options,
    providers=providers
)

def predict_batch(texts: list[str]) -> list[dict]:
    inputs = tokenizer(
        texts, padding=True, truncation=True,
        max_length=128, return_tensors="np"
    )
    outputs = session.run(None, dict(inputs))
    logits = outputs[0]
    probs = softmax(logits, axis=1)
    return [
        {"label": LABELS[np.argmax(p)], "score": float(np.max(p))}
        for p in probs
    ]

ONNX Runtime для LLM: onnxruntime-genai

Для генеративных моделей Microsoft разработал специализированный пакет:

import onnxruntime_genai as og

# Загрузка предоптимизированной модели (phi-3, llama, mistral)
model = og.Model("./phi3-mini-onnx/")
tokenizer = og.Tokenizer(model)

params = og.GeneratorParams(model)
params.set_search_options(max_length=200, temperature=0.7)
params.input_ids = tokenizer.encode("Explain ONNX Runtime")

generator = og.Generator(model, params)
while not generator.is_done():
    generator.compute_logits()
    generator.generate_next_token()
    token = generator.get_next_tokens()[0]
    print(tokenizer.decode([token]), end="", flush=True)

Производительность на CPU

На Intel Xeon Platinum 8375C (32 cores), Phi-3-mini (3.8B), batch=8:

Конфигурация Throughput
HF transformers (FP32) 12 tok/s
ORT (FP32) 28 tok/s
ORT (INT8 dynamic) 67 tok/s
ORT (INT4) 124 tok/s