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

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

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

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

ca8cc21f6838eccc.png

מה תלמדו

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

משוב

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

2. מדריך למתחילים ב-Google Colaboratory

ב-Lab הזה נעשה שימוש ב-Google Collaboratory, ולא נדרשת הגדרה מצדכם. ‫Colaboratory היא פלטפורמת מחברות אונליין למטרות חינוכיות. הוא מציע הדרכה בחינם בנושא CPU, ‏ GPU ו-TPU.

688858c21e3beff2.png

אתם יכולים לפתוח את מחברת הדוגמה הזו ולהריץ כמה תאים כדי להכיר את Colaboratory.

c3df49e90e5a654f.png Welcome to Colab.ipynb

בחירת TPU backend

8832c6208c99687d.png

בתפריט Colab, בוחרים באפשרות Runtime > Change runtime type (זמן ריצה > שינוי הסוג של זמן הריצה) ואז בוחרים באפשרות TPU. בשיעור ה-Lab הזה תשתמשו ב-TPU (Tensor Processing Unit) עוצמתי שמגובה באימון מואץ באמצעות חומרה. החיבור לסביבת זמן הריצה יתבצע באופן אוטומטי בהרצה הראשונה, או שתוכלו להשתמש בלחצן 'Connect' בפינה השמאלית העליונה.

הרצת Notebook

76d05caa8b4db6da.png

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

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

429f106990037ec4.png

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

תאים מוסתרים

edc3dba45d26f12a.png

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

אימות

cdd4b41413100543.png

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

3. ‫[INFO] מהם Tensor Processing Units (TPUs)?

על קצה המזלג

f88cf6facfc70166.png

הקוד לאימון מודל ב-TPU ב-Keras (עם חזרה ל-GPU או ל-CPU אם 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 מודרניים מאורגנים סביב 'ליבות' שניתנות לתכנות, ארכיטקטורה גמישה מאוד שמאפשרת להם לטפל במגוון משימות כמו עיבוד תלת-ממד, למידה עמוקה, סימולציות פיזיות וכו'. לעומת זאת, יחידות TPU משלבות מעבד וקטורי קלאסי עם יחידת כפל מטריצות ייעודית, והן מצטיינות בכל משימה שבה כפל מטריצות גדול הוא הדומיננטי, כמו רשתות נוירונים.

8eb3e718b8e2ed08.png

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

החומרה

MXU ו-VPU

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

7d68944718f76b18.png

נקודה צפה עם דיוק מעורב ו-bfloat16

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

19c5fc432840c714.png

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

לכן Google הציגה את הפורמט bfloat16 ב-TPU. ‏bfloat16 הוא float32 קטום עם אותם ביטים של מעריך ואותו טווח כמו 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 יחידות כפל-צבירה, שום דבר אחר. הן כל כך קטנות, עד שאפשר להטמיע 16K מהן ב-MXU בגודל 128x128 ולעבד את הכפלת המטריצות הזו בבת אחת.

f1b283fc45966717.gif

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

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

Cloud TPU

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

dfce5522ed644ece.png

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

TPU pods

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

2ec1e0d341e7fc34.jpeg

איור: אשכול TPU v3 Pod. לוחות ומתלים של TPU שמחוברים באמצעות HPC interconnect.

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

d97b9cc5d40fdb1d.gif

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

התוכנה

אימון עם גודל אצווה גדול

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

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

da534407825f01e3.png

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

הפרטים הטכניים: XLA

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

edce61112cd57972.png

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

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

החל מ-Tensorflow 2.1, יש תמיכה ב-TPU דרך Keras API. התמיכה ב-Keras פועלת במעבדי TPU וב-TPU pods. דוגמה שפועלת ב-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=...)

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

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

משימות נפוצות להעברת קוד ל-TPU

  • יש הרבה דרכים לטעון נתונים במודל Tensorflow, אבל כשמשתמשים ב-TPU, חובה להשתמש ב-tf.data.Dataset API.
  • יחידות 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 סוגי פרחים. טעינת הנתונים מתבצעת באמצעות tf.data.Dataset API. קודם כל, נכיר את ה-API.

מעשי

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

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

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

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

המוצר

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

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

מה נכלל

  • 🤔 tf.data.Dataset.list_files
  • ‫🤔 tf.data.Dataset.map
  • 🤔 מערכי נתונים של טאפלים
  • ‫😀 iterating through Datasets

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

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

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

קריאה ראשונה

הקוד שמעמיס קובצי תמונה, משנה את הגודל שלהם לגודל משותף ואז מאחסן אותם ב-16 קובצי TFRecord מופיע במחברת הבאה. כדאי לקרוא אותו במהירות. אין צורך להריץ את הפקודה הזו כי נספק נתונים בפורמט 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

‫VarLenFeature מחזיר וקטור דליל, ונדרש שלב נוסף אחרי פענוח ה-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 [ קישור למשוב].

HR.png

Martin Görner ID small.jpg
המחבר: מרטין גרנר (Martin Görner)
טוויטר: ‎@martin_gorner