הצג באתר TensorFlow.org | הפעל בגוגל קולאב | צפה במקור ב-GitHub | הורד מחברת |
סקירה כללית
קוונטיזציה של מספרים שלמים היא אסטרטגיית אופטימיזציה הממירה מספרי נקודה צפה של 32 סיביות (כגון משקלים ויציאות הפעלה) למספרי הנקודה הקבועה הקרובה ביותר של 8 סיביות. תוצאות זה במודל קטן ומהירות היסקים גדל, שהוא בעל ערך עבור התקנים בעלי צריכת חשמל נמוכה כגון מיקרו-בקרים . פורמט נתונים זה גם נדרש על ידי מספר שלם בלבד מאיצים כמו אדג TPU .
במדריך זה, תוכל לאמן מודל MNIST מאפס, להמיר אותו לקובץ לייט Tensorflow, ו לקוואנטיזציה אותו באמצעות קוונטיזציה-הכשרה פוסט . לבסוף, תבדוק את הדיוק של הדגם שהוסב ותשווה אותו לדגם המצוף המקורי.
למעשה יש לך כמה אפשרויות לגבי כמה אתה רוצה לכמת דגם. במדריך זה, תבצעו "כימת מספרים שלמים מלאה", אשר ממירה את כל המשקולות ותפוקות ההפעלה לנתונים שלמים של 8 סיביות - בעוד שאסטרטגיות אחרות עשויות להשאיר כמות מסוימת של נתונים בנקודה צפה.
כדי ללמוד עוד על אסטרטגיות קוונטיזציה השונות, לקרוא על אופטימיזציה מודל לייט TensorFlow .
להכין
על מנת לכמת את טנסור הקלט והפלט, עלינו להשתמש בממשקי 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 לייט באמצעות 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.
המודל עכשיו קצת יותר קטן עם משקלים כמותיים, אבל נתונים משתנים אחרים עדיין בפורמט צף.
המרה באמצעות קוונטיזציה מסוג Float Fallback
לקוואנטיזציה הנתונים משתנה (כגון דגם קלט / פלט ביניים בין השכבות), אתה צריך לספק 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 .