YOLO Vision 2026:

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

Ultralytics ofrece utilidades de exportación independientes bajo ultralytics.utils.export que agrupan múltiples backends tras una interfaz coherente. Puedes exportar cualquier torch.nn.Module, incluidos modelos de imagen de timm, clasificadores y detectores de torchvision o tus propias arquitecturas personalizadas, a ONNX, TorchScript, OpenVINO, CoreML, NCNN, PaddlePaddle, MNN, ExecuTorch y TensorFlow SavedModel sin tener que aprender cada backend por separado.

Desplegar modelos de 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, y así sucesivamente. Cada herramienta tiene su propia API, particularidades de dependencias y convenciones de salida. Estas utilidades reducen todo eso a un único patrón de llamada.

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

  • Una 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 se encuentran en ultralytics.utils.export, por lo que, una vez instalados los paquetes de los backends, puedes mantener el mismo patrón de llamada en todos los formatos.
  • Mismo camino de código que las exportaciones de YOLO: los mismos ayudantes impulsan cada exportación de YOLO de Ultralytics.
  • Cuantización FP16 e INT8 integrada para los formatos que la 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 más configuración que 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#

Las funciones de torch2* 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 intermediario de ONNX o Keras. No se requiere ningún atributo específico de YOLO en ninguno de los casos.

FormatoFunciónInstalarSalida
ONNXtorch2onnx()pip install onnxarchivo .onnx
TorchScripttorch2torchscript()incluido con PyTorcharchivo .torchscript
OpenVINOtorch2openvino()pip install openvinodirectorio _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
TF SavedModelonnx2saved_model()consulta los requisitos detallados a continuacióndirectorio _saved_model/
TF Frozen Graphkeras2pb()consulta los requisitos detallados a continuaciónarchivo .pb
NCNNtorch2ncnn()pip install ncnn pnnxdirectorio _ncnn_model/
MNNonnx2mnn()pip install MNNarchivo .mnn
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddledirectorio _paddle_model/
ExecuTorchtorch2executorch()pip install executorchdirectorio _executorch_model/
ONNX como formato intermedio

Las exportaciones a MNN, TF SavedModel y TF Frozen Graph pasan por ONNX como paso intermedio. Exporta primero a ONNX y luego realiza la conversión.

Incrustar metadatos

Varias funciones de exportación aceptan un diccionario opcional metadata (por ejemplo, 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, una ResNet-18 preentrenada de timm en modo 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, la normalización por lotes y otras capas exclusivas de entrenamiento se comportan de manera 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 conjunto de operadores predeterminado es 14 y el nombre de entrada predeterminado es "images". Puedes sobrescribirlos 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 de nombre fijo model.xml y model.bin:

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

Pasa dynamic=True para formas de entrada dinámicas, quantize=16 para FP16 o quantize=8 para cuantización INT8. INT8 requiere además un argumento 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, classifier_names=None, output_file="resnet18.mlpackage")

Para modelos de clasificación, pasa una lista de nombres de clases a classifier_names para añadir una cabecera de clasificación al modelo CoreML.

Requiere coremltools>=9.0, torch>=1.11 y numpy<=2.3.5. No es compatible con Windows.

Error `BlobWriter not loaded`

coremltools>=9.0 ofrece ruedas (wheels) para Python 3.10–3.13 en macOS y Linux. En versiones más recientes de Python, la extensión nativa de C no se carga correctamente. Utiliza Python 3.10–3.13 para la exportación a CoreML.

Exportar a TensorFlow SavedModel#

La exportación a TF SavedModel pasa por 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 de 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
  • ai-edge-litert>=1.2.0,<1.4.0 en macOS (ai-edge-litert>=1.2.0 en otras plataformas)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Exportar a TensorFlow Frozen Graph#

Continuando con la exportación del SavedModel anterior, convierte el keras_model devuelto en un grafo congelado (.pb):

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 contenedor Python:

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

torch2ncnn() comprueba la existencia de ncnn y pnnx en su primer uso.

Exportar a MNN#

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

from ultralytics.utils.export import onnx2mnn, torch2onnx

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

Compatible con quantize=16 para FP16 y quantize=8 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 de PaddlePaddle adecuada 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 CPUs

No compatible con 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 entorno de ejecución de ExecuTorch compatible (pip install executorch). Para su uso en ejecución, consulta la integración de ExecuTorch.

Verifica tu modelo exportado#

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

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}")  # typically ~1e-5, well under 1e-4 for FP32
Diferencia esperada

Para exportaciones en FP32, la diferencia absoluta máxima suele rondar 1e-5 y debe mantenerse muy por debajo de 1e-4. Las diferencias mayores indican operaciones no compatibles, una forma de entrada incorrecta o un modelo que no está en modo de evaluación. Las exportaciones en FP16 y INT8 tienen tolerancias más laxas. Valida con datos reales en lugar de tensores aleatorios.

Para otros entornos de ejecución, 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 toma por defecto "images".

Limitaciones conocidas#

  • La compatibilidad con múltiples entradas es irregular: torch2onnx y torch2openvino aceptan una tupla o lista de tensores de ejemplo para modelos con múltiples entradas. torch2torchscript, torch2coreml, torch2ncnn, torch2paddle y torch2executorch asumen un único tensor de entrada.
  • ExecuTorch necesita flatc: El entorno de ejecución de ExecuTorch requiere el compilador FlatBuffers. Instálalo con brew install flatbuffers en macOS o apt install flatbuffers-compiler en Ubuntu.
  • Sin inferencia mediante Ultralytics: Los modelos que no son de YOLO exportados no se pueden volver a cargar a través de YOLO() para realizar inferencias. Utiliza el entorno de ejecución nativo de cada formato (ONNX Runtime, OpenVINO Runtime, etc.).
  • Formatos exclusivos de YOLO: Las exportaciones a Axelera y Sony IMX500 requieren atributos de modelo específicos de YOLO y no están disponibles para modelos genéricos.
  • Formatos específicos de plataforma: TensorRT requiere una GPU NVIDIA. RKNN requiere el SDK rknn-toolkit2 (solo para Linux). Edge TPU requiere el binario edgetpu_compiler (solo para Linux).

Conclusión#

Estas utilidades transforman cualquier modelo de PyTorch desde un simple torch.nn.Module hasta un artefacto listo para producción en ONNX, OpenVINO, CoreML, TensorFlow o entornos móviles mediante una API coherente. Elige el formato que coincida con tu hardware de destino, verifica la paridad numérica con respecto al modelo original y, a continuación, sigue la guía de integración correspondiente para conocer los pasos de despliegue específicos del entorno de ejecución.

FAQ#

  • 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 con múltiples entradas.

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

  • Usa >=8.4.38 de Ultralytics, que incluye el módulo ultralytics.utils.export y los argumentos estandarizados output_file/output_dir.

  • Sí. Los modelos de clasificación, detección y segmentación de torchvision se exportan a .mlpackage mediante torch2coreml. Para los modelos de clasificación de imágenes, pasa una lista de nombres de clases a classifier_names para incorporar una cabecera de clasificación. Ejecuta la exportación en macOS o Linux. CoreML no es compatible con Windows. Consulta la integración de CoreML para obtener detalles sobre el despliegue en iOS.

  • Sí, para varios formatos. Pasa quantize=16 para FP16 o quantize=8 para INT8 al exportar a OpenVINO, CoreML, MNN o NCNN. INT8 en OpenVINO requiere además un argumento calibration_dataset para la cuantización posterior al entrenamiento. Consulta la página de integración de cada formato para conocer las ventajas e inconvenientes de la cuantización.

  • Ejecuta el modelo original de PyTorch y el modelo exportado con la misma entrada y, a continuación, compara las salidas. Carga el archivo exportado con el backend correspondiente (por ejemplo, ONNXBackend para ONNX) y comprueba la diferencia absoluta máxima. En las exportaciones con FP32, suele rondar 1e-5 y debe mantenerse muy por debajo de 1e-4; las desviaciones mayores indican operaciones no compatibles, una forma de entrada incorrecta o un modelo que no está en modo de evaluación. Consulta Verifica tu modelo exportado para ver un ejemplo ejecutable.

Comentarios