CLI tool
MPolaris/onnx2tflite avatar
MPolaris/onnx2tflite

onnx2tflite: two backends for getting an ONNX model onto a phone

Tool for onnx->keras or onnx->tflite. Hope this tool can help you.

578 stars55 forksPythonApache-2.0

At a glance

What is it?
onnx2tflite converts an ONNX model into TensorFlow Lite, either through a Keras backend that covers more operators or a direct IR backend that skips Keras entirely, with FP16 and INT8 quantization and calibration data support along the way.
Who is it for?
onnx2tflite fits a PyTorch developer with a trained model who needs a TFLite artefact for mobile deployment and does not want to rewrite the network in TensorFlow, and it fits someone who needs a specific operator that the direct path does not handle, because the Keras backend stays available for exactly that reason.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 124 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Installing it, and the Python version it needs

The install is a clone and a setup script run.

bash
git clone https://github.com/MPolaris/onnx2tflite.git
cd onnx2tflite
python setup.py install

The packaging is explicit about its environment. It requires Python 3.8 to 3.10, with classifiers for each of those three versions, and the runtime dependencies are onnx, onnxruntime, onnx-simplifier, numpy at 1.24 or lower, and TensorFlow between 2.5 and 2.13. The version number is read out of a version file rather than hard coded, and the development status is listed as Beta. The numpy and TensorFlow pins are the part to notice, because they are what makes the tool run on an older machine but also what stops it running on a current one without a separate environment.

The command line, from a basic conversion to quantization

The command line interface is the main entry point, and it is invoked as a Python module. A basic conversion names the weights.

bash
python -m onnx2tflite --weights "./your_model.onnx"

Output location and format are flags, and the format flag accepts more than one value so you can ask for both artefacts at once.

bash
python -m onnx2tflite --weights "./your_model.onnx" --outpath "./save_path"
python -m onnx2tflite --weights "./your_model.onnx" --formats "tflite" "keras"

Subgraph extraction cuts the model down by layer names, keeping only what is between the named inputs and outputs, which is how you isolate one part of a large network for a smaller deployment.

bash
python -m onnx2tflite --weights "./your_model.onnx" --input-node-names "layer_in" --output-node-names "layer_out1" "layer_out2"

And the quantization flags cover the three modes, with the int8 flag taking a calibration file.

bash
python -m onnx2tflite --weights "./your_model.onnx" --formats "tflite" --weigthquant     # weight-only int8
python -m onnx2tflite --weights "./your_model.onnx" --formats "tflite" --fp16              # fp16
python -m onnx2tflite --weights "./your_model.onnx" --formats "tflite" --int8              # int8 (random calib, low accuracy)
python -m onnx2tflite --weights "./your_model.onnx" --formats "tflite" --int8              # int8 with calibration
    --calibration-data "./calib_input.npy"

The comments in that last block are the practical warning, since int8 without a real calibration set is labelled as random calibration with low accuracy. The direct IR path is a separate flag on the same command line.

bash
python -m onnx2tflite --weights "./your_model.onnx" --formats "tflite" --direct-ir

The Python API and the two backends

The library exposes the same functionality through onnx_converter. The basic call names the model, the output directory and the formats wanted.

python
from onnx2tflite import onnx_converter

onnx_converter("model.onnx", output_path="./out", target_formats=['tflite'])

The two backends are selected by a flag. The direct IR path, enabled with use_direct_ir=True, builds TFLite FlatBuffer models directly from the ONNX graph and bypasses Keras entirely, which gives faster conversion with no Keras model building or saving, more control over the generated TFLite ops, and support for most common operators. The Keras backend is the default and remains available for operators the direct path does not yet support, with Erf named as an example.

python
onnx_converter("model.onnx", use_direct_ir=True)

The feature list gives the claimed properties, and they are worth reading as claims to verify rather than guarantees. High consistency, with the comparison against ONNX outputs showing an average error of less than 1e-5 per element. A speed claim of 30 percent faster output than onnx_tf. Auto channel alignment, converting the PyTorch NCHW layout to the TensorFlow NHWC layout. Deployment support through FP16 and INT8 quantization, both weight-only and full integer. And multi-input INT8, where calibration data arrives as .npy files and models with several inputs are supported.

Calibration data, and why the format matters

INT8 quantization needs a representative dataset, and the format for it is specified tightly. Calibration data is provided as .npy files, one file per model input, with a shape of (N, ...) where N is the number of calibration samples, recommended between 50 and 200. The remaining dimensions must match the model input shape, and the data must already be preprocessed, meaning normalized, resized and channel-ordered, before saving. For a multi-input model you provide one file per input in the same order as the ONNX model inputs. The example shows a single-input case with a shape of one, three, 224, 224 and a hundred samples, and a two-input case pairing an image shape with a vector shape.

python
import numpy as np

# Single-input model: shape (1, 3, 224, 224)
calib = np.random.randn(100, 3, 224, 224).astype(np.float32)
np.save("./calib_input.npy", calib)

# Multi-input model: two inputs with shapes (1, 3, 224, 224) and (1, 1000)
np.save("./calib_img.npy", np.random.randn(100, 3, 224, 224).astype(np.float32))
np.save("./calib_vec.npy", np.random.randn(100, 1000).astype(np.float32))

The preprocessing requirement is where most of the silent accuracy loss comes from, because a calibration set in the wrong layout will still calibrate, it will just calibrate the wrong distribution.

Adding an operator the converter does not know

When you hit an unsupported operator, the project gives you two choices: add it yourself or open an issue. The two backends have separate extension points, and the Keras one is a class registered under the operator name, taking the tensor graph, node weights, node inputs, node attributes, node outputs and the layout dictionary. The example is a HardSigmoid handler, reading alpha and beta from the node attributes with defaults of 0.2 and 0.5, and calling it clips the scaled input between zero and one.

python
@OPERATOR.register_operator("HardSigmoid")
class TFHardSigmoid():
    def __init__(self, tensor_graph, node_weights, node_inputs,
                 node_attribute, node_outputs, layout_dict, *args, **kwargs):
        super().__init__()
        self.alpha = node_attribute.get("alpha", 0.2)
        self.beta = node_attribute.get("beta", 0.5)

    def __call__(self, inputs):
        return tf.clip_by_value(self.alpha*inputs+self.beta, 0, 1)

The direct IR backend has its own location, in tflite_backend/ops/, where you add a handler function rather than a class. The repository is laid out with an onnx2tflite/ package, a test/ directory, and the two backend directories inside the package.

The model list, and where this approach stops working

The validated models list is the best guide to what this converter handles today, and it is dominated by detection and segmentation networks. SSD, HRNet for facial landmark detection, and the YOLO family from YOLOX through YOLOV3, YOLOV4, YOLOV5, YOLOV6, YOLOV7 and YOLOV10 are all listed, along with MoveNet, UNet and FPN, ViT from torchvision, SwinTransformerV1, an MLP and DCGAN marked as custom, AutoEncoder and VAE, all torchvision classification models, some segmentation models in torchvision, and one or two dimensional CNNs without special operators, also marked custom. The limits follow from the design. This is a converter for models whose operators already exist in ONNX and TensorFlow, not a translator for arbitrary PyTorch graphs, and the operators it does not know have to be added by hand. The Python and dependency pins make it a tool for an existing environment rather than something to drop into a current one, and with no GitHub releases and the last push on 2026-06-21, you would be vendoring the code rather than pinning a version.

Editorial conclusion

onnx2tflite fits a PyTorch developer with a trained model who needs a TFLite artefact for mobile deployment and does not want to rewrite the network in TensorFlow, and it fits someone who needs a specific operator that the direct path does not handle, because the Keras backend stays available for exactly that reason. It does not fit a team that wants a maintained converter with a versioned release stream, because the project publishes no GitHub releases and its last push was on 2026-06-21, and it does not fit anyone on Python 3.11 or newer, since the packaging declares support for Python 3.8 to 3.10 and pins numpy to 1.24 or lower with TensorFlow between 2.5 and 2.13. Before you convert, run the tool on your model without quantization to check the error against the ONNX outputs, then add INT8 with a real calibration set rather than the random one.

Frequently asked questions

How do I install onnx2tflite?

Clone the repository, change into the onnx2tflite directory and run python setup.py install. The package requires Python 3.8 to 3.10, and its dependencies are onnx, onnxruntime, onnx-simplifier, numpy at 1.24 or lower and TensorFlow between 2.5 and 2.13.

What is the difference between the Keras backend and the direct IR backend?

The direct IR path, enabled with use_direct_ir=True, builds TFLite FlatBuffer models directly from the ONNX graph and bypasses Keras, giving faster conversion and more control over the generated ops, and it supports most common operators. The Keras backend is the default and stays available for operators the direct path does not yet support, such as Erf.

What format does INT8 calibration data need?

It is supplied as .npy files, one per model input, with shape (N, ...) where N is the number of calibration samples, recommended between 50 and 200, the remaining dimensions matching the model input shape, and the data already preprocessed by being normalized, resized and channel-ordered.

How do I convert a model to a TFLite file from the command line?

Run python -m onnx2tflite with the --weights flag pointing at the ONNX file, and add --outpath for a specific output directory, --formats for the output formats, and --direct-ir to bypass Keras. Subgraph extraction is done with --input-node-names and --output-node-names.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. MPolaris/onnx2tflite on GitHub
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mpolaris-onnx2tflite.svg)](https://hysenlabs.com/projects/mpolaris-onnx2tflite)

Was this useful?