דלג לתוכן
4/30פרק 4 מתוך 30

סיווג, אנטרופיה צולבת, ואיך לא לעבוד על עצמך

בונים מסווג לוגיסטי מפונקציית ההפסד של פרק 2 ומהירידה של פרק 3, ומגלים למה 98% דיוק יכול להיות מודל שלא מוצא כלום.

בעמוד הזה

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

שני המשפטים מתארים את אותו מודל. המרחק ביניהם הוא הפרק הזה.

החצי הראשון בונה את המסווג. כמעט לא צריך בו שום דבר חדש: פרק 2 נתן את המתכון להפיכת הנחה על אופן יצירת הנתונים לפונקציית הפסד, ו-פרק 3 נתן את המנגנון להליכה במורד כל הפסד שהמתכון הזה מחזיר. מיישמים את שניהם על שאלת כן/לא ומקבלים רגרסיה לוגיסטית, ועוד רעיון חדש אחד — logit — שיחזור לגבות תשלום ב-פרק 17.

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

החשבון כאן רץ על 20,000 שורות, לכן הוא וקטורי לכל אורך הדרך — NumPy עושה את העבודה מאז פרק 2, ומכאן והלאה כבר לא שווה לציין את זה.

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

belt.pyPYTHON
import numpy as np

rng = np.random.default_rng(4)
N = 20_000
width  = rng.normal(22.0, 0.9, N)      # millimetres
weight = rng.normal(57.0, 3.0, N)      # grams

z_true = -5.90 + 1.90 * (width - 22.0) + 0.42 * (weight - 57.0)
y = (rng.random(N) < 1 / (1 + np.exp(-z_true))).astype(float)

perm = rng.permutation(N)
train, val, test = perm[:12_000], perm[12_000:16_000], perm[16_000:]
TEXT
N = 20000  defects = 337  base rate = 0.0169
defects per split = 203 60 74

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

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

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

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

P(yp)=py(1p)1yP(y \mid p) = p^{\,y}\,(1-p)^{\,1-y}

שזו רק דרך קומפקטית לכתוב "pp אם y=1y = 1, ו-1p1-p אם y=0y = 0". לוקחים את ה-log של זה והופכים סימן, וההפסד עבור דוגמה אחת הוא

L=[ylogp+(1y)log(1p)]L = -\big[\,y \log p + (1 - y)\log(1 - p)\,\big]

זו אנטרופיה צולבת בינארית. היא לא נבחרה כי היא נוחה; היא ה-negative log-likelihood של ההתפלגות היחידה שיכולה להיות להטלת מטבע. לא הייתה אפשרות אחרת.

מה שעדיין חסר הוא מאיפה pp מגיע. המודל מחשב סכום משוקלל s=wx+bs = \mathbf{w}\cdot\mathbf{x} + b, שהוא מספר ממשי ונע על פני כל הישר, והסתברות חייבת לחיות ב-(0,1)(0,1). הפונקציה שמעבירה ביניהם היא הסיגמואיד הלוגיסטי:

σ(s)=11+es\sigma(s) = \frac{1}{1 + e^{-s}}
TEXT
logit -4.0  ->  p = 0.0180        loss when y=1 and p=0.9  : 0.1054
logit -1.0  ->  p = 0.2689        loss when y=1 and p=0.5  : 0.6931
logit  0.0  ->  p = 0.5000        loss when y=1 and p=0.01 : 4.6052
logit  4.0  ->  p = 0.9820

קראו את העמודה הימנית כמחירון. להיות צודק בביטחון של 90 % עולה 0.105. לסרב להתחייב עולה 0.693 — שזה log2\log 2, מחיר משיכת הכתפיים. לטעות בביטחון עולה 4.6, פי ארבעים וארבע יותר, והמחיר עולה ללא גבול ככל שהמודל נעשה בטוח יותר בטעות. אנטרופיה צולבת לא רק סופרת שגיאות: היא גובה תשלום על יהירות.

פרק 3 אמר: כדי לאמן משהו, משיגים את הנגזרת של ההפסד ביחס לכל פרמטר. עושים זאת עבור דוגמה אחת. עם s=wx+bs = \mathbf{w}\cdot\mathbf{x} + b ו-p=σ(s)p = \sigma(s):

Ls=py,Lw=(py)x,Lb=py\frac{\partial L}{\partial s} = p - y, \qquad \frac{\partial L}{\partial \mathbf{w}} = (p - y)\,\mathbf{x}, \qquad \frac{\partial L}{\partial b} = p - y
הצגת פרטים

שתי השורות שגורמות לבלגן להתבטל. לסיגמואיד יש נגזרת נעימה במיוחד, σ(s)=σ(s)(1σ(s))=p(1p)\sigma'(s) = \sigma(s)\,(1 - \sigma(s)) = p(1-p). וההפסד נגזר ל-

Lp=yp+1y1p=pyp(1p)\frac{\partial L}{\partial p} = -\frac{y}{p} + \frac{1-y}{1-p} = \frac{p - y}{p\,(1-p)}

כופלים את השניים לפי כלל השרשרת ו-p(1p)p(1-p) מופיע פעם אחת למעלה ופעם אחת למטה. הוא מתבטל בדיוק, ו-pyp - y הוא מה שנשאר. הביטול הזה אינו צירוף מקרים — זה מה שקורה בכל פעם שההפסד הוא ה-negative log-likelihood של התפלגות ופונקציית הפלט היא זו שההתפלגות משתמשת בה באופן טבעי. לזיווג הזה יש שם — מודל ליניארי מוכלל — וה-gradient המסודר הוא טביעת האצבע שלו.1

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

logistic.pyPYTHON
def sigmoid(z):
    return np.where(z >= 0, 1.0 / (1.0 + np.exp(-z)),
                    np.exp(np.minimum(z, 0)) / (1.0 + np.exp(np.minimum(z, 0))))


def fit_logistic(X, y, lr=0.5, epochs=4000):
    w, b = np.zeros(X.shape[1]), 0.0
    for _ in range(epochs):
        p = sigmoid(X @ w + b)
        g = p - y                        
        w -= lr * (X.T @ g) / len(y)     
        b -= lr * g.sum() / len(y)       
    return w, b

ה-np.where ב-sigmoid אינו קוסמטי. חישוב 1/(1+es)1/(1+e^{-s}) ישירות גורם ל-overflow עבור ss שלילי גדול; ההסתעפות בוחרת את הצורה האלגברית הזהה ששומרת את המעריך שלילי. זו קופסת הנקודה הצפה מפרק 2 שגובה את החוב הראשון שלה, והיא תגבה חוב גדול יותר בעוד שני סעיפים.

למה לא שגיאה ריבועית, ולמה התשובה היא על ה-gradient

קישור למקטע: למה לא שגיאה ריבועית, ולמה התשובה היא על ה-gradient

ההסבר הסטנדרטי להעדפת אנטרופיה צולבת על פני שגיאה ריבועית הוא טיעון הנראות למעלה: שגיאה ריבועית מתקבלת מהנחה של רעש גאוסי, תוויות אינן גאוסיות, ולכן אל תעשו זאת. זה נכון וזה לא משכנע אף אחד, כי אפשר לכתוב L=(py)2L = (p - y)^2 מעל סיגמואיד וזה יתאמן.

הטיעון שפוגע הוא על ה-gradient. שמים שגיאה ריבועית על סיגמואיד וכלל השרשרת נותן

Ls=2(py)p(1p)\frac{\partial L}{\partial s} = 2\,(p - y)\,p\,(1-p)

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

ציון ssppאנטרופיה צולבת L/s\partial L/\partial sשגיאה ריבועית L/s\partial L/\partial sיחס
8-80.0003350.999665-0.9996650.000670-0.0006701,491
4-40.0179860.982014-0.9820140.034690-0.03469028.3
2-20.1192030.880797-0.8807970.184956-0.1849564.8
000.5000000.500000-0.5000000.250000-0.2500002.0
+2+20.8807970.119203-0.1192030.025031-0.0250314.8

ב-s=8s = -8 המודל טועה ככל שאפשר לטעות, ושגיאה ריבועית מגיבה ב-gradient קטן פי 1,491 מזה של אנטרופיה צולבת. ככל שהטעות גרועה יותר, המודל לומד ממנה פחות. ה-gradient של אנטרופיה צולבת, בינתיים, רווי ב-1-1: טעות מקסימלית מייצרת אות גדול באופן מקסימלי, ולא גדול יותר.

מריצים את המרוץ. אלפיים נקודות מאוזנות, משקלי התחלה זהים שנבחרו להיות שגויים בביטחון (w=[6,6]\mathbf{w} = [-6, -6]), קצב למידה זהה, רק ההפסד שונה. שתי הריצות נמדדות עם אנטרופיה צולבת כדי שהעמודות יהיו בנות השוואה.

epochהפסד אנטרופיה צולבתדיוקהפסד שגיאה ריבועיתדיוק
15.48650.23005.94990.2290
101.55250.24605.90420.2290
500.46420.77805.69130.2320
1000.46390.77705.39550.2410
2000.46390.77704.63110.2745
5000.46390.77700.52910.7660
1,0000.46390.77700.46400.7765

אנטרופיה צולבת מסיימת עד epoch 50. שגיאה ריבועית עדיין ב-24 % דיוק ב-epoch 100 — ולא זזה מ-23 % ב-epoch 10 — גרוע מניחוש, כי היא התחילה בטעות בביטחון וה-gradient שהיה מציל אותה הוכפל ב-0.0007. היא נמלטת בערך ב-epoch 500 ונוחתת באותו מקום. לכן הסיכום ההוגן הוא ששגיאה ריבועית מעל סיגמואיד אינה שגויה; היא איטית בדיוק במקום שבו מהירות חשובה ביותר. במודל עם שני פרמטרים מאבדים 450 epochs. ברשת עם מאה שכבות, שבה איזושהי יחידה איפשהו תמיד טועה בביטחון, מאבדים את ריצת האימון.

אנטרופיה, אנטרופיה צולבת ו-KL, בעמוד אחד

קישור למקטע: אנטרופיה, אנטרופיה צולבת ו-KL, בעמוד אחד

שלוש כמויות, שנצטרך כמו שצריך ב-פרק 8 עבור perplexity וב-פרק 11 עבור העונש ששומר policy שעבר fine-tuning קרוב לרפרנס שלו. הן קלות יותר מהמוניטין שלהן.2

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

H(p)=ipilog2piH(p) = -\sum_i p_i \log_2 p_i

אנטרופיה צולבת היא מה שמשלמים כשמשתמשים בקוד שנבנה עבור qq על נתונים שבפועל מגיעים מ-pp:

H(p,q)=ipilog2qiH(p, q) = -\sum_i p_i \log_2 q_i

דיברגנציית KL היא העודף — הבזבוז, בביטים, שנגרם מאמונה ב-qq כשהאמת היא pp:

DKL(pq)=H(p,q)H(p)D_{\mathrm{KL}}(p \parallel q) = H(p,q) - H(p)

בודקים את שלושתן על המסוע:

TEXT
test defect rate                                = 0.0185
entropy of that coin                            = 0.1329 bits
cross-entropy of the constant predictor on test = 0.1330 bits
KL(test coin || fair coin)                      = 0.8671 bits
H + KL                                          = 1.0000 bits
cross-entropy of the p=0.5 predictor on test    = 1.0000 bits

שני דברים נראים שם. ראשית, מודל שפשוט מדווח על שיעור הבסיס באימון, 1.69 %, משיג אנטרופיה צולבת של 0.1330 ביטים, כמעט בדיוק האנטרופיה של תוויות הבדיקה — כפי שהוא חייב, מאחר שיש לו את ההתפלגות הנכונה ואין לו מידע אחר. אנטרופיה היא הרצפה שבורות לגבי הפרט קונה לכם. שנית, מודל שמושך כתפיים ואומר 0.5 משלם בדיוק ביט אחד, והפער בין השניים, 0.8671 ביטים, הוא בדיוק דיברגנציית KL. H+DKL=H(p,q)H + D_{\mathrm{KL}} = H(p,q) אינה זהות לשינון; היא חשבון שאפשר לראות מצטבר.

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

יותר משתי תשובות: softmax, וההזזה שלא עולה כלום

קישור למקטע: יותר משתי תשובות: softmax, וההזזה שלא עולה כלום

פגום הוא לא דבר אחד. ביציקה, חלק יכול לצאת כ-short shot (לא מספיק חומר), flash (יותר מדי, שנדחק החוצה מהתבנית), או burn. ארבע תוצאות, לכן ארבעה logits, והם חייבים להפוך לארבע הסתברויות שסכומן אחת. זה softmax:

softmax(z)i=ezijezj\operatorname{softmax}(\mathbf{z})_i = \frac{e^{z_i}}{\sum_j e^{z_j}}

יש לו תכונה שנראית כמו תאונה ולמעשה היא כל המימוש:

softmax(z+c)=softmax(z)\operatorname{softmax}(\mathbf{z} + c) = \operatorname{softmax}(\mathbf{z})

עבור כל קבוע cc, כי ezi+c=ecezie^{z_i + c} = e^{c} e^{z_i} וה-ece^c מתבטל למעלה ולמטה. רק הבדלים בין logits אומרים משהו. הרמה המוחלטת אינה מידע.

למזלנו, כי הרמה המוחלטת היא מה ששובר את המחשב:

TEXT
logits            = [800. 801. 799.]
naive softmax     = [nan nan nan]
shifted by -max   = [0.2447 0.6652 0.09  ]
same softmax after adding 1000 to every logit: True

e800e^{800} גורם ל-overflow ב-float של 64 ביט, הסכום נעשה אינסוף, ואינסוף חלקי אינסוף הוא nan — לא שגיאה, לא קריסה, רק חור שקט במקום שבו היו שלוש הסתברויות. חיסור ה-logit המקסימלי לא משנה כלום מתמטית ומשנה הכול נומרית, כי המעריך הגדול ביותר נעשה בדיוק e0=1e^0 = 1. זה טריק ה-logsumexp מפרק 2 בבגדי עבודה, וכל מימוש רציני עושה זאת:

softmax.pyPYTHON
def softmax(Z):
    Z = Z - Z.max(axis=1, keepdims=True)   
    E = np.exp(Z)
    return E / E.sum(axis=1, keepdims=True)


def fit_softmax(X, Y, lr=1.0, epochs=6000):
    W, b = np.zeros((X.shape[1], Y.shape[1])), np.zeros(Y.shape[1])
    for _ in range(epochs):
        G = (softmax(X @ W + b) - Y) / len(X)   
        W -= lr * (X.T @ G)
        b -= lr * G.sum(0)
    return W, b

ה-gradient הוא שוב חיזוי פחות אמת, עכשיו עם YY one-hot. המקרה הבינארי היה מקרה פרטי כל הזמן.

אחרי אימון על 3,000 חלקים ובדיקה על 1,000, עם שלוש מדידות לכל אחד (רוחב, משקל, טמפרטורת התכה), הוא מגיע ל-94.00 % דיוק. הנה מה שהמספר הזה מסתיר:

אמת ↓ / נחזה →תקיןshort shotflashburnrecall
תקין8505900.984
short shot2221000.488
flash2003010.588
burn300390.929
precision0.9500.8080.7690.975

המודל מוצא פחות ממחצית ה-short shots. דיוק לא יכול לראות את זה, כי 86 % מהחלקים תקינים וזיהוי נכון שלהם מספיק כדי לסחוב את הממוצע. Macro F1 — הממוצע של ציוני F1 לכל מחלקה, שמעניק למחלקה נדירה אותו משקל כמו למחלקה נפוצה — הוא 0.7983, לעומת micro F1 של 0.9400 שבהגדרה זהה לדיוק. בכל פעם שמישהו מדווח על מספר F1 אחד, שאלו איזה.

זה סוף המידול. שאר הפרק עוסק במספרים.

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

מודלדיוקאנטרופיה צולבתהפסד ממוצע כשצודקהפסד ממוצע כשטועההפסד יחיד גרוע ביותר
מהסס (logits × 0.35)0.98300.15490.13691.19902.80
כמו שאומן0.98300.05640.01472.46897.82
בטוח מדי (logits × 4)0.98300.15630.00099.142727.63

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

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

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

TEXT
always-say-fine baseline: accuracy = 0.9815
confusion (tn, fp, fn, tp) = (3926, 0, 74, 0)

98.15 %. עכשיו המודל הלוגיסטי המאומן, בסף ברירת המחדל 0.5:

TEXT
logistic @0.5: accuracy=0.9830 precision=0.8000 recall=0.1081 F1=0.1905
confusion (tn, fp, fn, tp) = (3924, 2, 66, 8)

98.30 %. הוא ניצח את הבסיס ב-0.15 נקודת אחוז, וכל דוח שיעצור בדיוק יקרא לזה ניצחון. מטריצת הבלבול אומרת מה באמת קרה:

נחזה תקיןנחזה פגום
באמת תקין3,9242
באמת פגום668

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

  • Precision =TP/(TP+FP)=8/10=0.800= \mathrm{TP}/(\mathrm{TP}+\mathrm{FP}) = 8/10 = 0.800. מתוך החלקים שסומנו, כמה באמת היו פגומים. זו העלות של בדיקות מבוזבזות.
  • Recall =TP/(TP+FN)=8/74=0.108= \mathrm{TP}/(\mathrm{TP}+\mathrm{FN}) = 8/74 = 0.108. מתוך החלקים הפגומים, כמה נתפסו. זו העלות של שליחת חלק פגום ללקוח.
  • F1 =2PR/(P+R)=0.190= 2PR/(P+R) = 0.190, הממוצע ההרמוני שלהם, שנשאר קרוב לקטן מביניהם ולכן מסרב להיות מוחמא מאחד מהם בלבד.

מה חשוב תלוי במפעל, לא במתמטיקה: בדיקה עולה כמה שניות ופגם שנשלח עולה הודעת recall, לכן כאן recall שולט ו-0.108 הוא כישלון.

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

סףTPFPFNדיוקprecisionrecallF1
0.50082660.98300.8000.1080.190
0.2002728470.98120.4910.3650.419
0.10042118320.96250.2630.5680.359
0.05054236200.93600.1860.7300.297
0.0206757070.85580.1050.9050.188
0.005711,36030.65930.0500.9590.094

קראו את עמודת הדיוק מלמעלה למטה. היא יורדת לאורך כל הדרך — מ-98.30 % ל-65.93 % — בזמן שהמודל עובר מלתפוס 8 פגמים ללתפוס 71 מתוך 74. כל דבר שימושי שהמודל הזה יכול לעשות מחמיר את הדיוק שלו. צוות שממטב את מספר הכותרת היה משיק את הגרסה שלא מוצאת כלום.

הצגת פרטים

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

משקל על חיובייםדיוקprecisionrecallF1AUC
10.98300.8000.1080.1900.9363
100.96050.2530.5810.3520.9361
600.82900.0910.9190.1660.9361

Precision ו-recall זזים הרבה. ה-AUC — ההסתברות שהמודל מדרג חלק פגום אקראי מעל חלק תקין אקראי, שמתעלמת לגמרי מהסף — זז ב-0.0002, שזה כלום. שקלול מחדש החליק את אותו מודל לאורך אותה עקומת trade-off. זה לעיתים קרובות מה שרוצים, וזה אף פעם לא מידע חדש: אם הדירוג גרוע, שום סכמת שקלול לא תציל אותו.

שלוש חלוקות, והדליפה שאתם עומדים למצוא

קישור למקטע: שלוש חלוקות, והדליפה שאתם עומדים למצוא

למה שלוש חלוקות ולא שתיים? כי ברגע שמשתמשים בקבוצת דוגמאות כדי לבחור משהו — סף, קצב למידה, איזה משישה מודלים להשיק — הקבוצה הזו שימשה להתאמה, והציון שלה מפסיק להיות בלתי מוטה.3 במדידה על המסוע הזה: סריקת הסף על סט האימות בוחרת 0.196, ואז המודל מקבל F1 = 0.4122 על סט הבדיקה שלא נגעו בו. אילו הסריקה הייתה מתבצעת ישירות על סט הבדיקה, הטוב ביותר שהיה אפשר להשיג שם היה 0.4186 — מספר שאף אחד לא זכאי לדווח עליו.

הפער קטן כאן, 0.006, כי זה hyperparameter אחד שנסרק פעם אחת מול 4,000 דוגמאות אימות. הוא גדל עם כל החלטה נוספת ועם כל הקטנה של סט האימות. שימו לב גם שהכיוון אינו מובטח בריצה יחידה: הסף שנבחר קיבל 0.3902 באימות ו-0.4122 בבדיקה, כך שהאימות המעיט בערכו הפעם. ההטיה שיטתית על פני החלטות רבות, לא גלויה באחת.4

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

מודלדיוקprecisionrecallF1אנטרופיה צולבתAUC
רוחב + משקל0.98300.8000.1080.1900.05640.9363
+ station_seconds0.99200.7920.7700.7810.02360.9970

Recall עולה מ-10.8 % ל-77.0 %. F1 גדל ביותר מפי ארבעה. ושימו לב מה הדיוק עשה: 98.30 % → 99.20 %, רווח של תשע עשיריות נקודה, שזה מסוג המספרים שמתעגלים ל-"בערך 99 % כך או כך" בשקף סיכום. הדיוק לא ראה את הכישלון קודם ועכשיו לא רואה את התרמית.

לפני שממשיכים לקרוא: המודל מרמה. מצאו איך.

איך לצוד דליפה, בסדר שמוצא אותה הכי מהר.

  1. השוו אימון ובדיקה. התאמת יתר מופיעה כפער גדול. כאן: מודל ישר 0.9838 אימון / 0.9830 בדיקה; מודל דולף 0.9936 אימון / 0.9920 בדיקה. שני הפערים מתחת ל-0.2 נקודות. דליפה לא נראית כמו התאמת יתר — התכונה הדולפת זמינה באותה מידה בזמן הבדיקה, לכן המודל מכליל יפה לעולם שלא קיים.

  2. אמנו מודל אחד לכל תכונה, לבדה. כל דבר שנושא את התשובה יכריז על עצמו:

    תכונה לבדהדיוקrecallF1AUC
    רוחב0.98150.0140.0260.8691
    משקל0.98150.0000.0000.7914
    station_seconds0.98500.4050.5000.9960

    עמודה אחת, לבדה, מדרגת פגמים ב-AUC 0.9960. שתי מדידות שנלקחו בקליבר ובמשקל מצליחות 0.87 ו-0.79. האסימטריה הזו היא האזעקה.

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

the planted leakPYTHON
station = 1.8 + rng.exponential(0.35, N)                     # a part just passing through
audited = rng.random(N) < 0.006                              # random spot checks
station[audited] += rng.uniform(6.0, 26.0, audited.sum())
station[y == 1] = 9.0 + rng.exponential(7.0, (y == 1).sum())  

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

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

כמה דוגמאות בדיקה אני צריך?

קישור למקטע: כמה דוגמאות בדיקה אני צריך?

נניח שאתם מדרגים מודל על 20 דוגמאות והוא צודק ב-17. אתם מדווחים 85 %.

TEXT
17 correct out of 20 -> accuracy 0.8500
  Wilson    95% CI : [0.6396, 0.9476]
  bootstrap 95% CI : [0.7000, 1.0000]
  P(a 65% model scores 17 or more out of 20) = 0.0444
  P(an 85% model scores 17 or more out of 20) = 0.6477

הקריאה הכנה של 17/20 היא איפשהו בין 64 % ל-95 %. מודל שבאמת עומד על 65 % מייצר את התוצאה הזו ב-4.4 % מהמקרים — ריצה אחת מתוך עשרים ושלוש — ואם ניסיתם קומץ prompts ודיווחתם על הטוב ביותר, ייצרתם את הריצה הזו בעצמכם. שבע-עשרה מתוך עשרים לא יכולות להבדיל בין מודל של 85 % למודל של 65 %.

שתי דרכים לשים רווח סמך על שיעור, ושתיהן שייכות לארגז הכלים שלכם:

uncertainty.pyPYTHON
def wilson(k, n, z=1.959963985):
    """95% interval for k successes in n trials. Correct at small n; no simulation."""
    ph, d = k / n, 1 + z * z / n
    centre = (ph + z * z / (2 * n)) / d
    half = z * (ph * (1 - ph) / n + z * z / (4 * n * n)) ** 0.5 / d
    return centre - half, centre + half


def bootstrap_ci(correct, n_resamples=10_000, alpha=0.05, seed=0):
    """95% interval for the mean of any per-example score array. Works on F1 too."""
    rng = np.random.default_rng(seed)
    correct = np.asarray(correct, dtype=float)
    draws = correct[rng.integers(0, len(correct), size=(n_resamples, len(correct)))]
    lo, hi = np.quantile(draws.mean(axis=1), [alpha / 2, 1 - alpha / 2])
    return float(correct.mean()), float(lo), float(hi)

השתמשו ב-Wilson6 לשיעור הצלחה פשוט; הוא נשאר מתנהג היטב בכל nn ואינו דורש אקראיות. שימו לב למעלה שב-n=20n = 20 הקצה העליון של ה-bootstrap הוא 1.0000 — דגימה מחדש של 20 נקודות יכולה בקלות למשוך 20 נכונות, לכן היא לא יכולה לייצג רווח סמך צר יותר מהגרנולריות שלה עצמה. השתמשו ב-bootstrap7 במקום שבו אין נוסחה, וזה רוב המקרים המעניינים: F1, ממוצעי macro, BLEU, pass@1, ציון של שופט מבוסס rubric. במסוע הזה, ה-F1 של המודל המכוונן, 0.4122, נושא רווח bootstrap של [0.3009, 0.5156] — וזה המספר שצריך להופיע בדוח, כי האומדן הנקודתי לבדו מזמין השוואה שהוא לא יכול לתמוך בה.

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

TEXT
model A: 0.8580  95% CI [0.8260, 0.8880]
model B: 0.8120  95% CI [0.7780, 0.8460]
the two intervals overlap: True
paired difference A-B: 0.0460  95% CI [0.0260, 0.0680]
they disagree on 31 of 500 examples (A right 27, B right 4)

הרווחים שלהם חופפים, וכלל האצבע — פסי שגיאה חופפים פירושם שאין הבדל מובהק — היה קורא להשוואה לא חד-משמעית. היא לא. שני המודלים רצו על אותן דוגמאות, לכן הכמות הנכונה היא ההפרש לכל דוגמה, שהרווח שלו הוא [0.0260, 0.0680], בנוחות מעל אפס. הם חולקים רק על 31 מתוך 500 פריטים, ו-A מנצח ב-27 מאותן מחלוקות; הדוגמאות המשותפות, קלות וקשות כאחד, מתבטלות במקום להוסיף רעש. השוו מודלים בזוגות, ותגיעו לאותה מסקנה משבריר מהנתונים.

עכשיו יש לכם מודל שמוציא הסתברויות מכוילות, הפסד שנגזר מטענה על הנתונים במקום להיבחר מטעמי נוחות, gradient שהוא ממש חיזוי פחות אמת, ו — חשוב יותר — את המנגנון לברר אם משהו מזה עובד. רווח Wilson בן עשר השורות למעלה משמש שוב כלשונו: הוא נושא את וריאציות ה-prompt ב-פרק 15, את טבלאות השליפה ב-פרק 19, ואת ה-golden set ב-פרק 29. ה-bootstrap הוא מה שפונים אליו כשאין נוסחה.

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

שם נגמר ה-gradient המסודר של הפרק הזה. כל מה שלמעלה עבד כי L/s=py\partial L/\partial s = p - y היה ניתן לכתיבה ידנית, פעם אחת, עבור מודל עם שכבה אחת בין הקלט להפסד. שמים שכבה שנייה באמצע והשאלה משנה צורה: מה הנגזרת של ההפסד ביחס למשקל שלא נוגע בכלל בפלט — כזה שההשפעה שלו מגיעה רק דרך שכבה אחרת, אולי לאורך כמה נתיבים בבת אחת?

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

זה פרק 5, וזה המנוע שעליו רץ שאר הקורס.


כדאי לקרוא לצד הפרק הזה גם את Bishop, Pattern Recognition and Machine Learning §1.2, §1.5, §1.6 ו-§4.3, שמכסה הסתברות, תורת החלטות, תורת האינפורמציה וסיווג ליניארי בסדר שהפרק הזה הולך לפיו; Murphy, Probabilistic Machine Learning: An Introduction, פרקים 6 ו-10; Prince, Understanding Deep Learning §5.4–5.7; ואת Saito and Rehmsmeier, The Precision-Recall Plot Is More Informative than the ROC Plot When Evaluating Binary Classifiers on Imbalanced Datasets (PLOS ONE, 2015) — למה ה-AUC שצוטט למעלה לא צריך להיות המספר היחיד בלי סף שאתם מסתכלים עליו כש-1.7 % מהחלקים פגומים.

  1. Ma, T. and Ng, A. CS229 Lecture Notes, Stanford University, פרקים 2 ו-3. המקום שבו הביטול שמייצר את pyp - y מפסיק להיראות כמו מזל: בוחרים את התפלגות משפחת האקספוננציאל שמתאימה לפלט, משתמשים בקישור הקנוני שלה, וה-gradient הוא תמיד חיזוי פחות אמת.

  2. Olah, C. Visual Information Theory (2015), colah.github.io/posts/2015-09-Visual-Information. ההסבר הברור ביותר הזמין לאנטרופיה, אנטרופיה צולבת ודיברגנציית KL כעלויות בביטים ולא כנוסחאות.

  3. Abu-Mostafa, Y. S., Magdon-Ismail, M. and Lin, H.-T. Learning From Data (AMLBook, 2012), הרצאות 13 ו-17 בקורס של Caltech. הרצאה 13 היא אימות; הרצאה 17, על שלושת עקרונות הלמידה, היא המקום שבו data snooping מקבל שם. יחד הן המקור למשמעת בפרק הזה: כל מבט על סט נתונים הוא החלטת התאמה, בין שהרצתם optimiser ובין שלא.

  4. James, G., Witten, D., Hastie, T. and Tibshirani, R. An Introduction to Statistical Learning, מהדורה שנייה (Springer, 2021), פרקים 2 ו-5, עבור פירוק bias–variance ועבור דגימה מחדש. הכרך המשלים הוא המקום שבו מלכודת הבחירה נאמרת במפורש: Hastie, Tibshirani and Friedman, The Elements of Statistical Learning, מהדורה שנייה, §7.10.2, The Wrong and Right Way to Do Cross-validation.

  5. Kaufman, S., Rosset, S., Perlich, C. and Stitelman, O. Leakage in Data Mining: Formulation, Detection, and Avoidance. ACM Transactions on Knowledge Discovery from Data 6(4), 2012. טיפול פורמלי בכשל שהודגם למעלה, עם מקרי בוחן מתחרויות שנוצחו על ידי מודל שלמד ארטיפקט של אופן הרכבת הנתונים.

  6. Wilson, E. B. Probable Inference, the Law of Succession, and Statistical Inference. Journal of the American Statistical Association 22(158), עמ' 209–212 (1927). רווח הציון המשמש ב-wilson() למעלה, ועדיין ברירת המחדל הנכונה לפרופורציה. הרווח מספר הלימוד p^±zp^(1p^)/n\hat{p} \pm z\sqrt{\hat{p}(1-\hat{p})/n} הוא זה שיש להימנע ממנו: הוא נותן שטויות ליד 0 ו-1, ומכסה בחסר קשות ב-nn קטן.

  7. Efron, B. Bootstrap Methods: Another Look at the Jackknife. The Annals of Statistics 7(1), עמ' 1–26 (1979). הרעיון שמאפשר לשים רווח סמך על כל סטטיסטיקה שאפשר לחשב, כולל כאלה שאין להן תורת דגימה.


נוצר על ידי

David Vicente Campos

מייסד NeuraLIA Labs ושותף-מייסד MyRealFood

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

עוד על המחבר

פורסם על ידי NeuraLIA Labs.

פוסטים חדשים ישירות לתיבת הדואר

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

תוכן הקורס

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev10 דקות קריאה

מודל ה-AI Jev נבנה להחלטות, לא לפרוזה

Jev של TypeSafe AI מושך תשומת לב כי הוא מתייחס לאינטליגנציית תוכנה כאל בעיית הסתברות: לבחור את ההסתעפות הנכונה, להצמיד ביטחון, ולהימנע מתשלום ל-LLM כדי שיכתוב טקסט כשהקוד צריך החלטה.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering10 דקות קריאה

הנדסת הקשר לסוכני AI ארוכי־טווח

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

מוכנים לתת ל-LIA לבחור?

בנו עם כל מודלי ה-AI במקום אחד — התחילו בחינם עוד היום.