Ultralytics YOLO27: Inscreve-te na lista de espera.
Get Started

Como exportar modelos PyTorch que não são YOLO com o Ultralytics#

O Ultralytics inclui utilitários de exportação independentes em ultralytics.utils.export, que encapsulam vários backends em uma interface consistente. Você pode exportar qualquer torch.nn.Module, incluindo modelos de imagem timm, classificadores e detectores torchvision ou suas próprias arquiteturas personalizadas, para TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN e ExecuTorch sem precisar aprender a usar cada backend separadamente.

Implantar modelos PyTorch em produção geralmente significa lidar com um exportador diferente para cada destino: torch.onnx.export para ONNX, coremltools para dispositivos Apple, onnx2tf para TensorFlow, pnnx para NCNN e assim por diante. Cada ferramenta tem sua própria API, particularidades de dependências e convenções de saída. Esses utilitários reúnem tudo isso em um único padrão de chamada.

Por que usar o Ultralytics para exportar modelos que não são YOLO?#

  • Uma API para 11 formatos: aprenda uma única convenção de chamada em vez de uma dúzia.
  • O mesmo fluxo de código das exportações YOLO: os mesmos auxiliares são usados em todas as exportações YOLO do Ultralytics.
  • Quantização FP16 e INT8 por meio de um único argumento quantize para os formatos compatíveis.
  • Funciona na CPU: não é necessária uma GPU para a etapa de exportação, então você pode executá-la localmente em um laptop; a exportação CoreML não é compatível com Windows, e a exportação Core AI requer macOS 26 ou posterior em Apple silicon, ou Linux x86_64 com glibc 2.34 ou posterior, com Python 3.11 a 3.14.

Início rápido#

O caminho mais rápido é exportar para ONNX em duas linhas, sem código YOLO e sem configuração além de pip install ultralytics onnx timm:

import timm
import torch

from ultralytics.utils.export import torch2onnx

model = timm.create_model("resnet18", pretrained=True).eval()
torch2onnx(model, torch.randn(1, 3, 224, 224), output_file="resnet18.onnx")

Formatos de exportação compatíveis#

As funções torch2* recebem um torch.nn.Module padrão e um tensor de entrada de exemplo. MNN, TF SavedModel e TF Frozen Graph usam um artefato intermediário ONNX ou Keras. Em nenhum dos casos são necessários atributos específicos do YOLO.

FormatoFunçãoInstalaçãoSaída
TorchScripttorch2torchscript()incluído no PyTorcharquivo .torchscript
ONNXtorch2onnx()pip install onnxarquivo .onnx
OpenVINOtorch2openvino()pip install openvinodiretório _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
Core AItorch2coreai()pip install coreai-torch (Apple silicon, macOS 26+; Linux x86_64, glibc 2.34+; Python 3.11–3.14)diretório .aimodel
TF SavedModelonnx2saved_model()consulte os requisitos detalhados abaixodiretório _saved_model/
TF Frozen Graphkeras2pb()consulte os requisitos detalhados abaixoarquivo .pb
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddlediretório _paddle_model/
MNNonnx2mnn()pip install MNNarquivo .mnn
NCNNtorch2ncnn()pip install ncnn pnnxdiretório _ncnn_model/
ExecuTorchtorch2executorch()pip install executorchdiretório _executorch_model/
Incorporação de metadados

Várias funções de exportação aceitam um dicionário opcional metadata (por exemplo, torch2torchscript(..., metadata={"author": "me"})) que incorpora pares de chave-valor personalizados ao artefato exportado, quando o formato oferece suporte.

Exemplos passo a passo#

Todos os exemplos abaixo usam a mesma configuração: um ResNet-18 pré-treinado do timm no modo de avaliação:

import timm
import torch

model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)
Sempre chame `model.eval()` antes de exportar

Dropout, normalização em lote e outras camadas usadas apenas no treinamento se comportam de maneira diferente durante a inferência. Ignorar .eval() gera exportações com saídas incorretas.

Exportar para ONNX#

from ultralytics.utils.export import torch2onnx

torch2onnx(model, im, output_file="resnet18.onnx")

Para usar um tamanho de lote dinâmico, passe um dicionário dynamic:

torch2onnx(model, im, output_file="resnet18_dyn.onnx", dynamic={"images": {0: "batch_size"}})

O opset padrão é 14 e o nome de entrada padrão é "images". Substitua esses valores usando os argumentos opset, input_names ou output_names.

Exportar para TorchScript#

Não são necessárias dependências adicionais. Usa torch.jit.trace internamente.

from ultralytics.utils.export import torch2torchscript

torch2torchscript(model, im, output_file="resnet18.torchscript")

Exportar para OpenVINO#

from ultralytics.utils.export import torch2openvino

ov_model = torch2openvino(model, im, output_dir="resnet18_openvino_model")

O diretório contém um par de arquivos com nomes fixos: model.xml e model.bin:

resnet18_openvino_model/
├── model.xml
└── model.bin

Passe dynamic=True para formas de entrada dinâmicas, quantize=16 para FP16 ou quantize=8 para quantização INT8. A quantização INT8 também requer o argumento calibration_dataset.

Requer openvino>=2024.0.0 (ou >=2025.2.0 no macOS 15.4+) e torch>=2.1.

Exportar para CoreML#

import coremltools as ct

from ultralytics.utils.export import torch2coreml

inputs = [ct.TensorType("input", shape=(1, 3, 224, 224))]
ct_model = torch2coreml(model, inputs, im, classifier_names=None, output_file="resnet18.mlpackage")

Para modelos de classificação, passe uma lista de nomes de classes para classifier_names para adicionar uma cabeça de classificação ao modelo CoreML.

Requer coremltools>=9.0, torch>=1.11 e numpy<=2.3.5. Não é compatível com Windows.

Erro `BlobWriter not loaded`

coremltools>=9.0 disponibiliza pacotes wheel para Python 3.10–3.13 no macOS e no Linux. Em versões mais recentes do Python, a extensão C nativa não consegue carregar. Use Python 3.10–3.13 para exportar para CoreML.

Exportar para Core AI#

from ultralytics.utils.export import torch2coreai

torch2coreai(model, im, output_file="resnet18.aimodel")

O artefato .aimodel é um diretório:

resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.json

A exportação é executada no macOS 26 ou posterior em Apple silicon, ou no Linux x86_64 com glibc 2.34 ou posterior, com Python 3.11 a 3.14 (pip install coreai-torch), e quantize=16 grava um artefato FP16 que recebe entradas float16; o artefato é executado no iOS 27 e no macOS 27. Consulte a integração Core AI, incluindo a observação sobre artefatos FP16 que falham ao carregar.

Exportar para TensorFlow SavedModel#

A exportação TF SavedModel usa ONNX como etapa intermediária:

from ultralytics.utils.export import onnx2saved_model, torch2onnx

torch2onnx(model, im, output_file="resnet18.onnx")
keras_model = onnx2saved_model("resnet18.onnx", output_dir="resnet18_saved_model")

A função retorna um modelo Keras e também gera arquivos FP32 e FP16 do LiteRT (.tflite) dentro do diretório de saída:

resnet18_saved_model/
├── saved_model.pb
├── variables/
├── assets/
├── fingerprint.pb
├── resnet18_float32.tflite
└── resnet18_float16.tflite

Passe quantize=8 para adicionar um .tflite INT8 junto com eles.

A exportação TensorFlow não é executada no macOS com Python 3.13 ou posterior; use Python 3.12 ou anterior no macOS, ou Linux.

Requisitos para Python 3.12 ou anterior (no Python 3.13 ou posterior, a exportação requer tensorflow>2.19.0, tf_keras>2.19.0, onnx2tf>=2.3.0,<2.3.16 e protobuf>=6.31.1,<7.0.0):

  • tensorflow>=2.0.0,<=2.19.0
  • onnx2tf>=1.26.3,<1.29.0
  • tf_keras<=2.19.0
  • sng4onnx>=1.0.1
  • onnx_graphsurgeon>=0.3.26
  • ai-edge-litert>=1.2.0,<1.4.0 no macOS (ai-edge-litert>=1.2.0 em outras plataformas)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Exportar para TensorFlow Frozen Graph#

Dando continuidade à exportação SavedModel acima, converta o keras_model retornado em um grafo .pb congelado:

from pathlib import Path

from ultralytics.utils.export import keras2pb

keras2pb(keras_model, output_file=Path("resnet18_saved_model/resnet18.pb"))

Exportar para NCNN#

from ultralytics.utils.export import torch2ncnn

torch2ncnn(model, im, output_dir="resnet18_ncnn_model")

O diretório contém arquivos param e bin com nomes fixos, além de um wrapper Python:

resnet18_ncnn_model/
├── model.ncnn.param
├── model.ncnn.bin
└── model_ncnn.py

Exportar para MNN#

A exportação MNN requer um arquivo ONNX como entrada. Primeiro exporte para ONNX e, em seguida, converta:

from ultralytics.utils.export import onnx2mnn, torch2onnx

torch2onnx(model, im, output_file="resnet18.onnx")
onnx2mnn("resnet18.onnx", output_file="resnet18.mnn")

Oferece suporte a quantize=16 para quantização FP16 e a quantize=8 para quantização INT8. Requer MNN>=2.9.6 e torch>=1.10.

Exportar para PaddlePaddle#

from ultralytics.utils.export import torch2paddle

torch2paddle(model, im, output_dir="resnet18_paddle_model")

O diretório contém o modelo e os arquivos de parâmetros do PaddlePaddle:

resnet18_paddle_model/
├── inference_model/
│   ├── model.json
│   └── model.pdiparams
├── model.pdparams
└── x2paddle_code.py

Requer x2paddle e a distribuição correta do PaddlePaddle para a tua plataforma:

  • paddlepaddle-gpu>=3.0.0,<3.3.0 em CUDA
  • paddlepaddle==3.0.0 em CPU ARM64
  • paddlepaddle>=3.0.0,<3.3.0 em outras CPUs

Não é compatível com NVIDIA Jetson.

Exportar para ExecuTorch#

from ultralytics.utils.export import torch2executorch

torch2executorch(model, im, output_dir="resnet18_executorch_model")

O arquivo .pte exportado é salvo no diretório de saída:

resnet18_executorch_model/
└── model.pte

Requer torch>=2.9.0 e um runtime ExecuTorch compatível (pip install executorch). Para saber como usar o runtime, consulta a integração do ExecuTorch.

Verificar o modelo exportado#

Após exportar, verifica a paridade numérica com o modelo PyTorch original antes de colocar em produção. Um teste rápido com ONNXBackend de ultralytics.nn.backends compara as saídas e detecta erros de rastreamento ou quantização logo no início:

import numpy as np
import timm
import torch

from ultralytics.nn.backends import ONNXBackend

model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)
with torch.no_grad():
    pytorch_output = model(im).numpy()

onnx_model = ONNXBackend("resnet18.onnx", device=torch.device("cpu"))
onnx_output = onnx_model(im)[0]

diff = np.abs(pytorch_output - onnx_output).max()
print(f"Max difference: {diff:.6f}")  # ~1e-5 for an FP32 ONNX export
Diferença esperada

Na maioria das exportações de ResNet-18 em FP32, a diferença fica em torno de 1e-5 em relação ao PyTorch, e TorchScript corresponde exatamente. Ainda assim, três runtimes podem calcular uma exportação FP32 com precisão reduzida e apresentar diferenças próximas de 1e-2 a 1e-1: NCNN ativa operações aritméticas FP16 em CPUs compatíveis, MNNBackend carrega modelos com precision="low", e o plug-in de CPU do OpenVINO executa automaticamente em FP16 no modo de execução PERFORMANCE padrão em alguns hardwares, como os chips Apple silicon. Uma diferença muito acima da referência do próprio formato indica operações não compatíveis, uma forma de entrada incorreta ou um modelo que não está no modo de avaliação. As exportações FP16 e INT8 têm tolerâncias mais amplas. Valida com dados reais, não com tensores aleatórios.

Em outros runtimes, o nome do tensor de entrada pode ser diferente. O OpenVINO, por exemplo, usa o nome do argumento forward do modelo (normalmente x para modelos genéricos), enquanto torch2onnx usa "images" por padrão.

Executar o modelo exportado#

Modelos não YOLO exportados são carregados novamente pela API normal YOLO(). As exportações acima não incluem metadados de tarefa nem de tamanho de entrada da Ultralytics, portanto, informa task explicitamente e imgsz de acordo com o tensor de exemplo usado na exportação:

from ultralytics import YOLO

results = YOLO("resnet18.onnx", task="classify")("path/to/image.jpg", imgsz=224)
print(results[0].probs.top1)

imgsz é importante quando a exportação tem uma forma de entrada fixa: as exportações ONNX e TF SavedModel acima rejeitam o valor padrão de 640. As exportações TorchScript e NCNN acima aceitam outros tamanhos, mas nenhum dos exportadores garante isso: ambos rastreiam a partir do tensor de exemplo, então um modelo que é achatado em uma camada Linear permanece com tamanho fixo. Verifica a tua própria exportação.

O valor é então arredondado para cima até um múltiplo do stride do modelo, que é 32 sem metadados. Portanto, uma exportação com forma fixa de 200x200 recebe uma entrada de 224x224 e é rejeitada, embora imgsz=200 corresponda a ela. Para tamanhos de entrada que não sejam múltiplos de 32, chama o backend diretamente.

Chamar um backend diretamente#

Para tensores brutos sem o pré-processamento e o pós-processamento da Ultralytics, usa as classes específicas de cada formato em ultralytics.nn.backends, como no exemplo de verificação acima. Cada classe recebe o artefato exportado e um dispositivo, e pode ser chamada:

FormatoBackendLayout de entrada
TorchScriptTorchScriptBackendBCHW
ONNXONNXBackendBCHW
OpenVINOOpenVINOBackendBCHW
CoreMLCoreMLBackendBHWC
Core AICoreAIBackendBCHW
TF SavedModel, Frozen GraphTensorFlowBackendBHWC
LiteRTLiteRTBackendBCHW
PaddlePaddlePaddleBackendBCHW
MNNMNNBackendBCHW
NCNNNCNNBackendBCHW
ExecuTorchExecuTorchBackendBCHW

TensorFlowBackend abrange dois formatos e usa format="saved_model" por padrão; portanto, informa format="pb" para um grafo congelado.

Três tarefas que a rota YOLO() executa para ti, mas uma chamada direta não:

  • Layout de entrada: CoreMLBackend e TensorFlowBackend esperam BHWC. Primeiro, transpõe com im.permute(0, 2, 3, 1); um tensor BCHW causa um erro de incompatibilidade de forma.
  • Autograd: envolve as chamadas em torch.inference_mode(). TorchScriptBackend retorna um tensor que ainda contém um grafo de gradiente.
  • Pós-processamento: sem metadados, um backend mantém task como None e names vazio. LiteRTBackend ainda desfaz a normalização de qualquer saída 3D pelo tamanho da imagem, supondo que ela contenha caixas YOLO, o que está errado para um modelo não YOLO com saída 3D. Saídas bidimensionais, como os logits de classificadores, não são afetadas.

Limitações conhecidas#

  • O suporte a várias entradas é desigual: torch2onnx, torch2openvino e torch2torchscript aceitam uma tupla de tensores de exemplo para modelos com várias entradas. torch2coreml, torch2coreai, torch2ncnn, torch2paddle e torch2executorch pressupõem um único tensor de entrada.
  • Formatos exclusivos para YOLO: as exportações Axelera e Sony IMX500 exigem atributos específicos de modelos YOLO e não estão disponíveis para modelos genéricos.
  • Formatos específicos de plataforma: TensorRT requer uma GPU NVIDIA. RKNN requer o SDK rknn-toolkit2 (somente Linux). Edge TPU requer o binário edgetpu_compiler (somente Linux x86_64).

Conclusão#

Esses utilitários convertem qualquer modelo PyTorch, desde um torch.nn.Module simples até um artefato ONNX, OpenVINO, CoreML, TensorFlow ou de runtime móvel pronto para implantação, por meio de uma única API consistente. Escolhe o formato compatível com o hardware de destino, verifica a paridade numérica em relação ao modelo original e segue o guia de integração correspondente para ver as etapas de implantação específicas do runtime.

Perguntas frequentes#

  • Qualquer torch.nn.Module. Isso inclui modelos do timm, torchvision ou qualquer modelo PyTorch personalizado. O modelo precisa estar no modo de avaliação (model.eval()) antes da exportação. ONNX, OpenVINO e TorchScript também aceitam uma tupla de tensores de exemplo para modelos com várias entradas.

  • Todos os formatos compatíveis (TorchScript, ONNX, OpenVINO, CoreML, Core AI, TF SavedModel, TF Frozen Graph, PaddlePaddle, MNN, NCNN, ExecuTorch) podem ser exportados em CPU. O processo de exportação em si não requer GPU. TensorRT é o único formato que requer uma GPU NVIDIA.

  • Usa a versão mais recente. O argumento quantize requer a Ultralytics >=8.4.81, e a exportação Core AI requer >=8.4.131 (>=8.4.163 no Linux ou com coreai-torch>=0.4.3).

  • Sim. Classificadores, detectores e modelos de segmentação do torchvision são exportados para .mlpackage por meio de torch2coreml. Para modelos de classificação de imagens, passa uma lista de nomes de classes para classifier_names para incorporar uma cabeça de classificação. Executa a exportação no macOS ou Linux. CoreML não é compatível com Windows. Consulta a integração do CoreML para ver detalhes sobre a implantação no iOS.

  • Sim, em vários formatos. Passa quantize=16 para FP16 ou quantize=8 para INT8 ao exportar para OpenVINO, CoreML ou MNN; onnx2saved_model recebe quantize=8 para um arquivo LiteRT INT8, e NCNN e Core AI exportam em FP32 por padrão, aceitam quantize=16 para FP16 e não oferecem suporte a INT8. INT8 no OpenVINO também requer o argumento calibration_dataset para a quantização pós-treinamento. Consulta a página de integração de cada formato para conhecer as compensações da quantização.

  • Executa o modelo PyTorch original e o modelo exportado com a mesma entrada e, em seguida, compara as saídas. Carrega o arquivo exportado com o backend correspondente (por exemplo, ONNXBackend para ONNX) e verifica a diferença absoluta máxima. Avalia a diferença em relação à referência do próprio formato: NCNN, MNNBackend e OpenVINO em algumas CPUs podem executar exportações FP32 com precisão reduzida e apresentar diferenças próximas de 1e-2 a 1e-1, enquanto a maioria dos outros formatos fica perto de 1e-5. Uma diferença muito maior indica operações não compatíveis, uma forma de entrada incorreta ou um modelo que não está no modo de avaliação. Consulta Verificar o modelo exportado para ver um exemplo executável.

Comentários