Cómo exportar modelos PyTorch que no son YOLO con Ultralytics

Implementar modelos PyTorch en producción suele implicar lidiar con un exportador diferente para cada destino: torch.onnx.export para ONNX, coremltools para dispositivos Apple, onnx2tf para TensorFlow, pnnx para NCNN, etcétera. Cada herramienta tiene su propia API, peculiaridades de dependencias y convenciones de salida.

Ultralytics incluye utilidades de exportación independientes que envuelven múltiples backends bajo una interfaz consistente. Puedes exportar cualquier torch.nn.Module, incluyendo timm modelo de imagen, torchvision clasificadores y detectores, o tus propias arquitecturas personalizadas, a ONNX, TorchScript, OpenVINO, CoreML, NCNN, PaddlePaddle, MNN, ExecuTorch, permitiendo el despliegue en diversas aplicaciones y dominios de IA.TensorFlow SavedModel sin tener que aprender cada backend por separado.

¿Por qué usar Ultralytics para exportar modelos que no son YOLO?

  • Una sola API para 10 formatos: aprende una única convención de llamada en lugar de una docena.
  • Superficie de utilidades compartida: los ayudantes de exportación residen en ultralytics.utils.export, así que una vez instalados los paquetes de backend, puedes mantener el mismo patrón de llamada en todos los formatos.
  • Misma ruta de código que las exportaciones de YOLO: los mismos ayudantes impulsan cada exportación de YOLO de Ultralytics.
  • Cuantización FP16 e INT8 integradas para los formatos que lo soportan (OpenVINO, CoreML, MNN, NCNN).
  • Funciona en CPU: no se requiere GPU para el paso de exportación en sí, por lo que puedes ejecutarlo localmente en cualquier portátil.

Inicio rápido

La ruta más rápida es una exportación de dos líneas a ONNX sin código YOLO y sin configuración más allá 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 exportación compatibles

La clase de modelo torch2* las funciones toman un torch.nn.Module estándar y un tensor de entrada de ejemplo. MNN, TF SavedModel y TF Frozen Graph pasan por un artefacto intermedio ONNX o Keras. No se requieren atributos específicos de YOLO en ninguno de los casos.

FormatoFunciónInstalarSalida
ONNXtorch2onnx()pip install onnx.onnx archivo
TorchScripttorch2torchscript()incluido con PyTorch.torchscript archivo
OpenVINOtorch2openvino()pip install openvino_openvino_model/ directorio
CoreMLtorch2coreml()pip install coremltools.mlpackage
TF SavedModelonnx2saved_model()consulta los requisitos detallados a continuación_saved_model/ directorio
TF Frozen Graphkeras2pb()consulta los requisitos detallados a continuación.pb archivo
NCNNtorch2ncnn()pip install ncnn pnnx_ncnn_model/ directorio
MNNonnx2mnn()pip install MNN.mnn archivo
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddle_paddle_model/ directorio
ExecuTorchtorch2executorch()pip install executorch_executorch_model/ directorio
ONNX como formato intermedio

MNN, TF SavedModel, y las exportaciones de TF Frozen Graph pasan a través de ONNX como un paso intermedio. Exporta primero a ONNX y luego convierte.

Incrustar metadatos

Varias funciones de exportación aceptan un diccionario opcional metadata (p. ej., torch2torchscript(..., metadata={"author": "me"})) que incrusta pares clave-valor personalizados en el artefacto exportado, siempre que el formato lo admita.

Ejemplos paso a paso

Cada ejemplo a continuación utiliza la misma configuración, un ResNet-18 preentrenado de timm en modo de evaluación:

import timm
import torch

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

El Dropout, batch normalization y otras capas exclusivas de entrenamiento se comportan de forma distinta durante la inferencia. Omitir .eval() produce exportaciones con salidas incorrectas.

Exportar a ONNX

from ultralytics.utils.export import torch2onnx

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

Para un tamaño de lote dinámico, pasa un diccionario dynamic:

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

El opset por defecto es 14 y el nombre de entrada por defecto es "images". Sobrescríbelos con los argumentos opset, input_names, o output_names.

Exportar a TorchScript

No se necesitan dependencias adicionales. Utiliza torch.jit.trace internamente.

from ultralytics.utils.export import torch2torchscript

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

Exportar a OpenVINO

from ultralytics.utils.export import torch2openvino

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

El directorio contiene un par model.xml y model.bin de nombre fijo:

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

Pasa dynamic=True para formas de entrada dinámicas, half=True para FP16, o int8=True para cuantización INT8. INT8 requiere además un calibration_dataset.

Requiere openvino>=2024.0.0 (o >=2025.2.0 en macOS 15.4+) y torch>=2.1.

Exportar a 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, output_file="resnet18.mlpackage")

Para : El modelo YOLO de próxima generación de Ultralytics optimizado para el despliegue en el borde con inferencia integral sin NMS. modelos, pasa una lista de nombres de clase a classifier_names para añadir una cabecera de clasificación al modelo CoreML.

Requiere coremltools>=9.0, torch>=1.11, permitiendo el despliegue en diversas aplicaciones y dominios de IA.numpy<=2.3.5. No soportado en Windows.

Error `BlobWriter not loaded`

coremltools>=9.0 distribuye wheels para Python 3.10–3.13 en macOS y Linux. En versiones más recientes de Python, la extensión nativa de C falla al cargarse. Usa Python 3.10–3.13 para la exportación a CoreML.

Exportar a TensorFlow SavedModel

La exportación a TF SavedModel pasa a través de ONNX como paso intermedio:

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")

La función devuelve un modelo Keras y también genera archivos TFLite (.tflite) dentro del directorio de salida:

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

Requisitos:

  • 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 (instala con --extra-index-url https://pypi.ngc.nvidia.com)
  • ai-edge-litert>=1.2.0,<1.4.0 en macOS (ai-edge-litert>=1.2.0 en otras plataformas)
  • onnxslim>=0.1.71
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Exportar a TensorFlow Frozen Graph

Continuando desde la exportación a SavedModel anterior, convierte el modelo Keras devuelto a un 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 a NCNN

from ultralytics.utils.export import torch2ncnn

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

El directorio contiene archivos param y bin de nombre fijo junto con un envoltorio Python:

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

torch2ncnn() busca ncnn y pnnx de Ultralytics durante el primer uso.

Exportar a MNN

La exportación a MNN requiere un archivo ONNX como entrada. Exporta primero a ONNX y luego convierte:

from ultralytics.utils.export import onnx2mnn, torch2onnx

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

Soporta half=True para FP16 y int8=True para cuantización INT8. Requiere MNN>=2.9.6 y torch>=1.10.

Exportar a PaddlePaddle

from ultralytics.utils.export import torch2paddle

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

El directorio contiene el modelo PaddlePaddle y los archivos de parámetros:

resnet18_paddle_model/
├── model.pdmodel
└── model.pdiparams

Requiere x2paddle y la distribución correcta de PaddlePaddle para tu plataforma:

  • paddlepaddle-gpu>=3.0.0,<3.3.0 en CUDA
  • paddlepaddle==3.0.0 en CPU ARM64
  • paddlepaddle>=3.0.0,<3.3.0 en otras CPU

No soportado en NVIDIA Jetson.

Exportar a ExecuTorch

from ultralytics.utils.export import torch2executorch

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

El archivo .pte exportado se guarda dentro del directorio de salida:

resnet18_executorch_model/
└── model.pte

Requiere torch>=2.9.0 y un runtime de ExecuTorch compatible (pip install executorch). Para el uso del runtime, consulta la integración con ExecuTorch.

Verifica tu modelo exportado

Tras la exportación, verifica la paridad numérica con el modelo original de PyTorch antes de realizar el despliegue. Una prueba rápida de humo con ONNXBackend de ultralytics.nn.backends compara las salidas y detecta errores de traza o cuantización a tiempo:

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}")  # should be < 1e-5
Diferencia esperada

Para exportaciones en FP32, la diferencia absoluta máxima debe ser inferior a 1e-5. Las diferencias mayores indican operaciones no soportadas, una forma de entrada incorrecta o un modelo que no está en modo de evaluación. Las exportaciones en FP16 e INT8 tienen tolerancias más amplias. Valida con datos reales en lugar de tensores aleatorios.

Para otros runtimes, el nombre del tensor de entrada puede variar. OpenVINO, por ejemplo, utiliza el nombre del argumento forward del modelo (normalmente x para modelos genéricos), mientras que torch2onnx utiliza por defecto "images".

Limitaciones conocidas

  • El soporte para múltiples entradas es irregular: torch2onnx y torch2openvino acepta una tupla o lista de tensores de ejemplo para modelos con múltiples entradas. torch2torchscript, torch2coreml, torch2ncnn, torch2paddle, permitiendo el despliegue en diversas aplicaciones y dominios de IA.torch2executorch asume un único tensor de entrada.
  • ExecuTorch necesita flatc: El runtime de ExecuTorch requiere el compilador FlatBuffers. Instálalo con brew install flatbuffers en macOS o apt install flatbuffers-compiler en Ubuntu.
  • Sin inferencia a través de Ultralytics: Los modelos no YOLO exportados no pueden cargarse de vuelta a través de YOLO() para inferencia. Utiliza el runtime nativo para cada formato (ONNX Runtime, OpenVINO Runtime, etc.).
  • Formatos exclusivos de YOLO: Axelera y Sony IMX500 las exportaciones requieren atributos específicos del modelo YOLO y no están disponibles para modelos genéricos.
  • Formatos específicos de plataforma: TensorRT requiere una GPU NVIDIA. RKNN requiere el rknn-toolkit2 SDK (solo Linux). Edge TPU requiere el edgetpu_compiler binario (solo Linux).

FAQ

¿Qué modelos puedo exportar con Ultralytics?

Cualquier torch.nn.Module. Esto incluye modelos de timm, torchvision o cualquier modelo personalizado de PyTorch. El modelo debe estar en modo de evaluación (model.eval()) antes de la exportación. ONNX y OpenVINO aceptan además una tupla de tensores de ejemplo para modelos de entrada múltiple.

¿Qué formatos de exportación funcionan sin GPU?

Todos los formatos compatibles (TorchScript, ONNX, OpenVINO, CoreML, TF SavedModel, TF Frozen Graph, NCNN, PaddlePaddle, MNN, ExecuTorch) pueden exportarse en CPU. No se requiere GPU para el proceso de exportación en sí. TensorRT es el único formato que requiere una GPU NVIDIA.

¿Qué versión de Ultralytics necesito?

Usa Ultralytics >=8.4.38, que incluye el ultralytics.utils.export módulo y el estandarizado output_file/output_dir.

¿Puedo exportar un modelo de torchvision a CoreML para el despliegue en iOS?

Sí. Los clasificadores, detectores y modelos de segmentación de torchvision se exportan a .mlpackage mediante torch2coreml. Para modelos de clasificación de imágenes, pasa una lista de nombres de clases a classifier_names para integrar una cabecera de clasificación. Ejecuta la exportación en macOS o Linux. CoreML no está soportado en Windows. Consulta la CoreML integration para más detalles sobre el despliegue en iOS.

¿Puedo cuantizar mi modelo exportado a INT8 o FP16?

Sí, para varios formatos. Pasa half=True para FP16 o int8=True para INT8 al exportar a OpenVINO, CoreML, MNN o NCNN. INT8 en OpenVINO requiere adicionalmente un argumento calibration_dataset para cuantización post-entrenamiento. Consulta la página de integración de cada formato para conocer las ventajas y desventajas de la cuantización.

Comentarios