רשתות נוירונים מלאכותיות, עם Keras ומעבדי TPU

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

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

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

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

ca8cc21f6838eccc.png

מה תלמדו

  • כדי ליצור מסווג תמונות קונבולוציוני באמצעות מודל Keras Sequential.
  • כדי לאמן את מודל Keras ב-TPU
  • כדי לכוונן את המודל באמצעות בחירה טובה של שכבות קונבולוציה.

משוב

אם נתקלתם בבעיה כלשהי ב-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. ‫[INFO] סיווג רשתות נוירונים למתחילים

על קצה המזלג

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

למודלים שנבנו כרצף של שכבות, 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, קיצור של Rectified Linear Unit (יחידה ליניארית מתוקנת). זו פונקציה פשוטה מאוד, כפי שאפשר לראות בגרף שלמעלה.

הפעלת Softmax

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

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

ef0d98c0952c262d.png d51252f75894479e.gif

Cross-entropy loss

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

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

7bdf8753d20617fb.png

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

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

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

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

gradient descent2.png

חלוקה לקבוצות קטנות ומומנטום

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

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

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

52e824fe4716c4a0.png

איור: נקודת אוכף. השיפוע הוא 0, אבל זה לא מינימום בכל הכיוונים. (‫Attribution של התמונה Wikimedia: By Nicoguaro - Own work, CC BY 3.0)

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

מילון מונחים

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

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

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

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

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

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

logits: הפלט של שכבת נוירונים לפני החלת פונקציית ההפעלה נקרא logits. המונח הזה מגיע מהפונקציה הלוגיסטית, שנקראת גם פונקציית סיגמואיד, שהייתה פונקציית ההפעלה הכי פופולרית. השם 'Neuron outputs before logistic function' (פלט של נוירון לפני פונקציה לוגיסטית) קוצר ל-'logits'.

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

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

קידוד "חם-יחיד": מחלקה 3 מתוך 5 מקודדת כווקטור של 5 אלמנטים, כולם אפסים חוץ מהאלמנט השלישי שהוא 1.

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

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

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

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

5. [מידע חדש] רשתות נוירונים מלאכותיות (CNN)

על קצה המזלג

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

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

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

Screen Shot 2016-07-29 at 16.02.37.png

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

d1b557707bcd1cb9.png

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

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

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

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

2b2d4263bb8470b.gif

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

סיווג באמצעות רשת קונבולוציה

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

4a61aaffb6cba3d1.png

איור: מסווג תמונות שמשתמש בשכבות קונבולוציה ו-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'])

6. רשת עצבית קונבולוציונית בהתאמה אישית

מעשי

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

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

Global average pooling

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

93240029f59df7c2.png

המוצר

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

c3df49e90e5a654f.png Keras_Flowers_TPU (solution).ipynb

מה נכלל

  • 🤔 שיחקת עם שכבות קונבולוציה
  • 🤓 ניסיתי שיטות שונות של איגום (pooling), כמו איגום מקסימלי, צעדים (strides), איגום ממוצע גלובלי וכו'.
  • ‫😀 ביצע איטרציה מהירה במודל מהעולם האמיתי, ב-TPU

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

7. מעולה!

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

מעבדי TPU בפועל

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

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

HR.png

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

tensorflow logo.jpg
www.tensorflow.org