Missed TensorFlow Dev Summit? Check out the video playlist. Watch recordings

Post-training weight quantization

View on TensorFlow.org Run in Google Colab View source on GitHub

Overview

TensorFlow Lite now supports converting weights to 8 bit precision as part of model conversion from tensorflow graphdefs to TensorFlow Lite's flat buffer format. Weight quantization achieves a 4x reduction in the model size. In addition, TFLite supports on the fly quantization and dequantization of activations to allow for:

  1. Using quantized kernels for faster implementation when available.
  2. Mixing of floating-point kernels with quantized kernels for different parts of the graph.

The activations are always stored in floating point. For ops that support quantized kernels, the activations are quantized to 8 bits of precision dynamically prior to processing and are de-quantized to float precision after processing. Depending on the model being converted, this can give a speedup over pure floating point computation.

In contrast to quantization aware training , the weights are quantized post training and the activations are quantized dynamically at inference in this method. Therefore, the model weights are not retrained to compensate for quantization induced errors. It is important to check the accuracy of the quantized model to ensure that the degradation is acceptable.

This tutorial trains an MNIST model from scratch, checks its accuracy in TensorFlow, and then converts the model into a Tensorflow Lite flatbuffer with weight quantization. Finally, it checks the accuracy of the converted model and compare it to the original float model.

Build an MNIST model

Setup

import logging
logging.getLogger("tensorflow").setLevel(logging.DEBUG)

try:
  # %tensorflow_version only exists in Colab.
  import tensorflow.compat.v2 as tf
except Exception:
  pass
tf.enable_v2_behavior()

from tensorflow import keras
import numpy as np
import pathlib

Train a TensorFlow model

# Load MNIST dataset
mnist = keras.datasets.mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# Normalize the input image so that each pixel value is between 0 to 1.
train_images = train_images / 255.0
test_images = test_images / 255.0

# Define the model architecture
model = keras.Sequential([
  keras.layers.InputLayer(input_shape=(28, 28)),
  keras.layers.Reshape(target_shape=(28, 28, 1)),
  keras.layers.Conv2D(filters=12, kernel_size=(3, 3), activation=tf.nn.relu),
  keras.layers.MaxPooling2D(pool_size=(2, 2)),
  keras.layers.Flatten(),
  keras.layers.Dense(10, activation=tf.nn.softmax)
])

# Train the digit classification model
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
model.fit(
  train_images,
  train_labels,
  epochs=1,
  validation_data=(test_images, test_labels)
)
Train on 60000 samples, validate on 10000 samples
60000/60000 [==============================] - 13s 223us/sample - loss: 0.2963 - accuracy: 0.9153 - val_loss: 0.1449 - val_accuracy: 0.9593

<tensorflow.python.keras.callbacks.History at 0x7f7dcc5d02b0>

For the example, since you trained the model for just a single epoch, so it only trains to ~96% accuracy.

Convert to a TensorFlow Lite model

Using the Python TFLiteConverter, you can now convert the trained model into a TensorFlow Lite model.

Now load the model using the TFLiteConverter:

converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()

Write it out to a tflite file:

tflite_models_dir = pathlib.Path("/tmp/mnist_tflite_models/")
tflite_models_dir.mkdir(exist_ok=True, parents=True)
tflite_model_file = tflite_models_dir/"mnist_model.tflite"
tflite_model_file.write_bytes(tflite_model)
83424

To quantize the model on export, set the optimizations flag to optimize for size:

converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]
tflite_quant_model = converter.convert()
tflite_model_quant_file = tflite_models_dir/"mnist_model_quant.tflite"
tflite_model_quant_file.write_bytes(tflite_quant_model)
22616

Note how the resulting file, is approximately 1/4 the size.

ls -lh {tflite_models_dir}
total 176K
-rw-rw-r-- 1 kbuilder kbuilder 43K Jan 12 04:08 mnist_model_quant_f16.tflite
-rw-rw-r-- 1 kbuilder kbuilder 23K Jan 12 04:09 mnist_model_quant_io.tflite
-rw-rw-r-- 1 kbuilder kbuilder 23K Jan 12 04:11 mnist_model_quant.tflite
-rw-rw-r-- 1 kbuilder kbuilder 82K Jan 12 04:11 mnist_model.tflite

Run the TFLite models

Run the TensorFlow Lite model using the Python TensorFlow Lite Interpreter.

Load the model into an interpreter

interpreter = tf.lite.Interpreter(model_path=str(tflite_model_file))
interpreter.allocate_tensors()
interpreter_quant = tf.lite.Interpreter(model_path=str(tflite_model_quant_file))
interpreter_quant.allocate_tensors()

Test the model on one image

test_image = np.expand_dims(test_images[0], axis=0).astype(np.float32)

input_index = interpreter.get_input_details()[0]["index"]
output_index = interpreter.get_output_details()[0]["index"]

interpreter.set_tensor(input_index, test_image)
interpreter.invoke()
predictions = interpreter.get_tensor(output_index)
import matplotlib.pylab as plt

plt.imshow(test_images[0])
template = "True:{true}, predicted:{predict}"
_ = plt.title(template.format(true= str(test_labels[0]),
                              predict=str(np.argmax(predictions[0]))))
plt.grid(False)

Evaluate the models

# A helper function to evaluate the TF Lite model using "test" dataset.
def evaluate_model(interpreter):
  input_index = interpreter.get_input_details()[0]["index"]
  output_index = interpreter.get_output_details()[0]["index"]

  # Run predictions on every image in the "test" dataset.
  prediction_digits = []
  for test_image in test_images:
    # Pre-processing: add batch dimension and convert to float32 to match with
    # the model's input data format.
    test_image = np.expand_dims(test_image, axis=0).astype(np.float32)
    interpreter.set_tensor(input_index, test_image)

    # Run inference.
    interpreter.invoke()

    # Post-processing: remove batch dimension and find the digit with highest
    # probability.
    output = interpreter.tensor(output_index)
    digit = np.argmax(output()[0])
    prediction_digits.append(digit)

  # Compare prediction results with ground truth labels to calculate accuracy.
  accurate_count = 0
  for index in range(len(prediction_digits)):
    if prediction_digits[index] == test_labels[index]:
      accurate_count += 1
  accuracy = accurate_count * 1.0 / len(prediction_digits)

  return accuracy
print(evaluate_model(interpreter))
0.9593

Repeat the evaluation on the weight quantized model to obtain:

print(evaluate_model(interpreter_quant))
0.9592

In this example, the compressed model has no difference in the accuracy.

Optimizing an existing model

Resnets with pre-activation layers (Resnet-v2) are widely used for vision applications. Pre-trained frozen graph for resnet-v2-101 is available on Tensorflow Hub.

You can convert the frozen graph to a TensorFLow Lite flatbuffer with quantization by:

import tensorflow_hub as hub

resnet_v2_101 = tf.keras.Sequential([
  keras.layers.InputLayer(input_shape=(224, 224, 3)),
  hub.KerasLayer("https://tfhub.dev/google/imagenet/resnet_v2_101/classification/4")
])

converter = tf.lite.TFLiteConverter.from_keras_model(resnet_v2_101)
# Convert to TF Lite without quantization
resnet_tflite_file = tflite_models_dir/"resnet_v2_101.tflite"
resnet_tflite_file.write_bytes(converter.convert())
178386468
# Convert to TF Lite with quantization
converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]
resnet_quantized_tflite_file = tflite_models_dir/"resnet_v2_101_quantized.tflite"
resnet_quantized_tflite_file.write_bytes(converter.convert())
45052280
ls -lh {tflite_models_dir}/*.tflite
-rw-rw-r-- 1 kbuilder kbuilder  43K Jan 12 04:08 /tmp/mnist_tflite_models/mnist_model_quant_f16.tflite
-rw-rw-r-- 1 kbuilder kbuilder  23K Jan 12 04:09 /tmp/mnist_tflite_models/mnist_model_quant_io.tflite
-rw-rw-r-- 1 kbuilder kbuilder  23K Jan 12 04:11 /tmp/mnist_tflite_models/mnist_model_quant.tflite
-rw-rw-r-- 1 kbuilder kbuilder  82K Jan 12 04:11 /tmp/mnist_tflite_models/mnist_model.tflite
-rw-rw-r-- 1 kbuilder kbuilder  43M Jan 12 04:11 /tmp/mnist_tflite_models/resnet_v2_101_quantized.tflite
-rw-rw-r-- 1 kbuilder kbuilder 171M Jan 12 04:11 /tmp/mnist_tflite_models/resnet_v2_101.tflite

The model size reduces from 171 MB to 43 MB. The accuracy of this model on imagenet can be evaluated using the scripts provided for TFLite accuracy measurement.

The optimized model top-1 accuracy is 76.8, the same as the floating point model.