הוספת מטא-נתונים למודלים של TensorFlow Lite

המטא-נתונים של TensorFlow Lite מספקים תקן לתיאורי מודלים. המטא-נתונים הם מקור חשוב לידע על מה שהמודל עושה ועל פרטי הקלט / פלט שלו. המטא-נתונים מורכבים

כל המודלים של התמונות שפורסמו ב-Kaggle Models מאוכלסים במטא-נתונים.

מודל עם פורמט מטא-נתונים

model_with_metadata
איור 1. מודל TFLite עם מטא-נתונים וקבצים משויכים.

המטא-נתונים של המודל מוגדרים ב-metadata_schema.fbs, קובץ FlatBuffer. כפי שמוצג באיור 1, הוא מאוחסן בשדה המטא-נתונים בסכימת מודל TFLite, מתחת לשם "TFLITE_METADATA". לחלק מהמודלים עשויים להיות קבצים משויכים, כמו קובצי תוויות של סיווג. הקבצים האלו משורשרים לסוף קובץ המודל המקורי כ-ZIP, באמצעות מצב 'append' ZipFile (במצב 'a'). כלי התרגום של TFLite יכול להשתמש בפורמט הקובץ החדש באותו אופן כמו קודם. מידע נוסף זמין במאמר סידור הקבצים המשויכים.

בהוראות שבהמשך מוסבר איך לאכלס, להמחיש ולקרוא מטא-נתונים.

הגדרת כלי המטא-נתונים

לפני הוספת המטא-נתונים למודל, צריך להגדיר סביבת תכנות באמצעות Python כדי להריץ את TensorFlow. כאן תוכלו למצוא מדריך מפורט להגדרה הזו.

אחרי שמגדירים את סביבת התכנות Python, תצטרכו להתקין כלים נוספים:

pip install tflite-support

הכלים למטא-נתונים של TensorFlow Lite תומכים ב-Python 3.

הוספת מטא-נתונים באמצעות Flatbuffers Python API

המטא-נתונים של המודל כוללים שלושה חלקים בסכימה:

  1. מידע על המודל – תיאור כללי של המודל ופריטים כמו תנאי רישיון. ראו ModelMetadata.
    1. מידע על קלט – תיאור של קלט ועיבוד מראש, כמו נירמול. ראו SubGraphMetadata.input_tensor_metadata.
      1. פרטי פלט – תיאור של הפלט והעיבוד לאחר עיבוד, למשל מיפוי לתוויות. אפשר לעיין במאמר SubGraphMetadata.output_tensor_metadata.

נכון לעכשיו, ב-TensorFlow Lite יש תמיכה רק בתת-סעיף יחיד, לכן במחולל הקוד של TensorFlow Lite ובתכונת הקישור ל-ML של Android Studio ייעשה שימוש ב-ModelMetadata.name וב-ModelMetadata.description במקום ב-SubGraphMetadata.name וב-SubGraphMetadata.description בהצגת המטא-נתונים וביצירת קוד.

סוגי קלט / פלט נתמכים

המטא-נתונים של TensorFlow Lite לקלט ולפלט לא מתוכננים לפי סוגי מודלים ספציפיים, אלא סוגי קלט ופלט. אפשר להעביר את המודל באופן פונקציונלי בלי להשתמש בו, כל עוד סוגי הקלט והפלט כוללים את הדברים הבאים או שילוב שלהם, ויש תמיכה במטא-נתונים של TensorFlow Lite:

  • תכונה – מספרים שהם מספרים שלמים לא חתומים או מספרים מסוג float32.
  • תמונה – מטא-נתונים תומכים כרגע בתמונות RGB ובגווני אפור.
  • תיבה מתוחכמת - תיבות תוחמות עם צורה מלבנית. הסכימה תומכת במגוון של סכימות מספור.

אורזים את הקבצים המשויכים

מודלים של TensorFlow Lite עשויים להגיע עם קבצים משויכים שונים. לדוגמה, מודלים של שפה טבעית הם בדרך כלל קובצי vocab שממפים קטעי מילים למזהי מילים. מודלים של סיווג עשויים לכלול קובצי תוויות שמציינים קטגוריות של אובייקטים. המודל לא יפעל כראוי בלי הקבצים המשויכים (אם יש כאלה).

עכשיו אפשר לקבץ את הקבצים המשויכים עם המודל באמצעות ספריית המטא-נתונים של Python. מודל TensorFlow Lite החדש הופך לקובץ ZIP שמכיל גם את המודל וגם את הקבצים המשויכים. אפשר לפרוק אותו בכלי ZIP נפוצים. פורמט המודל החדש הזה ממשיך להשתמש באותה סיומת קובץ, .tflite. הוא תואם ל-framework ולתרגום שיחה פעילה של TFLite. למידע נוסף, ראו טעינת מטא-נתונים וקבצים משויכים למודל.

אפשר לתעד את פרטי הקובץ המשויכים במטא-נתונים. בהתאם לסוג הקובץ ולמקום שאליו הקובץ מצורף (למשל, ModelMetadata, SubGraphMetadata ו-TensorMetadata), מחולל הקוד של TensorFlow Lite ל-Android עשוי להחיל על האובייקט באופן אוטומטי את העיבוד התואם של לפני/אחרי. פרטים נוספים מופיעים בקטע <Codegen usage> של כל סוג קובץ משויך בסכימה.

פרמטרים של נירמול וקונטיזציה

נירמול הוא שיטה נפוצה לעיבוד נתונים מראש בלמידת מכונה. מטרת הנירמול היא לשנות את הערכים לסולם משותף, בלי לעוות את ההבדלים בין טווחי הערכים.

קוונטיזציה של מודל היא שיטה שמאפשרת ייצוגים פחות מדויקים של משקולות, ואופציונלית גם הפעלה של אחסון וגם של חישוב.

מבחינת עיבוד מראש ועיבוד לאחר מכן, נירמול וכימת הם שני שלבים בלתי תלויים. הנה הפרטים.

נירמול כימות

דוגמה לערכי הפרמטר של תמונת הקלט ב-MobileNet למודלים צפים וכמויים, בהתאמה.
מודל צף:
- ממוצע: 127.5
- std: 127.5
מודל כמות:
- ממוצע: 127.5
- std: 127.5
מודל צף:
- 0Point: 0
- קנה מידה: 1.0
מודל כמות:
- אפסPoint: 128.0
- קנה מידה:0.0078125f




מתי להפעיל?


קלט: אם נתוני הקלט מנורמלים באימון, צריך לנרמל בהתאם את נתוני הקלט של ההסקה.
פלט: לא יתבצע נירמול של נתוני הפלט באופן כללי.
אין צורך לכימות במודלים צפופים.
מודל כמותי עשוי או לא צריך לכימות בתהליך עיבוד לפני/אחרי. הוא תלוי בסוג הנתונים של רכיבי הקדש של הקלט/פלט.
- רכיבי floating-tensor: לא צריך ליצור כימות בעיבוד לפני/אחרי. פעולות כמויות ופעולות מהירות מוכנסות לתרשים המודל.
- int8/uint8 tensors: נדרש קוונטיזציה של עיבוד נתונים לפני/אחרי.


נוסחה


regular_input = (input - mean) / std
יצירת כמות לקלט:
q = f / קנה מידה + zeroPoint
מכפלה לפלטים:
f = (q -0Point) * קנה מידה

איפה נמצאים הפרמטרים
ממולא על ידי יוצר המודל ומאוחסן במטא-נתונים של המודל, באופן הבא: NormalizationOptions מילוי אוטומטי על ידי ממיר TFLite, ומאוחסן בקובץ מודל tflite.
איך מקבלים את הפרמטרים? באמצעות API של MetadataExtractor [2] באמצעות TFLite Tensor API [1] או דרך API של MetadataExtractor [2]
האם למודלים צפים וכמויות יש ערך זהה? כן, למודלי ציפה וכמויות יש אותם פרמטרים של נירמול לא, אין צורך לכימות את המודל הצף.
האם מחולל קוד TFLite או קישור ל-Android Studio ML יוצרים אותו באופן אוטומטי בעיבוד נתונים?
כן

כן

[1] TensorFlow Lite Java API ו-TensorFlow Lite C++ API.
[2] הספרייה לחילוץ מטא-נתונים

כשמעבדים נתוני תמונה במודלים של uint8, לפעמים המערכת מדלגת על נירמול וכימות. אפשר לעשות זאת כאשר ערכי הפיקסלים הם בטווח של [0, 255]. אבל באופן כללי, צריך תמיד לעבד את הנתונים בהתאם לפרמטרים של הנירמול והקוונטיזציה, במקרים הרלוונטיים.

דוגמאות

כאן תוכלו לראות דוגמאות לאופן שבו צריך לאכלס את המטא-נתונים לסוגים שונים של מודלים:

סיווג תמונות

מורידים את הסקריפט כאן, שמאכלס את המטא-נתונים אל mobilenet_v1_0.75_160_quantized.tflite. מריצים את הסקריפט כך:

python ./metadata_writer_for_image_classifier.py \
    --model_file=./model_without_metadata/mobilenet_v1_0.75_160_quantized.tflite \
    --label_file=./model_without_metadata/labels.txt \
    --export_directory=model_with_metadata

כדי לאכלס מטא-נתונים במודלים אחרים של סיווג תמונות, מוסיפים לסקריפט את מפרטי המודלים, כמו זה. שאר החלקים במדריך הזה ידגישו כמה מהקטעים המרכזיים בדוגמה לסיווג תמונות, כדי להדגים את הרכיבים העיקריים.

ניתוח מעמיק של הדוגמה לסיווג תמונות

פרטי דגם

מטא-נתונים מתחילים ביצירת פרטי מודל חדש:

from tflite_support import flatbuffers
from tflite_support import metadata as _metadata
from tflite_support import metadata_schema_py_generated as _metadata_fb

""" ... """
"""Creates the metadata for an image classifier."""

# Creates model info.
model_meta = _metadata_fb.ModelMetadataT()
model_meta.name = "MobileNetV1 image classifier"
model_meta.description = ("Identify the most prominent object in the "
                          "image from a set of 1,001 categories such as "
                          "trees, animals, food, vehicles, person etc.")
model_meta.version = "v1"
model_meta.author = "TensorFlow"
model_meta.license = ("Apache License. Version 2.0 "
                      "http://www.apache.org/licenses/LICENSE-2.0.")

פרטי הקלט / פלט

בקטע הזה מוסבר איך לתאר את חתימת הקלט והפלט של המודל. מחוללי קוד אוטומטיים יכולים להשתמש במטא-נתונים האלה כדי ליצור קוד לפני ואחרי העיבוד. כדי ליצור פרטי קלט או פלט של טנצור:

# Creates input info.
input_meta = _metadata_fb.TensorMetadataT()

# Creates output info.
output_meta = _metadata_fb.TensorMetadataT()

קלט תמונה

שימוש בתמונה הוא סוג קלט נפוץ ללמידת מכונה. המטא-נתונים של TensorFlow Lite תומכים במידע כמו מרחב צבעים ומידע לפני עיבוד, כמו נירמול. מידות התמונה לא נדרשות למפרט ידני, כי הוא כבר סופק על ידי הצורה של img_tensor, ואפשר להסיק אותו באופן אוטומטי.

input_meta.name = "image"
input_meta.description = (
    "Input image to be classified. The expected image is {0} x {1}, with "
    "three channels (red, blue, and green) per pixel. Each value in the "
    "tensor is a single byte between 0 and 255.".format(160, 160))
input_meta.content = _metadata_fb.ContentT()
input_meta.content.contentProperties = _metadata_fb.ImagePropertiesT()
input_meta.content.contentProperties.colorSpace = (
    _metadata_fb.ColorSpaceType.RGB)
input_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.ImageProperties)
input_normalization = _metadata_fb.ProcessUnitT()
input_normalization.optionsType = (
    _metadata_fb.ProcessUnitOptions.NormalizationOptions)
input_normalization.options = _metadata_fb.NormalizationOptionsT()
input_normalization.options.mean = [127.5]
input_normalization.options.std = [127.5]
input_meta.processUnits = [input_normalization]
input_stats = _metadata_fb.StatsT()
input_stats.max = [255]
input_stats.min = [0]
input_meta.stats = input_stats

פלט התווית

אפשר למפות את התווית למשתנה פלט באמצעות קובץ משויך באמצעות TENSOR_AXIS_LABELS.

# Creates output info.
output_meta = _metadata_fb.TensorMetadataT()
output_meta.name = "probability"
output_meta.description = "Probabilities of the 1001 labels respectively."
output_meta.content = _metadata_fb.ContentT()
output_meta.content.content_properties = _metadata_fb.FeaturePropertiesT()
output_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.FeatureProperties)
output_stats = _metadata_fb.StatsT()
output_stats.max = [1.0]
output_stats.min = [0.0]
output_meta.stats = output_stats
label_file = _metadata_fb.AssociatedFileT()
label_file.name = os.path.basename("your_path_to_label_file")
label_file.description = "Labels for objects that the model can recognize."
label_file.type = _metadata_fb.AssociatedFileType.TENSOR_AXIS_LABELS
output_meta.associatedFiles = [label_file]

יצירת Flatbuffers של המטא-נתונים

הקוד הבא משלב את פרטי המודל עם פרטי הקלט והפלט:

# Creates subgraph info.
subgraph = _metadata_fb.SubGraphMetadataT()
subgraph.inputTensorMetadata = [input_meta]
subgraph.outputTensorMetadata = [output_meta]
model_meta.subgraphMetadata = [subgraph]

b = flatbuffers.Builder(0)
b.Finish(
    model_meta.Pack(b),
    _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)
metadata_buf = b.Output()

חבילות מטא-נתונים וקבצים משויכים במודל

אחרי שיוצרים את המטא-נתונים Flatbuffers, המטא-נתונים וקובץ התווית נכתבים בקובץ ה-TFLite באמצעות השיטה populate:

populator = _metadata.MetadataPopulator.with_model_file(model_file)
populator.load_metadata_buffer(metadata_buf)
populator.load_associated_files(["your_path_to_label_file"])
populator.populate()

אפשר לדחוס כמה קבצים משויכים למודל באמצעות load_associated_files. עם זאת, הוא נדרש לארוז לפחות את הקבצים המתועדים במטא-נתונים. בדוגמה הזו, חובה לארוז את קובץ התווית.

הצגה חזותית של מטא-נתונים

אפשר להשתמש ב-Netron כדי להמחיש את המטא-נתונים באופן חזותי, או לקרוא את המטא-נתונים ממודל TensorFlow Lite בפורמט json באמצעות MetadataDisplayer:

displayer = _metadata.MetadataDisplayer.with_model_file(export_model_path)
export_json_file = os.path.join(FLAGS.export_directory,
                                os.path.splitext(model_basename)[0] + ".json")
json_file = displayer.get_metadata_json()
# Optional: write out the metadata as a json file
with open(export_json_file, "w") as f:
  f.write(json_file)

ב-Android Studio יש גם תמיכה בהצגת מטא-נתונים באמצעות תכונת הקישור ל-ML ב-Android Studio.

ניהול גרסאות של מטא-נתונים

הגרסאות של סכימת המטא-נתונים הן לפי מספר ניהול הגרסאות הסמנטי, שעוקב אחרי השינויים בקובץ הסכימה, וגם באמצעות זיהוי הקובץ Flatbuffers, שמציין את התאימות האמיתית של הגרסה.

המספר לניהול גרסאות סמנטי

הגרסה של סכימת המטא-נתונים מבוססת על מספר ניהול הגרסאות הסמנטי, כמו MAJOR.MINOR.PATCH. הוא עוקב אחרי שינויים בסכימה בהתאם לכללים המפורטים כאן. להיסטוריית השדות שנוספה אחרי גרסה 1.0.0

זיהוי הקובץ Flatbuffers

ניהול גרסאות סמנטי מבטיח תאימות אם פועלים לפי הכללים, אבל הוא לא מרמז על חוסר תאימות אמיתי. כשמעלים את המספר הראשי (MAJOR), זה לא בהכרח אומר שהתאימות לאחור לא תקינה. לכן אנחנו משתמשים בזיהוי הקובץ של Flatbuffers, file_identifier, כדי לציין את התאימות האמיתית של סכימת המטא-נתונים. מזהה הקובץ הוא באורך של 4 תווים בדיוק. הוא קבוע לסכימת מטא-נתונים מסוימת והמשתמשים לא יכולים לשנות אותו. אם צריך לשבור מסיבה כלשהי את התאימות לאחור של סכימת המטא-נתונים, הקובץ file_identifier ישתנה, למשל, מ-M001 ל-M002. צפוי שה-File_identifier ישתנה בתדירות הרבה יותר נמוכה מה-Metadata_version.

הגרסה המינימלית הדרושה של מנתח המטא-נתונים

הגרסה המינימלית הדרושה של מנתח המטא-נתונים היא הגרסה המינימלית של מנתח המטא-נתונים (הקוד שנוצר על ידי Flatbuffers) שיכול לקרוא את מערך המטא-נתונים באופן מלא. הגרסה היא למעשה מספר הגרסה הגדול ביותר מבין הגרסאות של כל השדות שאוכלסו, והגרסה התואמת הקטנה ביותר שמצוינת באמצעות מזהה הקובץ. הגרסה המינימלית הנדרשת של מנתח המטא-נתונים מאוכלסת באופן אוטומטי על ידי MetadataPopulator כשהמטא-נתונים מאוכלסים במודל TFLite. מידע נוסף על אופן השימוש בגרסה המינימלית הנדרשת של מנתח המטא-נתונים זמין בכלי לחילוץ מטא-נתונים.

קריאת המטא-נתונים ממודלים

ספריית חילוץ המטא-נתונים היא כלי נוח לקרוא את המטא-נתונים ואת הקבצים המשויכים ממודלים בפלטפורמות שונות (ראו גרסת Java וגרסת C++). אתם יכולים ליצור כלי משלכם לחילוץ מטא-נתונים בשפות אחרות באמצעות הספרייה שלFlatbuffers.

קריאת המטא-נתונים ב-Java

כדי להשתמש בספרייה של חילוץ מטא-נתונים באפליקציה ל-Android, מומלץ להשתמש ב-TensorFlow Lite Metadata AAR שמתארח ב-MavenCentral. הוא מכיל את המחלקה MetadataExtractor, וגם את קישורי FlatBuffers של Java לסכימת המטא-נתונים ולסכימת המודל.

אפשר לציין זאת ביחסי התלות של build.gradle באופן הבא:

dependencies {
    implementation 'org.tensorflow:tensorflow-lite-metadata:0.1.0'
}

כדי להשתמש בתמונות מצב ליליות, ודאו שהוספתם את מאגר תמונת המצב של Sonatype.

אפשר לאתחל אובייקט MetadataExtractor באמצעות ByteBuffer שמצביע למודל:

public MetadataExtractor(ByteBuffer buffer);

ה-ByteBuffer צריך להישאר ללא שינוי כל משך החיים של האובייקט MetadataExtractor. האתחול עלול להיכשל אם מזהה הקובץ של Flatbuffers של המטא-נתונים של המודל לא תואם לזה של מנתח המטא-נתונים. למידע נוסף, ראו ניהול גרסאות של מטא-נתונים.

עם מזהי קבצים תואמים, הכלי לחילוץ מטא-נתונים יקרא בהצלחה מטא-נתונים שנוצרו מכל הסכימות בעבר ובעתיד בגלל מנגנון התאימות קדימה ואחורה של Flatbuffers. עם זאת, אי אפשר לחלץ שדות מסכימות עתידיות על ידי כלים ישנים יותר לחילוץ מטא-נתונים. גרסת המנתח המינימלית הדרושה של המטא-נתונים מציינת את הגרסה המינימלית של מנתח המטא-נתונים שיכול לקרוא את המטא-נתונים של Flatbuffers באופן מלא. אפשר להשתמש בשיטה הבאה כדי לבדוק אם מתקיים התנאי המינימלי של גרסת המנתח:

public final boolean isMinimumParserVersionSatisfied();

מותר להעביר מודל ללא מטא-נתונים. עם זאת, הפעלת methods שקוראות מהמטא-נתונים תגרום לשגיאות בסביבת זמן הריצה. כדי לבדוק אם למודל יש מטא-נתונים, מפעילים את ה-method hasMetadata:

public boolean hasMetadata();

MetadataExtractor מספק פונקציות נוחות לקבלת המטא-נתונים של רכיבי הקלט/פלט. לדוגמה,

public int getInputTensorCount();
public TensorMetadata getInputTensorMetadata(int inputIndex);
public QuantizationParams getInputTensorQuantizationParams(int inputIndex);
public int[] getInputTensorShape(int inputIndex);
public int getoutputTensorCount();
public TensorMetadata getoutputTensorMetadata(int inputIndex);
public QuantizationParams getoutputTensorQuantizationParams(int inputIndex);
public int[] getoutputTensorShape(int inputIndex);

למרות שסכימת המודל TensorFlow Lite תומכת במספר תתי-גרפים, בשלב זה, התרגום של TFLite תומך רק בתת-תרשים אחד. לכן, MetadataExtractor משמט את אינדקס המשנה כארגומנט קלט בשיטות שלו.

קריאת הקבצים המשויכים ממודלים

מודל TensorFlow Lite עם מטא-נתונים וקבצים משויכים הוא בעצם קובץ ZIP שאפשר לפרק בכלי ZIP נפוצים כדי לקבל את הקבצים המשויכים. לדוגמה, אפשר לחלץ את קובץ התווית mobilenet_v1_0.75_160_quantized ולחלץ את קובץ התווית במודל באופן הבא:

$ unzip mobilenet_v1_0.75_160_quantized_1_metadata_1.tflite
Archive:  mobilenet_v1_0.75_160_quantized_1_metadata_1.tflite
 extracting: labels.txt

אפשר גם לקרוא את הקבצים המשויכים באמצעות הספרייה 'חילוץ מטא-נתונים'.

ב-Java, מעבירים את שם הקובץ ל-method MetadataExtractor.getAssociatedFile:

public InputStream getAssociatedFile(String fileName);

באופן דומה, ב-C++ אפשר לעשות זאת באמצעות ה-method ModelMetadataExtractor::GetAssociatedFile:

tflite::support::StatusOr<absl::string_view> GetAssociatedFile(
      const std::string& filename) const;