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

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

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

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

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

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

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

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

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

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

pip install tflite-support

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

נירמול כמות

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




מתי להפעיל את התכונה?


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


נוסחה


Normalized_input = (input - average) / std
קביעת כמות לקלטים:
q = f / בראש + ZroPoint
פיגור לפלטים:
f = (q - ZeroPoint) * scale

איפה נמצאים הפרמטרים
מילוי על ידי יוצר המודל ומאוחסן במטא-נתונים של המודל, בתור NormalizationOptions מתמלא אוטומטית על ידי הממיר שלTFLite ומאוחסן בקובץ מודלים של tflite.
איך מוצאים את הפרמטרים? דרך MetadataExtractor API [2] דרך TFLite API Tensor [1] או דרך MetadataExtractor API [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.")

מידע על קלט / פלט

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

# 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 פלט באמצעות קובץ משויך באמצעות 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]

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

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

# 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 יש גם תמיכה בהצגת מטא-נתונים באמצעות התכונה Android Studio ML Binding.

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

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

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

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

זיהוי הקובץ Flatbuffers

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

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

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

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

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

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

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

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

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

כדי להשתמש בקובצי snapshot של לילה, ודאו שהוספתם את מאגר קובצי ה-sonatype של קובצי ה-sonatype.

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

public MetadataExtractor(ByteBuffer buffer);

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

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

public final boolean isMinimumParserVersionSatisfied();

מותר להעביר מודל ללא מטא-נתונים. עם זאת, שימוש בשיטות שנקראות מהמטא-נתונים יגרום לשגיאות בזמן הריצה. כדי לבדוק אם למודל יש מטא-נתונים, מפעילים את השיטה 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++ אפשר לעשות זאת באמצעות השיטה ModelMetadataExtractor::GetAssociatedFile:

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