O que é o ONNX Runtime

O ONNX Runtime e um motor de inferência de alta performance desenvolvido pela Microsoft, projetado para executar modelos de machine learning com velocidade máxima em qualquer plataforma. "ONNX" significa Open Neural Network Exchange, um formato aberto que permite exportar modelos de frameworks como PyTorch, TensorFlow e scikit-learn para um formato padronizado e eficiente.

Lançado em 2018 como projeto open source, o ONNX Runtime se tornou um dos pilares da infraestrutura de IA em produção no mundo inteiro. Empresas como Microsoft, NVIDIA, Intel e Qualcomm contribuem ativamente com o projeto, o que garante otimizações específicas para os principais hardwares do mercado.

Para desenvolvedores brasileiros que estão levando modelos de IA para produção, o ONNX Runtime resolve um problema clássico: o gap de performance entre treinar um modelo no notebook e roda-lo em produção com latência aceitável. E possível ganhar de 2x a 10x de velocidade em relação a inferência nativa do framework original.

💡
Dica

O formato ONNX atua como uma camada de tradução universal: você treina no PyTorch, exporta para .onnx e executa com o ONNX Runtime em qualquer linguagem ou hardware. Não precisa mais "reescrever o modelo" para produção.

Como funciona o ONNX Runtime

O ONNX Runtime funciona como um executor de grafos computacionais. Quando você exporta um modelo para o formato ONNX, o resultado e um arquivo que descreve as operações matemáticas do modelo (convoluções, multiplicações de matriz, ativações) de forma independente de qualquer framework.

Na hora da inferência, o ONNX Runtime carrega esse grafo e aplica otimizações automáticas: fusão de operadores (combinar múltiplas operações em uma só), eliminação de operações redundantes, e quantização (converter pesos de float32 para int8 para ganhar velocidade e reduzir memoria). Essas otimizações são feitas estaticamente antes da primeira inferência, o que significa que a execução e muito eficiente.

O sistema de Execution Providers e o que torna o ONNX Runtime versátil. Cada provider e um backend otimizado para um hardware específico: CPUExecutionProvider para CPU genérica, CUDAExecutionProvider para GPUs NVIDIA, TensorRTExecutionProvider para NVIDIA com TensorRT, CoreMLExecutionProvider para Apple Silicon, e assim por diante. Você escolhe o provider e o runtime cuida das otimizações específicas daquele hardware.

⚠️
Atenção

Nem toda operação do PyTorch tem suporte nativo no ONNX. Ao exportar modelos com operações customizadas ou ops experimentais, você pode receber erros de conversão. Teste sempre a exportação com dados de exemplo antes de ir para produção.

Principais recursos do ONNX Runtime

O ONNX Runtime e uma ferramenta rica em funcionalidades que atendem desde prototipagem até produção em escala:

  • Cross-platform nativo: roda em Windows, Linux, macOS, Android e iOS. O mesmo código de inferência funciona em todas as plataformas com performance otimizada para cada uma.
  • Quantização automática: converte modelos float32 para int8 ou float16, reduzindo o tamanho do modelo em até 4x e acelerando a inferência em CPUs e hardware especializado.
  • APIs em múltiplas linguagens: Python, C++, C#, Java, JavaScript (Node.js e browser), Go e Objective-C/Swift. Útil para integrar em qualquer stack.
  • Perfil de performance: ferramenta built-in para identificar quais operações do modelo são gargalo e quanto tempo cada uma leva.
  • Suporte a LLMs: desde 2023, o ONNX Runtime tem suporte específico para modelos de linguagem grandes (LLMs), com otimizações para geração de texto e atenção.

Para projetos de ML em produção, o diferencial mais importante e a estabilidade: o ONNX Runtime e mantido por dezenas de empresas e tem SLA de compatibilidade entre versões, o que não e garantido pelos frameworks de treinamento.

🚀
Pro tip

Use o ONNX Runtime com TensorRT provider em GPUs NVIDIA para inferência de visão computacional. O ganho pode chegar a 5-8x em relação ao PyTorch puro, especialmente em modelos de classificação e detecção de objetos.

Como começar: instalação e primeiro modelo

A forma mais rápida de começar e via pip. O pacote básico funciona em CPU, e versões com GPU requerem instalação específica:

# Instalação básica (CPU)
pip install onnxruntime

# Versão com suporte a GPU NVIDIA (CUDA 12.x)
pip install onnxruntime-gpu

# Para exportar modelos PyTorch para ONNX
pip install torch onnx

Para exportar um modelo PyTorch existente:

import torch
import onnx

# Carregue seu modelo treinado
model = MeuModeloPyTorch()
model.load_state_dict(torch.load("modelo.pth"))
model.eval()

# Crie um tensor de exemplo com o formato correto
dummy_input = torch.randn(1, 3, 224, 224)  # batch, canais, altura, largura

# Exporte para ONNX
torch.onnx.export(
    model,
    dummy_input,
    "modelo.onnx",
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={"input": {0: "batch_size"}}  # permite batch variável
)
print("Modelo exportado com sucesso!")

Depois de exportar, a inferência fica assim:

import onnxruntime as ort
import numpy as np

# Carrega o modelo
session = ort.InferenceSession("modelo.onnx")

# Prepara o input (numpy array)
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)

# Inferência
outputs = session.run(
    None,  # None = retorna todos os outputs
    {"input": input_data}
)
print(f"Output shape: {outputs[0].shape}")

Exemplo prático: modelo de classificação de imagens

Vamos usar o ONNX Runtime para rodar um modelo ResNet50 pre-treinado para classificar imagens. Esse e um cenário real de produção onde o ganho de performance e significativo.

import onnxruntime as ort
import numpy as np
from PIL import Image

def preprocessar_imagem(caminho: str) -> np.ndarray:
    """Prepara a imagem para o ResNet50"""
    img = Image.open(caminho).convert("RGB")
    img = img.resize((224, 224))
    arr = np.array(img).astype(np.float32) / 255.0
    # Normalização ImageNet
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    arr = (arr - mean) / std
    # NCHW: batch, canais, altura, largura
    return arr.transpose(2, 0, 1)[np.newaxis, ...]

# Carrega o modelo (baixe resnet50-v2-7.onnx do repositório ONNX Model Zoo)
sessão = ort.InferenceSession(
    "resnet50-v2-7.onnx",
    providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
)

entrada = preprocessar_imagem("minha_foto.jpg")
resultado = sessão.run(None, {"data": entrada})

# O resultado e um array com probabilidades para 1000 classes
top5 = np.argsort(resultado[0][0])[-5:][::-1]
print(f"Top 5 classes: {top5}")

Nesse exemplo, o ONNX Runtime seleciona automaticamente a GPU NVIDIA se disponível, com fallback para CPU. A latência típica para uma imagem 224x224 fica abaixo de 5ms em GPU moderna, contra 15-20ms com PyTorch puro no mesmo hardware.

Comparação com alternativas

O ONNX Runtime não e a única opcao para inferência em produção. Veja como ele se compara:

  • TensorFlow Serving: solução robusta para modelos TensorFlow, com suporte nativo a HTTP/gRPC. Mais complexo de configurar, mas excelente para APIs de ML em produção. O ONNX Runtime e mais versátil em termos de frameworks suportados.
  • TorchServe: equivalente ao TF Serving para PyTorch. Bom para quem já esta no ecossistema PyTorch e não quer mudar de formato. O ONNX Runtime oferece mais portabilidade.
  • OpenVINO (Intel): otimizado especificamente para hardware Intel (CPUs, GPUs Intel, VPUs). Para inferência em borda em hardware Intel, pode superar o ONNX Runtime. Para cenários heterogéneos, o ONNX Runtime ganha em flexibilidade.
  • TensorRT (NVIDIA): máximo de performance para GPUs NVIDIA, mas só roda em NVIDIA. O ONNX Runtime pode usar o TensorRT como backend via ExecutionProvider, dando o melhor dos dois mundos.

Pontos positivos e limitações

Vantagens claras:

  • Open source com manutenção ativa por grandes empresas
  • Funciona em CPU, GPU, NPU e hardware de borda sem mudar o código
  • Performance consistentemente melhor que inferência nativa na maioria dos casos
  • API simples e bem documentada em múltiplas linguagens
  • Suporte a modelos grandes (LLMs, Vision Transformers)

Limitações reais:

  • Conversão ONNX pode falhar em modelos com operações customizadas do PyTorch
  • Modelos com estrutura dinâmica (grafos condicionais, tamanhos de input variados) tem suporte parcial
  • Debugging de erros de conversão pode ser trabalhoso sem familiaridade com o formato ONNX
  • Para LLMs muito grandes, o gerenciamento de memoria pode ser desafiador comparado a soluções especializadas
🔴
Cuidado

Ao quantizar modelos para int8, a precisão pode cair em alguns casos. Sempre compare a acuracia do modelo quantizado com o original antes de ir para produção usando um dataset de validação representativo.

Casos de uso reais

O ONNX Runtime e usado em cenários muito diferentes, dependendo do perfil da equipe:

  • Startup de visão computacional: você treina o modelo no PyTorch, exporta para ONNX e roda em uma API Flask ou FastAPI com o ONNX Runtime. Sem precisar manter um servidor TF Serving separado, e a latência fica abaixo do threshold de 100ms do SLA.
  • App mobile com IA: modelos exportados para ONNX rodam direto no dispositivo via ONNX Runtime Mobile, sem dependência de API externa. Fundamental para apps que precisam funcionar offline ou com latência mínima.
  • Pipeline de MLOps: times que usam vários frameworks (parte da equipe usa PyTorch, parte usa scikit-learn) podem padronizar a inferência em produção com o ONNX Runtime, independente do framework de treino.
  • Edge computing e IoT: dispositivos ARM (Raspberry Pi, Jetson Nano) rodam o ONNX Runtime com otimizações para NÉON/SIMD, permitindo inferência local sem GPU dedicada.

Dicas e boas práticas

💡
Dica

Sempre use a opca de dynamic_axes ao exportar do PyTorch. Isso permite variar o tamanho do batch e das dimensões de input sem precisar reexportar o modelo para cada configuração.

💡
Dica

Ative o paralelismo intra-op e inter-op no ONNX Runtime para CPUs com vários núcleos: sess_options.intra_op_num_threads = 4 e sess_options.inter_op_num_threads = 2. O impacto e significativo em inferência de batches maiores.

🚀
Pro tip

Use o OrtValue para manter tensores na GPU entre inferências consecutivas, evitando o overhead de copiar dados entre CPU e GPU a cada chamada. Isso pode reduzir a latência em até 30% em pipelines de processamento em streaming.

⚠️
Atenção

O ONNX Runtime versão GPU e CPU são pacotes separados no pip (onnxruntime e onnxruntime-gpu). Instalar os dois ao mesmo tempo pode causar conflito. Remova um antes de instalar o outro.

Vale a pena usar o ONNX Runtime?

Para qualquer projeto que já esta treinando modelos de ML e precisa ir para produção, a resposta e quase sempre sim. O ganho de performance e real e mensurável, a migração e baixo risco (você continua treinando no framework que já usa), e a comunidade ativa garante suporte para os principais hardwares e casos de uso.

Se você esta começar um projeto novo de inferência em produção, o ONNX Runtime deve ser o padrão. A única exceção e se você esta 100% preso no ecossistema TensorFlow e tem necessidades específicas de serving como model versioning e A/B testing, onde o TensorFlow Serving tem vantagem.

O próximo passo e baixar o ONNX Model Zoo (GitHub.com/onnx/models) e experimentar modelos pre-convertidos. Em menos de 30 minutos você tem um classificador de imagens rodando em produção com latência de milissegundos.