Оптимізація інференсу нейромереж: конвертація у TensorRT на NVIDIA GPU

Конвертація моделей у TensorRT

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1439
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    997
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1264
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    712
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1002

Конвертація моделей у TensorRT

При інференсі BERT-base на T4 з batch=8 затримка в PyTorch FP32 становить ~12ms — для real-time систем це забагато. TensorRT знижує latency до 2.9ms у FP16 та 1.8ms у INT8. Розбираємося, як цього досягти на практиці.

Ми спеціалізуємося на конвертації моделей будь-якої складності: від BERT до LLaMA. Виконали 20+ проєктів. Гарантуємо сумісність і прискорення. Наші інженери сертифіковані NVIDIA. Замовте конвертацію — ми підготуємо оптимізований engine за 3–10 днів. Зв'яжіться з нами для оцінки вашого проєкту.

Як TensorRT прискорює інференс?

TensorRT оптимізує граф обчислень: ф'юзінг шарів, видалення dead-операцій, вибір оптимальних kernel-реалізацій під конкретну GPU. Додатково застосовує precision scaling (FP16, INT8) з мінімальною втратою точності. Результат — до 8x прискорення порівняно з FP32 PyTorch. NVIDIA TensorRT Documentation підтверджує ці показники.

Конвертація з ONNX у TensorRT

import tensorrt as trt TRT_LOGGER = trt.Logger(trt.Logger.WARNING) def build_engine(onnx_path: str, engine_path: str, fp16: bool = True, int8: bool = False): builder = trt.Builder(TRT_LOGGER) network = builder.create_network( 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) ) parser = trt.OnnxParser(network, TRT_LOGGER) with open(onnx_path, "rb") as f: if not parser.parse(f.read()): for i in range(parser.num_errors): print(f"ONNX parse error: {parser.get_error(i)}") raise RuntimeError("Failed to parse ONNX") config = builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 4 * 1024 ** 3) # 4 GB if fp16 and builder.platform_has_fast_fp16: config.set_flag(trt.BuilderFlag.FP16) if int8: config.set_flag(trt.BuilderFlag.INT8) # Потрібен calibrator для INT8 config.int8_calibrator = MyCalibrator(calibration_data) # Dynamic shapes — важливо для змінної довжини входу profile = builder.create_optimization_profile() profile.set_shape( "input_ids", min=(1, 1), opt=(8, 128), max=(32, 512) ) config.add_optimization_profile(profile) serialized_engine = builder.build_serialized_network(network, config) with open(engine_path, "wb") as f: f.write(serialized_engine) print(f"Engine saved to {engine_path}") 

Інференс з TensorRT

import tensorrt as trt import numpy as np import pycuda.driver as cuda import pycuda.autoinit class TRTInferenceSession: def __init__(self, engine_path: str): runtime = trt.Runtime(TRT_LOGGER) with open(engine_path, "rb") as f: self.engine = runtime.deserialize_cuda_engine(f.read()) self.context = self.engine.create_execution_context() self.inputs = [] self.outputs = [] self.bindings = [] for binding in self.engine: shape = self.engine.get_tensor_shape(binding) size = trt.volume(shape) * np.dtype(np.float32).itemsize device_mem = cuda.mem_alloc(size) self.bindings.append(int(device_mem)) if self.engine.get_tensor_mode(binding) == trt.TensorIOMode.INPUT: self.inputs.append({"name": binding, "mem": device_mem, "shape": shape}) else: self.outputs.append({"name": binding, "mem": device_mem, "shape": shape}) self.stream = cuda.Stream() def infer(self, inputs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: for inp in self.inputs: data = inputs[inp["name"]].astype(np.float32).ravel() cuda.memcpy_htod_async(inp["mem"], data, self.stream) self.context.execute_async_v2(self.bindings, self.stream.handle) results = {} for out in self.outputs: output = np.empty(out["shape"], dtype=np.float32) cuda.memcpy_dtoh_async(output, out["mem"], self.stream) results[out["name"]] = output self.stream.synchronize() return results 

INT8 Calibration

INT8 потребує калібрувальних даних для визначення діапазонів значень:

class BertCalibrator(trt.IInt8EntropyCalibrator2): def __init__(self, calibration_texts: list[str], cache_file: str = "calibration.cache"): super().__init__() self.tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased") self.cache_file = cache_file self.data = iter( self.tokenizer(calibration_texts, padding="max_length", truncation=True, max_length=128, return_tensors="np") ) self.device_input = cuda.mem_alloc(128 * 4) # input_ids buffer def get_batch_size(self) -> int: return 16 def get_batch(self, names: list[str]) -> list | None: try: batch = {k: next(self.data) for k in ["input_ids", "attention_mask"]} cuda.memcpy_htod(self.device_input, batch["input_ids"].astype(np.int32).ravel()) return [int(self.device_input)] except StopIteration: return None def read_calibration_cache(self) -> bytes | None: if os.path.exists(self.cache_file): with open(self.cache_file, "rb") as f: return f.read() return None def write_calibration_cache(self, cache: bytes) -> None: with open(self.cache_file, "wb") as f: f.write(cache) 

Використання torch-tensorrt

Більш простий шлях для PyTorch моделей:

import torch_tensorrt trt_model = torch_tensorrt.compile( model, inputs=[ torch_tensorrt.Input( min_shape=[1, 1], opt_shape=[8, 128], max_shape=[32, 512], dtype=torch.int32 ) ], enabled_precisions={torch.float16}, workspace_size=4 * 1024 ** 3, truncate_long_and_double=True ) torch.jit.save(trt_model, "bert_trt.ts") 

Типовий приріст продуктивності

На T4 GPU, BERT-base, batch=8, seq=128:

Режим Latency Speedup
PyTorch FP32 12.3ms 1x
PyTorch FP16 6.8ms 1.8x
TensorRT FP16 2.9ms 4.2x
TensorRT INT8 1.8ms 6.8x

Чому варто використовувати INT8 калібрування?

INT8 дає максимальний приріст швидкості (6-8x), але вимагає акуратного калібрування. Без нього точність може впасти на 2-5%. Ми використовуємо Entropy Calibrator 2, який мінімізує втрати. Якщо точність критична, залишаємо FP16 — він безпечніший і дає 4x прискорення. TensorRT INT8 також економить GPU-години: задача, що виконувалася годину на PyTorch FP32, з INT8 вирішується за 9 хвилин.

Типові помилки при конвертації

Проблема Причина Рішення
Помилка парсингу ONNX Непідтримувана операція (e.g., aten::view) Заміна на підтримувану або використання torch.onnx.export з opset_version=18
Out of memory при білді Нестача workspace Збільшити workspace_size до 8-16 GB
Дроп точності INT8 > 5% Непідходящий калібрувальний датасет Використовувати репрезентативні дані, збільшити розмір датасету до 1000+ семплів
Dynamic shapes не працюють Неправильний optimization profile Перевірити min/opt/max, переконатися, що вхідні тензори позначені як dynamic
Просадка продуктивності на small batches Домінування overhead Використовувати фіксований batch size для real-time випадків

Етапи конвертації: покроковий how-to

  1. Експорт моделі в ONNX: Використовуйте torch.onnx.export з dynamic_axes для трансформерів.
  2. Перевірка сумісності: Прогнати ONNX через onnx.checker та onnxsim.
  3. Збір калібрувального датасету (для INT8): 500–2000 семплів з вашої предметної області.
  4. Побудова engine: Вибір precision, налаштування dynamic shapes, запуск білдера.
  5. Валідація: Порівняти виходи TensorRT та оригінальної моделі на тестових даних.
  6. Інтеграція: Адаптувати пайплайн під TensorRT runtime (Python або C++).
  7. Профілювання: Виміряти latency p99, перевірити стабільність.

Що входить в роботу

Результат конвертації — не просто engine, а повний пакет:

  • Оптимізований TensorRT engine у форматі .plan.
  • Код інференс-сесії на Python або C++ з прикладами.
  • Калібрувальний датасет і скрипти калібрування (при INT8).
  • Інтеграція у ваш пайплайн: контейнеризація, CI/CD.
  • Документація з описом конфігурацій та рекомендацій щодо масштабування.
  • Підтримка після інтеграції — консультації з профілювання та тюнінгу.

Як ми працюємо

  • Аналіз: Вивчаємо модель, виявляємо вузькі місця (наприклад, неефективні view-операції).
  • Експорт: Конвертуємо в ONNX, усуваємо помилки сумісності.
  • Калібрування: Підбираємо калібрувальні дані, налаштовуємо Entropy Calibrator 2.
  • Генерація engine: Збираємо TensorRT з dynamic shapes та обраною точністю.
  • Інтеграція: Вбудовуємо engine у ваш інференс-пайплайн (Python/C++).
  • Документація: Передаємо конфіги, код, інструкції.

Термін виконання — від 3 до 10 днів залежно від складності моделі. Вартість розраховується індивідуально. Замовте конвертацію — ми підготуємо оптимізований engine за 3–10 днів.

Наш досвід

Багаторічний досвід в AI/ML, 20+ проєктів з оптимізації інференсу. Працювали з моделями BERT, GPT-2/3, LLaMA, YOLOv8, ResNet. Гарантуємо прискорення до заявлених значень. Звертайтеся за консультацією — оцінимо ваш проєкт.