Skip to main content

timmx

PyPI version Python versions License Ask DeepWiki

An extensible CLI and Python package for exporting timm models to various deployment formats. Born out of having too many one-off export scripts for fine-tuned timm models — timmx unifies them behind a single command-line interface with a plugin-based backend system.

Supported Formats

Format Command Output
ONNX timmx export onnx .onnx
OpenVINO timmx export openvino .xml + .bin
Core ML timmx export coreml .mlpackage / .mlmodel
Core AI timmx export coreai .aimodel
LiteRT / TFLite timmx export litert .tflite
ncnn timmx export ncnn directory (.param + .bin)
TensorRT timmx export tensorrt .engine
ExecuTorch timmx export executorch .pte
torch.export timmx export torch-export .pt2
TorchScript timmx export torchscript .pt

Requirements

  • Python >=3.11,<3.15
  • uv

Installation

Core install (includes timm, torch, typer, rich, numpy):

pip install timmx

Install with specific backend extras:

pip install 'timmx[onnx]'           # ONNX export (onnxruntime included for verification)
pip install 'timmx[openvino]'       # OpenVINO IR export
pip install 'timmx[coreml]'         # Core ML export
pip install 'timmx[coreai]'         # Core AI export (Apple; runtime is macOS-only)
pip install 'timmx[litert]'         # LiteRT/TFLite export
pip install 'timmx[ncnn]'           # ncnn export (via pnnx; ncnn runtime for verification)
pip install 'timmx[executorch]'     # ExecuTorch export (XNNPack, CoreML delegates)
pip install 'timmx[onnx,coreml]'    # multiple backends

TensorRT requires CUDA and must be installed separately:

pip install tensorrt  # Linux/Windows with CUDA only

Note: timmx tracks the latest release of every dependency and is only tested against those (torch>=2.13). coremltools has no Python 3.14 wheels yet, so the coreml extra needs Python <=3.13. litert-torch currently pins torch<2.14, so installing the litert extra holds torch one release back; it also has no Windows or Python 3.14 wheels. coreai-core ships wheels only for Python <=3.13 on macOS 26+ (arm64) and manylinux x86_64.

Check which backends are available:

timmx doctor

Quick Start

uv sync --extra onnx --extra openvino --extra coreml --extra ncnn --extra coreai --extra executorch --extra litert --group dev
uv run timmx doctor
uv run timmx --help

Export Verification

Every backend reloads the file it just wrote, runs it on the sample input, and compares the output with PyTorch (cosine similarity and max abs diff are printed). An export whose output diverges (cosine similarity below 0.9) fails with exit code 2 so silently broken artifacts never ship; the file is kept for inspection. Quantized exports are compared on the first calibration batch. Pass --no-verify to skip the check (for example when the runtime is not available on the export machine).

Model Info

Inspect a model's metadata (parameter count, input size, number of classes, etc.) without exporting:

uv run timmx info resnet18 --pretrained

This displays architecture details, parameter counts, default input size, and whether weights are loaded.

Listing Models

Browse and search available timm models:

uv run timmx list resnet                        # search by substring
uv run timmx list "resnet*"                     # search by glob pattern
uv run timmx list --pretrained-only resnet      # only models with pretrained weights

Usage Examples

ONNX

uv run timmx export onnx resnet18 --pretrained --output ./artifacts/resnet18.onnx

Export a fine-tuned checkpoint with dynamic batching:

uv run timmx export onnx resnet18 \
  --checkpoint ./checkpoints/model.pth \
  --input-size 3 224 224 \
  --dynamic-batch \
  --output ./artifacts/resnet18_finetuned.onnx

Export with built-in normalization and softmax (the model will expect unnormalized [0, 1] float input and output probabilities):

uv run timmx export onnx resnet18 \
  --pretrained \
  --normalize --softmax \
  --output ./artifacts/resnet18_with_preprocess.onnx

--normalize embeds the timm model's mean/std normalization into the graph. --softmax adds a softmax layer on the output. Use both flags together if you want a self-contained export that accepts raw [0, 1] float input and outputs probabilities; use --softmax alone if your inputs are already normalized. --mean / --std override the embedded normalization and therefore require --normalize. --in-chans currently supports only 1 or 3; for grayscale (--in-chans 1) exports, RGB mean/std values are averaged down to a single channel.

Exported models are automatically optimized with onnxslim (constant folding, dead-code elimination, operator fusion). To skip optimization:

uv run timmx export onnx resnet18 --pretrained --no-slim --output ./artifacts/resnet18.onnx

OpenVINO

Writes an OpenVINO IR pair (.xml + .bin); weights are compressed to fp16 by default.

uv run timmx export openvino resnet18 \
  --pretrained \
  --output ./artifacts/resnet18.xml

Dynamic batch with fp32 weights:

uv run timmx export openvino resnet18 \
  --pretrained \
  --dynamic-batch \
  --no-fp16 \
  --output ./artifacts/resnet18_dynamic.xml

Core AI

Writes a Core AI asset — a .aimodel directory holding the Core AI IR bytecode, for Apple's Core AI inference stack. Conversion goes through torch.export, so it handles CNNs and transformers alike, and the graph's input and output are named input and output.

uv run timmx export coreai resnet18 \
  --pretrained \
  --output ./artifacts/resnet18.aimodel

Dynamic batch (needs --batch-size >= 2 for symbolic shape capture, then accepts any batch size at runtime):

uv run timmx export coreai resnet18 \
  --pretrained \
  --batch-size 2 \
  --dynamic-batch \
  --output ./artifacts/resnet18_dynamic.aimodel

Note: conversion works on macOS and Linux, but the Core AI runtime only executes on Apple platforms. On Linux --verify therefore only checks that the asset reads back, and cannot compare outputs against PyTorch.

Core ML

uv run timmx export coreml resnet18 \
  --pretrained \
  --convert-to mlprogram \
  --compute-precision float16 \
  --output ./artifacts/resnet18.mlpackage

Models are captured with torch.export by default; the exported model has one input named input and one output named output. If a model fails to capture, fall back to torch.jit.trace:

uv run timmx export coreml resnet18 \
  --pretrained \
  --source trace \
  --convert-to mlprogram \
  --compute-precision float16 \
  --output ./artifacts/resnet18_traced.mlpackage

Flexible batch size:

uv run timmx export coreml resnet18 \
  --dynamic-batch \
  --batch-size 2 \
  --batch-upper-bound 8 \
  --output ./artifacts/resnet18_dynamic.mlpackage

Weight quantization (post-conversion, applied to model weights):

# 8-bit linear quantization (mlpackage)
uv run timmx export coreml resnet18 \
  --pretrained \
  --convert-to mlprogram \
  --compute-precision float16 \
  --int8 \
  --output ./artifacts/resnet18_int8.mlpackage

# 4-bit k-means quantization (mlpackage only)
uv run timmx export coreml resnet18 \
  --pretrained \
  --convert-to mlprogram \
  --compute-precision float16 \
  --int4 \
  --output ./artifacts/resnet18_int4.mlpackage

# fp16 weight quantization (neuralnetwork)
uv run timmx export coreml resnet18 \
  --pretrained \
  --convert-to neuralnetwork \
  --half \
  --output ./artifacts/resnet18_half.mlmodel

# 8-bit linear quantization (neuralnetwork)
uv run timmx export coreml resnet18 \
  --pretrained \
  --convert-to neuralnetwork \
  --int8 \
  --output ./artifacts/resnet18_int8.mlmodel

--int4 palettizes weights with per-tensor k-means and is lossy (cosine similarity around 0.98 on ResNet-18). Check the verify: line printed after export before shipping it.

LiteRT / TFLite

Supported modes: fp32, fp16 (fp16 weights), dynamic-int8 (int8 weights, fp32 activations) and int8 (full integer). Only int8 needs calibration data.

uv run timmx export litert resnet18 \
  --mode fp16 \
  --output ./artifacts/resnet18_fp16.tflite

Dynamic-range INT8 (no calibration needed):

uv run timmx export litert resnet18 \
  --mode dynamic-int8 \
  --output ./artifacts/resnet18_dynamic_int8.tflite

Full INT8 with calibration data (point to an image directory — timm transforms are applied automatically). Weights are quantized per-channel by default; pass --no-per-channel for per-tensor:

uv run timmx export litert resnet18 \
  --mode int8 \
  --calibration-data ./my-images/ \
  --output ./artifacts/resnet18_int8.tflite

int8 models take and return int8 tensors. Quantize inputs with the scale and zero_point from the interpreter's input details (round(x / scale) + zero_point) and dequantize the output the same way; --normalize keeps that input in [0, 1] before quantization. LiteRT has no --dynamic-batch, but Interpreter.resize_tensor_input() works at runtime for convolutional models (not for ViT-style models whose reshapes bake in the batch size).

Limit the number of calibration images loaded:

uv run timmx export litert resnet18 \
  --mode int8 \
  --calibration-data ./my-images/ \
  --calibration-samples 64 \
  --output ./artifacts/resnet18_int8.tflite

For fine-tuned models with custom normalization, override calibration preprocessing with --mean / --std:

uv run timmx export litert resnet18 \
  --mode int8 \
  --calibration-data ./my-images/ \
  --mean 0.5 0.5 0.5 --std 0.5 0.5 0.5 \
  --output ./artifacts/resnet18_int8.tflite

For image-directory calibration, --in-chans currently supports only 1 or 3; grayscale models average RGB mean/std values down to one channel automatically.

A pre-saved torch tensor (N, C, H, W) is also accepted:

uv run timmx export litert resnet18 \
  --mode int8 \
  --calibration-data ./calibration.pt \
  --calibration-steps 8 \
  --output ./artifacts/resnet18_int8.tflite

Use --random-calibration to skip providing real data (not recommended for production):

uv run timmx export litert resnet18 \
  --mode int8 \
  --random-calibration \
  --output ./artifacts/resnet18_int8.tflite

NHWC input layout:

uv run timmx export litert resnet18 \
  --mode fp32 \
  --nhwc-input \
  --output ./artifacts/resnet18_nhwc.tflite

ncnn

Exports via pnnx and writes a deployment-ready ncnn model directory containing model.ncnn.param, model.ncnn.bin, and model_ncnn.py. pnnx intermediate files are removed automatically.

uv run timmx export ncnn resnet18 \
  --pretrained \
  --output ./artifacts/resnet18_ncnn

ncnn models have no batch dimension, so --batch-size must stay 1. Models with batch-dependent reshapes (ViT-style attention) cannot be converted by pnnx; the export fails verification instead of writing a silently wrong model.

Export without fp16 weight quantization:

uv run timmx export ncnn resnet18 \
  --pretrained \
  --no-fp16 \
  --output ./artifacts/resnet18_ncnn_fp32

TensorRT

Requires an NVIDIA GPU with CUDA, TensorRT 11 or newer (pip install tensorrt) and the onnx extra. Engines are built as strongly typed networks: fp16 runs the whole graph in half precision behind fp32 inputs/outputs, and int8 inserts explicit Q/DQ quantization (symmetric int8, per-channel weights) on every conv/linear layer, which needs pip install torchao. The engine is run once after the build and compared with PyTorch. Static int8 suits convolutional networks (ResNet-18 keeps a cosine similarity of 0.9997 to PyTorch); transformer activations quantize poorly this way (ViT-Tiny drops to 0.92), so prefer fp16 for those.

uv run timmx export tensorrt resnet18 \
  --pretrained \
  --mode fp16 \
  --output ./artifacts/resnet18_fp16.engine

INT8 with calibration (image directory or torch tensor):

uv run timmx export tensorrt resnet18 \
  --pretrained \
  --mode int8 \
  --calibration-data ./my-images/ \
  --output ./artifacts/resnet18_int8.engine

Override calibration normalization for fine-tuned models with --mean / --std:

uv run timmx export tensorrt resnet18 \
  --pretrained \
  --mode int8 \
  --calibration-data ./my-images/ \
  --mean 0.5 0.5 0.5 --std 0.5 0.5 0.5 \
  --output ./artifacts/resnet18_int8.engine

Dynamic batch size:

uv run timmx export tensorrt resnet18 \
  --pretrained \
  --dynamic-batch \
  --batch-size 4 \
  --batch-min 1 \
  --batch-max 32 \
  --output ./artifacts/resnet18_dynamic.engine

ExecuTorch

Export with XNNPack delegation (default, runs on CPU across all platforms):

uv run timmx export executorch resnet18 \
  --pretrained \
  --output ./artifacts/resnet18.pte

CoreML delegation (macOS — targets Apple Neural Engine / GPU / CPU):

uv run timmx export executorch resnet18 \
  --pretrained \
  --delegate coreml \
  --output ./artifacts/resnet18_coreml.pte

CoreML with explicit fp32 compute precision (default is fp16):

uv run timmx export executorch resnet18 \
  --pretrained \
  --delegate coreml \
  --compute-precision float32 \
  --output ./artifacts/resnet18_coreml_fp32.pte

INT8 quantized with XNNPack:

uv run timmx export executorch resnet18 \
  --pretrained \
  --mode int8 \
  --calibration-data ./my-images/ \
  --output ./artifacts/resnet18_int8.pte

Override calibration normalization for fine-tuned models with --mean / --std:

uv run timmx export executorch resnet18 \
  --pretrained \
  --mode int8 \
  --calibration-data ./my-images/ \
  --mean 0.5 0.5 0.5 --std 0.5 0.5 0.5 \
  --output ./artifacts/resnet18_int8.pte

Dynamic INT8 (int8 weights, activations quantized on the fly at runtime; no calibration data). This is the mode to use for transformer models, where static INT8 loses accuracy:

uv run timmx export executorch vit_tiny_patch16_224 \
  --pretrained \
  --mode dynamic-int8 \
  --output ./artifacts/vit_tiny_dynamic_int8.pte

INT8 quantized with CoreML:

uv run timmx export executorch resnet18 \
  --pretrained \
  --delegate coreml \
  --mode int8 \
  --random-calibration \
  --output ./artifacts/resnet18_coreml_int8.pte

Dynamic batch size:

uv run timmx export executorch resnet18 \
  --pretrained \
  --dynamic-batch \
  --batch-size 2 \
  --batch-upper-bound 16 \
  --output ./artifacts/resnet18_dynamic.pte

ExecuTorch plans memory ahead of time, so the runtime accepts batches from 1 up to --batch-upper-bound (default 8) and rejects larger ones. --mode dynamic-int8 does not support --dynamic-batch.

torch.export

uv run timmx export torch-export resnet18 \
  --pretrained \
  --dynamic-batch \
  --batch-size 2 \
  --output ./artifacts/resnet18.pt2

When using --dynamic-batch, set --batch-size to at least 2 so PyTorch can capture a symbolic batch dimension.

TorchScript

uv run timmx export torchscript resnet18 \
  --pretrained \
  --output ./artifacts/resnet18.pt

Export with built-in normalization (model accepts unnormalized [0, 1] float input):

uv run timmx export torchscript resnet18 \
  --pretrained \
  --normalize \
  --output ./artifacts/resnet18_normalized.pt

For fine-tuned models with custom normalization, override with --mean / --std:

uv run timmx export torchscript resnet18 \
  --pretrained \
  --normalize \
  --mean 0.5 0.5 0.5 --std 0.5 0.5 0.5 \
  --output ./artifacts/resnet18_custom_norm.pt

Grayscale TorchScript export behaves the same as ONNX here: --in-chans is currently limited to 1 or 3, and RGB mean/std values are averaged down to one channel for --in-chans 1.

Use torch.jit.script instead of the default trace:

uv run timmx export torchscript resnet18 \
  --pretrained \
  --method script \
  --output ./artifacts/resnet18_scripted.pt

Diagnostics

Run timmx info <model> to inspect any model's metadata, or timmx doctor to check your installation and see which backends are available:

timmx doctor

This shows the timmx version, Python/torch versions, and a table of backend availability with install hints for any missing dependencies.

Roadmap

  • ONNX
  • Core ML
  • LiteRT / TFLite
  • ncnn
  • torch.export
  • TensorRT
  • TorchScript
  • ExecuTorch (XNNPack + CoreML delegates)
  • OpenVINO
  • Core AI (via coreai-torch)
  • TensorFlow (SavedModel / .pb)
  • TensorFlow.js
  • TFLite Edge TPU
  • MNN
  • PaddlePaddle

Development

uv sync --extra onnx --extra openvino --extra coreml --extra ncnn --extra coreai --extra executorch --group dev  # install extras + pytest
uvx ruff format .                                              # format
uvx ruff check .                                               # lint
uv run pytest                                                  # test
uv build                                                       # build

Adding a New Backend

See CONTRIBUTING.md for a step-by-step guide on implementing and registering a new export backend.

AI Disclaimer

This project is developed with the assistance of AI tools. The original export logic comes from various standalone scripts I wrote for exporting fine-tuned timm models to different deployment formats. The process of consolidating these scripts into a unified CLI tool has been aided by AI, with my oversight at every step, reviewing generated code, manually fixing issues during backend porting, and validating that exports produce correct results.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

timmx-0.8.0.tar.gz (41.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

timmx-0.8.0-py3-none-any.whl (52.3 kB view details)

Uploaded Python 3

File details

Details for the file timmx-0.8.0.tar.gz.

File metadata

  • Download URL: timmx-0.8.0.tar.gz
  • Upload date:
  • Size: 41.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for timmx-0.8.0.tar.gz
Algorithm Hash digest
SHA256 169252686670f78f481583078e70bc9c0b4d9a984ef46095b28904bea1c84562
MD5 237af7ee92ba86cc78e23ff33de71adb
BLAKE2b-256 406d93315f4efc2d65131155b80bc26eb8ae9277f937ca5d48d1b901f05ed713

See more details on using hashes here.

File details

Details for the file timmx-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: timmx-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 52.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for timmx-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 98eee107eb73ba423b9e78c24abaf5d244fa82ada0fe59b5929b5a31b3cfec8c
MD5 19324bc7a9d12dc6f744cbd22818b6b7
BLAKE2b-256 23e4f522aecb529b86d67e653711353b7b91c925b8d6bf3bc66f0f3cbe22c549

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 files

0.7.0

2 files

0.6.0

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page