פרק 1 - הקדמה ל-supervised learning (למידה מונחית)
בינה מלאכותית (או 'למידת מכונה') הוא תחום הנועד לפתרון בעיות שאי אפשר לפותרן באמצעים תכנותיים "רגילים", קרי, באמצעות תנאים ולולאות פשוטים.
הסיבה לקושי היא במרחב האפשרויות העצום. אין דרך לנסח כללים לאלפי או מיליוני משתנים המשפיעים על פתרון הבעיה.
לדוגמא, אם נרצה למשל לסווג פציינטים ל"בריא/לא בריא", על סמך מדדים רפואיים כמו חום, דופק, לחץ דם ועוד, נצטרך להזין לתוכנית המחשב אינספור מצבים וקומבינציות אפשריות של המדדים הללו כדי שנוכל לעשות זאת.
למעשה, אפילו בניית תנאים כאלו עשויה להיות משימה בלתי אפשרית. כדי להבין את התנאים והקשרים בין המשתנים עלינו לנסח אותם מתוך אלפי, או מיליוני, מקרים של חולים אחרים עם פרמטרים שונים. מספר המדדים הרפואיים עשוי להגיע לעשרות, אם לא מאות, פרמטרים. בבעיות אחרות, מספר המשתנים אף עשוי להגיע למיליונים.
לצורך כך פותחו אלגוריתמים שונים שנועדו לפתור בעיות מסוג זה. ניתן לחלק את האלגוריתמים השונים ל-3 משפחות מרכזיות:
Supervised learning - אלגוריתמים אלו מאפשרים לנבא labels (תוויות) מתוך features (מאפיינים) על סמך דוגמאות קודמות. למשל, נוכל לנבא האם פציינט מסויים חולה (-label) לפי פרמטרים רפואיים, כמו חום ולחץ דם (-features) על סמך דוגמאות של חולים אחרים.
Unsupervised learning (למידה בלתי-מונחית) - אלגוריתמים אלו מאפשרים בעיקר קיבוץ של נתונים שונים לקבוצות עם סגמנטים דומים. למשל, נוכל לקבץ צופי טלוויזיה לקטגוריות שונות כמו מדע בדיוני ורומנטיקה (נטפליקס ידועה כמי שעושה שימוש באלגוריתמים אלו לניתוח הרגלי הצפיה של לקוחותיה בכדי להציע להם סרטים וסדרות שרלוונטיים להם).
Reinforcement learning (למידת חיזוק) - אלגוריתמים אלו, בשונה מהשניים הקודמים, אינם פועלים על סמך נתונים קודמים, אלא באמצעות "חיזוקים" לביצועים טובים (כמו ניצחון בשחמט), ו"עונשים" לביצועים גרועים. על האלגוריתם "להבין" איזה מהחלטותיו הביאה לביצועים הטובים ואלו לגרועים ולהשתפר בהתאם.
בפרקים הבאים נתמקד באלגוריתמים ממשפחת supervised learning.
notebook ו-scikit learn
בקישור הזה תוכלו להשתמש און ליין בסביבת העבודה notebook ללא התקנות מיותרות. לחילופין, תוכלו להתקין את anaconda - המכילה גם את notebook.
notebook נוחה מאוד לעיבוד נתונים, משום שהיא מאפשרת הרצות חוזרות של קטעי קוד והצגה שלהם בצורה נוחה. השימוש הוא אינטואיטיבי ואינו מצריך לימוד מיוחד. כדי להריץ קטע קוד השתמשו באייקון הבא מתחת לסרגל הכלים:
notebook מכילה את הספרייה scikit learn. זוהי הספרייה העיקרית בה נשתמש בפרקים הבאים.
מאגרי מידע לתרגילים ניתן להוריד מכאן.
features ו-labels
"גיל", "שכר" ו"מקצוע" הם דוגמאות למשתנים בדוגמא שלנו. כשנרצה לנבא מהו השכר של עובד עם משתנים מסויימים - נקרא למשתנה השכר "label". את כל שאר המשתנים נכנה "features".
באותה מידה, כשנרצה לנבא את גיל העובד על פי משתנים אחרים, כמו שכר ומקצוע, נקרא למשתנה הגיל - "label" ואת כל שאר המשתנים, כמו השכר והמקצוע, נכנה "features".
סוגי משתנים
משתני continuous (כמותי*) הם משתנים הנמצאים על רצף מסויים. לדוגמא, עובד יכול להיות בגיל 30, 31, 32, 33 וכו'.
משתני discrete (נבדל) הם משתנים שלא מכילים רצף. לדוגמא, מקצועות של שכירים במשק: נגר, צלם, סנדלר וכו'.
משתני ה-discrete הם בעייתיים יותר ונדרש עיבוד מקדים לנתונים כדי שניתן יהיה לבצע עליהם חישובים. בהמשך נראה כיצד לעשות זאת.
labels ו-features יכולים להיות גם משתני continuous וגם משתני discrete (אם כי יש הבדלים בסוגי האלגוריתמים בהם נוכל להשתמש, כפי שנראה בהמשך).
classification ו-regression
כשמשתנה המטרה (ה-label) יהיה discrete, אנו נאמר שהבעיה שאנו עוסקים בה היא בעיית classification (קלסיפיקציה - סיווג). כלומר שאנו מנסים לנבא את המחלקה (-class) אליה משתייך האובייקט.
כשמשתנה המטרה יהיה continuous, נאמר שהבעיה היא בעיית regression (רגרסיה). דהיינו, למשתנה המטרה אין מחלקה מסויימת, אלא הוא נמצא על רצף כלשהו.
ניבוי
התבוננו בגרף הבא:
במקרה שבו יש רק 2 משתנים, כמו בגרף 1, נוכל בדרך כלל לבצע ניבויים בטביעת עין. ניתן לנבא מהתבוננות בגרף בלבד שעובד בגיל 28 יקבל שכר של 17,000 שקל למרות שהנתון הזה לא מופיע בנתונים.
אבל פעמים רבות יש מספר גדול הרבה יותר של משתנים. פעמים שמדובר באלפי משתנים. במקרה כזה לא נוכל לבצע ניבוי בטביעת עין ונוכל להשתמש באלגוריתמים ממשפחת supervised learning.
בשלב הראשון, האלגוריתמים מבצעים "אימון" (train) על הנתונים. דהיינו, בונים מודל, או סדרה של חוקים, שמתארים בצורה הטובה ביותר את הקשר בין המאפיינים של הנתונים לבין משתנה המטרה. במקרה המתואר בגרף למעלה, נוכל לומר שהקו האדום הוא המודל שמתאר את ה"חוק" לפיו יש קשר לינארי בין הגיל לבין השכר (-משתנה המטרה).
בשלב השני, נוכל לתת לאלגוריתם אובייקט חדש, שלא הופיע בנתונים המקוריים, ובהתאם למודל שהאלגוריתם בנה לנבא מה יהיה הערך של משתנה המטרה שלו. דהיינו, כפי שאמרנו, נוכל לנבא שעובד בגיל 28 יקבל שכר של 17000 שקל למרות שהנתון עצמו אינו בנתונים המקוריים.
ההנחה היא כמובן שהאובייקט שאותו אנו מנסים לנבא מגיע מאותו עולם של הנתונים שלפנינו. אם לדוגמא הנתונים נאספו בישראל - לא נוכל לנבא מה יהיה גובה השכר של עובד בארה"ב.
נתונים
את הנתונים נאחסן ב-DataFrame של הספריה pandas. כך נוכל לבצע חישובים מורכבים ביעילות.
למרות שהשורה הראשונה באקסל אינה חלק מהנתונים, היא מאפשרת גישה קלה:
preprocessing (עיבוד מקדים)
אלגוריתמים של supervised learning יכולים לעבד רק נתונים מספריים. כך שנצטרך להמיר את הנתונים שאינם מספרים, כמו 'מקצוע', לנתונים מספריים.
נוכל לכאורה לבצע הקצאה שרירותית של מספרים לכל משתנה. לדוגמא, לקבוע ש: 'נגר' = 0, 'צלם' = 1, 'סנדלר' = 2. הבעיה היא שאין שום דבר בסנדלר שהופך אותו במובן כלשהו ל"יותר" מצלם (אם כי נוכל לעשות זאת למשתנים בינאריים שהערכים שלהם הם 'כן' ו'לא').
לכן, האפשרות המקובלת היא להרחיב את כל המשתנים שאינם מספרים למשתנים בינאריים על ידי משתני דמו שמקבלים ערכים של 0 או 1 בלבד. כך, בדוגמא שלנו, במקום משתנה אחד בשם 'מקצוע' שיכול לקבל ערכים כמו 'נגר', 'צלם', ו'סנדלר', נקבל את המשתנים הבאים: 'נגר', 'צלם' ו'סנדלר', כשכל אחד ממשתנים אלו יכול לקבל את הערכים 0 ו-1.
משתנים שכבר היו בינאריים מראש, כמו 'יש/אין ילדים', שהערכים שלהם הם 'כן' ו'לא', יקבלו את הערכים 0 ו-1 ללא צורך במשתני דמו.
נוכל לעשות זאת כך:
או, כשנרצה לעשות זאת על כל ה-data:
bias-variance tradeoff
הסתכלו בגרף להלן. קל יהיה לנבא שעובד שמרוויח מעל 15,000 שקל עובד בתעשיית ההייטק.
בגרף הבא כבר יהיה יותר קשה לנבא האם עובד שמרוויח 9,000 שקל (בנקודה המסומנת) אכן עובד בתעשיית ההייטק.
נוכל להשאיר את קו הניבוי כמקודם:
אך ניתן לראות בקלות שהמודל מכיל שגיאות רבות עוד לפני שביצענו ניבויים. בודאי ובודאי כשנרצה לבצע ניבויים.
לשגיאה מסוג זה אנו קוראים bias (הטיה). המציאות מורכבת יותר מהמתואר בגרף, וקביעת מקום התעסוקה של העובד על סמך גובה השכר בלבד לא משקפת את המציאות ממנה נדגמו הנתונים. במילים אחרות, המודל "פשוט" מדי ולא מכיל קשרים סיבתיים דקים יותר שיכולים לנבא את מקום העבודה של העובד. במציאות, גובה השכר של העובד היא לא חזות הכל ביחס למקום העבודה שלו.
נוכל להתמודד עם הבעיה בכמה דרכים.
אפשרות אחת היא להפוך את קו הניבוי ל"מורכב" יותר באמצעות פרמטרים שונים (בפרקים על האלגוריתמים עצמם נגדיר בדיוק מה זה "מורכב" ואיך משתמשים בפרמטרים כדי לעשות זאת בצורה מתמטית). למשל כך:
לפי מודל זה - נקודות שיופיעו מעל קו הניבוי ישויכו להייטק, ואילו הנקודות שיופיעו מתחת לקו הניבוי ישוייכו ל"לא-הייטק".
החיסרון באפשרות הזו הוא די ברור. קו ההחלטה הזה הוא לא הגיוני, אלא הוא "מאולץ" כדי לתמוך בנתונים. במקרה זה ברור שאין אפילו טעות אחת ביחס לנתונים, אבל נוכל להניח בבטחה שכשנרצה לנבא שכר של עובד בפועל - ניתקל בשגיאות.
בעיה זו נקראת overfitting - התאמת יתר של המודל לנתונים. דהיינו, המודל אינו יכול להכליל (generalize) את המודל ולבצע ניבויים.
אפשרות שניה לפתור את בעיית ה-overfitting היא בעזרת הוספה של משתנים. התבוננו למשל בגרף הבא ב-3 מימדים:
הנקודות האדומות והכחולות מייצגות את המשתנה הבינארי "עובד/לא עובד בהייטק", בנוסף לגיל והשכר. עברנו אם כן לנתונים ב-3 מימדים.
ניתן לראות שהאנומליה בנתונים, שהמודל הקודם התקשה להסביר - מובנים עכשיו בהחלט. נתוני העובדים בשכר הנמוך שעובדים בהיי-טק, הם למעשה עובדים בגיל צעיר יותר, ולהיפך - נתוני העובדים בשכר הגבוה שאינם עובדים בהיי-טק, הם למעשה עובדים בגיל מבוגר יותר. זה די מתבקש, בהתחשב בכך שהבעיה במודל הקודם הייתה בעיית bias - דהיינו, ה"פשטות" של המודל. לאחר הוספת המשתנה הנוסף אנו מקבלים מודל טוב יותר ללא הוספת מורכבות.
אבל פתרון זה מעורר בעיות אחרות. הוספת משתנה, דהיינו הוספה של מימד, מכפילה את הנפח ומדללת את הנתונים ש"ממלאים" את החלל הזה. כשם שמעבר מ-2 מימדים ל-3 מימדים בעולם הפיזי מגדילה את החלל.
ניתן לראות זאת בדוגמא שלנו בקנה מידה קטן:
נתונים על המקומות המסומנים, המייצגים את העובדים בשכר וגיל חציוני, חסרים. לא נוכל לנבא בדיוק סביר האם עובדים אלו עובדים בהייטק או לא.
העניין מחמיר ככל שמדובר במספר מימדים גדול יותר. השטח האפשרי גדל בצורה מעריכית (x^n) ומשאיר כיסים גדולים ריקים מנתונים שבהם אין לנו יכולת לנבא על סמך דוגמאות קודמות.
בעיה זו נקראת קללת המימד. מכיוון שמדובר בגידול מעריכי, מהר מאוד נהיה בלתי אפשרי למלאות את כל החלל והמודל הופך שוב להיות מותאם מדי ולסבול מ-overfitting. במילים אחרות, העלאת מימדים בלא תמיכה בנתונים נוספים (בדרך כלל מספר הנתונים הוא נתון קבוע ואין לנו יכולת להוסיף נתונים כרצוננו) - עלולה לגרום ל-overfitting.
בעיות אלו, bias ו-overfitting הן למעשה בעיות הפכיות. ככל שנגמיש יותר את ה"מורכבות" של המודל, או נוריד את מספר המימדים, כך נקבל מודל פשוט מדי המכיל שגיאות רבות עוד בשלב בניית המודל, כל-שכן בזמן הניבוי. וככל שנוסיף מורכבות למודל, או נוסיף מימדים ללא תוספת מקבילה במספר הנתונים - כך המודל יהיה מותאם ומאולץ מדי על הנתונים ונראה שגיאות רבות יותר בזמן הניבוי.
זהו ה- bias overfitting tradeoff. המטרה היא, כמובן, למצוא את האיזון שבין הקצוות הללו ולהעלות למקסימום את שיעור הניבוי.
“שיעור הניבוי: מדדים שונים קובעים את שיעור הניבוי. בבעיות classification, לדוגמא, ניתן לבצע חלוקה של מספר הניבויים המוצלח במספר הניבויים הכולל. כך לדוגמא 80 ניבויים מוצלחים מתוך 100 ניבויים יעמיד את שיעור הניבוי על 0.8. במדד זה יעמוד שיעור הניבוי בין 0 (- כל הניבויים כשלו) ל-1 (- כל הניבויים צלחו).”
ישנם מדדי ניבוי נוספים. נחזור אל כמה מהם בהמשך.
כיצד נמצא את נקודת האיזון?
trainnig ו-testing
כדי למצוא את האיזון המבוקש, נוכל לשמור חלק מהנתונים שבידינו לצורך בחינה (test) שתנטר את המורכבות של הנתונים ותאפשר לנו למדוד האם אנו נמצאים בבעיית bias או בבעיית overfitting. מיד נסביר.
מקובל לשמור בין 20 ל-25 אחוזים מהנתונים לצורך test. לסט הנתונים הזה נקרא test set, בעוד שליתרת הנתונים נקרא training set ועליהם בלבד נבצע את האימון של המודל. כך נוכל לבחון את המודל על ידי נתונים "חיצוניים" שהמודל לא נבנה על ידם.
כדי להבין כיצד ה-test set עוזר לנו למצוא את נקודת האיזון, נוכל לצייר את שיעור הניבוי של ה-train set וה-test set כפונקציה של כמות הנתונים:
ניתן לראות שעקומות הניבוי מתכנסות לשיעור ניבוי די נמוך, וכמות הנתונים מפסיקה להשפיע בשלב מסוים. זוהי התנהגות אופיינית למודל הסובל מבעיית bias - המודל פשוט מדי ונתונים נוספים לא משפרים את הניבוי.
הגרפים הבאים מדגימים בעיית overfitting:
כפי שניתן לצפות מבעיית overfitting, אחוז הניבוי של ה-training set מאוד גבוה - המודל מותאם במיוחד לנתונים אלו. מן העבר השני, אחוז הניבוי של ה-test set הרבה יותר נמוך. דהיינו, אין התכנסות של העקומות לאחוז ניבוי דומה, ונוצר ביניהם פער, כפי שניתן לראות בגרף.
הגרף הבא מתאר את נקודת האיזון, בו העקומות מתכנסות עם אחוז ניבוי גבוה יותר:
אחוז ניבוי נמוך, אם כן, הוא סימן לבעיית bias, ואילו פער בשיעור הניבוי בין ה-training set לבין ה-test set הוא סימן לבעיית overfitting.
שימת לב לעובדה, שנתונים נוספים הם דבר טוב, אך רק אם איננו נמצאים בבעיית bias. בעיית overfitting, לעומת זאת, יכולה להיפתר על ידי תוספת נתונים.
training ו-testing - קוד
לפני שנחלק את הנתונים, יש להיזהר מנתונים מסודרים. יתכן בהחלט שהנתונים הגולמיים, כפי שהם מגיעים אלינו, סודרו לפי גודל של פרמטר מסויים. למשל, הנתונים סודרו מרמות השכר הנמוכות אל רמות השכר הגבוהות, או להיפך.
חלוקה פשוטה ל-75% training set ו-25% test set עלולה להביא לתוצאות מוזרות, של ניבוי מושלם או ניבוי אפסי, או כל תוצאה אחרת בטווח הזה, מהסיבה הפשוטה שהנתונים המחולקים מכילים קטבים שונים של הנתונים. למשל חלוקת מערך בצורה כזו:
תגרום למודל שינבא תמיד 0, בעוד שבזמן ה-test ה-label תמיד יהיה 1.
מסיבה זו, נרצה קודם החלוקה "לערבב" את הנתונים. נוכל לבצע את ה"ערבוב" והחלוקה בפקודה אחת:
KFold
בגלל חשיבות ה-test set כבקר על יעילות המודל, נהוג לחלק את הנתונים ל-4 או 5 חלקים שווים ולהריץ את המודל כשכל פעם חלק אחר משמש כ-test set. ניתן לעשות זאת ביעילות על ידי שימוש ב-KFold, כך:
ניבוי ושגיאות
באופן כללי ניתן לחלק את סוגי השגיאות לשתי קטגוריות:
false positive. זיהוי שגוי של אירוע חיובי. לדוגמא, זיהוי שגוי של בריא כחולה בסרטן.
false negative. זיהוי שגוי של אירוע שלילי. לדוגמא, זיהוי שגוי של חולה בסרטן כבריא.
להרחבה ניתן לקרוא כאן בויקיפדיה (עברית): שגיאות מסוג I ו-II.
חשוב לציין בהקשר זה כי כל ציון של שיעור ניבוי יכול להיחשב בהקשרים מסוימים ל"טוב" ופעמים ל"גרוע". לדוגמא, שיעור ניבוי של 0.8 הוא אולי ציון טוב כשננסה לנבא את סיכוייו של סטודנט לנשור מהלימודים, אבל ציון גרוע כשננסה לנבא נוכחות של תאי סרטן בגוף הנבדק - ההשלכות של false negative, כמו טיפול כימותרפי לשווא, עלולות להיות חמורות מדי (אם כי פעמים שציון הוא גרוע פשוט מפני שהאלטרנטיבות האחרות לחישוב טובות יותר).
* התרגום אינו מדוייק, אבל משקף את המושג המקביל לו בשפה העברית ולכן בחרתי בו. כאן ניתן למצוא הרחבה על סוגי משתנים בעברית.