tensorflow / tensorflow/model-optimization

Puring cannot reduce tflite model size

Open
#722 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
1.6k
Forks
349
Avg merge
3d 2h
Merged PRs (30d)
1

Description

Describe the bug
prune_low_magnitude cannot reduce the size of tflite model. I run the pruning with keras example (https://github.com/tensorflow/model-optimization/blob/master/tensorflow_model_optimization/g3doc/guide/pruning/pruning_with_keras.ipynb). The size of pruned Keras model does be smaller than the baseline Keras model. But the size is no difference after converted to tflite model no matter whether the model is pruned or not.

System information

TensorFlow version (installed from source or binary):2.5.0

TensorFlow Model Optimization version (installed from source or binary):0.5.0

Python version: 3.7.10

Describe the expected behavior
The size of tflite model converted from pruned model should smaller than the orignal one.
Describe the current behavior
The size is no difference after converted to tflite model no matter whether the model is pruned or not.
Code to reproduce the issue
https://colab.research.google.com/drive/1wHWT6iixdse2IVfwFcJ0VueKOoraaUCm?usp=sharing

! pip install -q tensorflow-model-optimization

# Commented out IPython magic to ensure Python compatibility.
import tempfile
import os

import tensorflow as tf
import numpy as np

from tensorflow import keras

# %load_ext tensorboard

"""## Train a model for MNIST without pruning"""

# 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='relu'),
  keras.layers.MaxPooling2D(pool_size=(2, 2)),
  keras.layers.Flatten(),
  keras.layers.Dense(10)
])

# Train the digit classification model
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

model.fit(
  train_images,
  train_labels,
  epochs=4,
  validation_split=0.1,
)

"""Evaluate baseline test accuracy and save the model for later usage."""

_, baseline_model_accuracy = model.evaluate(
    test_images, test_labels, verbose=0)

print('Baseline test accuracy:', baseline_model_accuracy)

_, keras_file = tempfile.mkstemp('.h5')
tf.keras.models.save_model(model, keras_file, include_optimizer=False)
print('Saved baseline model to:', keras_file)

"""## Fine-tune pre-trained model with pruning

### Define the model

You will apply pruning to the whole model and see this in the model summary.

In this example, you start the model with 50% sparsity (50% zeros in weights)
and end with 80% sparsity.

In the [comprehensive guide](https://www.tensorflow.org/model_optimization/guide/pruning/comprehensive_guide.md), you can see how to prune some layers for model accuracy improvements.
"""

import tensorflow_model_optimization as tfmot

prune_low_magnitude = tfmot.sparsity.keras.prune_low_magnitude

# Compute end step to finish pruning after 2 epochs.
batch_size = 128
epochs = 2
validation_split = 0.1 # 10% of training set will be used for validation set. 

num_images = train_images.shape[0] * (1 - validation_split)
end_step = np.ceil(num_images / batch_size).astype(np.int32) * epochs

# Define model for pruning.
pruning_params = {
      'pruning_schedule': tfmot.sparsity.keras.PolynomialDecay(initial_sparsity=0.50,
                                                               final_sparsity=0.80,
                                                               begin_step=0,
                                                               end_step=end_step)
}

model_for_pruning = prune_low_magnitude(model, **pruning_params)

# `prune_low_magnitude` requires a recompile.
model_for_pruning.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

model_for_pruning.summary()

"""### Train and evaluate the model against baseline

Fine tune with pruning for two epochs.

`tfmot.sparsity.keras.UpdatePruningStep` is required during training, and `tfmot.sparsity.keras.PruningSummaries` provides logs for tracking progress and debugging.
"""

logdir = tempfile.mkdtemp()

callbacks = [
  tfmot.sparsity.keras.UpdatePruningStep(),
  tfmot.sparsity.keras.PruningSummaries(log_dir=logdir),
]
  
model_for_pruning.fit(train_images, train_labels,
                  batch_size=batch_size, epochs=epochs, validation_split=validation_split,
                  callbacks=callbacks)

"""For this example, there is minimal loss in test accuracy after pruning, compared to the baseline."""

_, model_for_pruning_accuracy = model_for_pruning.evaluate(
   test_images, test_labels, verbose=0)

print('Baseline test accuracy:', baseline_model_accuracy) 
print('Pruned test accuracy:', model_for_pruning_accuracy)

"""The logs show the progression of sparsity on a per-layer basis."""

# Commented out IPython magic to ensure Python compatibility.
#docs_infra: no_execute
# %tensorboard --logdir={logdir}

"""For non-Colab users, you can see [the results of a previous run](https://tensorboard.dev/experiment/sRQnrycaTMWQOaswXzClYA/#scalars&_smoothingWeight=0) of this code block on [TensorBoard.dev](https://tensorboard.dev/).

## Create 3x smaller models from pruning

Both `tfmot.sparsity.keras.strip_pruning` and applying a standard compression algorithm (e.g. via gzip) are necessary to see the compression
benefits of pruning.

*   `strip_pruning` is necessary since it removes every tf.Variable that pruning only needs during training, which would otherwise add to model size during inference
*   Applying a standard compression algorithm is necessary since the serialized weight matrices are the same size as they were before pruning. However, pruning makes most of the weights zeros, which is
added redundancy that algorithms can utilize to further compress the model.

First, create a compressible model for TensorFlow.
"""

model_for_export = tfmot.sparsity.keras.strip_pruning(model_for_pruning)

_, pruned_keras_file = tempfile.mkstemp('.h5')
tf.keras.models.save_model(model_for_export, pruned_keras_file, include_optimizer=False)
print('Saved pruned Keras model to:', pruned_keras_file)

"""Then, create a compressible model for TFLite."""

converter = tf.lite.TFLiteConverter.from_keras_model(model_for_export)
pruned_tflite_model = converter.convert()

_, pruned_tflite_file = tempfile.mkstemp('.tflite')

with open(pruned_tflite_file, 'wb') as f:
  f.write(pruned_tflite_model)

print('Saved pruned TFLite model to:', pruned_tflite_file)

basline_converter = tf.lite.TFLiteConverter.from_keras_model(model)
baseline_tflite_model = basline_converter.convert()

_, baseline_tflite_file = tempfile.mkstemp('.tflite')

with open(baseline_tflite_file, 'wb') as f:
  f.write(baseline_tflite_model)

print('Saved baseline TFLite model to:', baseline_tflite_file)

"""Define a helper function to actually compress the models via gzip and measure the zipped size."""

def get_gzipped_model_size(file):
  # Returns size of gzipped model, in bytes.
  import os
  import zipfile

  _, zipped_file = tempfile.mkstemp('.zip')
  with zipfile.ZipFile(zipped_file, 'w', compression=zipfile.ZIP_DEFLATED) as f:
    f.write(file)

  return os.path.getsize(zipped_file)

"""Compare and see that the models are 3x smaller from pruning."""

print("Size of gzipped baseline Keras model: %.2f bytes" % (get_gzipped_model_size(keras_file)))
print("Size of gzipped pruned Keras model: %.2f bytes" % (get_gzipped_model_size(pruned_keras_file)))
print("Size of gzipped pruned TFlite model: %.2f bytes" % (get_gzipped_model_size(pruned_tflite_file)))
print("Size of gzipped baseline TFlite model: %.2f bytes" % (get_gzipped_model_size(baseline_tflite_file)))

"""## Create a 10x smaller model from combining pruning and quantization

You can apply post-training quantization to the pruned model for additional benefits.
"""

converter = tf.lite.TFLiteConverter.from_keras_model(model_for_export)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
quantized_and_pruned_tflite_model = converter.convert()

_, quantized_and_pruned_tflite_file = tempfile.mkstemp('.tflite')

with open(quantized_and_pruned_tflite_file, 'wb') as f:
  f.write(quantized_and_pruned_tflite_model)

basline_converter = tf.lite.TFLiteConverter.from_keras_model(model)
basline_converter.optimizations = [tf.lite.Optimize.DEFAULT]
baseline_tflite_model = basline_converter.convert()

_, quantized_baseline_tflite_file = tempfile.mkstemp('.tflite')

with open(quantized_baseline_tflite_file, 'wb') as f:
  f.write(baseline_tflite_model)

print('Saved quantized and pruned TFLite model to:', quantized_and_pruned_tflite_file)

print("Size of gzipped baseline Keras model: %.2f bytes" % (get_gzipped_model_size(keras_file)))
print("Size of gzipped pruned and quantized TFlite model: %.2f bytes" % (get_gzipped_model_size(quantized_and_pruned_tflite_file)))
print("Size of gzipped quantized baseline TFlite model: %.2f bytes" % (get_gzipped_model_size(quantized_baseline_tflite_file)))

Screenshots
Size of gzipped baseline Keras model: 78194.00 bytes
Size of gzipped pruned Keras model: 25741.00 bytes
Size of gzipped pruned TFlite model: 25211.00 bytes
Size of gzipped baseline TFlite model: 25211.00 bytes

Size of gzipped baseline Keras model: 78194.00 bytes
Size of gzipped pruned and quantized TFlite model: 8166.00 bytes
Size of gzipped quantized baseline TFlite model: 8166.00 bytes

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked Colab reproduction and the pruning-with-Keras example, then compare the generated baseline and pruned TFLite files and their gzipped sizes. Check the strip_pruning and TFLite conversion steps shown in the report. Done means the reported size difference is explained and the expected pruned-model behavior is fixed or documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
machine-learning
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.