XNNPACK을 사용한 온 디바이스 추론을위한 프 루닝

TensorFlow.org에서보기 Google Colab에서 실행 GitHub에서 소스보기 노트북 다운로드

를 통해 온 - 디바이스 추론의 대기 시간을 개선하기위한 가지 치기 Keras 무게의 가이드에 오신 것을 환영합니다 XNNPACK .

이 가이드 선물 새롭게 도입의 사용 tfmot.sparsity.keras.PruningPolicy API는과가 사용하는 현대 CPU에서 주로 길쌈 모델을 가속에 사용할 수있는 방법을 보여줍니다 XNNPACK 스파 스 추론을 .

이 가이드는 모델 생성 프로세스의 다음 단계를 다룹니다.

  • 밀집된 기준선 구축 및 훈련
  • 가지 치기로 모델 미세 조정
  • TFLite로 변환
  • 온 디바이스 벤치 마크

이 가이드에서는 가지 치기를 통한 미세 조정에 대한 모범 사례를 다루지 않습니다. 이 주제에 대한 자세한 내용은, 우리 확인하시기 바랍니다 포괄적 인 가이드를 .

설정

 pip install -q tf-nightly
 pip install -q tensorflow-model-optimization==0.5.1.dev0
import tempfile

import tensorflow as tf
import numpy as np

from tensorflow import keras
import tensorflow_datasets as tfds
import tensorflow_model_optimization as tfmot

%load_ext tensorboard

조밀 한 모델 구축 및 훈련

우리는 구축에 대한 분류 작업에 대한 간단한 기준 CNN 훈련 CIFAR10의 데이터 세트.

# Load CIFAR10 dataset.
(ds_train, ds_val, ds_test), ds_info = tfds.load(
    'cifar10',
    split=['train[:90%]', 'train[90%:]', 'test'],
    as_supervised=True,
    with_info=True,
)

# Normalize the input image so that each pixel value is between 0 and 1.
def normalize_img(image, label):
  """Normalizes images: `uint8` -> `float32`."""
  return tf.image.convert_image_dtype(image, tf.float32), label

# Load the data in batches of 128 images.
batch_size = 128
def prepare_dataset(ds, buffer_size=None):
  ds = ds.map(normalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)
  ds = ds.cache()
  if buffer_size:
    ds = ds.shuffle(buffer_size)
  ds = ds.batch(batch_size)
  ds = ds.prefetch(tf.data.experimental.AUTOTUNE)
  return ds

ds_train = prepare_dataset(ds_train,
                           buffer_size=ds_info.splits['train'].num_examples)
ds_val = prepare_dataset(ds_val)
ds_test = prepare_dataset(ds_test)

# Build the dense baseline model.
dense_model = keras.Sequential([
    keras.layers.InputLayer(input_shape=(32, 32, 3)),
    keras.layers.ZeroPadding2D(padding=1),
    keras.layers.Conv2D(
        filters=8,
        kernel_size=(3, 3),
        strides=(2, 2),
        padding='valid'),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.DepthwiseConv2D(kernel_size=(3, 3), padding='same'),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.Conv2D(filters=16, kernel_size=(1, 1)),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.ZeroPadding2D(padding=1),
    keras.layers.DepthwiseConv2D(
        kernel_size=(3, 3), strides=(2, 2), padding='valid'),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.Conv2D(filters=32, kernel_size=(1, 1)),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.GlobalAveragePooling2D(),
    keras.layers.Flatten(),
    keras.layers.Dense(10)
])

# Compile and train the dense model for 10 epochs.
dense_model.compile(
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    optimizer='adam',
    metrics=['accuracy'])

dense_model.fit(
  ds_train,
  epochs=10,
  validation_data=ds_val)

# Evaluate the dense model.
_, dense_model_accuracy = dense_model.evaluate(ds_test, verbose=0)
Epoch 1/10
352/352 [==============================] - 16s 32ms/step - loss: 2.0021 - accuracy: 0.2716 - val_loss: 2.0871 - val_accuracy: 0.2106
Epoch 2/10
352/352 [==============================] - 9s 24ms/step - loss: 1.7056 - accuracy: 0.3779 - val_loss: 1.7434 - val_accuracy: 0.3364
Epoch 3/10
352/352 [==============================] - 8s 24ms/step - loss: 1.6049 - accuracy: 0.4144 - val_loss: 1.6463 - val_accuracy: 0.3834
Epoch 4/10
352/352 [==============================] - 8s 23ms/step - loss: 1.5485 - accuracy: 0.4359 - val_loss: 1.7435 - val_accuracy: 0.3808
Epoch 5/10
352/352 [==============================] - 8s 24ms/step - loss: 1.5099 - accuracy: 0.4516 - val_loss: 1.5217 - val_accuracy: 0.4300
Epoch 6/10
352/352 [==============================] - 9s 24ms/step - loss: 1.4806 - accuracy: 0.4632 - val_loss: 1.5367 - val_accuracy: 0.4404
Epoch 7/10
352/352 [==============================] - 8s 24ms/step - loss: 1.4548 - accuracy: 0.4724 - val_loss: 1.5238 - val_accuracy: 0.4470
Epoch 8/10
352/352 [==============================] - 8s 24ms/step - loss: 1.4401 - accuracy: 0.4782 - val_loss: 1.7590 - val_accuracy: 0.3754
Epoch 9/10
352/352 [==============================] - 8s 24ms/step - loss: 1.4255 - accuracy: 0.4859 - val_loss: 1.4854 - val_accuracy: 0.4598
Epoch 10/10
352/352 [==============================] - 8s 24ms/step - loss: 1.4127 - accuracy: 0.4889 - val_loss: 1.8831 - val_accuracy: 0.3708

희소 모델 구축

로부터의 지시에 사용하여 포괄적 인 가이드를 , 우리는 적용 tfmot.sparsity.keras.prune_low_magnitude 매개 변수를 해당 대상에-장치 가속을 통해 가지 치기 즉와 기능을 tfmot.sparsity.keras.PruneForLatencyOnXNNPack 정책.

prune_low_magnitude = tfmot.sparsity.keras.prune_low_magnitude

# Compute end step to finish pruning after after 5 epochs.
end_epoch = 5

num_iterations_per_epoch = len(ds_train)
end_step =  num_iterations_per_epoch * end_epoch

# Define parameters for pruning.
pruning_params = {
      'pruning_schedule': tfmot.sparsity.keras.PolynomialDecay(initial_sparsity=0.25,
                                                               final_sparsity=0.75,
                                                               begin_step=0,
                                                               end_step=end_step),
      'pruning_policy': tfmot.sparsity.keras.PruneForLatencyOnXNNPack()
}

# Try to apply pruning wrapper with pruning policy parameter.
try:
  model_for_pruning = prune_low_magnitude(dense_model, **pruning_params)
except ValueError as e:
  print(e)
Could not find a `GlobalAveragePooling2D` layer with `keepdims = True` in all output branches

호출 prune_low_magnitude 결과 ValueError 메시지와 함께 Could not find a GlobalAveragePooling2D layer with keepdims = True in all output branches . 이 메시지는 모델이 정책을 치기에 대해 지원하지 않음을 나타냅니다 tfmot.sparsity.keras.PruneForLatencyOnXNNPack 레이어 특히 GlobalAveragePooling2D 매개 변수가 필요합니다 keepdims = True . 하자 수정이와 다시 적용 prune_low_magnitude 기능.

fixed_dense_model = keras.Sequential([
    keras.layers.InputLayer(input_shape=(32, 32, 3)),
    keras.layers.ZeroPadding2D(padding=1),
    keras.layers.Conv2D(
        filters=8,
        kernel_size=(3, 3),
        strides=(2, 2),
        padding='valid'),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.DepthwiseConv2D(kernel_size=(3, 3), padding='same'),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.Conv2D(filters=16, kernel_size=(1, 1)),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.ZeroPadding2D(padding=1),
    keras.layers.DepthwiseConv2D(
        kernel_size=(3, 3), strides=(2, 2), padding='valid'),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.Conv2D(filters=32, kernel_size=(1, 1)),
    keras.layers.BatchNormalization(),
    keras.layers.ReLU(),
    keras.layers.GlobalAveragePooling2D(keepdims=True),
    keras.layers.Flatten(),
    keras.layers.Dense(10)
])

# Use the pretrained model for pruning instead of training from scratch.
fixed_dense_model.set_weights(dense_model.get_weights())

# Try to reapply pruning wrapper.
model_for_pruning = prune_low_magnitude(fixed_dense_model, **pruning_params)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/engine/base_layer.py:2233: 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 '

의 호출 prune_low_magnitude 모델이 완벽하게 지원됩니다 것을 의미 오류없이 완료 tfmot.sparsity.keras.PruneForLatencyOnXNNPack 정책과 사용을 촉진시킬 수 XNNPACK 스파 스 추론을 .

희소 모델 미세 조정

다음 치기 예를 조밀 한 모델의 가중치를 사용하여, 우리는 미세 조정 희소 모델. 25 % 희소성 (가중치의 25 %가 0으로 설정 됨)으로 모델의 미세 조정을 시작하고 75 % 희소성으로 끝납니다.

logdir = tempfile.mkdtemp()

callbacks = [
  tfmot.sparsity.keras.UpdatePruningStep(),
  tfmot.sparsity.keras.PruningSummaries(log_dir=logdir),
]

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

model_for_pruning.fit(
  ds_train,
  epochs=15,
  validation_data=ds_val,
  callbacks=callbacks)

# Evaluate the dense model.
_, pruned_model_accuracy = model_for_pruning.evaluate(ds_test, verbose=0)

print('Dense model test accuracy:', dense_model_accuracy)
print('Pruned model test accuracy:', pruned_model_accuracy)
Epoch 1/15
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/array_ops.py:5065: 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.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/array_ops.py:5065: 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.
352/352 [==============================] - 10s 25ms/step - loss: 1.4274 - accuracy: 0.4850 - val_loss: 1.5313 - val_accuracy: 0.4336
Epoch 2/15
352/352 [==============================] - 8s 24ms/step - loss: 1.4519 - accuracy: 0.4756 - val_loss: 2.2348 - val_accuracy: 0.3022
Epoch 3/15
352/352 [==============================] - 8s 23ms/step - loss: 1.4864 - accuracy: 0.4622 - val_loss: 1.7750 - val_accuracy: 0.3752
Epoch 4/15
352/352 [==============================] - 8s 24ms/step - loss: 1.4758 - accuracy: 0.4634 - val_loss: 1.7347 - val_accuracy: 0.3742
Epoch 5/15
352/352 [==============================] - 9s 24ms/step - loss: 1.4509 - accuracy: 0.4736 - val_loss: 1.6406 - val_accuracy: 0.4166
Epoch 6/15
352/352 [==============================] - 8s 24ms/step - loss: 1.4345 - accuracy: 0.4788 - val_loss: 1.7445 - val_accuracy: 0.3804
Epoch 7/15
352/352 [==============================] - 9s 24ms/step - loss: 1.4196 - accuracy: 0.4865 - val_loss: 2.5808 - val_accuracy: 0.2624
Epoch 8/15
352/352 [==============================] - 9s 25ms/step - loss: 1.4093 - accuracy: 0.4900 - val_loss: 1.5336 - val_accuracy: 0.4498
Epoch 9/15
352/352 [==============================] - 9s 24ms/step - loss: 1.4023 - accuracy: 0.4940 - val_loss: 1.9210 - val_accuracy: 0.3654
Epoch 10/15
352/352 [==============================] - 9s 24ms/step - loss: 1.3968 - accuracy: 0.4960 - val_loss: 1.5129 - val_accuracy: 0.4406
Epoch 11/15
352/352 [==============================] - 9s 24ms/step - loss: 1.3882 - accuracy: 0.4983 - val_loss: 1.7009 - val_accuracy: 0.3896
Epoch 12/15
352/352 [==============================] - 9s 25ms/step - loss: 1.3807 - accuracy: 0.5020 - val_loss: 2.3179 - val_accuracy: 0.2984
Epoch 13/15
352/352 [==============================] - 8s 24ms/step - loss: 1.3781 - accuracy: 0.5034 - val_loss: 1.6146 - val_accuracy: 0.4324
Epoch 14/15
352/352 [==============================] - 9s 25ms/step - loss: 1.3735 - accuracy: 0.5054 - val_loss: 2.3618 - val_accuracy: 0.3062
Epoch 15/15
352/352 [==============================] - 8s 24ms/step - loss: 1.3748 - accuracy: 0.5040 - val_loss: 1.5962 - val_accuracy: 0.4312
Dense model test accuracy: 0.37400001287460327
Pruned model test accuracy: 0.4334000051021576

로그는 레이어별로 희소성의 진행을 보여줍니다.

%tensorboard --logdir={logdir}

가지 치기로 미세 조정 한 후 테스트 정확도는 조밀 한 모델에 비해 약간의 향상 (43 % ~ 44 %)을 보여줍니다. 의 대기 시간 사용에 대한-장치 비교하자 TFLite 벤치 마크 .

모델 변환 및 벤치마킹

TFLite에 제거 된 모델을 변환하려면, 우리는 교체 할 필요가 PruneLowMagnitude 비아 원래 레이어 래퍼를 strip_pruning 기능. (제거 된 모델의 무게 때문에 또한, model_for_pruning ) 제로 주로, 우리는 최적화 적용 할 수 tf.lite.Optimize.EXPERIMENTAL_SPARSITY 효율적으로는 TFLite 모델을 결과를 저장합니다. 이 최적화 플래그는 조밀 한 모델에 필요하지 않습니다.

converter = tf.lite.TFLiteConverter.from_keras_model(dense_model)
dense_tflite_model = converter.convert()

_, dense_tflite_file = tempfile.mkstemp('.tflite')
with open(dense_tflite_file, 'wb') as f:
  f.write(dense_tflite_model)

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

converter = tf.lite.TFLiteConverter.from_keras_model(model_for_export)
converter.optimizations = [tf.lite.Optimize.EXPERIMENTAL_SPARSITY]
pruned_tflite_model = converter.convert()

_, pruned_tflite_file = tempfile.mkstemp('.tflite')
with open(pruned_tflite_file, 'wb') as f:
  f.write(pruned_tflite_model)
INFO:tensorflow:Assets written to: /tmp/tmp9is7dj3q/assets
INFO:tensorflow:Assets written to: /tmp/tmp9is7dj3q/assets
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/tmp9kw8dwue/assets
INFO:tensorflow:Assets written to: /tmp/tmp9kw8dwue/assets

의 지시에 따라 TFLite 모델 벤치마킹 도구 , 우리는 도구를 구축 조밀하고 정리 TFLite 모델 및 벤치 마크 장치의 두 모델과 함께 안드로이드 장치에 업로드합니다.

! adb shell /data/local/tmp/benchmark_model \
    --graph=/data/local/tmp/dense_model.tflite \
    --use_xnnpack=true \
    --num_runs=100 \
    --num_threads=1
/bin/bash: adb: command not found
! adb shell /data/local/tmp/benchmark_model \
    --graph=/data/local/tmp/pruned_model.tflite \
    --use_xnnpack=true \
    --num_runs=100 \
    --num_threads=1
/bin/bash: adb: command not found

픽셀 4 벤치 마크는 제거 된 모델의 조밀 한 모델과 12us에 대한 17us의 평균 추론 시간 결과. 온 - 디바이스 벤치 마크는 명확한 5us 또는 소형 모델에 대한 대기 시간이 30 % 향상을 보여줍니다. 우리의 경험에서, 더 큰 모델을 기반으로 MobileNetV3 또는 EfficientNet 라이트 쇼 유사한 성능 향상. 속도 향상은 전체 모델에 대한 1x1 컨볼 루션의 상대적 기여도에 따라 다릅니다.

결론

이 튜토리얼에서는 TF MOT API 및 XNNPack에 도입 된 새로운 기능을 사용하여 더 빠른 기기 성능을 위해 희소 모델을 만드는 방법을 보여줍니다. 이러한 희소 모델은 조밀 한 모델보다 작고 빠르며 품질을 유지하거나 능가합니다.

장치에 모델을 배포하는 데 특히 중요 할 수있는이 새로운 기능을 사용해 보는 것이 좋습니다.