O Dia da Comunidade de ML é dia 9 de novembro! Junte-nos para atualização de TensorFlow, JAX, e mais Saiba mais

Pruning, preservando quantização ciente de treinamento (PQAT) Exemplo de Keras

Ver no TensorFlow.org Executar no Google Colab Ver no GitHub Baixar caderno

Visão geral

Este é um fim exemplo final mostrando o uso da API poda preservar quantização formação consciente (PQAT), parte do pipeline de otimização de colaboração do TensorFlow modelo de otimização do Toolkit.

Outras páginas

Para uma introdução ao gasoduto e outras técnicas disponíveis, consulte a página de visão geral otimização colaborativo .

Conteúdo

No tutorial, você irá:

  1. Treinar um tf.keras modelo para o conjunto de dados MNIST a partir do zero.
  2. Ajuste o modelo com poda, usando a API esparsa, e veja a precisão.
  3. Aplique QAT e observe a perda de esparsidade.
  4. Aplique PQAT e observe que a dispersão aplicada anteriormente foi preservada.
  5. Gere um modelo TFLite e observe os efeitos da aplicação de PQAT nele.
  6. Compare a precisão do modelo PQAT alcançada com um modelo quantizado usando a quantização pós-treinamento.

Configurar

Você pode executar este Notebook Jupyter em seu local de virtualenv ou colab . Para mais detalhes sobre a criação de dependências, consulte o guia de instalação .

 pip install -q tensorflow-model-optimization
import tensorflow as tf

import numpy as np
import tempfile
import zipfile
import os

Treine um modelo tf.keras para MNIST sem poda

# Load MNIST dataset
mnist = tf.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

model = tf.keras.Sequential([
  tf.keras.layers.InputLayer(input_shape=(28, 28)),
  tf.keras.layers.Reshape(target_shape=(28, 28, 1)),
  tf.keras.layers.Conv2D(filters=12, kernel_size=(3, 3),
                         activation=tf.nn.relu),
  tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),
  tf.keras.layers.Flatten(),
  tf.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,
    validation_split=0.1,
    epochs=10
)
Epoch 1/10
1688/1688 [==============================] - 8s 4ms/step - loss: 0.2940 - accuracy: 0.9172 - val_loss: 0.1183 - val_accuracy: 0.9693
Epoch 2/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.1150 - accuracy: 0.9674 - val_loss: 0.0863 - val_accuracy: 0.9770
Epoch 3/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.0821 - accuracy: 0.9760 - val_loss: 0.0738 - val_accuracy: 0.9797
Epoch 4/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.0673 - accuracy: 0.9806 - val_loss: 0.0661 - val_accuracy: 0.9837
Epoch 5/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.0583 - accuracy: 0.9826 - val_loss: 0.0632 - val_accuracy: 0.9835
Epoch 6/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.0520 - accuracy: 0.9845 - val_loss: 0.0635 - val_accuracy: 0.9820
Epoch 7/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.0460 - accuracy: 0.9857 - val_loss: 0.0746 - val_accuracy: 0.9815
Epoch 8/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.0423 - accuracy: 0.9875 - val_loss: 0.0601 - val_accuracy: 0.9848
Epoch 9/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.0384 - accuracy: 0.9885 - val_loss: 0.0604 - val_accuracy: 0.9855
Epoch 10/10
1688/1688 [==============================] - 7s 4ms/step - loss: 0.0347 - accuracy: 0.9894 - val_loss: 0.0648 - val_accuracy: 0.9835
<tensorflow.python.keras.callbacks.History at 0x7fb08657b5d0>

Avalie o modelo de linha de base e salve-o para uso posterior

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

print('Baseline test accuracy:', baseline_model_accuracy)

_, keras_file = tempfile.mkstemp('.h5')
print('Saving model to: ', keras_file)
tf.keras.models.save_model(model, keras_file, include_optimizer=False)
Baseline test accuracy: 0.9810000061988831
Saving model to:  /tmp/tmp0akp2126.h5

Limpe e ajuste o modelo para 50% de dispersão

Aplique o prune_low_magnitude() API para podar todo o modelo pré-treinados para demonstrar e observar a sua eficácia na redução do tamanho do modelo ao aplicar zip, mantendo a precisão. Para a melhor forma de usar a API para obter a melhor taxa de compressão, mantendo a sua precisão de destino, consulte o guia completo poda .

Defina o modelo e aplique a API esparsa

O modelo precisa ser pré-treinado antes de usar a API sparsity.

import tensorflow_model_optimization as tfmot

prune_low_magnitude = tfmot.sparsity.keras.prune_low_magnitude

pruning_params = {
      'pruning_schedule': tfmot.sparsity.keras.ConstantSparsity(0.5, begin_step=0, frequency=100)
  }

callbacks = [
  tfmot.sparsity.keras.UpdatePruningStep()
]

pruned_model = prune_low_magnitude(model, **pruning_params)

# Use smaller learning rate for fine-tuning
opt = tf.keras.optimizers.Adam(learning_rate=1e-5)

pruned_model.compile(
  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
  optimizer=opt,
  metrics=['accuracy'])

pruned_model.summary()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/engine/base_layer.py:2191: UserWarning: `layer.add_variable` is deprecated and will be removed in a future version. Please use `layer.add_weight` method instead.
  warnings.warn('`layer.add_variable` is deprecated and '
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
prune_low_magnitude_reshape  (None, 28, 28, 1)         1         
_________________________________________________________________
prune_low_magnitude_conv2d ( (None, 26, 26, 12)        230       
_________________________________________________________________
prune_low_magnitude_max_pool (None, 13, 13, 12)        1         
_________________________________________________________________
prune_low_magnitude_flatten  (None, 2028)              1         
_________________________________________________________________
prune_low_magnitude_dense (P (None, 10)                40572     
=================================================================
Total params: 40,805
Trainable params: 20,410
Non-trainable params: 20,395
_________________________________________________________________

Ajuste o modelo e avalie a precisão em relação à linha de base

Afine o modelo com poda por 3 épocas.

# Fine-tune model
pruned_model.fit(
  train_images,
  train_labels,
  epochs=3,
  validation_split=0.1,
  callbacks=callbacks)
Epoch 1/3
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/array_ops.py:5049: calling gather (from tensorflow.python.ops.array_ops) with validate_indices is deprecated and will be removed in a future version.
Instructions for updating:
The `validate_indices` argument has no effect. Indices are always validated on CPU and never validated on GPU.
1688/1688 [==============================] - 9s 5ms/step - loss: 0.1243 - accuracy: 0.9580 - val_loss: 0.1146 - val_accuracy: 0.9650
Epoch 2/3
1688/1688 [==============================] - 8s 4ms/step - loss: 0.0883 - accuracy: 0.9713 - val_loss: 0.0945 - val_accuracy: 0.9720
Epoch 3/3
1688/1688 [==============================] - 8s 4ms/step - loss: 0.0722 - accuracy: 0.9774 - val_loss: 0.0858 - val_accuracy: 0.9753
<tensorflow.python.keras.callbacks.History at 0x7fafb6c80310>

Defina funções auxiliares para calcular e imprimir a dispersão do modelo.

def print_model_weights_sparsity(model):

    for layer in model.layers:
        if isinstance(layer, tf.keras.layers.Wrapper):
            weights = layer.trainable_weights
        else:
            weights = layer.weights
        for weight in weights:
            # ignore auxiliary quantization weights
            if "quantize_layer" in weight.name:
                continue
            weight_size = weight.numpy().size
            zero_num = np.count_nonzero(weight == 0)
            print(
                f"{weight.name}: {zero_num/weight_size:.2%} sparsity ",
                f"({zero_num}/{weight_size})",
            )

Verifique se o modelo foi podado corretamente. Precisamos remover o invólucro de poda primeiro.

stripped_pruned_model = tfmot.sparsity.keras.strip_pruning(pruned_model)

print_model_weights_sparsity(stripped_pruned_model)
conv2d/kernel:0: 50.00% sparsity  (54/108)
conv2d/bias:0: 0.00% sparsity  (0/12)
dense/kernel:0: 50.00% sparsity  (10140/20280)
dense/bias:0: 0.00% sparsity  (0/10)

Para este exemplo, há uma perda mínima na precisão do teste após a poda, em comparação com a linha de base.

_, pruned_model_accuracy = pruned_model.evaluate(
  test_images, test_labels, verbose=0)

print('Baseline test accuracy:', baseline_model_accuracy)
print('Pruned test accuracy:', pruned_model_accuracy)
Baseline test accuracy: 0.9810000061988831
Pruned test accuracy: 0.973800003528595

Aplicar QAT e PQAT e verificar o efeito na dispersão do modelo em ambos os casos

Em seguida, aplicamos o QAT e o QAT de preservação da poda (PQAT) no modelo podado e observamos que o PQAT preserva a esparsidade no modelo podado. Note que nós despojado poda invólucros de seu modelo podados com tfmot.sparsity.keras.strip_pruning antes de aplicar PQAT API.

# QAT
qat_model = tfmot.quantization.keras.quantize_model(stripped_pruned_model)

qat_model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])
print('Train qat model:')
qat_model.fit(train_images, train_labels, batch_size=128, epochs=1, validation_split=0.1)

# PQAT
quant_aware_annotate_model = tfmot.quantization.keras.quantize_annotate_model(
              stripped_pruned_model)
pqat_model = tfmot.quantization.keras.quantize_apply(
              quant_aware_annotate_model,
              tfmot.experimental.combine.Default8BitPrunePreserveQuantizeScheme())

pqat_model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])
print('Train pqat model:')
pqat_model.fit(train_images, train_labels, batch_size=128, epochs=1, validation_split=0.1)
Train qat model:
422/422 [==============================] - 4s 8ms/step - loss: 0.0386 - accuracy: 0.9891 - val_loss: 0.0607 - val_accuracy: 0.9832
Train pqat model:
422/422 [==============================] - 4s 8ms/step - loss: 0.0416 - accuracy: 0.9881 - val_loss: 0.0590 - val_accuracy: 0.9832
<tensorflow.python.keras.callbacks.History at 0x7fafb5e48f50>
print("QAT Model sparsity:")
print_model_weights_sparsity(qat_model)
print("PQAT Model sparsity:")
print_model_weights_sparsity(pqat_model)
QAT Model sparsity:
conv2d/bias:0: 0.00% sparsity  (0/12)
conv2d/kernel:0: 6.48% sparsity  (7/108)
dense/bias:0: 0.00% sparsity  (0/10)
dense/kernel:0: 5.67% sparsity  (1149/20280)
PQAT Model sparsity:
conv2d/bias:0: 0.00% sparsity  (0/12)
conv2d/kernel:0: 50.00% sparsity  (54/108)
dense/bias:0: 0.00% sparsity  (0/10)
dense/kernel:0: 50.00% sparsity  (10140/20280)

Veja os benefícios da compressão do modelo PQAT

Defina a função auxiliar para obter o arquivo de modelo compactado.

def get_gzipped_model_size(file):
  # It returns the size of the gzipped model in kilobytes.

  _, 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)/1000

Por ser um modelo pequeno, a diferença entre os dois modelos não é muito perceptível. Aplicar poda e PQAT a um modelo de produção maior resultaria em uma compressão mais significativa.

# QAT model
converter = tf.lite.TFLiteConverter.from_keras_model(qat_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
qat_tflite_model = converter.convert()
qat_model_file = 'qat_model.tflite'
# Save the model.
with open(qat_model_file, 'wb') as f:
    f.write(qat_tflite_model)

# PQAT model
converter = tf.lite.TFLiteConverter.from_keras_model(pqat_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
pqat_tflite_model = converter.convert()
pqat_model_file = 'pqat_model.tflite'
# Save the model.
with open(pqat_model_file, 'wb') as f:
    f.write(pqat_tflite_model)

print("QAT model size: ", get_gzipped_model_size(qat_model_file), ' KB')
print("PQAT model size: ", get_gzipped_model_size(pqat_model_file), ' KB')
WARNING:absl:Found untraced functions such as reshape_layer_call_fn, reshape_layer_call_and_return_conditional_losses, conv2d_layer_call_fn, conv2d_layer_call_and_return_conditional_losses, flatten_layer_call_fn while saving (showing 5 of 20). These functions will not be directly callable after loading.
INFO:tensorflow:Assets written to: /tmp/tmpurndy6nz/assets
INFO:tensorflow:Assets written to: /tmp/tmpurndy6nz/assets
WARNING:absl:Found untraced functions such as reshape_layer_call_fn, reshape_layer_call_and_return_conditional_losses, conv2d_layer_call_fn, conv2d_layer_call_and_return_conditional_losses, flatten_layer_call_fn while saving (showing 5 of 20). These functions will not be directly callable after loading.
INFO:tensorflow:Assets written to: /tmp/tmpgsy8_zld/assets
INFO:tensorflow:Assets written to: /tmp/tmpgsy8_zld/assets
QAT model size:  16.326  KB
PQAT model size:  14.022  KB

Veja a persistência de precisão de TF para TFLite

Defina uma função auxiliar para avaliar o modelo TFLite no conjunto de dados de teste.

def eval_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 i, test_image in enumerate(test_images):
    if i % 1000 == 0:
      print(f"Evaluated on {i} results so far.")
    # 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)

  print('\n')
  # Compare prediction results with ground truth labels to calculate accuracy.
  prediction_digits = np.array(prediction_digits)
  accuracy = (prediction_digits == test_labels).mean()
  return accuracy

Você avalia o modelo, que foi podado e quantizado, e vê que a precisão do TensorFlow persiste no back-end TFLite.

interpreter = tf.lite.Interpreter(pqat_model_file)
interpreter.allocate_tensors()

pqat_test_accuracy = eval_model(interpreter)

print('Pruned and quantized TFLite test_accuracy:', pqat_test_accuracy)
print('Pruned TF test accuracy:', pruned_model_accuracy)
Evaluated on 0 results so far.
Evaluated on 1000 results so far.
Evaluated on 2000 results so far.
Evaluated on 3000 results so far.
Evaluated on 4000 results so far.
Evaluated on 5000 results so far.
Evaluated on 6000 results so far.
Evaluated on 7000 results so far.
Evaluated on 8000 results so far.
Evaluated on 9000 results so far.


Pruned and quantized TFLite test_accuracy: 0.9815
Pruned TF test accuracy: 0.973800003528595

Aplicar a quantização pós-treinamento e comparar com o modelo PQAT

Em seguida, usamos a quantização pós-treinamento normal (sem ajuste fino) no modelo podado e verificamos sua precisão em relação ao modelo PQAT. Isso demonstra por que você precisaria usar o PQAT para melhorar a precisão do modelo quantizado.

Primeiro, defina um gerador para o conjunto de dados de calibração das primeiras 1000 imagens de treinamento.

def mnist_representative_data_gen():
  for image in train_images[:1000]:  
    image = np.expand_dims(image, axis=0).astype(np.float32)
    yield [image]

Quantize o modelo e compare a precisão com o modelo PQAT adquirido anteriormente. Observe que o modelo quantizado com ajuste fino atinge maior precisão.

converter = tf.lite.TFLiteConverter.from_keras_model(stripped_pruned_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = mnist_representative_data_gen
post_training_tflite_model = converter.convert()
post_training_model_file = 'post_training_model.tflite'
# Save the model.
with open(post_training_model_file, 'wb') as f:
    f.write(post_training_tflite_model)

# Compare accuracy
interpreter = tf.lite.Interpreter(post_training_model_file)
interpreter.allocate_tensors()

post_training_test_accuracy = eval_model(interpreter)

print('PQAT TFLite test_accuracy:', pqat_test_accuracy)
print('Post-training (no fine-tuning) TF test accuracy:', post_training_test_accuracy)
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
INFO:tensorflow:Assets written to: /tmp/tmpk4ymv7ej/assets
INFO:tensorflow:Assets written to: /tmp/tmpk4ymv7ej/assets
Evaluated on 0 results so far.
Evaluated on 1000 results so far.
Evaluated on 2000 results so far.
Evaluated on 3000 results so far.
Evaluated on 4000 results so far.
Evaluated on 5000 results so far.
Evaluated on 6000 results so far.
Evaluated on 7000 results so far.
Evaluated on 8000 results so far.
Evaluated on 9000 results so far.


PQAT TFLite test_accuracy: 0.9815
Post-training (no fine-tuning) TF test accuracy: 0.9734

Conclusão

Neste tutorial, você aprendeu como criar um modelo, podá-lo usando a API de dispersão e aplicar o treinamento ciente de quantização com preservação de dispersão (PQAT) para preservar a dispersão ao usar QAT. O modelo PQAT final foi comparado ao QAT para mostrar que a esparsidade é preservada no primeiro e perdida no segundo. Em seguida, os modelos foram convertidos para TFLite para mostrar os benefícios de compressão da poda em cadeia e técnicas de otimização de modelo PQAT e o modelo TFLite foi avaliado para garantir que a precisão persiste no backend TFLite. Finalmente, o modelo PQAT foi comparado a um modelo podado quantizado obtido usando a API de quantização pós-treinamento para demonstrar a vantagem do PQAT em recuperar a perda de precisão da quantização normal.