Meet YOLO26: next-gen vision AI.

Link to this sectionCómo exportar modelos PyTorch que no son YOLO con Ultralytics#

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

Ultralytics ofrece utilidades de exportación independientes que integran múltiples backends bajo una interfaz coherente. Puedes exportar cualquier torch.nn.Module, incluidos modelos de imagen 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.

Link to this section¿Por qué usar Ultralytics para exportaciones que no son YOLO?#

  • Una única API para 10 formatos: aprende una sola convención de llamada en lugar de una docena.
  • Superficie de utilidad compartida: los asistentes de exportación se encuentran en ultralytics.utils.export, por lo que una vez instalados los paquetes del backend, puedes mantener el mismo patrón de llamada entre diferentes formatos.
  • Misma ruta de código que las exportaciones de YOLO: los mismos asistentes impulsan todas las exportaciones de Ultralytics YOLO.
  • Cuantización FP16 e INT8 integrada para formatos que la admiten (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.

Link to this sectionInicio 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")

Link to this sectionFormatos de exportación compatibles#

Las funciones 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 intermedio ONNX o Keras. No se requieren atributos específicos 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 un paso intermedio. Exporta primero a ONNX y luego convierte.

Incrustación de metadatos

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

Link to this sectionEjemplos 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, la normalización por lotes y otras capas solo de entrenamiento se comportan de forma diferente durante la inferencia. Omitir .eval() produce exportaciones con salidas incorrectas.

Link to this sectionExportar 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 predeterminado es 14 y el nombre de entrada predeterminado es "images". Sobrescríbelo con los argumentos opset, input_names o output_names.

Link to this sectionExportar 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")

Link to this sectionExportar a OpenVINO#

from ultralytics.utils.export import torch2openvino

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

El directorio contiene un par de archivos model.xml y model.bin con 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 adicionalmente un argumento calibration_dataset.

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

Link to this sectionExportar 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 modelos de clasificación, 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 y numpy<=2.3.5. No es compatible con Windows.

Error `BlobWriter not loaded`

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

Link to this sectionExportar a TensorFlow SavedModel#

La exportación a TF SavedModel pasa por ONNX como un 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

Link to this sectionExportar a TensorFlow Frozen Graph#

Continuando desde la exportación a SavedModel anterior, convierte el modelo Keras devuelto a un gráfico .pb congelado:

from pathlib import Path

from ultralytics.utils.export import keras2pb

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

Link to this sectionExportar 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 en Python:

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

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

Link to this sectionExportar 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")

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

Link to this sectionExportar 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 correcta 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 es compatible con NVIDIA Jetson.

Link to this sectionExportar 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 tiempo de ejecución de ExecuTorch compatible (pip install executorch). Para el uso en tiempo de ejecución, consulta la integración de ExecuTorch.

Link to this sectionVerifica tu modelo exportado#

Después de exportar, verifica la paridad numérica con el modelo PyTorch original antes de realizar el despliegue. Una prueba rápida con ONNXBackend de ultralytics.nn.backends compara las salidas e identifica errores de trazado o cuantización desde el principio:

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. Diferencias mayores indican operaciones no admitidas, 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 flexibles. Valida con datos reales en lugar de tensores aleatorios.

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

Link to this sectionLimitaciones conocidas#

  • El soporte para entradas múltiples es desigual: 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 tiempo de ejecución de ExecuTorch requiere el compilador de 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 que no son YOLO exportados no pueden volver a cargarse mediante YOLO() para realizar inferencias. Utiliza el tiempo de ejecución nativo para cada formato (ONNX Runtime, OpenVINO Runtime, etc.).
  • Formatos solo para 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 Linux). Edge TPU requiere el binario edgetpu_compiler (solo Linux).

Link to this sectionPreguntas frecuentes#

Link to this section¿Qué modelos puedo exportar con Ultralytics?#

Cualquier torch.nn.Module. Esto incluye modelos de timm, torchvision o cualquier modelo PyTorch personalizado. 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 múltiples entradas.

Link to this section¿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.

Link to this section¿Qué versión de Ultralytics necesito?#

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

Link to this section¿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 incluir una cabecera de clasificación. Realiza 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.

Link to this section¿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 además un argumento calibration_dataset para la 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