במורד: Gradient Descent, ושני הצעדים שכולם מדלגים עליהם
חשבו את התקרה המדויקת לקצב הלמידה, ואז ראו חיפוש brute-force על 3,600 כיוונים מגלה מחדש את ה-gradient בלי שאמרו לו.
בעמוד הזה
הפרק הקודם הסתיים בעמק.
לא מטפורי: עקומה אמיתית, ההפסד מול פרמטר יחיד, יורד למטה ועולה בחזרה. וההפסד שמתחתיה לא נבחר כי הוא היה מסודר — הוא נגזר מתוך טענה על הרעש במדידות, והשגיאה הריבועית יצאה מהצד השני כתוצאה ולא כמוסכמה.
אז יש לנו נוף עם תחתית, וסיבה להאמין שהתחתית היא המקום הנכון להיות בו. מה שאין לנו הוא דרך להגיע לשם.
הפרק הזה בונה אחת, וזה האלגוריתם שמאמן כל מודל בשאר הקורס הזה — כל אחד, בלי יוצא מן הכלל, עד וכולל אלה עם מאות מיליארדי פרמטרים. הוא נכנס בערך לעשרים שורות. שני החלקים הקשים לא נמצאים בעשרים השורות האלה, והם שני הדברים שכמעט כל הסבר מדלג עליהם:
- למה סימן המינוס. העדכון מחסר את ה-gradient. כל מדריך כותב את זה; מעטים מאוד אומרים למה ה-gradient הוא הכיוון שעולה למעלה, שהיא העובדה היחידה שהופכת את סימן המינוס למשהו שאינו אקט של אמונה.
- כמה גדול הצעד. ״גדול מדי מתבדר, קטן מדי איטי״ זה נכון וחסר תועלת. יש מספר מדויק, אפשר לחשב אותו מתוך ההפסד, והפרק הזה מחשב אותו פעמיים — פעם לפרבולה צעצוע ופעם לנתונים האמיתיים.
ההגדרה, ולמה אי אפשר פשוט לחפש
קישור למקטע: ההגדרה, ולמה אי אפשר פשוט לחפשננסח מחדש כדי שהפרק הזה יעמוד בפני עצמו: שמונת החלקים מהמסוע של פרק 1, אבל עם שאלה אחרת. לא לקבל או לדחות — זה יחזור אחר כך — אלא לחזות את משקל החלק לפי הרוחב שלו.
import numpy as np
WIDTH = np.array([18.0, 19.5, 20.2, 21.0, 24.0, 25.5, 23.0, 26.0])
WEIGHT = np.array([47.0, 52.0, 49.0, 55.0, 61.0, 66.0, 70.0, 58.0])
x = WIDTH - WIDTH.mean() # 22.15 mm
y = WEIGHT - WEIGHT.mean() # 57.25 gהמדידות ממורכזות, בדיוק כמו בפרק 1 ומסיבה שתחזור עם ריבית לפני סוף הפרק הזה. המודל הוא קו, , וההפסד הוא השגיאה הריבועית הממוצעת שהפרק הקודם גזר:
שני פרמטרים. למה לא פשוט לנסות הרבה ערכים? בואו באמת נעשה את זה — grid מ- עד ומ- עד , בצעדים של :
grid 501 x 1001 = 501,501 evaluations in 3.67 s
best found: a = 2.1000, b = -0.0000, L = 24.592450חצי מיליון הערכות כדי לנעול שני מספרים לשתי ספרות אחרי הנקודה — והשנייה הזאת היא זמן שעון על מכונה אחת, כך שהרצה חוזרת יכולה לנחות בכל מקום בין שלוש לשש; מספר ההערכות והמינימום הם החלקים שמשתחזרים. Gradient descent, בסוף הפרק הזה, מגיע לארבע ספרות אחרי הנקודה בשמונה צעדים ולתשובת float64 המלאה בשלושים ושישה.
אבל מהירות היא לא הטיעון, וזו הנקודה שמכריעה את כל הקורס. Grid search עולה הערכות עבור פרמטרים ב- ערכים לכל אחד. עם אלף ערכים לכל ציר:
| מודל | פרמטרים | הערכות grid |
|---|---|---|
| הקו הזה | 2 | |
| רשת ה-XOR של פרק 5 | 9 | |
| רשת רב-שכבתית קטנה | 20,000 |
השורה השלישית היא לא מספר גדול, היא מספר חסר משמעות — יש בערך אטומים ביקום הנצפה. חיפוש לא נעשה איטי יותר כשהמודלים גדלים; הוא מפסיק להתקיים. כל מה שבא עכשיו קיים בגלל הטבלה הזאת.
נגזרת היא מדידה שאפשר לקחת
קישור למקטע: נגזרת היא מדידה שאפשר לקחתקבעו את לרגע כך שיהיה פרמטר אחד ועקומה אחת, שזה הציור שהפרק האחרון השאיר לכם. קחו נקודה עליה, , ושאלו: אם אזיז את בכמות קטנה , כמה ההפסד זז, לכל יחידת הזזה?
היחס הזה הוא עלייה חלקי ריצה — השיפוע של הקו הישר שעובר דרך שתי נקודות על העקומה. ככל ש- מתכווץ, שתי הנקודות מחליקות זו אל זו והקו נעשה המשיק. השיפוע שלו הוא הנגזרת : הקצב שבו ההפסד משתנה לכל יחידת שינוי ב-. לא קירוב של משהו, ולא כמות אינסופית קטנה. גבול של יחסים רגילים.
שווה להריץ את זה, כי המספרים אומרים משהו שההגדרה לא אומרת:
def loss1(a):
return np.mean((a * x - y) ** 2)
for h in [1.0, 1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12, 1e-14]:
q = (loss1(1.0 + h) - loss1(1.0)) / h
print(f"h = {h:<8.0e} slope estimate = {q:.10f} error = {abs(q + 16.385):.3e}")h = 1e+00 slope estimate = -8.9400000000 error = 7.445e+00
h = 1e-02 slope estimate = -16.3105500000 error = 7.445e-02
h = 1e-04 slope estimate = -16.3842555001 error = 7.445e-04
h = 1e-06 slope estimate = -16.3849925556 error = 7.444e-06
h = 1e-08 slope estimate = -16.3850003787 error = 3.787e-07
h = 1e-10 slope estimate = -16.3850444324 error = 4.443e-05
h = 1e-12 slope estimate = -16.3851154866 error = 1.155e-04
h = 1e-14 slope estimate = -17.0530256582 error = 6.680e-01שני דברים קורים כאן, ושניהם נושאים משקל.
השגיאה לא פרופורציונלית באופן מעורפל ל- — היא בדיוק . חלקו את במאה, והשגיאה מתחלקת במאה, לארבע ספרות משמעותיות בכל פעם. הקבוע הזה אינו קישוט: הוא חצי הנגזרת השנייה של ההפסד, והוא ההופעה הראשונה של רעיון שיגיע בעוד שני סעיפים — שעקומה ליד נקודה נראית כמו קו ועוד תיקון פרופורציונלי ל-.
ואז התבנית נשברת. מתחת ל- האומדן נעשה גרוע יותר, וב- הוא שגוי כבר בספרה השנייה. לא קרה שום דבר מתמטי; קופסת הנקודה הצפה מהפרק הקודם קרתה. ו- מסכימים בעשר הספרות הראשונות שלהם, החיסור שלהם הורס את הספרות האלה, וחלוקה של ההריסות במספר זעיר מגבירה את מה שנשאר. יש מיטבי — כאן סביב , בערך השורש הריבועי של אפסילון המכונה — ולהקטין עוד זה לא זהיר יותר, זה פחות. זכרו את זה; פונקציה בסוף הפרק הזה תלויה בזה.
השיפוע המדויק, מחשבון אינפיניטסימלי ולא ממדידה, הוא . אז אפשר להפסיק למדוד ולהתחיל לגזור.
הרכבה, וכלל השרשרת
קישור למקטע: הרכבה, וכלל השרשרתהנה הרעיון שעליו בנוי שאר הקורס, נאמר פעם אחת, בפשטות.
להרכיב שתי פונקציות פירושו להזין אחת לתוך השנייה: . לא יותר.
רשת עמוקה אינה כמו הרכבה. היא הרכבה. שכבה היא פונקציה; לערום שכבות פירושו להרכיב אותן; ״עומק״ הוא מספר הפונקציות בשרשרת. כשפרק 5 בונה רשת, הוא בונה את ושום דבר אחר. כלומר, כלל החשבון החשוב ביותר לענייננו הוא הכלל שגוזר הרכבה:
קצבים מוכפלים. אם משתנה פי שלושה מהר יותר מ-, ו- משתנה פי שניים מהר יותר מ-, אז משתנה פי שישה מהר יותר מ-. זה כל התוכן, וזו הסיבה שאות שעובר אחורה דרך עשר שכבות מוכפל בעשרה מספרים — ולכן פרק 6 מקדיש סעיף למה שקורה כשהמספרים האלה כולם קצת פחות מאחד.
השתמשו בזה על ההפסד שלנו. כתבו את השארית , כך ש-. כל תלוי ב- דרך הפונקציה הפנימית , שהנגזרת שלה היא . כלל השרשרת, איבר אחר איבר:
סימני ה- המסולסלים האלה מסמנים נגזרת חלקית: גוזרים ביחס למשתנה אחד ומתייחסים לכל האחרים כקבועים. לא קורה כאן משהו חדש — זה אותו גבול כמו קודם, רק לאורך ציר אחד. אוספים את הנגזרות החלקיות לווקטור ומקבלים את ה-gradient:
בנקודה הווקטור הזה הוא . שני מספרים. השאלה היא מה הם אומרים, וזה הצעד הראשון שכולם מדלגים עליו.
למה ה-gradient מצביע בעלייה
קישור למקטע: למה ה-gradient מצביע בעלייהה-gradient הוא וקטור של שיפועים לאורך הצירים. זה כל מה שהוכחנו. לא מובן מאליו — ולא אמור להיות מובן מאליו — שחיבור שלהם לווקטור מייצר משהו שמצביע לכיוון מסוים.
אז נגדיר את הדבר שאנחנו באמת רוצים. בחרו וקטור יחידה , כיוון. הנגזרת הכיוונית היא הקצב שבו ההפסד משתנה כשצועדים בכיוון הזה:
כלל השרשרת הופך את זה למשהו שאפשר לחשב. הליכה לאורך משנה את בקצב ואת בקצב , והתרומות מסתכמות:
קצב השינוי בכל כיוון הוא המכפלה הסקלרית של ה-gradient עם הכיוון הזה. ועכשיו הפאנץ׳, שהוא שורת גאומטריה אחת. כשכותבים את המכפלה הסקלרית עם הזווית בין הווקטורים,
כי ל- יש אורך 1. הדבר היחיד שאתם שולטים בו הוא , שהוא מקסימלי ב- ומינימלי בחצי סיבוב, מעלות. לכן:
- העלייה התלולה ביותר היא לאורך עצמו, והשיפוע שם הוא בדיוק .
- הירידה התלולה ביותר היא לאורך , והשיפוע שם הוא .
- בניצב ל-gradient, ההפסד לא משתנה בכלל. זו הסיבה שקווי מפה טופוגרפית חוצים את ה-gradient בזוויות ישרות.
זה סימן המינוס. לא מוסכמה, לא היפוך סימן שמישהו בחר: כיוון הירידה המהירה ביותר הוא ה-gradient השלילי כי מינימלי בחצי סיבוב, ולא משום סיבה אחרת.
מכיוון שזו טענה על כל הכיוונים, בדקו אותה מול כל הכיוונים. דגמו 3,600 מהם, אחד לכל עשירית מעלה, ומדדו כל אחד באמצעות הזזה קטנה:
theta = np.array([1.0, 4.0])
g = grad(theta)
print("gradient ", g)
print("its length ", np.linalg.norm(g))
print("its angle ", np.degrees(np.arctan2(g[1], g[0])) % 360, "degrees")
best = max(
((loss(theta + 1e-6 * u) - loss(theta - 1e-6 * u)) / 2e-6, np.degrees(ang))
for ang, u in (
(a, np.array([np.cos(a), np.sin(a)])) for a in np.arange(3600) * 2 * np.pi / 3600
)
)
print("steepest slope", best[0], "at", best[1], "degrees")gradient [-16.385 8. ]
its length 18.23371122399386
its angle 153.97598928042032 degrees
steepest slope 18.233709624837502 at 154.0 degreesחיפוש שלא יודע שום דבר על gradients, על פני 3,600 כיוונים, מוצא את הטיפוס התלול ביותר שלו ב-154.0 מעלות — הכיוון של ה-gradient עצמו, עד רזולוציית 0.1 המעלה של החיפוש. והשיפוע שהוא מוצא שם, 18.2337, הוא אורך ה-gradient לשש ספרות. המשפט אינו סיפור על המשמעות של gradients; הוא עובדה שאפשר למדוד, וזו המדידה.
למה צעד קטן במורד באמת עוזר
קישור למקטע: למה צעד קטן במורד באמת עוזרעכשיו הצעד השני שמדלגים עליו. אנחנו יודעים איפה למטה. לא נובע מזה שהליכה לשם תוריד את ההפסד, כי ״למטה״ הוא טענה על הזזה אינפיניטסימלית וצעד אינו אינפיניטסימלי.
הגשר הוא לינאריזציה. ליד נקודה, פונקציה חלקה היא המשיק שלה ועוד תיקון:
זו התפשטות טיילור מסדר ראשון. ה- שהושלך הוא העקמומיות — אותו איבר שגרם לאומדן בטבלת השיפועים לטעות בדיוק ב-. נציב את הצעד שאנחנו מתכוונים לקחת, :
ההפסד יורד ב-. כל חלק בזה אינו שלילי, כך שההבטחה אמיתית — עבור קטן מספיק, כי האיבר שהוזנח גדל כמו ובסוף אוכל אותה. זו כל התיאוריה. הנה ההבטחה מתקיימת, ואז נשברת:
eta = 0.2 promised 66.49364500 delivered -16.01619240 ratio -0.240868
eta = 0.1 promised 33.24682250 delivered 12.61936315 ratio 0.379566
eta = 0.01 promised 3.32468225 delivered 3.11840766 ratio 0.937957
eta = 0.001 promised 0.33246822 delivered 0.33040548 ratio 0.993796
eta = 0.0001 promised 0.03324682 delivered 0.03322620 ratio 0.999380
eta = 1e-05 promised 0.00332468 delivered 0.00332448 ratio 0.999938קראו מלמטה. ככל ש- מתכווץ, הירידה שהתקבלה מתכנסת לזו שהובטחה — יחס 0.99938, ואז 0.99994 — וזה משפט טיילור עובד. קראו מלמעלה וב- ה״ירידה״ שהתקבלה היא מינוס שש-עשרה. הצעד הלך במורד וההפסד עלה.
אז כלל העדכון הוא
והוא מגיע עם תנאי שאף אחד לא אומר, והוא ש- קטן מספיק. קטן מספיק ביחס למה, בדיוק, זה הסעיף הבא.
לקצב הלמידה יש תקרה, ואפשר לחשב אותה
קישור למקטע: לקצב הלמידה יש תקרה, ואפשר לחשב אותהנתחיל בעמק הפשוט ביותר שיש, , שבו . צעד אחד של gradient descent הוא
המיקום מוכפל ב- בכל צעד. זו סדרה הנדסית, ולסדרות הנדסיות יש בדיוק כלל אחד: הן מתכווצות כשהכופל קטן מ-1 בערך מוחלט וגדלות אחרת. לכן , כלומר .
הגבול נמצא בדיוק ב-. לא ״בערך 1״, לא ״1 בדרך כלל גדול מדי״. ב- הכופל הוא והנקודה קופצת בין ל- לנצח, לא מתקרבת ולא בורחת. מתחת לזה, מתכנסת; מעל לזה, מתבדרת. המרווח מתפצל שוב ב-, שם הכופל מחליף סימן: מתחת לזה ההתקרבות מונוטונית, מעל לזה הנקודה מפספסת מעבר למינימום ומחליפה צדדים, ובדיוק ב- הכופל הוא 0 וצעד יחיד נוחת על המינימום.
ארבעה משטרים, מארבע שורות אלגברה. לכו וחצו את הגבולות בעצמכם:
ועכשיו המקרה המעניין:
עכשיו הכלל הכללי, שנופל מאותו טיעון. הכופל היה בעצם , וליד מינימום להפסד רב-פרמטרי יש מספר כזה לכל כיוון — הערכים העצמיים של מטריצת הנגזרות השניות. כל הכיוונים צריכים להיות יציבים בבת אחת, לכן התקרה נקבעת על ידי הגדול ביותר:
עבור , , התקרה 1, וזה מה שגזרנו עכשיו. עבור המסוע שלנו, מטריצת הנגזרות השניות היא כאשר היא מטריצת הקלטים בעלת שתי העמודות, והערכים העצמיים שלה הם 2 ו-14.89, כך שהתקרה היא . זו תחזית עם חמש ספרות משמעותיות. בדקו אותה:
lr=0.1343 -> L = 24.5924
lr=0.13431 -> L = 24.5924
lr=0.13432 -> L = 4707.8 BLEW UP
lr=0.13433 -> L = 4.00452e+16 BLEW UP
lr=0.1344 -> L = 1.18229e+107 BLEW UPחמש ספרות אחרי הנקודה של התאמה בין שורת אלגברה ליניארית למאה אלף איטרציות של לולאת for.
וכאן פרק 1 חוזר. כל מה שמעל השתמש במדידות הממורכזות. הריצו את אותו קוד בדיוק על מילימטרים וגרמים גולמיים והערכים העצמיים הם 0.0298 ו-998.1 במקום 2 ו-14.89. התקרה קורסת מ-0.134 ל-0.002004 — באותה מידה של דיוק, מתכנסת ב-lr=0.002003 ומתפוצצת ב-lr=0.002004.
גרוע יותר מהתקרה הוא היחס בין הערכים העצמיים. מספר התנאי מודד כמה העמק רחוק מלהיות עגול: תעלה ארוכה וצרה כופה קצב קטן מספיק עבור הקירות התלולים, ואז הולכים על רצפת התעלה באותה זחילה. אצלנו הוא עובר מ-7.44 בממורכז ל-33,452 בגולמי. עם הקצב הטוב ביותר שכל גרסה יכולה לקחת:
| features | מספר תנאי | הקצב הטוב ביותר | צעדים עד מרחק 1% מהאופטימום |
|---|---|---|---|
| ממורכז | 7.44 | 0.1184 | 10 |
| מילימטרים וגרמים גולמיים | 33,452 | 0.0020037 | 79,513 |
אותם נתונים, אותו קוד, אותה תשובה בסוף — ופי שמונת אלפים עבודה, כי אף אחד לא חיסר ממוצע. בפרק 1 אותה השמטה עלתה ל-perceptron בפקטור של ששת אלפים ב-epochs, והאבחנה שם הייתה גאומטרית: הנתונים צפו רחוק מהראשית. זו אותה גאומטריה כאן בתחפושת של אופטימיזציה, ולכן נרמול קלטים הוא לא עצת היגיינה אלא חשבון.1
עשרים שורות
קישור למקטע: עשרים שורותשום דבר ממה שהיה למעלה לא דרש ספרייה. הנה כל האופטימייזר.
def loss(theta):
a, b = theta
return np.mean((a * x + b - y) ** 2)
def grad(theta):
a, b = theta
residual = a * x + b - y
return np.array([np.mean(2 * residual * x), np.mean(2 * residual)])
def descend(theta, lr, steps):
theta = np.array(theta, dtype=float)
for _ in range(steps):
theta = theta - lr * grad(theta)
return theta
theta = descend([0.0, 0.0], lr=0.05, steps=60)
print(theta, loss(theta))[ 2.10040296e+00 -2.76445533e-15] 24.592448791134984תשובת least-squares הסגורה עבור שמונה הנקודות האלה היא , , עם הפסד של . הלולאה מצאה אותה לשמונה ספרות משמעותיות בלי לדעת שקיימת צורה סגורה — וזה חשוב, כי מפרק 5 ואילך לא תהיה אחת.
המסלול, כי לראות אותו זו הנקודה:
0 a=0.000000 b=0.000000 L=57.437500
1 a=1.563750 b=0.000000 L=26.736582
2 a=1.963288 b=-0.000000 L=24.732418
5 a=2.098116 b=-0.000000 L=24.592488
10 a=2.100400 b=-0.000000 L=24.592449
60 a=2.100403 b=-0.000000 L=24.592449רוב המרחק נחתך בשני הצעדים הראשונים, כי ה-gradient הוא הגדול ביותר כשאתם הכי רחוקים מהתחתית ומתכווץ כשמתקרבים. Gradient descent מאט אוטומטית ליד מינימום. זו תכונה, ובפרק 6 זו גם בעיה.
איפה עוד השיפוע הוא אפס
קישור למקטע: איפה עוד השיפוע הוא אפסבטיעון עד כאן יש חור. הצעד נעצר כש-, וקראנו לזה ״המינימום״. נקודה עם gradient אפס היא נקודה קריטית, ולהיות מינימום היא רק אחת הדרכים להיות כזו:
- מינימום מקומי: עלייה בכל כיוון, אבל ייתכן שאינו הנקודה הנמוכה ביותר מסוגה בכלל;
- מקסימום מקומי: ירידה בכל כיוון;
- נקודת אוכף: עלייה בכמה כיוונים וירידה באחרים. למשטח יש , שהוא אפס בראשית, שם הפונקציה היא מינימום לאורך ציר ה- ומקסימום לאורך ציר ה- בו-זמנית.
Gradient descent לא יכול להבדיל ביניהם, כי הוא מסתכל רק על ה-gradient, וה-gradient הוא אפס בשלושתם.
לקו שלנו יש נקודה קריטית אחת והיא התשובה — הפסד של שגיאה ריבועית על מודל ליניארי הוא קמור, קערה יחידה, וירידה עליו לא יכולה להיכשל במציאת המינימום הגלובלי. התכונה הזאת לא שורדת מגע עם הקורס הזה. ההפסד של רשת נוירונים אינו קמור, ומפרק 5 ואילך ״המינימום״ הוא לא דבר שקיים: יש רבים, בעומקים שונים, ואיזה מהם תקבלו תלוי מאיפה התחלתם. זה משפט אחד ונשאר משפט אחד, כי התיאוריה גדולה והמשמעות המעשית קטנה.
אפשר לראות את כל התוצאה על עקומה אחת. קחו את , שיש לה שני עמקים בעומקים שונים:
x = -1.046681 f(x) = -0.352386 minimum
x = 0.101031 f(x) = 0.005026 maximum
x = 0.945649 f(x) = -0.152639 minimumנחיתה בעמק הרדוד גרועה ב-56.7% מבחינת ההפסד, ולאלגוריתם אין דרך לדעת, כי מתוך עמק כל כיוון הוא עלייה. אין לזה תיקון ב-gradient descent ולא מגיע אחד. מה שכן יש, בפועל, הוא הממצא שזה חשוב הרבה פחות ממה שהתמונה הזאת מרמזת — בממדים הגבוהים מאוד של רשת אמיתית רוב הנקודות הקריטיות מתבררות כאוכפים ולא כמלכודות,2 ופרק 5 מודד באיזו תדירות רשת קטנה באמת נתקעת.
צעדים זולים יותר: סטוכסטי, minibatch, מומנטום
קישור למקטע: צעדים זולים יותר: סטוכסטי, minibatch, מומנטוםדבר אחד לגבי grad למעלה אמור להטריד אתכם: הוא מסכם על פני כל מערך הנתונים בכל צעד. שמונה חלקים זה כלום. מיליון הם מיליון חישובי gradient כדי להזיז את הפרמטרים פעם אחת.
הדרך החוצה היא שה-gradient הוא ממוצע, וממוצע אפשר לאמוד מתוך מדגם. חשבו אותו על קומץ אקראי — minibatch — וצעדו על זה. האומדן רועש; הוא גם חסר הטיה, ומאות צעדים רועשים זולים מנצחים צעד מדויק יקר אחד. על מאה אלף חלקים סינתטיים, כשסופרים gradients פר-דוגמה ולא צעדים:
| שיטה | צעדים עד מרחק 0.1% מהאופטימום | gradients פר-דוגמה |
|---|---|---|
| full batch | 7 | 700,000 |
| minibatch של 32 | 100 | 3,200 |
| דוגמה אחת בכל פעם | 17,580 | 17,580 |
פי מאתיים ותשע-עשרה פחות חשבון כדי להגיע לאותו מקום. והקיצון — דוגמה אחת בכל פעם, הקירוב הסטוכסטי המקורי של Robbins ו-Monro3 — הוא לא המנצח: הוא גרוע פי חמישה מאצוות של 32, כי 32 דוגמאות כמעט לא עולות יותר מאחת על חומרה שמכפילה מטריצות, בזמן שהרעש יורד עם השורש הריבועי של גודל האצווה. הטרייד-אוף הזה הוא הסיבה שבכל סקריפט אימון שתקראו אי פעם יש batch_size.
מומנטום הוא התיקון הזול השני, והוא מכוון ישר לתעלה. בעמק עם מספר תנאי גרוע, הצעדים מזגזגים לרוחב הכיוון הצר בזמן שהם זוחלים לאורך הארוך. מומנטום שומר ממוצע רץ של gradients קודמים, כך שהרכיבים המתנדנדים מתבטלים והרכיב העקבי מצטבר:4
שתי שורות נוספות. על המסוע הגולמי הלא-ממורכז — מספר תנאי 33,452, המקרה הגרוע ביותר שיש לנו — בקצב הטוב ביותר שירידה רגילה יכולה לקחת:
momentum beta=0.0 -> 79,513 steps to 1%
momentum beta=0.9 -> 1,609 steps to 1%
momentum beta=0.99 -> 461 steps to 1%פקטור של 172 עבור שתי שורות קוד. פרק 6 הופך את זה ל-Adam; המנגנון כבר כאן.
הבדיקה שתצטרכו בפרק 5
קישור למקטע: הבדיקה שתצטרכו בפרק 5כל gradient בפרק הזה נגזר ביד ולכן יכול להיות שגוי. התיקון הוא טבלת השיפועים מההתחלה: למדוד את הנגזרת נומרית ולהשוות. השתמשו בהפרש המרכזי, , שמבטל את איבר השגיאה המוביל ומדויק הרבה יותר עבור אותו .
def numeric_grad(f, theta, h=1e-5):
theta = np.asarray(theta, dtype=float)
out = np.zeros_like(theta)
for i in range(theta.size):
bump = np.zeros_like(theta)
bump[i] = h
out[i] = (f(theta + bump) - f(theta - bump)) / (2 * h)
return out
def gradcheck(f, df, theta, h=1e-5):
analytic = np.asarray(df(theta), dtype=float)
numeric = numeric_grad(f, theta, h)
return np.max(np.abs(analytic - numeric) / np.maximum(1e-8, np.abs(analytic) + np.abs(numeric)))הצורה היחסית של ההשוואה חשובה: הפרש מוחלט של הוא אסון על gradient בגודל וחסר משמעות על אחד בגודל .
relative error: 1.8929136036763527e-11
with 2 dropped: 0.33333333331650744השורה הראשונה היא ה-gradient שנגזר ביד למעלה. השנייה היא אותה פונקציה עם הפקטור 2 שנשאר מחוץ לרכיב אחד — טעות הקלדה של תו יחיד — והבדיקה תופסת אותה מיד. כל דבר מתחת בערך ל- הוא הסכמה; כל דבר מעל הוא באג. שמרו את הפונקציה הזאת: פרק 5 משתמש בה כדי לנפות באגים במנוע גזירה אוטומטית, והיא הסיבה היחידה שאפשר בכלל למצוא gradient שגוי.
לאן זה ממשיך
קישור למקטע: לאן זה ממשיךכל מה שבפרק הזה נשען על הנחה אחת שמעולם לא נאמרה: שאפשר לכתוב את .
עבור קו עם שני פרמטרים, זו הייתה שורת אלגברה. זה מפסיק להיות כזה כמעט מיד. בקשו ממערכת אלגברה סימבולית את הנגזרת של הפסד של רשת ביחס למשקל יחיד בשכבה הראשונה, עבור דוגמה יחידה, וספרו את פעולות החשבון בתשובה:
| רשת | פעולות בנגזרת חלקית אחת |
|---|---|
| ארבע יחידות חבויות, שכבה אחת | 40 |
| ארבע יחידות חבויות, שתי שכבות | 301 |
| ארבע יחידות חבויות, שלוש שכבות | 1,717 |
השורה השלישית היא רשת עם 57 פרמטרים — רשת כל כך קטנה שהיא הייתה הערת שוליים בפרק 6 — ולכתוב את ה-gradient שלה ביד פירושו בערך 97,869 פעולות עבור דוגמת אימון אחת. אין סימון שמציל מזה. מה שמציל הוא ההבחנה שלכלל השרשרת כשהוא מיושם על הרכבה יש מבנה עצום, שאותן כמויות ביניים מופיעות שוב ושוב, ושחישוב שלהן בסדר הנכון נותן את כל הנגזרות בערך במחיר של מעבר קדימה אחד. זה פרק 5.
אבל יש קודם בעיה קטנה יותר, והיא מחכה מיד.
עכשיו יש לנו מכונה שתתגלגל במורד על כל הפסד גזיר. כוונו אותה לשאלה המקורית של המסוע — לקבל או לדחות, יעד שהוא 1 או 0 — שימו sigmoid על הפלט כדי שיחזה הסתברות, ומזערו שגיאה ריבועית. היא תרוץ. היא גם בקושי תזוז כשהיא הכי טועה, וה-gradient מסביר למה:
| פלט | חיזוי | אמת | gradient עם שגיאה ריבועית | gradient עם cross-entropy |
|---|---|---|---|---|
| 0.5000 | 1 | |||
| 0.1192 | 1 | |||
| 0.0025 | 1 | |||
| 1 |
מודל שטועה בביטחון, באופן קטסטרופלי — חוזה 0.0000454 כשהתשובה היא 1 — מייצר gradient של שגיאה ריבועית בגודל . אין לו מושג שהוא בצרות. העמודה השנייה, מהפסד שעדיין לא גזרנו, מדווחת 1.0: דחיפות מקסימלית, בדיוק איפה שמגיע.
וזה מעלה את השאלה שהפרק הבא נפתח בה. הפרק הקודם אמר שהפסד הוא הנחה על הרעש, וששגיאה ריבועית מניחה רעש גאוסי. איזה מודל רעש יש לתשובת כן-או-לא — ואיזה הפסד יוצא כשמריצים עליו את אותה גזירה?
מקורות ושיטה
קישור למקטע: מקורות ושיטההשיטה עתיקה יותר מכל אלה: Cauchy תיאר אותה בהערה ל-Académie des Sciences ב-1847, כדרך לפתור מערכות משוואות באמצעות הליכה במורד על סכום השאריות הריבועיות שלהן. כדאי לקרוא לצד הפרק הזה גם את An overview of gradient descent optimization algorithms של Sebastian Ruder (arXiv:1609.04747), שמכסה מומנטום עד Adam בארבעה-עשר עמודים קריאים; פרק 3 ב-Numerical Optimization של Nocedal ו-Wright (מהדורה 2, Springer, 2006), שמשפט 3.3 שלו נותן את קצב ההתכנסות של steepest descent על ריבועית במונחי מספר התנאי — זו התיאוריה שמאחורי הסיבה ש-conditioning קובע את מספר הצעדים, אף שהוא מטפל ב-line search ולא בתקרת בצעד קבוע שנמדדה למעלה, או §5.8 ו-§7.1 ב-Mathematics for Machine Learning של Deisenroth, Faisal ו-Ong לאותה קרקע עם פחות מכונות; §6.1 ב-Understanding Deep Learning של Prince ו-§4.3 ב-Deep Learning של Goodfellow, Bengio ו-Courville; Dive into Deep Learning §12.1–12.3, שיש בו את ניתוח ה-minibatch עם יותר מדידות ממה שיש כאן מקום; ופרק 4 ב-Hands-On Machine Learning של Géron (מהדורה 3), הטיפול המעשי ביותר בקצב הלמידה כדבר שמכוונים ולא גוזרים. הערות MIT 6.390 שמות את gradient descent לפני סיווג, כמו שהקורס הזה עושה ומאותה סיבה.
הפניות
קישור למקטע: הפניות-
LeCun, Y., Bottou, L., Orr, G. B. and Müller, K.-R. Efficient BackProp, in Neural Networks: Tricks of the Trade (Springer, 1998), pp. 9–50. סעיף 4.3 נותן את ההמלצה וסעיף 5.1 את הטיעון ששימש בתיבת הפירוט למעלה: מירכוז וסקיילינג של קלטים משנים את הערכים העצמיים של מטריצת הנגזרות השניות, ולכן את מספר הצעדים, ולא רק את הנוחות הנומרית. ↩
-
Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. and Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). הטיעון שבממדים גבוהים נקודות קריטיות הן ברובן המכריע אוכפים ולא מינימות מקומיות, כי מינימום דורש שכל אחד מאלפי הכיוונים יתעקם כלפי מעלה בו-זמנית. ↩
-
Robbins, H. and Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). המאמר שקבע שאומדן רועש של gradient מספיק, בהינתן גודל צעד שמתכווץ בדרך הנכונה. ↩
-
Polyak, B. T. Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), pp. 1–17 (1964). שיטת heavy-ball, שהיא עדכון המומנטום שלמעלה, עשרים ושתיים שנה לפני ש-backpropagation הגיע לתחום הזה. ↩