אנדריי קארפאת'י והיעילות הלא-סבירה של רשתות ניורונים

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

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

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

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

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

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

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

מבחינה סכמטית ניתן להציג זאת כך:

מתוך הפוסט של קארפאת'י

האותיות שמוקלדות הן הבלוקים האדומים, בהם כל אות מיוצגת כוקטור עם '1' במיקום של האות באלף-בית ו-0 בשאר המקומות. בדוגמה הזו באלף-בית יש רק 4 אותיות (h, e, l, o). התוכן של הזיכרון הפנימי, בבלוקים הירוקים, הוא וקטור 3-מימדי, והוא משתנה אחרי כל הקלדה כפונקציה של האות שהוקלדה והערך הקודם של הזיכרון. לאחר מכן התחזית לאות הבאה, בבלוקים הכחולים, מחושבת כפונקציה של הזיכרון הנוכחי. היינו רוצים שבבלוקים הכחולים המספרים הירוקים (במיקום של האות הבאה) יהיו גבוהים והאדומים נמוכים, וכך אנחנו מאמנים את הרשת. [התמונה מתוך הפוסט של קארפאת'י]

למעשה, כל הבלוקים הירוקים מבצעים בדיוק את אותו החישוב ובעצם יש להם שני קלטים ושני פלטים. הקלטים הם תוכן הזיכרון הקודם והאות הנוכחית, והפלטים הם תוכן הזיכרון המעודכן והחיזוי לאות הבאה. וכל שנותר הוא ללמוד את הפרמטרים של החישוב שממפה את הקלטים אל הפלטים (בדוגמה הם מסומנים כ-W_xh, W_hh ו-W_hy) בצורה שגורמת לטקסט להתכווץ כמה שיותר. למודל הזה קוראים באנגלית Recurrent Neural Network או RNN בקיצור.

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

מתוך XKCD

מתוך XKCD

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

bible1

bible2

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

אז עכשיו בואו ננסה להיכנס לנבכי הרשת ולהבין מה היא כותבת בזיכרון הפנימי שלה. נסיון לעשות reverse engineering לוקטור הזיכרון הפנימי [1], גילה שיש שם אלמנטים שמציינים אם כרגע יש סוגריים או מרכאות פתוחים (כמו עכשיו, כדי שנעלה את הסבירות לכך שהסוגר שלהם יגיע בקרוב… הנה), אם אנחנו בתוך טקסט שהוא לינק לאתר אחר, אם אנחנו קרובים לסוף שורה או שזה זמן טוב ללחוץ "אנטר" ולעבור לשורה הבאה (למשל אחרי נקודותיים). כשמזינים למערכת קוד C, יש תאים בזיכרון שמציינים אם הקוד הוא חלק מתנאי if, אם הוא חלק מהערה, ואת עומקו של ה-scope הנוכחי. כל אלו דברים שבאמת נראה הגיוני שצריך להיות מודעים אליהם כשכותבים טקסט או קוד תוכנה.

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

לקריאה נוספת:

[1] Visualizing and Understanding Recurrent Networks. Andrej Karpathy, Justin Johnson, Fei-Fei Li. Arxiv 2015.  [link]

מודעות פרסומת
פוסט זה פורסם בקטגוריה deep learning, עם התגים , , , . אפשר להגיע ישירות לפוסט זה עם קישור ישיר.

6 תגובות על אנדריי קארפאת'י והיעילות הלא-סבירה של רשתות ניורונים

  1. גלעד ב. הגיב:

    אז מה עם תעלומת המקף הכפול בטקסטים התנ"כיים שג'ונרטו? גילית כבר למה הרשת שלך מוסיפה אותם?

  2. lifesimulator הגיב:

    המקפים הכפולים מופיעים במקור. ראה למשל בבראשית א', פסוק כ':
    http://www.mechon-mamre.org/i/t/t0101.htm

    או בבראשית ד', פסוקים כ'-כ"ה:
    http://www.mechon-mamre.org/i/t/t0104.htm

  3. חגי שחר הגיב:

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

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

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

להשאיר תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת /  לשנות )

תמונת גוגל

אתה מגיב באמצעות חשבון Google שלך. לצאת מהמערכת /  לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת /  לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת /  לשנות )

מתחבר ל-%s