تكميم صحيح بعد التدريب

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

ملخص

التكميم الصحيح هو استراتيجية تحسين تقوم بتحويل أرقام الفاصلة العائمة ذات 32 بت (مثل الأوزان ومخرجات التنشيط) إلى أقرب أرقام النقطة الثابتة 8 بت. هذه النتائج في نموذج أصغر حجما وزيادة سرعة الاستدلال، الذي هو قيمة للأجهزة منخفضة الطاقة مثل ميكروكنترولر . مطلوب تنسيق البيانات هذا أيضا صحيح فقط من المعجلات مثل حافة TPU .

في هذا البرنامج التعليمي، سوف تقوم بتدريب نموذج MNIST من الصفر، وتحويله إلى ملف Tensorflow لايت، وثبت قيمة باستخدام تكميم بعد التدريب . أخيرًا ، ستتحقق من دقة النموذج المحول ومقارنته بنموذج الطفو الأصلي.

لديك بالفعل العديد من الخيارات فيما يتعلق بالمقدار الذي تريده لتكميم النموذج. في هذا البرنامج التعليمي ، ستقوم بإجراء "تكميم صحيح كامل" ، والذي يحول جميع الأوزان ومخرجات التنشيط إلى بيانات عدد صحيح 8 بت - بينما قد تترك الاستراتيجيات الأخرى قدرًا من البيانات في النقطة العائمة.

لمعرفة المزيد حول الاستراتيجيات تكميم المختلفة، قرأت عن TensorFlow لايت النموذج الأمثل .

يثبت

من أجل تحديد كمية موترات الإدخال والإخراج ، نحتاج إلى استخدام واجهات برمجة التطبيقات المضافة في TensorFlow r2.3:

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

import tensorflow as tf
import numpy as np
assert float(tf.__version__[:3]) >= 2.3

قم بإنشاء نموذج TensorFlow

سنقوم ببناء نموذج بسيط لأرقام تصنيف من مجموعة البيانات MNIST .

لن يستغرق هذا التدريب وقتًا طويلاً لأنك تقوم بتدريب النموذج لمدة 5 فترات فقط ، والتي تتدرب بدقة تصل إلى 98٪ تقريبًا.

# 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.astype(np.float32) / 255.0
test_images = test_images.astype(np.float32) / 255.0

# Define the model architecture
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='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,
  epochs=5,
  validation_data=(test_images, test_labels)
)
Epoch 1/5
1875/1875 [==============================] - 5s 2ms/step - loss: 0.2519 - accuracy: 0.9311 - val_loss: 0.1106 - val_accuracy: 0.9664
Epoch 2/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0984 - accuracy: 0.9724 - val_loss: 0.0828 - val_accuracy: 0.9743
Epoch 3/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0746 - accuracy: 0.9785 - val_loss: 0.0640 - val_accuracy: 0.9795
Epoch 4/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0620 - accuracy: 0.9814 - val_loss: 0.0620 - val_accuracy: 0.9793
Epoch 5/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0540 - accuracy: 0.9837 - val_loss: 0.0624 - val_accuracy: 0.9795
<keras.callbacks.History at 0x7fb44c988c90>

قم بالتحويل إلى نموذج TensorFlow Lite

الآن يمكنك تحويل نموذج تدريبهم على شكل TensorFlow لايت باستخدام TFLiteConverter API، وتطبيق درجات متفاوتة من تكميم.

احذر من أن بعض إصدارات التكميم تترك بعض البيانات في شكل عائم. تظهر الأقسام التالية كل خيار بكميات متزايدة من التقسيم ، حتى نحصل على نموذج كامل البيانات int8 أو uint8. (لاحظ أننا نكرر بعض الرموز في كل قسم حتى تتمكن من رؤية جميع خطوات التقسيم لكل خيار.)

أولاً ، هذا نموذج محوّل بدون تكميم:

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

tflite_model = converter.convert()
2021-10-30 12:04:56.623151: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/tmp3os2tr3n/assets
2021-10-30 12:04:57.031317: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format.
2021-10-30 12:04:57.031355: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency.

إنه الآن نموذج TensorFlow Lite ، لكنه لا يزال يستخدم قيم عائمة 32 بت لجميع بيانات المعلمات.

تحويل باستخدام تكميم النطاق الديناميكي

الآن دعونا تمكن الافتراضي optimizations العلم ثبت قيمة جميع المعلمات الثابتة (مثل الأوزان):

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]

tflite_model_quant = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmpi7xibvaj/assets
INFO:tensorflow:Assets written to: /tmp/tmpi7xibvaj/assets
2021-10-30 12:04:57.597982: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format.
2021-10-30 12:04:57.598020: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency.

أصبح النموذج الآن أصغر قليلاً مع الأوزان الكمية ، لكن البيانات المتغيرة الأخرى لا تزال في تنسيق عائم.

تحويل باستخدام تعويم التكمية الاحتياطية

إلى ثبت قيمة البيانات متغير (مثل نموذج الإدخال / الإخراج وسيطة بين الطبقات)، تحتاج إلى توفير RepresentativeDataset . هذه وظيفة منشئ توفر مجموعة من بيانات الإدخال كبيرة بما يكفي لتمثيل القيم النموذجية. يسمح للمحول بتقدير نطاق ديناميكي لجميع البيانات المتغيرة. (لا يلزم أن تكون مجموعة البيانات فريدة مقارنة بمجموعة بيانات التدريب أو التقييم.) لدعم المدخلات المتعددة ، تكون كل نقطة بيانات تمثيلية عبارة عن قائمة ويتم تغذية العناصر الموجودة في القائمة إلى النموذج وفقًا لمؤشراتها.

def representative_data_gen():
  for input_value in tf.data.Dataset.from_tensor_slices(train_images).batch(1).take(100):
    # Model has only one input so each data point has one element.
    yield [input_value]

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_data_gen

tflite_model_quant = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmp3gwloj7n/assets
INFO:tensorflow:Assets written to: /tmp/tmp3gwloj7n/assets
2021-10-30 12:04:58.159142: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format.
2021-10-30 12:04:58.159181: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0

الآن يتم تحديد كمية جميع الأوزان والبيانات المتغيرة ، وأصبح النموذج أصغر بكثير مقارنةً بنموذج TensorFlow Lite الأصلي.

ومع ذلك ، للحفاظ على التوافق مع التطبيقات التي تستخدم تقليديًا موترات إدخال وإخراج النموذج العائم ، يترك محول TensorFlow Lite موترات إدخال وإخراج النموذج في حالة تعويم:

interpreter = tf.lite.Interpreter(model_content=tflite_model_quant)
input_type = interpreter.get_input_details()[0]['dtype']
print('input: ', input_type)
output_type = interpreter.get_output_details()[0]['dtype']
print('output: ', output_type)
input:  <class 'numpy.float32'>
output:  <class 'numpy.float32'>

عادةً ما يكون هذا جيدًا للتوافق ، ولكنه لن يكون متوافقًا مع الأجهزة التي تقوم فقط بعمليات تستند إلى عدد صحيح ، مثل Edge TPU.

بالإضافة إلى ذلك ، قد تترك العملية المذكورة أعلاه عملية بتنسيق عائم إذا لم يتضمن TensorFlow Lite تنفيذًا كميًا لتلك العملية. تسمح هذه الإستراتيجية بإكمال التحويل بحيث يكون لديك نموذج أصغر وأكثر كفاءة ، ولكن مرة أخرى ، لن يكون متوافقًا مع أجهزة ذات عدد صحيح فقط. (جميع العمليات في نموذج MNIST هذا لها تنفيذ كمي.)

لذلك لضمان نموذج عدد صحيح فقط من طرف إلى طرف ، تحتاج إلى معلمتين إضافيتين ...

تحويل باستخدام عدد صحيح فقط تكميم

لتقدير موترات الإدخال والإخراج ، وجعل المحول يرمي خطأ إذا واجه عملية لا يمكن تكميمها ، قم بتحويل النموذج مرة أخرى ببعض المعلمات الإضافية:

def representative_data_gen():
  for input_value in tf.data.Dataset.from_tensor_slices(train_images).batch(1).take(100):
    yield [input_value]

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_data_gen
# Ensure that if any ops can't be quantized, the converter throws an error
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
# Set the input and output tensors to uint8 (APIs added in r2.3)
converter.inference_input_type = tf.uint8
converter.inference_output_type = tf.uint8

tflite_model_quant = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmp8ygc2_3y/assets
INFO:tensorflow:Assets written to: /tmp/tmp8ygc2_3y/assets
2021-10-30 12:04:59.308505: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format.
2021-10-30 12:04:59.308542: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 3, output_inference_type: 3
WARNING:absl:For model inputs containing unsupported operations which cannot be quantized, the `inference_input_type` attribute will default to the original type.

يظل التكميم الداخلي كما هو مذكور أعلاه ، ولكن يمكنك أن ترى أن موترات الإدخال والإخراج أصبحت الآن تنسيقًا صحيحًا:

interpreter = tf.lite.Interpreter(model_content=tflite_model_quant)
input_type = interpreter.get_input_details()[0]['dtype']
print('input: ', input_type)
output_type = interpreter.get_output_details()[0]['dtype']
print('output: ', output_type)
input:  <class 'numpy.uint8'>
output:  <class 'numpy.uint8'>

الآن لديك نموذج صحيح الكم الذي يستخدمه عدد صحيح بيانات المدخلات والمخرجات التنسورات للنموذج، بحيث انها متوافقة مع الأجهزة صحيح الوحيد مثل حافة TPU .

احفظ النماذج كملفات

عليك ان تحصل على .tflite الملف إلى نشر النموذج الخاص بك على أجهزة أخرى. لذلك دعونا نحفظ النماذج المحولة إلى ملفات ثم نقوم بتحميلها عند تشغيل الاستدلالات أدناه.

import pathlib

tflite_models_dir = pathlib.Path("/tmp/mnist_tflite_models/")
tflite_models_dir.mkdir(exist_ok=True, parents=True)

# Save the unquantized/float model:
tflite_model_file = tflite_models_dir/"mnist_model.tflite"
tflite_model_file.write_bytes(tflite_model)
# Save the quantized model:
tflite_model_quant_file = tflite_models_dir/"mnist_model_quant.tflite"
tflite_model_quant_file.write_bytes(tflite_model_quant)
24280

قم بتشغيل نماذج TensorFlow Lite

الآن سنقوم بتشغيل استدلالات باستخدام TensorFlow لايت Interpreter لمقارنة دقتها نموذج.

أولاً ، نحتاج إلى دالة تدير الاستدلال بنموذج وصور معينة ، ثم ترجع التنبؤات:

# Helper function to run inference on a TFLite model
def run_tflite_model(tflite_file, test_image_indices):
  global test_images

  # Initialize the interpreter
  interpreter = tf.lite.Interpreter(model_path=str(tflite_file))
  interpreter.allocate_tensors()

  input_details = interpreter.get_input_details()[0]
  output_details = interpreter.get_output_details()[0]

  predictions = np.zeros((len(test_image_indices),), dtype=int)
  for i, test_image_index in enumerate(test_image_indices):
    test_image = test_images[test_image_index]
    test_label = test_labels[test_image_index]

    # Check if the input type is quantized, then rescale input data to uint8
    if input_details['dtype'] == np.uint8:
      input_scale, input_zero_point = input_details["quantization"]
      test_image = test_image / input_scale + input_zero_point

    test_image = np.expand_dims(test_image, axis=0).astype(input_details["dtype"])
    interpreter.set_tensor(input_details["index"], test_image)
    interpreter.invoke()
    output = interpreter.get_tensor(output_details["index"])[0]

    predictions[i] = output.argmax()

  return predictions

اختبر النماذج على صورة واحدة

سنقوم الآن بمقارنة أداء النموذج العائم والنموذج الكمي:

  • tflite_model_file هو الأصلي نموذج TensorFlow لايت مع بيانات الفاصلة العائمة.
  • tflite_model_quant_file هو نموذج آخر نحن تحويلها باستخدام تكميم-صحيح الوحيد (ويستخدم البيانات uint8 لالمدخلات والمخرجات).

لنقم بإنشاء وظيفة أخرى لطباعة تنبؤاتنا:

import matplotlib.pylab as plt

# Change this to test a different image
test_image_index = 1

## Helper function to test the models on one image
def test_model(tflite_file, test_image_index, model_type):
  global test_labels

  predictions = run_tflite_model(tflite_file, [test_image_index])

  plt.imshow(test_images[test_image_index])
  template = model_type + " Model \n True:{true}, Predicted:{predict}"
  _ = plt.title(template.format(true= str(test_labels[test_image_index]), predict=str(predictions[0])))
  plt.grid(False)

اختبر الآن نموذج الطفو:

test_model(tflite_model_file, test_image_index, model_type="Float")

بي إن جي

واختبار النموذج الكمي:

test_model(tflite_model_quant_file, test_image_index, model_type="Quantized")

بي إن جي

تقييم النماذج على جميع الصور

لنقم الآن بتشغيل كلا النموذجين باستخدام جميع صور الاختبار التي قمنا بتحميلها في بداية هذا البرنامج التعليمي:

# Helper function to evaluate a TFLite model on all images
def evaluate_model(tflite_file, model_type):
  global test_images
  global test_labels

  test_image_indices = range(test_images.shape[0])
  predictions = run_tflite_model(tflite_file, test_image_indices)

  accuracy = (np.sum(test_labels== predictions) * 100) / len(test_images)

  print('%s model accuracy is %.4f%% (Number of test samples=%d)' % (
      model_type, accuracy, len(test_images)))

تقييم النموذج العائم:

evaluate_model(tflite_model_file, model_type="Float")
Float model accuracy is 97.9500% (Number of test samples=10000)

تقييم النموذج الكمي:

evaluate_model(tflite_model_quant_file, model_type="Quantized")
Quantized model accuracy is 97.9300% (Number of test samples=10000)

إذن لديك الآن عدد صحيح كمي لنموذج مع عدم وجود اختلاف تقريبًا في الدقة ، مقارنةً بنموذج الطفو.

لمعرفة المزيد حول استراتيجيات تكميم أخرى، قرأت عن TensorFlow لايت النموذج الأمثل .