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.
| Formato | Función | Instalar | Salida |
|---|---|---|---|
| ONNX | torch2onnx() | pip install onnx | .onnx archivo |
| TorchScript | torch2torchscript() | incluido con PyTorch | .torchscript archivo |
| OpenVINO | torch2openvino() | pip install openvino | _openvino_model/ directorio |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| TF SavedModel | onnx2saved_model() | consulta los requisitos detallados a continuación | _saved_model/ directorio |
| TF Frozen Graph | keras2pb() | consulta los requisitos detallados a continuación | .pb archivo |
| NCNN | torch2ncnn() | pip install ncnn pnnx | _ncnn_model/ directorio |
| MNN | onnx2mnn() | pip install MNN | .mnn archivo |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | _paddle_model/ directorio |
| ExecuTorch | torch2executorch() | pip install executorch | _executorch_model/ directorio |
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.
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)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.binPasa 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.
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.tfliteRequisitos:
tensorflow>=2.0.0,<=2.19.0onnx2tf>=1.26.3,<1.29.0tf_keras<=2.19.0sng4onnx>=1.0.1onnx_graphsurgeon>=0.3.26(instala con--extra-index-url https://pypi.ngc.nvidia.com)ai-edge-litert>=1.2.0,<1.4.0en macOS (ai-edge-litert>=1.2.0en otras plataformas)onnxslim>=0.1.71onnx>=1.12.0,<2.0.0protobuf>=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.pytorch2ncnn() 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.pdiparamsRequiere x2paddle y la distribución correcta de PaddlePaddle para tu plataforma:
paddlepaddle-gpu>=3.0.0,<3.3.0en CUDApaddlepaddle==3.0.0en CPU ARM64paddlepaddle>=3.0.0,<3.3.0en 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.pteRequiere 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-5Para 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:
torch2onnxytorch2openvinoacepta 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.torch2executorchasume un único tensor de entrada. - ExecuTorch necesita
flatc: El runtime de ExecuTorch requiere el compilador FlatBuffers. Instálalo conbrew install flatbuffersen macOS oapt install flatbuffers-compileren 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-toolkit2SDK (solo Linux). Edge TPU requiere eledgetpu_compilerbinario (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.