Ultralytics YOLO27:

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

A Ultralytics disponibiliza utilitários de exportação independentes em ultralytics.utils.export que encapsulam vários backends numa interface consistente. Podes exportar qualquer torch.nn.Module, incluindo modelos de imagem timm, classificadores e detetores torchvision ou as tuas próprias arquiteturas personalizadas, para TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN e ExecuTorch, sem precisares de aprender a usar cada backend separadamente.

Implementar modelos PyTorch em produção costuma implicar lidar com um exportador diferente para cada destino: torch.onnx.export para ONNX, coremltools para dispositivos Apple, onnx2tf para TensorFlow, pnnx para NCNN, entre outros. Cada ferramenta tem a sua própria API, dependências com particularidades e convenções de saída. Estes utilitários reúnem tudo num único padrão de chamada.

Porquê usar a Ultralytics para exportar modelos que não são YOLO?#

  • Uma API para 11 formatos: aprende uma única convenção de chamada em vez de uma dúzia.
  • Mesmo fluxo de código das exportações YOLO: os mesmos auxiliares são usados em todas as exportações YOLO da Ultralytics.
  • Quantização FP16 e INT8 por meio de um único argumento quantize para formatos compatíveis.
  • Funciona na CPU: não é necessária uma GPU para a própria etapa de exportação, então você pode executá-la localmente em um laptop; a exportação para CoreML não é compatível com Windows, e a exportação para 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](https://ultralytics-translation-0.invalid em duas linhas, sem código YOLO nem 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 artefacto ONNX ou Keras intermédio. Em nenhum dos casos são necessários atributos específicos de YOLO.

FormatoFunçãoInstalaçãoResultado
TorchScripttorch2torchscript()incluído no PyTorchficheiro .torchscript
ONNXtorch2onnx()pip install onnxficheiro .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()consulta os requisitos detalhados abaixodiretório _saved_model/
TF Frozen Graphkeras2pb()consulta os requisitos detalhados abaixoficheiro .pb
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddlediretório _paddle_model/
MNNonnx2mnn()pip install MNNficheiro .mnn
NCNNtorch2ncnn()pip install ncnn pnnxdiretório _ncnn_model/
ExecuTorchtorch2executorch()pip install executorchdiretório _executorch_model/
Incorporar 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 no artefacto exportado, quando o formato o permite.

Exemplos passo a passo#

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

import timm
import torch

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

Dropout, normalização em lotes e outras camadas usadas apenas durante o treino comportam-se de forma diferente na inferência. Se não chamares .eval(), as exportações terão resultados incorretos.

Exportar para ONNX#

from ultralytics.utils.export import torch2onnx

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

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

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

O opset predefinido é 14 e o nome de entrada predefinido é "images". Podes substituir estes valores com 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 com nomes fixos: model.xml e model.bin:

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

Passa 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, passa uma lista de nomes de classes a 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 é suportado no Windows.

Erro `BlobWriter not loaded`

coremltools>=9.0 disponibiliza wheels para Python 3.10–3.13 no macOS e Linux. Em versões mais recentes do Python, a extensão C nativa não carrega. Usa 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 recurso .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 recurso FP16 que recebe entradas float16; o recurso é executado no iOS 27 e no macOS 27. Consulte a integração com Core AI, incluindo a observação sobre recursos FP16 que abortam durante o carregamento.

Exportar para TensorFlow SavedModel#

A exportação TF SavedModel usa ONNX como etapa intermédia:

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 devolve um modelo Keras e também gera ficheiros FP32 e FP16 LiteRT (.tflite) no diretório de saída:

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

Passa quantize=8 para adicionar um .tflite INT8 aos restantes.

A exportação para TensorFlow não funciona no macOS com Python 3.13 ou posterior. Usa Python 3.12 ou anterior no macOS, ou Linux.

Requisitos para Python 3.12 ou anterior (com 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 noutras plataformas)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Exportar para TensorFlow Frozen Graph#

Continuando a partir da exportação SavedModel acima, converte o keras_model devolvido num 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 ficheiros 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 ficheiro ONNX como entrada. Exporta primeiro para ONNX e, em seguida, converte:

from ultralytics.utils.export import onnx2mnn, torch2onnx

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

Suporta quantize=16 para FP16 e 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 ficheiros 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 PaddlePaddle adequada à 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 noutras CPUs

Não é suportado no NVIDIA Jetson.

Exportar para ExecuTorch#

from ultralytics.utils.export import torch2executorch

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

O ficheiro .pte exportado é guardado 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 ExecuTorch.

Verificar o modelo exportado#

Depois de exportares, verifica a paridade numérica com o modelo PyTorch original antes de o disponibilizares. Um teste rápido com ONNXBackend de ultralytics.nn.backends compara os resultados e deteta precocemente erros de tracing ou quantização:

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-6 for an FP32 ONNX export
Diferença esperada

Em um ResNet-18, a maioria das exportações FP32 fica a cerca de 1e-5 do PyTorch, e o TorchScript corresponde exatamente. Ainda assim, três ambientes de execução podem calcular uma exportação FP32 com precisão reduzida e ficar próximo de 1e-2 a 1e-1: o NCNN habilita operações aritméticas FP16 em CPUs compatíveis, MNNBackend carrega modelos com precision="low", e o plug-in de CPU do OpenVINO é executado automaticamente em FP16 no modo de execução padrão PERFORMANCE em alguns hardwares, como Apple silicon. Uma diferença muito acima da linha de base do próprio formato aponta para 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. Valide com dados reais em vez de tensores aleatórios.

Noutros 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 tem por predefinição o valor "images".

Executar o modelo exportado#

Os modelos exportados que não são YOLO podem ser carregados novamente através da API normal YOLO(). As exportações acima não incluem metadados da tarefa nem do tamanho de entrada da Ultralytics, por isso passa explicitamente task e define 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 predefinido de 640. As exportações TorchScript e NCNN acima também aceitam outros tamanhos, mas nenhum dos exportadores garante isso: ambos fazem tracing com base no tensor de exemplo, por isso um modelo que achata os dados numa camada Linear mantém a forma fixa. Verifica a tua própria exportação.

Em seguida, o valor é arredondado para cima até ao múltiplo do stride do modelo, que é 32 sem metadados. Assim, uma exportação com forma fixa de 200x200 recebe uma entrada de 224x224 e é rejeitada, apesar de imgsz=200 corresponder à forma. Para tamanhos de entrada que não sejam múltiplos de 32, chama diretamente o backend.

Chamar um backend diretamente#

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

FormatoBackendDisposição da 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 predefinição. Para um grafo congelado, passa format="pb".

Três aspetos tratados pela abordagem YOLO() que uma chamada direta não trata:

  • Disposição da entrada: CoreMLBackend e TensorFlowBackend esperam BHWC. Transpõe primeiro com im.permute(0, 2, 3, 1); um tensor BCHW provoca uma incompatibilidade de formas.
  • Autograd: envolve as chamadas em torch.inference_mode(). TorchScriptBackend devolve um tensor que ainda contém um grafo de gradientes.
  • Pós-processamento: sem metadados, um backend deixa task como None e names vazio. LiteRTBackend continua a desnormalizar qualquer saída 3D pelo tamanho da imagem, partindo do princípio de que contém caixas YOLO, o que está errado para um modelo que não é YOLO e tem uma saída 3D. As saídas bidimensionais, como os logits de classificação, 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 de YOLO: as exportações Axelera e Sony IMX500 requerem atributos específicos dos 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 (apenas Linux). Edge TPU requer o binário edgetpu_compiler (apenas Linux).

Conclusão#

Estes utilitários convertem qualquer modelo PyTorch, desde um torch.nn.Module simples até um artefacto ONNX, OpenVINO, CoreML, TensorFlow ou de runtime móvel pronto para implementação, através de uma única API consistente. Escolhe o formato adequado ao teu hardware de destino, verifica a paridade numérica com o modelo original e, em seguida, segue o guia de integração correspondente para conheceres as etapas de implementação específicas do runtime.

Perguntas frequentes#

  • Qualquer torch.nn.Module. Isso inclui modelos do timm, do 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 suportados (TorchScript, ONNX, OpenVINO, CoreML, Core AI, TF SavedModel, TF Frozen Graph, PaddlePaddle, MNN, NCNN, ExecuTorch) podem ser exportados na CPU. Não é necessária uma GPU para o próprio processo de exportação. TensorRT é o único formato que requer uma GPU NVIDIA.

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

  • Sim. Os classificadores, detetores e modelos de segmentação 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 no Linux. CoreML não é compatível com Windows. Consulta a integração com CoreML para obter detalhes sobre a implantação no iOS.

  • Sim, para vários formatos. Passe 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 a exportação para NCNN e Core AI usa FP32 por padrão, recebe quantize=16 para FP16 e não oferece um caminho INT8. O INT8 no OpenVINO também requer um argumento calibration_dataset para quantização pós-treinamento. Consulte a página de integração de cada formato para conhecer as compensações da quantização.

  • Execute o modelo PyTorch original e o modelo exportado com a mesma entrada e, em seguida, compare as saídas. Carregue o arquivo exportado com o back-end correspondente (por exemplo, ONNXBackend para ONNX) e verifique a diferença absoluta máxima. Avalie a diferença em relação à linha de base do próprio formato: o NCNN, MNNBackend e o OpenVINO em algumas CPUs podem executar exportações FP32 com precisão reduzida e ficar próximo de 1e-2 a 1e-1, enquanto a maioria dos outros formatos fica próximo de 1e-5. Uma diferença muito maior aponta para operações não compatíveis, uma forma de entrada incorreta ou um modelo que não está no modo de avaliação. Consulte Verificar o modelo exportado para ver um exemplo executável.

Comentários