Keras וסנפירות מודרניות, במעבדי TPU

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

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

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

ca8cc21f6838eccc.png

מה תלמדו

  • כדי להשתמש ב-Keras וביחידות עיבוד נתונים טילר (TPU) כדי ליצור מודלים מותאמים אישית מהר יותר.
  • כדי להשתמש ב-tf.data.Dataset API ובפורמט TFRecord כדי לטעון נתוני אימון ביעילות.
  • כדי לרמות 😈, אפשר להשתמש בלמידה בהעברה במקום לבנות מודלים משלך.
  • כדי להשתמש בסגנונות של מודלים פונקציונליים ושל מודלים רצופיים ב-Keras.
  • כדי ליצור סיווג משלכם ב-Keras עם שכבת softmax ואובדן cross-entropy.
  • כדי לכוונן את המודל באמצעות בחירה טובה של שכבות קונבולוציה.
  • כדי לבחון רעיונות מודרניים לארכיטקטורה של רשתות נוירוניות קונבולוציוניות (CNN), כמו מודולים, צבירה גלובלית ממוצעת וכו'.
  • כדי ליצור רשת נוירונים קונבולוציונית (CNN) מודרנית ופשוטה באמצעות ארכיטקטורת Squeezenet.

משוב

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

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

בשיעור ה-Lab הזה נעשה שימוש ב'שיתוף פעולה עם Google' ולא נדרשת הגדרה כלשהי מצדך. אפשר להריץ אותו מ-Chromebook. כדי להכיר את קובצי ה-notebook של Colab, צריך לפתוח את הקובץ שלמטה ולהפעיל את התאים.

c3df49e90e5a654f.png Welcome to Colab.ipynb

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

8832c6208c99687d.png

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

ביצוע של Notebook

76d05caa8b4db6da.png

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

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

429f106990037ec4.png

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

תאים מוסתרים

edc3dba45d26f12a.png

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

אימות

cdd4b41413100543.png

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

3. [INFO] מהם מעבדי Tensor Processing Unit‏ (TPU)?

בקצרה

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 מודרניים מאורגנים לפי 'ליבות' שניתנות לתכנות, ארכיטקטורה גמישה מאוד שמאפשרת להם להתמודד עם מגוון משימות כמו רינדור בתלת-ממד, למידה עמוקה (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 יכול להטמיע 16,000 יחידות כאלה ב-MXU בגודל 128x128 ולעבד את הכפלת המטריצות הזו בבת אחת.

f1b283fc45966717.gif

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

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

Cloud TPU

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

dfce5522ed644ece.png

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

מארזי TPU

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

2ec1e0d341e7fc34.jpeg

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

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

d97b9cc5d40fdb1d.gif

איור: סנכרון הדרגה במהלך אימון באמצעות אלגוריתם כל ההפחתה ברשת ה-HPC של רשת ה-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. בנוסף, המהדר מבצע פעולות אופטימיזציה מתקדמות רבות בקוד ובפריסה של הזיכרון. ה-compilation מתבצע באופן אוטומטי כשהעבודה נשלחת ל-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 ב-VM שבו משתמשים בו, או להפעיל את TPUClusterResolver עם פרמטרים מפורשים: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy הוא החלק שמיישם את ההתפלגות ואת האלגוריתם לסנכרון הדרגתי של "all-reduce".
  • האסטרטגיה חלה באמצעות היקף. צריך להגדיר את המודל בתוך ה-scope() של האסטרטגיה.
  • הפונקציה tpu_model.fit מצפה לאובייקט tf.data.Dataset לקלט לצורך אימון TPU.

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

  • יש הרבה דרכים לטעון נתונים במודל Tensorflow, אבל ב-TPUs צריך להשתמש ב-tf.data.Dataset API.
  • TPUs הם מהירים מאוד, ולרוב צוואר הבקבוק בזמן ההרצה שלהם הוא הטמעת הנתונים. במדריך לביצועים של 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?

אפשר להשתמש ב-Datasets בכל הפונקציות של 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)

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

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

פתרון

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

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

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

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

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

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

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

קריאה

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

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. [INFO] מסווג רשת נוירונים 101

בקצרה

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

למודלים שנוצרים כרצף של שכבות, Keras מציע את Sequential API. לדוגמה, אפשר לכתוב ב-Keras מסווג תמונות שמשתמש בשלוש שכבות צפופות כך:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=[192, 192, 3]),
    tf.keras.layers.Dense(500, activation="relu"),
    tf.keras.layers.Dense(50, activation="relu"),
    tf.keras.layers.Dense(5, activation='softmax') # classifying into 5 classes
])

# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy']) # % of correct answers

# train the model
model.fit(dataset, ... )

688858c21e3beff2.png

רשת נוירונים צפופה

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

c21bae6dade487bc.png

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

נוירונים, הפעלות, RELU

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

644f4213a4ee70e5.png

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

הפעלת Softmax

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

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

ef0d98c0952c262d.png d51252f75894479e.gif

Cross-entropy loss

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

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

7bdf8753d20617fb.png

ירידה הדרגתית

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

ה-cross-entropy הוא פונקציה של משקולות, הטיות, פיקסלים של תמונת האימון והסיווג הידוע שלה.

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

הדרגתיות של ירידה2.png

מיני-אצווה ומומנטום

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

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

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

52e824fe4716c4a0.png

איור: נקודת אוכף. ההדרגתיות היא 0 אך הוא לא ערך מינימלי בכל הכיוונים. (קרדיט תמונה Wikimedia: By Nicoguaro - Own work, CC BY 3.0)

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

מילון מונחים

batch או mini-batch: תמיד מתבצע אימון על קבוצות של נתוני אימון ותוויתות. כך אפשר לעזור לאלגוריתם להגיע להסכמה. המאפיין 'קבוצה' הוא בדרך כלל המאפיין הראשון של טינסורים של נתונים. לדוגמה, Tensor של צורה [100, 192, 192, 3] מכיל 100 תמונות של 192x192 פיקסלים עם שלושה ערכים לכל פיקסל (RGB).

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

שכבה צפופה: שכבה של נוירונים שבה כל נוירונים מחובר לכל נוירונים בשכבה הקודמת.

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

labels: שם נוסף ל'כיתות' או לתשובות נכונות בבעיה של סיווג בפיקוח

שיעור הלמידה: חלק מהמדריגנט שבו משקולות ונטיות מועדכנים בכל חזרה של לולאת האימון.

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

loss: פונקציית השגיאה שמשויכת בין הפלט של רשת העצבים לתשובות הנכונות

neuron: מחשב את הסכום המשוקלל של הקלט, מוסיף הטיה ומזין את התוצאה באמצעות פונקציית הפעלה.

קידוד one-hot: הכיתה 3 מתוך 5 מקודדת כוקטור של 5 רכיבים, כולם אפס מלבד הרכיב השלישי שהוא 1.

relu: יחידה לינארית מתוקנת. פונקציית הפעלה פופולרית לנוירונים.

sigmoid: פונקציית הפעלה נוספת שהייתה פופולרית בעבר ועדיין שימושית במקרים מיוחדים.

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

tensor: 'tensor' דומה למטריצה, אבל עם מספר שרירותי של מימדים. טנזור חד-ממדי הוא וקטור. מפריד דו-ממדי הוא מטריצה. לאחר מכן אפשר ליצור טינסורים עם 3, 4, 5 או יותר מאפיינים.

7. למידת העברה

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

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

שיחה קולית

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

c3df49e90e5a654f.png Keras Flowers transfer learning (playground).ipynb

מידע נוסף

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

b8fc1efd2001f072.png

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

העברת הלמידה ב-Keras

ב-Keras, אפשר ליצור מודל שעבר אימון מראש מהאוסף tf.keras.applications.*. לדוגמה, MobileNet V2 היא ארכיטקטורה מתקפלת טובה מאוד שנשארת סבירה בגודלה. אם בוחרים באפשרות include_top=False, מקבלים את המודל שעבר אימון מראש בלי שכבת ה-softmax הסופית שלו, ואז אפשר להוסיף לו:

pretrained_model = tf.keras.applications.MobileNetV2(input_shape=[*IMAGE_SIZE, 3], include_top=False)
pretrained_model.trainable = False

model = tf.keras.Sequential([
    pretrained_model,
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(5, activation='softmax')
])

כדאי לשים לב גם להגדרה pretrained_model.trainable = False. המודל מקפיא את המשקולות ואת ההטיות של המודל שעבר אימון מראש, כדי לאמן את שכבת ה-softmax בלבד. בדרך כלל התהליך הזה כולל מעט משקולות ואפשר לבצע אותו במהירות ובלי צורך במערך נתונים גדול מאוד. עם זאת, אם יש לכם הרבה נתונים, תוכלו לשפר את הלמידה של ההעברה באמצעות pretrained_model.trainable = True. לאחר מכן, המשקלים שהותאמו מראש מספקים ערכים ראשוניים מצוינים, ועדיין אפשר לשנות אותם במהלך האימון כדי להתאים אותם טוב יותר לבעיה.

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

הגישה הזו אמורה לספק דיוק של כ-75%.

פתרון

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

c3df49e90e5a654f.png Keras Flowers transfer learning (solution).ipynb

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

  • 🤔 איך לכתוב סיווג ב-Keras
  • 🤓 שהוגדרו עם שכבה אחרונה של softmax, ואובדן ב-Cross-entropy
  • 😈 להעביר את הלמידה
  • 🤔 אימון המודל הראשון
  • 🧐 מעקב אחרי ההפסד והדיוק שלו במהלך האימון

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

8. [מידע] רשתות נוירונים מלאכותיות

בקצרה

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

convolutional.gif

איור: סינון תמונה באמצעות שני מסננים רצופים שמכילים 48 משקלים ללמידה בכל אחד מהם (4x4x3=48).

כך נראית רשת נוירונים מלאכותית פשוטה ב-Keras:

model = tf.keras.Sequential([
  # input: images of size 192x192x3 pixels (the three stands for RGB channels)
  tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu', input_shape=[192, 192, 3]),
  tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=6, padding='same', activation='relu'),
  tf.keras.layers.Flatten(),
  # classifying into 5 categories
  tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy'])

688858c21e3beff2.png

מבוא לרשתות נוירונים מלאכותיות (CNN)

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

עם זאת, 48 משקולות לא מספיקות. כדי להוסיף עוד דרגות חופש, חוזרים על אותה פעולה עם קבוצה חדשה של משקלים. כך נוצרת קבוצה חדשה של תוצאות מסנן. נקרא לזה 'ערוץ' של פלטים באמצעות אנלוגיה עם ערוצי R,G,B בתמונת הקלט.

Screen Shot 2016-07-29 at 16.02.37.png

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

d1b557707bcd1cb9.png

איור: רשת נוירונים מלאכותית (CNN) ממירה 'קוביות' של נתונים ל'קוביות' אחרות של נתונים.

קיפולים (קונבולציות) עם צעד, יצירת מאגרים מקסימליים

ביצוע המצטברים עם צעד של 2 או 3 מאפשר גם לכווץ את קוביית הנתונים שמתקבלת בממדים האופקיים שלה. אפשר לעשות זאת ב-2 דרכים נפוצות:

  • עיבוד נתונים סטריידי (Strided convolution): מסנן הזזה כמו למעלה, אבל עם צעד (stride) גדול מ-1
  • קיבוץ מקסימלי: חלון הזזה שמבצע את הפעולה MAX (בדרך כלל בתיקונים בגודל 2x2, שחוזר על עצמו כל 2 פיקסלים)

2b2d4263bb8470b.gif

איור: החלקה של חלון החישוב ב-3 פיקסלים מובילה לפחות ערכי פלט. קונבולוציות חדות או צבירה מקסימלית (מקסימום בחלון של 2x2 עם החלקה בצעד של 2) הן דרך לכווץ את קוביית הנתונים במידות האופקיות.

מסווג מסתובב

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

4a61aaffb6cba3d1.png

איור: סיווג תמונות באמצעות שכבות convolutional ו-softmax. הוא משתמש במסננים בגודל 3x3 ובגודל 1x1. השכבות של maxpool מחשבות את הערך המקסימלי של קבוצות של נקודות נתונים בגודל 2x2. 'הראש' של הסיווג מיושם באמצעות שכבה צפופה עם הפעלה של softmax.

ב-Keras

אפשר לכתוב ב-Keras את הערימה המתקפלת שמתוארת למעלה כך:

model = tf.keras.Sequential([
  # input: images of size 192x192x3 pixels (the three stands for RGB channels)    
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu', input_shape=[192, 192, 3]),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=16, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=8, padding='same', activation='relu'),
  tf.keras.layers.Flatten(),
  # classifying into 5 categories
  tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy'])

9. ה-CNN בהתאמה אישית

הדרכה מעשית

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

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

המטרה היא לשפר את רמת הדיוק של 75% של מודל למידת ההעברה. למודל הזה היה יתרון, כי הוא עבר אימון מראש על מערך נתונים של מיליוני תמונות, בעוד שיש לנו כאן רק 3,670 תמונות. יש לך אפשרות להתאים אותו לפחות?

מידע נוסף

כמה שכבות, מה הגודל?

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

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 192, 192, 16)      448       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 192, 192, 30)      4350      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 96, 96, 30)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 96, 96, 60)        16260     
_________________________________________________________________
 ... 
_________________________________________________________________
global_average_pooling2d (Gl (None, 130)               0         
_________________________________________________________________
dense (Dense)                (None, 90)                11790     
_________________________________________________________________
dense_1 (Dense)              (None, 5)                 455       
=================================================================
Total params: 300,033
Trainable params: 300,033
Non-trainable params: 0
_________________________________________________________________

כמה טיפים:

  • שכבות מרובות הן מה שהופך רשתות נוירונים "עמוקות" ליעילות. לבעיה הפשוטה הזו של זיהוי פרחים, 5 עד 10 שכבות הן הגיוניות.
  • להשתמש במסננים קטנים. בדרך כלל, מסננים בגודל 3x3 מתאימים בכל מקום.
  • אפשר גם להשתמש במסננים בגודל 1x1, והם זולים. הם לא באמת 'מסננים' שום דבר, אלא מחשבים שילובים לינאריים של ערוצים. כדאי להחליף אותם בפילטרים אמיתיים. (מידע נוסף על "קונבולציות של 1x1" מופיע בקטע הבא).
  • במקרה של בעיית סיווג כזו, מומלץ להוריד את הדגימה בתדירות גבוהה באמצעות שכבות מאגר מקסימלי (או קונבולוציות עם צעדים >1). לא משנה לך איפה הפרח נמצא, אלא רק שהוא ורד או שן הארי, כך שאיבוד המידע על ה-x ו-y אינו חשוב, והסינון של אזורים קטנים יותר הוא זול יותר.
  • מספר המסננים בדרך כלל דומה למספר המחלקות בסוף הרשת (למה? ראה למטה הטריק "מאגר ממוצע גלובלי"). אם מסווגים למאות מחלקות, מגדילים את מספר המסננים בהדרגה בשכבות עוקבות. במערך הנתונים של הפרחים עם 5 כיתות, סינון באמצעות 5 מסננים בלבד לא יספיק. אפשר להשתמש באותו מספר מסננים ברוב השכבות, לדוגמה 32 ולהקטין אותו לקראת הסוף.
  • השכבות הצפופות האחרונות יקרות. יכול להיות שיש להם יותר משקלים מאשר לכל שכבות הקוונטילציה יחד. לדוגמה, גם עם פלט סביר מאוד מקוביית הנתונים האחרונה של נקודות נתונים בגודל 24x24x10, שכבה של 100 צפופה נוירונים בעלות של 24x24x10x100=576,000 משקלים !!! כדאי להפעיל שיקול דעת או לנסות ליצור צבירה של ממוצעים גלובליים (ראו בהמשך).

אוסף ממוצע גלובלי

במקום להשתמש בשכבה צפופה יקרה בקצה של רשת עצבית מתקפלת, אתם יכולים לפצל את ה'קובייה' של הנתונים הנכנסים לכמה חלקים ככל שיש לכם מחלקות, לחשב ממוצע של הערכים שלהם ולהזין אותם באמצעות פונקציית הפעלה מסוג softmax. הדרך הזו לבניית 'הראש' של הסיווג לא כוללת עלות של משקולות. ב-Keras, התחביר הוא tf.keras.layers.GlobalAveragePooling2D().

93240029f59df7c2.png

פתרון

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

c3df49e90e5a654f.png Keras_Flowers_TPU (solution).ipynb

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

  • 🤔 הפעלה עם שכבות קונבולוציה
  • 🤓 ניסיתי max pooling,‏ strides,‏ global average pooling ועוד…
  • 😀 ביצענו חזרה על מודלים בעולם האמיתי במהירות, באמצעות TPU

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

10. [INFO] ארכיטקטורות נוירוניות קונבולוציוניות מודרניות

בקצרה

7968830b57b708c0.png

איור: 'מודול' קונבולוציה. מה הכי טוב בשלב זה? שכבת max-pool ואחריה שכבת convolutin 1x1 או שילוב שונה של שכבות? מנסים את כולם, משרשרים את התוצאות ונותנים לרשת להחליט. בצד ימין: הארכיטקטורה המתקפלת של " inception" באמצעות מודולים כאלה.

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

l = tf.keras.layers # syntax shortcut

y = l.Conv2D(filters=32, kernel_size=3, padding='same',
             activation='relu', input_shape=[192, 192, 3])(x) # x=input image

# module start: branch out
y1 = l.Conv2D(filters=32, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=32, kernel_size=3, padding='same', activation='relu')(y)
y = l.concatenate([y1, y3]) # output now has 64 channels
# module end: concatenation

# many more layers ...

# Create the model by specifying the input and output tensors.
# Keras layers track their connections automatically so that's all that's needed.
z = l.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, z)

688858c21e3beff2.png

טריקים זולים נוספים

פילטרים קטנים ביחס גובה-רוחב של 3x3

40a7b15fb7dbe75c.png

באיור הזה מוצגת התוצאה של שני מסננים רצופים בגודל 3x3. נסו לבדוק אילו נקודות נתונים תרמו לתוצאה: שני המסננים הבאים ברצף בגודל 3x3 מחשבים שילוב כלשהו של אזור בגודל 5x5. זה לא בדיוק אותו השילוב שמסנן בגודל 5x5 יחושב, אבל כדאי לנסות כי שני מסננים עוקבים מסוג 3x3 זולים יותר ממסנן יחיד בגודל 5x5.

קונבולוציות 1x1?

fd7cac16f8ecb423.png

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

11. סקווֶנט

דרך פשוטה לשלב את הרעיונות האלה הוצגה במאמר 'Squeezenet'. המחברים מציעים תכנון פשוט מאוד של מודול קונבולוציה, באמצעות שכבות קונבולוציה בגודל 1x1 ו-3x3 בלבד.

1730ac375379269b.png

איור: ארכיטקטורת מעיכה שמבוססת על 'מודולים של אש'. הם משתמשים בשכבה 1x1 שמצמצמת את הנתונים הנכנסים במאפיין האנכי, ואחריה בשתי שכבות עיבוד מקבילות של 1x1 ו-3x3 שמרחיבות שוב את עומק הנתונים.

שיחה קולית

ממשיכים ביומן הקודם ויוצרים רשת נוירונים מלאכותית מבוססת-קונטור בהשראת squeezenet. תצטרכו לשנות את קוד המודל ל'סגנון פונקציונלי' של Keras.

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

מידע נוסף

בתרגיל הזה כדאי להגדיר פונקציה מסייעת למודול מעיכה:

def fire(x, squeeze, expand):
  y = l.Conv2D(filters=squeeze, kernel_size=1, padding='same', activation='relu')(x)
  y1 = l.Conv2D(filters=expand//2, kernel_size=1, padding='same', activation='relu')(y)
  y3 = l.Conv2D(filters=expand//2, kernel_size=3, padding='same', activation='relu')(y)
  return tf.keras.layers.concatenate([y1, y3])

# this is to make it behave similarly to other Keras layers
def fire_module(squeeze, expand):
  return lambda x: fire(x, squeeze, expand)

# usage:
x = l.Input(shape=[192, 192, 3])
y = fire_module(squeeze=24, expand=48)(x) # typically, squeeze is less than expand
y = fire_module(squeeze=32, expand=64)(y)
...
model = tf.keras.Model(x, y)

הפעם היעד הוא להגיע לרמת דיוק של 80%.

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

מתחילים בשכבת convolve אחת, ואז ממשיכים עם "fire_modules", לסירוגין עם שכבות MaxPooling2D(pool_size=2). אפשר להתנסות עם 2 עד 4 שכבות max pooling ברשת, וגם עם 1, 2 או 3 מודולים של הפעלה רצופות בין שכבות ה-max pooling.

במודולים של אש, הפרמטר 'squeeze' צריך להיות בדרך כלל קטן מהפרמטר 'expand'. הפרמטרים האלה הם למעשה מספרי מסננים. בדרך כלל, הם יכולים לנוע בין 8 ל-196. אתם יכולים להתנסות בארכיטקטורות שבהן מספר המסננים גדל בהדרגה דרך הרשת או בארכיטקטורות פשוטות שבהן לכל המודולים של אש יש אותו מספר מסננים.

לדוגמה:

x = tf.keras.layers.Input(shape=[*IMAGE_SIZE, 3]) # input is 192x192 pixels RGB

y = tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu')(x)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.GlobalAveragePooling2D()(y)
y = tf.keras.layers.Dense(5, activation='softmax')(y)

model = tf.keras.Model(x, y)

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

נירמול באצווה

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

y = tf.keras.layers.BatchNormalization(momentum=0.9)(y)
# please adapt the input and output "y"s to whatever is appropriate in your context

צריך להקטין את ערך ברירת המחדל של הפרמטר 'תנופה' מ-0.99 ל-0.9 כי מערך הנתונים שלנו קטן. בינתיים, אפשר להתעלם מהפרט הזה.

הגדלת נתונים

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

4ed2958e09b487ca.png

ad795b70334e0d6b.png

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

def data_augment(image, label):
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_saturation(image, lower=0, upper=2)
    return image, label

לאחר מכן משתמשים בו בטרנספורמציה הסופית של הנתונים (מערכי נתונים של אימון ואימות, פונקציה "get_batched_dataset"):

dataset = dataset.repeat() # existing line
# insert this
if augment_data:
  dataset = dataset.map(data_augment, num_parallel_calls=AUTO)
dataset = dataset.shuffle(2048) # existing line

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

עכשיו אפשר להגיע לרמת דיוק של 80% ב-35 תקופות של זמן מערכת.

פתרון

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

c3df49e90e5a654f.png Keras_Flowers_TPU_squeezenet.ipynb

מה עסקנו בו

  • 🤔 מודלים מסוג 'סגנון פונקציונלי' של Keras
  • 🤓 ארכיטקטורת מעיכה
  • 🤓 הגדלת נתונים באמצעות tf.data.datset

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

12. Xception עם כוונון עדין

קיפולים (קונבולציות) שניתנות להפרדה

דרך אחרת ליישום שכבות קונבולוציה צברה לאחרונה פופולריות: קונבולוציות עם הפרדה בעומק. אני יודע, זה שם ארוך, אבל הרעיון פשוט למדי. הם מוטמעים ב-Tensorflow וב-Keras בתור tf.keras.layers.SeparableConv2D.

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

615720b803bf8dda.gif

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

קונבולציות הניתנות להפרדה משמשות בארכיטקטורות הרשתות הקונבולוציות האחרונות: MobileNetV2, Xception, EfficientNet. דרך אגב, MobileNetV2 היא השיטה שבה השתמשתם בעבר כדי להעביר את הלמידה.

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

שכבה מתקפלת: 4 x 4 x 3 x 5 = 240

שכבת convolutin נפרדת: 4 x 4 x 3 + 3 x 5 = 48 + 15 = 63

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

שיחה קולית

מתחילים מחדש מהמחברות של 'transfer learning', אבל הפעם בוחרים ב-Xception בתור המודל שעבר אימון מראש. ב-Xception נעשה שימוש רק בהתנזקות ניתנות להפרדה. צריך לאפשר אימון של כל המשקולות. אנחנו נכוונן את המשקולות לפני אימון על הנתונים שלנו במקום להשתמש ככה בשכבות שאומנו מראש.

c3df49e90e5a654f.png Keras Flowers transfer learning (playground).ipynb

המטרה: דיוק של יותר מ-95% (לא, ברצינות, זה אפשרי!)

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

מידע נוסף על כוונון עדין

Xception זמין במודלים הרגילים שעברו אימון מראש באפליקציה tf.keras.application.* הפעם לא לשכוח להשאיר את כל המשקולות כאימון.

pretrained_model = tf.keras.applications.Xception(input_shape=[*IMAGE_SIZE, 3],
                                                  include_top=False)
pretrained_model.trainable = True

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

9b1af213b2b36d47.png

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

ב-Keras, קצב הלמידה נקבע באמצעות קריאה חוזרת (callback) שבה אתם יכולים לחשב את קצב הלמידה המתאים לכל תקופה של זמן מערכת. Keras יעביר את קצב הלמידה הנכון לאופטימיזטור בכל תקופת אימון.

def lr_fn(epoch):
  lr = ...
  return lr

lr_callback = tf.keras.callbacks.LearningRateScheduler(lr_fn, verbose=True)

model.fit(..., callbacks=[lr_callback])

פתרון

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

c3df49e90e5a654f.png 07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb

מה עסקנו בו

  • 🤔 הסרה בקישורי עומק
  • 🤓 לוחות זמנים לקצב הלמידה
  • 😈. כוונון עדין של מודל שעבר אימון מראש.

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

13. מעולה!

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

מעבדי TPU בפועל

מעבדי TPU ומעבדי GPU זמינים ב-Vertex AI של Google Cloud:

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

HR.png

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