เข้าร่วม TensorFlow ที่ Google I/O วันที่ 11-12 พฤษภาคม ลงทะเบียนตอนนี้

การหาจำนวนเต็มหลังการฝึก

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

ภาพรวม

การหาจำนวนเต็มเป็นกลยุทธ์การปรับให้เหมาะสมที่แปลงตัวเลขทศนิยม 32 บิต (เช่นน้ำหนักและเอาต์พุตการเปิดใช้งาน) เป็นตัวเลขจุดคงที่ 8 บิตที่ใกล้ที่สุด ผลนี้ในรูปแบบที่มีขนาดเล็กลงและเพิ่มความเร็วในการสรุปอิงซึ่งเป็นประโยชน์สำหรับอุปกรณ์ที่ใช้พลังงานต่ำเช่น ไมโครคอนโทรลเลอร์ รูปแบบข้อมูลนี้ถูกต้องตามจำนวนเต็มเท่านั้นเร่งเช่น ขอบ TPU

ในการกวดวิชานี้คุณจะฝึกรูปแบบ MNIST จากรอยขีดข่วนแปลงเป็นไฟล์ Tensorflow Lite และ quantize โดยใช้ ควอนโพสต์การฝึกอบรม สุดท้าย คุณจะตรวจสอบความถูกต้องของแบบจำลองที่แปลงแล้วและเปรียบเทียบกับแบบจำลองลูกลอยดั้งเดิม

จริงๆ แล้วคุณมีหลายทางเลือกว่าต้องการหาปริมาณของแบบจำลองเท่าใด ในบทช่วยสอนนี้ คุณจะดำเนินการ "การหาปริมาณเต็มจำนวนเต็ม" ซึ่งจะแปลงน้ำหนักและเอาต์พุตการเปิดใช้งานทั้งหมดเป็นข้อมูลจำนวนเต็ม 8 บิต ในขณะที่กลยุทธ์อื่นๆ อาจทำให้ข้อมูลจำนวนหนึ่งอยู่ในทศนิยม

ต้องการเรียนรู้เพิ่มเติมเกี่ยวกับกลยุทธ์ quantization ต่างๆอ่านเกี่ยวกับ การเพิ่มประสิทธิภาพรุ่น TensorFlow Lite

ติดตั้ง

ในการหาปริมาณทั้งเทนเซอร์อินพุตและเอาต์พุต เราจำเป็นต้องใช้ API ที่เพิ่มใน 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 Lite ใช้ TFLiteConverter API และใช้องศาที่แตกต่างของควอน

ระวังว่าการควอนไทซ์บางรุ่นปล่อยให้ข้อมูลบางส่วนอยู่ในรูปแบบทศนิยม ดังนั้นส่วนต่อไปนี้จะแสดงแต่ละตัวเลือกด้วยจำนวนที่เพิ่มขึ้นของ quantization จนกว่าเราจะได้โมเดลที่เป็นข้อมูล 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 ธง quantize พารามิเตอร์ถาวรทั้งหมด (เช่นน้ำหนัก):

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.

ขณะนี้โมเดลมีขนาดเล็กลงเล็กน้อยโดยมีน้ำหนักเชิงปริมาณ แต่ข้อมูลตัวแปรอื่นๆ ยังอยู่ในรูปแบบโฟลต

แปลงโดยใช้ float fallback quantization

เพื่อ quantize ข้อมูลตัวแปร (เช่นรูปแบบอินพุต / เอาต์พุตและตัวกลางระหว่างชั้น), คุณต้องให้ 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 Converter จะปล่อยให้เทนเซอร์อินพุตและเอาต์พุตของโมเดลเป็นแบบลอย:

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

นอกจากนี้ กระบวนการข้างต้นอาจทำให้การดำเนินการอยู่ในรูปแบบ float หาก TensorFlow Lite ไม่ได้รวมการใช้งานเชิงปริมาณสำหรับการดำเนินการนั้น กลยุทธ์นี้ช่วยให้การแปลงเสร็จสมบูรณ์ เพื่อให้คุณมีโมเดลที่เล็กกว่าและมีประสิทธิภาพมากกว่า แต่จะใช้งานไม่ได้กับฮาร์ดแวร์ที่เป็นจำนวนเต็มเท่านั้น (ops ทั้งหมดในโมเดล 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'>

ตอนนี้คุณมีจำนวนเต็ม quantized รุ่นที่ใช้จำนวนเต็มข้อมูลสำหรับรูปแบบของอินพุตและเอาต์พุตเทนเซอร์เพื่อให้มันเข้ากันได้กับฮาร์ดแวร์จำนวนเต็มเท่านั้นเช่น ขอบ 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 Lite 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 Lite กับข้อมูลจุดลอยตัว
  • tflite_model_quant_file เป็นรุ่นสุดท้ายที่เราแปลงโดยใช้ควอนจำนวนเต็มเท่านั้น (มันใช้ข้อมูล uint8 สำหรับ input และ output)

มาสร้างฟังก์ชันอื่นเพื่อพิมพ์คำทำนายของเรากัน:

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")

png

และทดสอบแบบจำลองเชิงปริมาณ:

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

png

ประเมินรุ่นในภาพทั้งหมด

ตอนนี้ ให้เรียกใช้ทั้งสองรุ่นโดยใช้ภาพทดสอบทั้งหมดที่เราโหลดในตอนต้นของบทช่วยสอนนี้:

# 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)

ดังนั้นตอนนี้คุณจึงมีจำนวนเต็มในเชิงปริมาณของแบบจำลองโดยแทบไม่มีความแตกต่างในความแม่นยำเลย เมื่อเทียบกับแบบจำลองโฟลต

ต้องการเรียนรู้เพิ่มเติมเกี่ยวกับกลยุทธ์ quantization อื่น ๆ อ่านเกี่ยวกับ การเพิ่มประสิทธิภาพรุ่น TensorFlow Lite