צינורות נתונים במהירות TPU: tf.data.Dataset ו-TFRecords

צינורות נתונים במהירות TPU:
tf.data.Dataset ו-TFRecords

מידע על Codelab זה

subjectהעדכון האחרון: אוק׳ 4, 2021
account_circleנכתב על ידי Martin Görner - @martin_gorner

1.‏ סקירה כללית

מעבדי ה-TPU מהירים מאוד. הזרם של נתוני האימון חייב להתאים למהירות האימון שלהם. בשיעור ה-Lab הזה תלמדו איך לטעון נתונים מ-GCS באמצעות tf.data.Dataset API כדי להזין את ה-TPU.

שיעור ה-Lab הזה הוא חלק 1 של "Keras on TPU" סדרות. אפשר לעשות את זה בסדר הבא או בנפרד.

ca8cc21f6838eccc.png

מה תלמדו

  • להשתמש ב-API של tf.data.Dataset כדי לטעון נתוני אימון.
  • להשתמש בפורמט TFRecord כדי לטעון נתוני אימון ביעילות מ-GCS

משוב

אם תיתקלו בבעיה במעבדת הקוד הזו, נשמח לשמוע על כך. אפשר לשלוח משוב דרך 'בעיות ב-GitHub' [feedback link].

2.‏ התחלה מהירה של Google Colaboratory

בשיעור ה-Lab הזה נעשה שימוש ב'שיתוף פעולה עם Google' ולא נדרשת הגדרה כלשהי מצדך. Colaboratory היא פלטפורמת notebook אונליין למטרות חינוכיות. התוכנית כוללת הכשרה בחינם ביחידות CPU (מעבדים), GPU ו-TPU.

688858c21e3beff2.png

אפשר לפתוח את ה-notebook לדוגמה ולעבור דרך שני תאים כדי להכיר את Colaboratory.

c3df49e90e5a654f.png Welcome to Colab.ipynb

בחירת קצה עורפי של TPU

8832c6208c99687d.png

בתפריט Colab, בוחרים באפשרות סביבת זמן הריצה > משנים את הסוג של סביבת זמן הריצה ואז בוחרים באפשרות TPU. בשיעור ה-Lab הזה תלמדו להשתמש ב-TPU (יחידת עיבוד Tensor) חזקה, לגיבוי לצורך אימון עם האצת חומרה. החיבור לסביבת זמן הריצה יתבצע באופן אוטומטי בהפעלה הראשונה, או שאפשר להשתמש באפשרות Connect (התחברות) בפינה הימנית העליונה.

ביצוע Notebook

76d05caa8b4db6da.png

כדי להפעיל תאים בכל פעם, לוחצים על תא באמצעות מקש Shift-ENTER. אפשר גם להריץ את כל ה-notebook באמצעות סביבת זמן הריצה > הפעלה של הכול

תוכן העניינים

429f106990037ec4.png

בכל המחברות יש תוכן עניינים. אפשר לפתוח אותו באמצעות החץ השחור שמימין.

תאים מוסתרים

edc3dba45d26f12a.png

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

אימות

cdd4b41413100543.png

ל-Colab יש אפשרות לגשת לקטגוריות הפרטיות שלך ב-Google Cloud Storage בתנאי שביצעת אימות באמצעות חשבון מורשה. קטע הקוד שלמעלה יפעיל תהליך אימות.

3.‏ [מידע] מהן יחידות עיבוד של Tensor (TPU)?

בקצרה

f88cf6facfc70166.png

הקוד לאימון מודל על TPU ב-Keras (ומפסיקים להשתמש ב-GPU או במעבד אם ה-TPU לא זמין):

try: # detect TPUs
    tpu
= tf.distribute.cluster_resolver.TPUClusterResolver.connect()
    strategy
= tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy
= tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines

# use TPUStrategy scope to define model
with strategy.scope():
  model
= tf.keras.Sequential( ... )
  model
.compile( ... )

# train model normally on a tf.data.Dataset
model
.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)

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

688858c21e3beff2.png

למה דווקא מעבדי TPU?

מעבדי GPU מודרניים מסודרים לפי 'ליבות' שניתנות לתכנות, ארכיטקטורה גמישה מאוד שמאפשרת להם להתמודד עם מגוון משימות כמו רינדור בתלת-ממד, למידה עמוקה (Deep Learning), סימולציות פיזיות וכו'. מצד שני, יחידות TPU יוצרות התאמה בין מעבד וקטורי קלאסי עם יחידת הכפלה ייעודית של מטריצה, ומתבלטות בכל משימה שבה הכפולות של המטריצה הגדולה שולטות, כמו רשתות נוירונים.

8eb3e718b8e2ed08.png

איור: שכבה של רשת נוירונים צפופה ככפל של מטריצה, עם אצווה של שמונה תמונות מעובדות דרך רשת הנוירונים בבת אחת. בצעו את ההכפלה של עמודה אחת בשורה x כדי לוודא שהיא אכן מבצעת סכום משוקלל של כל ערכי הפיקסלים של תמונה. אפשר לייצג שכבות מתקפלות בתור הכפלות של מטריצות, אבל התהליך קצת יותר מורכב ( הסבר כאן, בסעיף 1).

החומרה

MXU ו-VPU

ליבת TPU v2 מורכבת מיחידת הכפלת מטריצות (MXU) שמפעילה הכפלת מטריצות ויחידה לעיבוד וקטורים (VPU) לכל המשימות האחרות כמו הפעלות, softmax וכו'. ה-VPU מטפל בחישובים של float32 ו-int32. מצד שני, MXU פועל בפורמט נקודה צפה (floating-point) ברמת דיוק מעורב של 16-32 ביט.

7d68944718f76b18.png

נקודה צפה (floating-point) מעורבת ו-bfloat16

ה-MXU מחשב את הכפלת המטריצות באמצעות קלט bfloat16 ופלט float32. הצטברות ביניים מבוצעות ברמת דיוק של float32.

19c5fc432840c714.png

האימון של רשת נוירונים עמיד בדרך כלל לרעש שנוצר על ידי דיוק מופחת של נקודה צפה (floating-point). יש מקרים שבהם רעש אפילו עוזר לכלי האופטימיזציה להתכנס. בעבר נעשה שימוש בדיוק של נקודה צפה (floating-point) של 16 ביט כדי להאיץ את החישובים, אבל לפורמטים float16 ו-float32 יש טווחים שונים מאוד. הפחתת רמת הדיוק מ-float32 ל-float16 בדרך כלל מובילה לזרימה חוזרת ונשנית. קיימים פתרונות, אבל בדרך כלל נדרשת עבודה נוספת כדי שהמערכת תזהה את התכונה float16.

לכן Google השיקה את הפורמט bfloat16 במעבדי TPU. bfloat16 הוא float קטוע 32 עם אותם ביטים וטווח של מעריך בדיוק כמו float32. זאת, בנוסף לעובדה שיחידות TPU מחשבים הכפלות של מטריצות ברמת דיוק מעורבת עם קלט bfloat16 אבל פלט float32, ופירוש הדבר שבדרך כלל לא נדרשים שינויים בקוד כדי לשפר את הביצועים ברמת דיוק מופחתת.

מערך סיסטולי

ה-MXU מטמיע בחומרה הכפלת מטריצות באמצעות מה שנקרא 'מערך סיסטולי' שבה רכיבי נתונים זורמים דרך מערך של יחידות מחשוב חומרה. (ברפואה, "סיסטולי" מתייחס להתכווצות לב ולזרימת הדם, כאן לזרימת הנתונים).

הרכיב הבסיסי של כפל מטריצות הוא מכפלה של קו בין קו ממטריצה אחת ועמודה מהמטריצה השנייה (ראו איור בחלק העליון של קטע זה). במטריצות Y=X*W, רכיב אחד של התוצאה יהיה:

Y[2,0] = X[2,0]*W[0,0] + X[2,1]*W[1,0] + X[2,2]*W[2,0] + ... + X[2,n]*W[n,0]

ב-GPU, משתמש יתכנת את מוצר הנקודה הזה ל'ליבה' של GPU. ואז לבצע אותו בכמה "ליבות" מכיוון שהם זמינים במקביל כדי לנסות ולחשב כל ערך של המטריצה שמתקבלת בבת אחת. אם המטריצה שמתקבלת היא 128x128 גדולה, יידרשו 128x128=16K "ליבות" להיות זמינים, ובדרך כלל זה לא אפשרי. למעבדי ה-GPU הגדולים ביותר יש כ-4,000 ליבות. מצד שני, TPU משתמש במינימום הנדרש של החומרה עבור יחידות המחשוב ב-MXU: רק bfloat16 x bfloat16 => float32 מכפילים, שום דבר אחר. הם כל כך קטנים ש-TPU יכול ליישם 16K מהם ב-MXU של 128x128 ולעבד את הכפלת המטריצה הזו בפעם אחת.

f1b283fc45966717.gif

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

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

Cloud TPU

כאשר תבקשו " Cloud TPU גרסה 2 ב-Google Cloud Platform, מקבלים מכונה וירטואלית (VM) עם לוח TPU שמחובר ל-PCI. בלוח ה-TPU יש ארבעה שבבי TPU עם שתי ליבות. כל ליבת TPU כוללת VPU (יחידת עיבוד וקטורי) ו-MXU 128x128 (יחידת הכפלה של MatriX). ה-"Cloud TPU" הזה בדרך כלל מחובר למכונה הווירטואלית שביקשה אותה דרך הרשת. התמונה המלאה נראית כך:

dfce5522ed644ece.png

איור: מכונה וירטואלית עם "Cloud TPU" שמחובר לרשת מאיץ. "The Cloud TPU" עצמה מורכבת ממכונה וירטואלית עם לוח TPU מחובר PCI עם ארבעה שבבי TPU עם שתי ליבות.

מארזי TPU

במרכזי הנתונים של Google, מערכות ה-TPU מחוברות לחיבור מחשוב בעל ביצועים גבוהים (HPC), שיכול לגרום להן להופיע כמאיץ אחד גדול מאוד. Google מכנה אותם Pods והם יכולים לכלול עד 512 ליבות TPU v2 או 2048 ליבות TPU v3.

2ec1e0d341e7fc34.jpeg

איור: pod TPU v3. לוחות ומדפים של TPU שמחוברים דרך חיבור HPC.

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

d97b9cc5d40fdb1d.gif

איור: סנכרון הדרגה במהלך אימון באמצעות אלגוריתם כל ההפחתה ברשת ה-HPC של רשת ה-HPC הדו-כיוונית של Google TPU.

התוכנה

הדרכה לקבוצות גדולות

גודל אצווה אידיאלי למעבדי TPU הוא 128 פריטי נתונים לכל ליבת TPU, אבל החומרה כבר יכולה להראות ניצול טוב מ-8 פריטי נתונים לכל ליבת TPU. חשוב לזכור של-Cloud TPU אחד יש 8 ליבות.

במעבדת הקוד הזו נשתמש ב-Keras API. ב-Keras, האצווה שתגדירו היא הגודל הגלובלי של אצווה של כל ה-TPU. האצוות יפוצלו אוטומטית ל-8 ויפעלו על 8 ליבות ה-TPU.

da534407825f01e3.png

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

טיפול יסודי: XLA

תוכנות Tensorflow מגדירות גרפים של מחשוב. ה-TPU לא מריץ ישירות קוד Python, אלא מריץ את תרשים החישוב שהוגדר על ידי תוכנת Tensorflow. אבל בקצרה, מהדר בשם XLA (מהדר (compiler לינארי) מואץ' ממיר את תרשים Tensorflow של צומתי החישוב לקוד של מכונת TPU. המהדר הזה מבצע גם הרבה אופטימיזציות מתקדמות בקוד שלכם ובפריסת הזיכרון שלכם. האיסוף מתבצע באופן אוטומטי כשהעבודה נשלחת ל-TPU. אין צורך לכלול XLA באופן מפורש בשרשרת ה-build.

edce61112cd57972.png

איור: כדי לרוץ ב-TPU, תרשים החישוב שהוגדר על ידי תוכנית Tensorflow מתורגם קודם לייצוג XLA (מהידר אלגברה מואצת) ואז הידור באמצעות XLA לקוד מכונה של TPU.

שימוש במעבדי TPU ב-Keras

החל מ-Tensorflow 2.1 יש תמיכה במעבדי TPU דרך Keras API. התמיכה ב-Keras פועלת עם מעבדי TPU ושקעי TPU. דוגמה שעובדת ב-TPU, ב-GPU ובמעבדים:

try: # detect TPUs
    tpu
= tf.distribute.cluster_resolver.TPUClusterResolver.connect()
    strategy
= tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy
= tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines

# use TPUStrategy scope to define model
with strategy.scope():
  model
= tf.keras.Sequential( ... )
  model
.compile( ... )

# train model normally on a tf.data.Dataset
model
.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)

בקטע הקוד הזה:

  • TPUClusterResolver().connect() מוצא את ה-TPU ברשת. התכונה פועלת ללא פרמטרים ברוב מערכות Google Cloud (משימות AI Platform, Colaboratory, Kubeflow, מכונות וירטואליות של למידה עמוקה שנוצרו באמצעות הכלי ctpu up). המערכות האלה יודעות איפה נמצא ה-TPU שלהן בזכות משתנה הסביבה TPU_NAME. אם יוצרים TPU באופן ידני, צריך להגדיר את סביבת TPU_NAME. משתנה במכונה הווירטואלית שממנה משתמשים בו, או קוראים לפונקציה TPUClusterResolver עם פרמטרים מפורשים: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy הוא החלק שבו מיישם את ההתפלגות ואת "all-reduce" אלגוריתם לסנכרון הדרגתי.
  • האסטרטגיה מיושמת באמצעות היקף. המודל חייב להיות מוגדר בתוך ההיקף() של האסטרטגיה.
  • הפונקציה tpu_model.fit מצפה לאובייקט tf.data.Dataset לקלט לצורך אימון TPU.

משימות נפוצות של ניוד TPU

  • במודל Tensorflow יש הרבה דרכים לטעון נתונים, אבל במעבדי TPU חובה להשתמש ב-API של tf.data.Dataset.
  • מעבדי TPU מהירים מאוד ומטמיעים נתונים בדרך כלל הופכים לצוואר בקבוק כשהם פועלים עליהם. במדריך הביצועים של TPU יש כלים שבהם אפשר להשתמש כדי לזהות צווארי בקבוק בנתונים וטיפים נוספים לשיפור הביצועים.
  • מספרי int8 או int16 נחשבים כ-int32. ל-TPU אין חומרת מספרים שלמים שפועלת על פחות מ-32 ביטים.
  • חלק מהפעולות של Tensorflow לא נתמכות. הרשימה כבר כאן. החדשות הטובות הן שההגבלה הזו חלה רק על קוד אימון, כלומר המעבר קדימה ואחורה במודל שלכם. עדיין אפשר להשתמש בכל פעולות Tensorflow בצינור עיבוד הנתונים של קלט הנתונים כי הן יבוצעו במעבד (CPU).
  • tf.py_func לא נתמך ב-TPU.

4.‏ טעינת נתונים

c0ecb860e4cad0a9.jpeg cc4781a7739c49ae.jpeg 81236b00f8bbf39e.jpeg 961e2228974076bb.jpeg 7517dc163bdffcd5.jpeg 96392df4767f566d.png

נעבוד עם מערך נתונים של תמונות פרחים. המטרה היא ללמוד לסווג אותם ל-5 סוגי פרחים. טעינת הנתונים מתבצעת באמצעות ה-API של tf.data.Dataset. קודם נכיר את ה-API.

הפעלה קולית

פותחים את ה-notebook הבא, מריצים את התאים (Shift-ENTER) ופועלים לפי ההוראות בכל מקום שבו מופיע הכיתוב 'נדרשת עבודה' התיוג.

c3df49e90e5a654f.png Fun with tf.data.Dataset (playground).ipynb

מידע נוסף

מידע על "פרחים" מערך נתונים

מערך הנתונים מסודר ב-5 תיקיות. כל תיקייה מכילה פרחים מסוג מסוים. השמות של התיקיות הם 'חמניות', 'חנית', 'שן הארי', 'צבעונים' ו'ורדים'. הנתונים מתארחים בקטגוריה ציבורית ב-Google Cloud Storage. קטע:

gs://flowers-public/sunflowers/5139971615_434ff8ed8b_n.jpg
gs
://flowers-public/daisy/8094774544_35465c1c64.jpg
gs
://flowers-public/sunflowers/9309473873_9d62b9082e.jpg
gs
://flowers-public/dandelion/19551343954_83bb52f310_m.jpg
gs
://flowers-public/dandelion/14199664556_188b37e51e.jpg
gs
://flowers-public/tulips/4290566894_c7f061583d_m.jpg
gs
://flowers-public/roses/3065719996_c16ecd5551.jpg
gs
://flowers-public/dandelion/8168031302_6e36f39d87.jpg
gs
://flowers-public/sunflowers/9564240106_0577e919da_n.jpg
gs
://flowers-public/daisy/14167543177_cd36b54ac6_n.jpg

למה להשתמש ב-tf.data.Dataset?

Keras ו-Tensorflow מקבלים מערכי נתונים בכל פונקציות האימון וההערכה שלהם. אחרי שטוענים נתונים במערך נתונים, ה-API מציע את כל הפונקציות הנפוצות שמועילות לנתוני אימון של רשת נוירונים:

dataset = ... # load something (see below)
dataset = dataset.shuffle(1000) # shuffle the dataset with a buffer of 1000
dataset = dataset.cache() # cache the dataset in RAM or on disk
dataset = dataset.repeat() # repeat the dataset indefinitely
dataset = dataset.batch(128) # batch data elements together in batches of 128
AUTOTUNE = tf.data.AUTOTUNE
dataset = dataset.prefetch(AUTOTUNE) # prefetch next batch(es) while training

טיפים לשיפור הביצועים ושיטות מומלצות לגבי מערכי נתונים זמינים במאמר הזה. מאמרי העזרה זמינים כאן.

מידע בסיסי על tf.data.Dataset

הנתונים בדרך כלל מגיעים בכמה קבצים, כאן תמונות. ניתן ליצור מערך נתונים של שמות קבצים באמצעות קריאה ל:

filenames_dataset = tf.data.Dataset.list_files('gs://flowers-public/*/*.jpg')
# The parameter is a "glob" pattern that supports the * and ? wildcards.

לאחר מכן לוחצים על 'מיפוי' פונקציה לכל שם קובץ, שלרוב תטען ותפענח את הקובץ לנתונים ממשיים בזיכרון:

def decode_jpeg(filename):
  bits
= tf.io.read_file(filename)
  image
= tf.io.decode_jpeg(bits)
 
return image

image_dataset
= filenames_dataset.map(decode_jpeg)
# this is now a dataset of decoded images (uint8 RGB format)

כדי לבצע איטרציה במערך נתונים:

for data in my_dataset:
 
print(data)

מערכי נתונים של צמדים

בלמידה מונחית, מערך נתונים לאימון עשוי בדרך כלל מזוגות של נתוני אימון ותשובות נכונות. כדי לאפשר זאת, פונקציית הפענוח יכולה להחזיר זוגות. לאחר מכן תקבלו מערך נתונים של צמדים וצמדים מוחזרים כשתחזרו אליו. הערכים המוחזרים הם רכיבי Tensorflow שמוכנים לצריכה על ידי המודל. אפשר לקרוא לפונקציה .numpy() כדי לראות ערכים גולמיים:

def decode_jpeg_and_label(filename):
  bits
= tf.read_file(filename)
  image
= tf.io.decode_jpeg(bits)
  label
= ... # extract flower name from folder name
 
return image, label

image_dataset
= filenames_dataset.map(decode_jpeg_and_label)
# this is now a dataset of (image, label) pairs

for image, label in dataset:
 
print(image.numpy().shape, label.numpy())

מסקנות:טעינת תמונות אחת אחרי השנייה היא איטית!

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

פתרון

הנה ה-notebook של הפתרון. אפשר להשתמש בו אם נתקעת.

c3df49e90e5a654f.png Fun with tf.data.Dataset (solution).ipynb

אילו נושאים דיברנו?

  • 🤔 tf.data.Dataset.list_files
  • 🤔 tf.data.Dataset.map
  • 🤔 מערכי נתונים של צמדים
  • 👋 חזרה באמצעות מערכי נתונים

כדאי להקדיש כמה רגעים כדי לעבור על רשימת המשימות הבאה בראש שקט.

5.‏ הנתונים נטענים במהירות

מאיצי החומרה של Tensor לעיבוד נתונים (TPU) שבהם נשתמש בשיעור ה-Lab הזה מהירים מאוד. האתגר בדרך כלל הוא להזין להם נתונים מהר מספיק כדי להעסיק אותם. Google Cloud Storage (GCS) יכול לשמור על תפוקה גבוהה מאוד, אבל כמו בכל מערכות האחסון בענן, תהליך החיבור כרוך בעלויות מסוימות של הרשת הלוך ושוב. לכן, אחסון הנתונים שלנו כאלפי קבצים נפרדים לא אידיאלי. אנחנו מתכננים לקבץ אותם במספר קטן יותר של קבצים ולהשתמש בעוצמה של tf.data.Dataset כדי לקרוא מכמה קבצים במקביל.

שיעור קריאה

הקוד שטוען קובצי תמונה, משנה את הגודל שלהם לגודל משותף ולאחר מכן מאחסן אותם ב-16 קובצי TFRecord ב-notebook הבא. מומלץ לקרוא אותו במהירות. אין צורך להפעיל אותו כי נתונים בפורמט TFRecord כראוי יסופקו לשאר ה-Codelab.

c3df49e90e5a654f.png Flower pictures to TFRecords.ipynb

פריסת נתונים אידיאלית לתפוקה אופטימלית של GCS

פורמט הקובץ TFRecord

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

filenames = tf.io.gfile.glob(FILENAME_PATTERN)
dataset
= tf.data.TFRecordDataset(filenames)
dataset
= dataset.map(...) # do the TFRecord decoding here - see below

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

AUTOTUNE = tf.data.AUTOTUNE
ignore_order
= tf.data.Options()
ignore_order
.experimental_deterministic = False

filenames
= tf.io.gfile.glob(FILENAME_PATTERN)
dataset
= tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE)
dataset
= dataset.with_options(ignore_order)
dataset
= dataset.map(...) # do the TFRecord decoding here - see below

תקציר של TFRecord

ב-TFRecords אפשר לאחסן שלושה סוגי נתונים: מחרוזות בייטים (רשימת בייטים), מספרים שלמים של 64 ביט וצפים של 32 ביט. הן תמיד נשמרות כרשימות, רכיב נתונים יחיד יהיה רשימה בגודל 1. אפשר להשתמש בפונקציות העוזרות הבאות כדי לאחסן נתונים ברשומות TFRecords.

כתיבת מחרוזות של בייטים

# warning, the input is a list of byte strings, which are themselves lists of bytes
def _bytestring_feature(list_of_bytestrings):
 
return tf.train.Feature(bytes_list=tf.train.BytesList(value=list_of_bytestrings))

כתיבת מספרים שלמים

def _int_feature(list_of_ints): # int64
 
return tf.train.Feature(int64_list=tf.train.Int64List(value=list_of_ints))

כתיבת צפים

def _float_feature(list_of_floats): # float32
 
return tf.train.Feature(float_list=tf.train.FloatList(value=list_of_floats))

כתיבת TFRecord, באמצעות כלי העזר שלמעלה

# input data in my_img_bytes, my_class, my_height, my_width, my_floats
with tf.python_io.TFRecordWriter(filename) as out_file:
  feature = {
    "image": _bytestring_feature([my_img_bytes]), # one image in the list
    "class": _int_feature([my_class]),            # one class in the list
    "size": _int_feature([my_height, my_width]),  # fixed length (2) list of ints
    "float_data": _float_feature(my_floats)       # variable length  list of floats
  }
  tf_record = tf.train.Example(features=tf.train.Features(feature=feature))
  out_file.write(tf_record.SerializeToString())

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

קריאה מ-TFRecords

def read_tfrecord(data):
  features = {
    # tf.string = byte string (not text string)
    "image": tf.io.FixedLenFeature([], tf.string), # shape [] means scalar, here, a single byte string
    "class": tf.io.FixedLenFeature([], tf.int64),  # shape [] means scalar, i.e. a single item
    "size": tf.io.FixedLenFeature([2], tf.int64),  # two integers
    "float_data": tf.io.VarLenFeature(tf.float32)  # a variable number of floats
  }

  # decode the TFRecord
  tf_record = tf.io.parse_single_example(data, features)

  # FixedLenFeature fields are now ready to use
  sz = tf_record['size']

  # Typical code for decoding compressed images
  image = tf.io.decode_jpeg(tf_record['image'], channels=3)

  # VarLenFeature fields require additional sparse.to_dense decoding
  float_data = tf.sparse.to_dense(tf_record['float_data'])

  return image, sz, float_data

# decoding a tf.data.TFRecordDataset
dataset = dataset.map(read_tfrecord)
# now a dataset of triplets (image, sz, float_data)

קטעי קוד שימושיים:

קריאת רכיבים בודדים של נתונים

tf.io.FixedLenFeature([], tf.string)   # for one byte string
tf
.io.FixedLenFeature([], tf.int64)    # for one int
tf
.io.FixedLenFeature([], tf.float32)  # for one float

קריאת רשימות בגודל קבוע של רכיבים

tf.io.FixedLenFeature([N], tf.string)   # list of N byte strings
tf.io.FixedLenFeature([N], tf.int64)    # list of N ints
tf.io.FixedLenFeature([N], tf.float32)  # list of N floats

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

tf.io.VarLenFeature(tf.string)   # list of byte strings
tf
.io.VarLenFeature(tf.int64)    # list of ints
tf
.io.VarLenFeature(tf.float32)  # list of floats

הפונקציה VerLenFeature מחזירה וקטור sparse ונדרש שלב נוסף אחרי פענוח ה-TFRecord:

dense_data = tf.sparse.to_dense(tf_record['my_var_len_feature'])

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

tf.io.FixedLenFeature([], tf.int64, default_value=0) # this field is optional

אילו נושאים דיברנו?

  • 🤔 פיצול של קובצי נתונים כדי לקבל גישה מהירה מ-GCS
  • 😓 איך לכתוב רשומות TFRecords. (כבר שכחת את התחביר? זה בסדר, כדאי להוסיף את הדף הזה לסימניות כתקציר)
  • 🤔 טעינת מערך נתונים מ-TFRecords באמצעות הפונקציה TFRecordDataset

כדאי להקדיש כמה רגעים כדי לעבור על רשימת המשימות הבאה בראש שקט.

6.‏ מעולה!

עכשיו אפשר להזין TPU עם נתונים. אפשר להמשיך לשיעור ה-Lab הבא

מעבדי TPU בפועל

מעבדי TPU ומעבדי GPU זמינים ב-Cloud AI Platform:

לבסוף, אנחנו אוהבים לקבל משוב. נשמח לשמוע אם משהו השתבש בשיעור ה-Lab הזה או אם לדעתכם צריך לשפר אותו. אפשר לשלוח משוב דרך 'בעיות ב-GitHub' [feedback link].

HR.png

Martin Görner ID Small.jpg
המחבר: מרטין גורנר
Twitter: @martin_gorner