پرش به محتوا
4/30فصل 4 از 30

طبقه‌بندی، Cross-Entropy، و چطور خودتان را فریب ندهید

یک logistic classifier بسازید و ببینید چرا دقت 98% می‌تواند یعنی مدلی که هیچ چیز را پیدا نمی‌کند.

در این صفحه

مدلی که درباره‌ی هر قطعه‌ای که از روی نوار می‌آید جواب می‌دهد این قطعه سالم است، در 98.15 % مواقع درست می‌گوید. اما بی‌ارزش هم هست: از 74 قطعه‌ی معیوب در مجموعه‌ی آزمون، هیچ‌کدام را پیدا نمی‌کند.

هر دو جمله یک مدل واحد را توصیف می‌کنند. فاصله‌ی میان آن‌ها همین فصل است.

نیمه‌ی اول classifier را می‌سازد. تقریباً هیچ چیز تازه‌ای لازم ندارد: فصل 2 دستور ساخت loss function از یک فرض درباره‌ی چگونگی تولید داده را داد، و فصل 3 ماشین لازم برای پایین‌رفتن از شیب هر lossی را که آن دستور تحویل می‌دهد فراهم کرد. هر دو را روی یک پرسش بله/خیر اعمال کنید و logistic regression بیرون می‌آید، به‌اضافه‌ی یک ایده‌ی تازه — یک logit — که در فصل 17 دوباره بابتش هزینه می‌پردازیم.

نیمه‌ی دوم سخت‌تر است. از این نقطه به بعد، همه‌چیز در این دوره با عددی قضاوت می‌شود که کسی اندازه گرفته است، و اگر نتوانید یک بهبود واقعی را از یک artefact اندازه‌گیری تشخیص دهید، هر فصل بعدی فقط تزئین است. پس: confusion matrix، precision و recall، سه split، leakage، و پرسشی که تقریباً هیچ‌کس صادقانه جوابش را نمی‌دهد — واقعاً به چند نمونه‌ی آزمون نیاز دارم؟

محاسبات اینجا روی 20,000 ردیف اجرا می‌شود، پس همه‌جا vectorised است — 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

سه split، نه دو تا. دلیلش ارزش بخش جداگانه دارد و پایین‌تر یکی می‌گیرد؛ فعلاً روی اولی train کنید، روی دومی tune کنید، و به سومی نگاه نکنید.

ویژگی‌ها standardised شده‌اند — میانگین کم شده، بر انحراف معیار تقسیم شده — و این کار فقط با آمارهای training انجام شده است، به همان دلیلی که فصل 1 با کران همگرایی perceptron نشان داد: داده‌ی نامرکز هندسه را خصمانه می‌کند. اینکه مجازید میانگین را از کدام ردیف‌ها حساب کنید، بعدتر در همین فصل به یک پرسش زنده تبدیل می‌شود.

perceptron یک علامت برمی‌گرداند. علامت نمی‌تواند رد را از رد، اما با اختلاف کم جدا کند، و همین تفاوت دقیقاً چیزی است که یک کارخانه برای تصمیم‌گرفتن درباره‌ی اینکه انسان اول کدام قطعات را دوباره بازرسی کند نیاز دارد.

پس دستور فصل 2 را عیناً دنبال کنید. بنویسید ادعایتان درباره‌ی اینکه label چگونه تولید می‌شود چیست، likelihood را بگیرید، log را بگیرید، منفی‌اش کنید، و یک loss دارید. برای خروجی بله/خیر، ادعا یک توزیع Bernoulli است: احتمالی 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 آن را بگیرید و منفی‌اش کنید، و loss برای یک نمونه می‌شود

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

این binary cross-entropy است. چون راحت است انتخاب نشده؛ negative log-likelihood تنها توزیعی است که یک پرتاب سکه می‌تواند داشته باشد. چیز دیگری در دسترس نبود.

چیزی که هنوز کم است این است که pp از کجا می‌آید. مدل یک جمع وزن‌دار s=wx+bs = \mathbf{w}\cdot\mathbf{x} + b حساب می‌کند، که یک عدد حقیقی است و روی کل خط حقیقی حرکت می‌کند، اما احتمال باید در (0,1)(0,1) زندگی کند. تابعی که میان این دو جابه‌جا می‌شود logistic sigmoid است:

σ(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 هزینه دارد، چهل‌وچهار برابر بیشتر، و هرچه مدل درباره‌ی یک اشتباه مطمئن‌تر شود، قیمت بدون حد بالا می‌رود. Cross-entropy فقط خطاها را نمی‌شمارد: بابت غرور پول می‌گیرد.

گرادیان یعنی پیش‌بینی منهای حقیقت

لینک به بخش: گرادیان یعنی پیش‌بینی منهای حقیقت

فصل 3 گفت: برای train کردن هر چیزی، مشتق loss نسبت به هر پارامتر را بگیرید. این کار را برای یک نمونه انجام دهید. با 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
نمایش جزئیات

دو خطی که آشفتگی را خنثی می‌کنند. sigmoid مشتقی غیرمعمولاً خوش‌رفتار دارد، σ(s)=σ(s)(1σ(s))=p(1p)\sigma'(s) = \sigma(s)\,(1 - \sigma(s)) = p(1-p). و loss مشتق می‌شود به

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

این دو را با chain rule ضرب کنید و p(1p)p(1-p) یک بار بالا و یک بار پایین ظاهر می‌شود. دقیقاً حذف می‌شود، و pyp - y باقی می‌ماند. این حذف تصادفی نیست — هر وقت loss همان negative log-likelihood یک توزیع باشد و تابع خروجی همان چیزی باشد که آن توزیع به‌طور طبیعی استفاده می‌کند، همین اتفاق می‌افتد. این جفت‌شدن نامی دارد — generalised linear model — و گرادیان تمیز اثر انگشت آن است.1

پس update یعنی پیش‌بینی منهای حقیقت، ضرب‌در ورودی. همین و بس. این کل trainer است، همان descent فصل 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}) برای ssهای منفیِ بزرگ overflow می‌کند؛ branch شکلی را انتخاب می‌کند که از نظر جبری یکسان است اما exponent را منفی نگه می‌دارد. این اولین بدهی‌ای است که جعبه‌ی floating-point فصل 2 وصول می‌کند، و دو بخش دیگر بدهی بزرگ‌تری خواهد گرفت.

چرا squared error نه، و چرا پاسخ درباره‌ی گرادیان است

لینک به بخش: چرا squared error نه، و چرا پاسخ درباره‌ی گرادیان است

توضیح استاندارد برای ترجیح cross-entropy به squared error همان استدلال likelihood بالاست: squared error چیزی است که از فرض Gaussian noise به دست می‌آید، labelها Gaussian نیستند، پس این کار را نکنید. درست است و هیچ‌کس را قانع نمی‌کند، چون می‌توانید L=(py)2L = (p - y)^2 را روی sigmoid بنویسید و train خواهد شد.

استدلالی که می‌نشیند درباره‌ی گرادیان است. squared error را روی sigmoid بگذارید و chain rule می‌دهد

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

آن p(1p)p(1-p) اضافه همان چیزی است که قبلاً حذف شد. حالا حذف نمی‌شود، و هر وقت مدل مطمئن باشد به صفر میل می‌کند — حتی وقتی مدل با اطمینان اشتباه است. هر دو را در چند امتیاز ارزیابی کنید، برای نمونه‌ای که label واقعی‌اش 1 است:

score ssppcross-entropy L/s\partial L/\partial ssquared error 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 مدل تا جای ممکن اشتباه است، و squared error با گرادیانی پاسخ می‌دهد که 1,491 بار کوچک‌تر از گرادیان cross-entropy است. هرچه اشتباه بدتر باشد، مدل کمتر از آن یاد می‌گیرد. گرادیان cross-entropy، در عوض، در 1-1 اشباع می‌شود: حداکثر اشتباه یک سیگنال حداکثری تولید می‌کند، و نه بزرگ‌تر.

مسابقه را اجرا کنید. دو هزار نقطه‌ی balanced، وزن‌های آغازین یکسان که طوری انتخاب شده‌اند که با اطمینان اشتباه باشند (w=[6,6]\mathbf{w} = [-6, -6])، learning rate یکسان، فقط loss فرق می‌کند. هر دو اجرا با cross-entropy امتیازدهی می‌شوند تا ستون‌ها قابل مقایسه باشند.

epochcross-entropy lossaccuracysquared-error lossaccuracy
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

Cross-entropy تا epoch 50 تمام کرده است. Squared error در epoch 100 هنوز روی 24 % accuracy است — و از 23 % در epoch 10 تکان نخورده بود — بدتر از حدس‌زدن، چون با اطمینان اشتباه شروع کرده و گرادیانی که باید نجاتش می‌داد در 0.0007 ضرب شده است. حوالی epoch 500 فرار می‌کند و به همان نقطه می‌رسد. پس خلاصه‌ی صادقانه این است که squared error روی sigmoid نادرست نیست؛ دقیقاً جایی کند است که سرعت بیشترین اهمیت را دارد. در یک مدل دوپارامتری 450 epoch از دست می‌دهید. در شبکه‌ای با صد لایه، جایی که همیشه یک unit در جایی با اطمینان اشتباه است، کل training run را از دست می‌دهید.

Entropy، cross-entropy و KL، در یک صفحه

لینک به بخش: Entropy، cross-entropy و KL، در یک صفحه

سه کمیت، که در فصل 8 برای perplexity و در فصل 11 برای جریمه‌ای که یک policy با fine-tuning را نزدیک reference نگه می‌دارد به‌درستی لازم‌اند. از شهرتشان آسان‌ترند.2

Entropy میانگین تعداد بیت‌هایی است که باید برای انتقال یک draw از یک توزیع خرج کنید، اگر بهترین code ممکن را برای آن استفاده کنید:

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

Cross-entropy چیزی است که وقتی code ساخته‌شده برای qq را روی داده‌ای استفاده می‌کنید که در واقع از pp می‌آید، خرج می‌کنید:

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

KL divergence اضافه‌هزینه است — هدررفت، بر حسب بیت، ناشی از باورداشتن به 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

دو چیز آنجا پیداست. اول، مدلی که صرفاً نرخ پایه‌ی training، یعنی 1.69 %، را گزارش می‌کند، به cross-entropy برابر 0.1330 بیت می‌رسد، تقریباً دقیقاً entropy labelهای test — همان‌طور که باید، چون توزیع درست را دارد و هیچ اطلاعات دیگری ندارد. Entropy کفی است که ناآگاهی از فرد برای شما می‌خرد. دوم، مدلی که شانه بالا می‌اندازد و می‌گوید 0.5 دقیقاً 1 بیت می‌پردازد، و فاصله‌ی میان این دو، 0.8671 بیت، دقیقاً KL divergence است. H+DKL=H(p,q)H + D_{\mathrm{KL}} = H(p,q) هویتی برای حفظ‌کردن نیست؛ صورتحسابی است که می‌توانید جمع‌شدنش را ببینید.

و اتصالش به training: وقتی label یک کلاس معلوم واحد است، توزیع «واقعی» one-hot است، entropy آن صفر است، و cross-entropy برابر KL divergence می‌شود. کمینه‌کردن cross-entropy و کشیدن توزیع مدل به سمت حقیقت یک کار واحدند.

بیش از دو پاسخ: 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} یک float 64-bit را overflow می‌کند، جمع بی‌نهایت می‌شود، و بی‌نهایت تقسیم بر بی‌نهایت می‌شود nan — نه خطا، نه crash، فقط حفره‌ای خاموش جایی که قبلاً سه احتمال بود. کم‌کردن بزرگ‌ترین logit از نظر ریاضی هیچ چیز را عوض نمی‌کند و از نظر عددی همه‌چیز را، چون بزرگ‌ترین exponent دقیقاً 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

گرادیان باز هم پیش‌بینی منهای حقیقت است، این بار با YY one-hot. حالت binary از اول فقط یک حالت خاص بود.

پس از train شدن روی 3,000 قطعه و test شدن روی 1,000 قطعه، با سه اندازه‌گیری برای هر کدام (عرض، وزن، دمای مذاب)، به 94.00 % accuracy می‌رسد. این چیزی است که آن عدد پنهان می‌کند:

حقیقت ↓ / پیش‌بینی →okshort shotflashburnrecall
ok8505900.984
short shot2221000.488
flash2003010.588
burn300390.929
precision0.9500.8080.7690.975

مدل کمتر از نیمی از short shotها را پیدا می‌کند. Accuracy این را نمی‌بیند، چون 86 % قطعات سالم‌اند و درست‌گرفتن آن‌ها برای بالا نگه‌داشتن میانگین کافی است. Macro F1 — میانگین F1های هر کلاس، که به کلاس نادر همان وزنی را می‌دهد که به کلاس رایج — برابر 0.7983 است، در برابر micro F1 برابر 0.9400 که بنا به تعریف با accuracy یکی است. هر وقت کسی یک عدد F1 گزارش کرد، بپرسید کدام.

این پایان مدل‌سازی است. بقیه‌ی فصل درباره‌ی عددهاست.

مدل binary آموزش‌دیده را بردارید و با ضرب‌کردن هر logit در یک ثابت دو variant بسازید: 0.35 برای نسخه‌ای مردد، 4 برای نسخه‌ای بیش‌ازحد مطمئن. ضرب در عدد مثبت هیچ علامتی را عوض نمی‌کند، پس هر سه مدل برای همه‌ی 4,000 قطعه‌ی test دقیقاً همان label را پیش‌بینی می‌کنند. Accuracy نمی‌تواند آن‌ها را از هم تشخیص دهد. Cross-entropy هیچ مشکلی ندارد:

modelaccuracycross-entropymean loss when rightmean loss when wrongworst single loss
مردد (logits × 0.35)0.98300.15490.13691.19902.80
همان train‌شده0.98300.05640.01472.46897.82
بیش‌ازحد مطمئن (logits × 4)0.98300.15630.00099.142727.63

مدل مردد روی هر قطعه یک مالیات کوچک می‌پردازد، حتی روی هزاران موردی که درست می‌گیرد. مدل بیش‌ازحد مطمئن وقتی درست است تقریباً رایگان است و وقتی اشتباه است فاجعه‌بار — یک قطعه در آن test set به‌تنهایی 27.63 nats هزینه دارد. این دو از مسیرهای مخالف تقریباً به مجموع یکسانی می‌رسند، و مدل train‌شده، که احتمال‌هایش با داده calibrated شده‌اند، سه برابر پایین‌تر از هر دو می‌نشیند.

این تیزترین روش برای بیان تفاوت میان loss و metric است. loss چیزی است که optimize می‌کنید: باید differentiable باشد، و هر چیزی را که مدل گفته می‌بیند، از جمله اینکه چقدر مطمئن بوده. metric چیزی است که با آن قضاوت می‌شوید: می‌تواند step function باشد، business rule باشد، شمارش defectهای ازدست‌رفته باشد. آن‌ها یک شیء نیستند و همیشه هم‌نظر نیستند — برای همین هر دو را پیش از شروع تعریف می‌کنید، و هرگز اجازه نمی‌دهید loss فقط چون روی صفحه است جای metric را بگیرد.

پیش از هر مدل، الزام این است: تنبل‌ترین پاسخ ممکن چه امتیازی می‌گیرد؟ روی این نوار، همیشه بگو سالم است:

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

98.15 %. حالا مدل logistic train‌شده، با threshold پیش‌فرض 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 %. baseline را 0.15 واحد درصد شکست داد، و هر گزارشی که روی accuracy توقف کند این را برد می‌نامد. confusion matrix می‌گوید واقعاً چه اتفاقی افتاده است:

predicted finepredicted defective
actually fine3,9242
actually defective668

از 74 قطعه‌ی معیوب، 8 تا را پیدا کرد و 66 تا را عبور داد. سه عدد سه روش خواندن آن جدول را نام‌گذاری می‌کنند:

  • 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، میانگین هارمونیک آن دو، که نزدیک کوچک‌ترشان می‌ماند و بنابراین اجازه نمی‌دهد یکی به‌تنهایی چاپلوسی کند.

اینکه کدام مهم است به کارخانه بستگی دارد، نه به ریاضی: بازرسی چند ثانیه هزینه دارد و defect ارسال‌شده یک اعلامیه‌ی recall، پس اینجا recall غالب است و 0.108 شکست است.

اما مشکل مدل نیست. threshold است، و threshold بخشی از مدل نیست — یک تصمیم کسب‌وکاری است که بعداً روی احتمال اعمال می‌شود. آن را sweep کنید:

thresholdTPFPFNaccuracyprecisionrecallF1
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

ستون accuracy را رو به پایین بخوانید. تمام مسیر سقوط می‌کند — از 98.30 % به 65.93 % — در حالی که مدل از گرفتن 8 defect به گرفتن 71 تا از 74 می‌رسد. هر کار مفیدی که این مدل می‌تواند انجام دهد accuracy آن را بدتر می‌کند. تیمی که عدد تیتر را optimize کند نسخه‌ای را ship می‌کند که هیچ چیز پیدا نمی‌کند.

نمایش جزئیات

Class weighting سیگنال نمی‌سازد، operating point را جابه‌جا می‌کند. واکنش معمول اول در برابر کلاس‌های imbalanced این است که کلاس نادر را در loss وزن‌دهی کنیم. با وزن‌های 1، 10 و 60 روی positiveها:

weight on positivesaccuracyprecisionrecallF1AUC
10.98300.8000.1080.1900.9363
100.96050.2530.5810.3520.9361
600.82900.0910.9190.1660.9361

Precision و recall خیلی جابه‌جا می‌شوند. AUC — احتمال اینکه مدل یک قطعه‌ی معیوب تصادفی را بالاتر از یک قطعه‌ی سالم تصادفی rank کند، که threshold را کاملاً نادیده می‌گیرد — فقط 0.0002 جابه‌جا می‌شود، که هیچ است. Reweighting همان مدل را روی همان منحنی trade-off سر داد. این اغلب همان چیزی است که می‌خواهید، و هرگز اطلاعات تازه نیست: اگر ranking بد باشد، هیچ طرح وزن‌دهی‌ای نجاتش نمی‌دهد.

سه split، و leakی که قرار است پیدا کنید

لینک به بخش: سه split، و leakی که قرار است پیدا کنید

چرا سه split و نه دو تا؟ چون لحظه‌ای که از مجموعه‌ای از نمونه‌ها برای انتخاب هر چیزی استفاده کنید — یک threshold، یک learning rate، اینکه کدام‌یک از شش مدل را ship کنید — آن مجموعه برای fitting استفاده شده است، و score آن دیگر unbiased نیست.3 اندازه‌گیری روی این نوار: sweep کردن threshold روی validation set مقدار 0.196 را انتخاب می‌کند، و سپس مدل روی test set دست‌نخورده F1 = 0.4122 می‌گیرد. اگر sweep مستقیماً روی test set اجرا شده بود، بهترین مقدار دست‌یافتنی آنجا 0.4186 بود — عددی که هیچ‌کس حق گزارش‌دادنش را ندارد.

فاصله اینجا کوچک است، 0.006، چون یک hyperparameter یک‌بار روی 4,000 نمونه‌ی validation sweep شده است. با هر تصمیم اضافه و هر کوچک‌شدن validation set بزرگ‌تر می‌شود. همچنین توجه کنید که جهت در یک اجرای منفرد تضمین‌شده نیست: threshold انتخاب‌شده روی validation مقدار 0.3902 و روی test مقدار 0.4122 گرفت، پس validation این بار آن را کمتر نشان داد. bias در میان تصمیم‌های بسیار systematic است، نه در یکی قابل مشاهده.4

حالا تمرین. log نوار با ستون سومی می‌آید، station_seconds: مدت زمانی که هر قطعه در ایستگاه بازرسی گذرانده است. افزودنش به preprocessing یک تغییر یک‌خطی است. این کاری است که می‌کند:

modelaccuracyprecisionrecallF1cross-entropyAUC
width + weight0.98300.8000.1080.1900.05640.9363
+ station_seconds0.99200.7920.7700.7810.02360.9970

Recall از 10.8 % به 77.0 % می‌رود. F1 بیش از چهار برابر می‌شود. و ببینید accuracy چه کرد: 98.30 % → 99.20 %، سودی برابر نه‌دهم یک point، همان عددی که در یک اسلاید خلاصه به «در هر صورت حدود 99 %» گرد می‌شود. Accuracy قبلاً شکست را ندید و حالا تقلب را نمی‌بیند.

پیش از ادامه: مدل دارد تقلب می‌کند. پیدا کنید چطور.

چطور leak را شکار کنیم، به ترتیبی که سریع‌تر پیدایش می‌کند.

  1. Train و test را مقایسه کنید. Overfitting خودش را به صورت شکاف بزرگ نشان می‌دهد. اینجا: مدل صادق 0.9838 train / 0.9830 test؛ مدل leaky برابر 0.9936 train / 0.9920 test. هر دو شکاف زیر 0.2 point هستند. Leak شبیه overfitting نیست — ویژگی leaky در test time هم همان‌قدر در دسترس است، پس مدل به جهانی که وجود ندارد به‌زیبایی generalise می‌کند.

  2. برای هر feature، یک مدل جداگانه و تنها train کنید. هر چیزی که answer را حمل کند خودش را لو می‌دهد:

    feature aloneaccuracyrecallF1AUC
    width0.98150.0140.0260.8691
    weight0.98150.0000.0000.7914
    station_seconds0.98500.4050.5000.9960

    یک ستون، به‌تنهایی، defectها را با AUC برابر 0.9960 rank می‌کند. دو اندازه‌گیری که با کولیس و ترازو گرفته شده‌اند به 0.87 و 0.79 می‌رسند. این عدم تقارن زنگ خطر است.

  3. بپرسید هر عدد چه زمانی نوشته شده است. میانگین dwell time: 2.23 ثانیه برای قطعاتی که قبول شدند، 15.56 ثانیه برای قطعاتی که رد شدند. البته که همین است. یک قطعه در ایستگاه می‌ماند چون بازرس آن را از روی نوار برداشته — اتفاقی که بعد از آن رخ می‌دهد، و فقط به این دلیل که، کسی تصمیم گرفته معیوب است. این ستون اندازه‌گیری قطعه نیست. اندازه‌گیری verdict است.

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

خط برجسته همان leak است: dwell time یک قطعه‌ی معیوب از توزیع متفاوتی draw می‌شود، چون انسان آن را از روی نوار برداشته است. این رایج‌ترین باگ جدی در machine learning کاربردی است، و نام دارد: target leakage — اطلاعاتی در ویژگی‌های training که در لحظه‌ای که prediction باید انجام شود در دسترس نخواهد بود.5 هیچ exceptionی پرتاب نمی‌کند. عدد بهتری تولید می‌کند. همه‌ی incentiveهای یک پروژه به سمت نگه‌داشتنش اشاره می‌کنند.

دفاع یک پرسش است، از هر ستون: در لحظه‌ای که به این prediction نیاز دارم، آیا این مقدار هنوز وجود دارد؟ روی نوار زنده، 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 % مواقع این نتیجه را تولید می‌کند — یک اجرا از هر بیست‌وسه — و اگر چند prompt را امتحان کرده باشید و بهترین را گزارش داده باشید، خودتان همان اجرا را ساخته‌اید. هفده از بیست نمی‌تواند مدل 85 % را از مدل 65 % تشخیص دهد.

دو راه برای گذاشتن interval روی یک rate، و هر دو باید در جعبه‌ابزارتان باشند:

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)

برای success rate ساده از Wilson6 استفاده کنید؛ در هر nn خوش‌رفتار می‌ماند و randomness لازم ندارد. بالا توجه کنید که در n=20n = 20 انتهای بالایی bootstrap برابر 1.0000 است — resampling از 20 نقطه به‌راحتی می‌تواند 20 مورد درست draw کند، پس نمی‌تواند intervalی باریک‌تر از granularity خودش نمایش دهد. از bootstrap7 جایی استفاده کنید که formula وجود ندارد، یعنی بیشتر موارد جالب: F1، macro-averageها، BLEU، pass@1، امتیاز یک judge مبتنی بر rubric. روی این نوار، F1 مدل tune‌شده برابر 0.4122 یک bootstrap interval برابر [0.3009, 0.5156] دارد — و این همان عددی است که باید در گزارش بیاید، چون point estimate به‌تنهایی مقایسه‌ای را دعوت می‌کند که نمی‌تواند پشتیبانی کند.

یک اندازه‌گیری دیگر، چون روش مقایسه‌ی دو مدل را عوض می‌کند. دو مدل روی همان 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)

Intervalهایشان overlap دارند، و قاعده‌ی عامیانه — overlapping error bars یعنی تفاوت معناداری نیست — مقایسه را inconclusive می‌نامید. این‌طور نیست. دو مدل روی همان نمونه‌ها اجرا شده‌اند، پس کمیت درست تفاوت per-example است، که interval آن [0.0260, 0.0680] است و با خیال راحت بالاتر از صفر قرار دارد. آن‌ها فقط روی 31 تا از 500 item اختلاف دارند، و A در 27 تا از آن اختلاف‌ها برنده است؛ نمونه‌های مشترک، آسان و سخت با هم، به‌جای افزودن noise، حذف می‌شوند. مدل‌ها را paired مقایسه کنید، و با کسری از داده به همان نتیجه می‌رسید.

اکنون مدلی دارید که probabilityهای calibrated خروجی می‌دهد، lossی که از ادعایی درباره‌ی داده مشتق شده نه از راحتی انتخاب شده، گرادیانی که عیناً پیش‌بینی منهای حقیقت است، و — مهم‌تر — ماشینی برای فهمیدن اینکه اصلاً چیزی از این‌ها کار می‌کند یا نه. interval ده‌خطی Wilson بالا عیناً دوباره استفاده می‌شود: variantهای prompt در فصل 15، جدول‌های retrieval در فصل 19، و golden set در فصل 29 را حمل می‌کند. وقتی formula وجود ندارد سراغ bootstrap می‌روید.

اما مدل هنوز یک layer است. یک خط می‌کشد، و فصل 1 با چهار ردیف XOR ثابت کرد که خط کافی نیست. راه‌حل stack کردن است: layer اول فضا را خم می‌کند، layer دوم خط را در فضای خم‌شده می‌کشد.

اینجاست که گرادیان تمیز این فصل تمام می‌شود. همه‌ی آنچه بالا آمد کار کرد چون L/s=py\partial L/\partial s = p - y را می‌شد یک بار، با دست، برای مدلی با یک layer بین input و loss نوشت. یک layer دوم وسط بگذارید و شکل پرسش عوض می‌شود: مشتق loss نسبت به وزنی که اصلاً به output دست نمی‌زند چیست — وزنی که اثرش فقط از طریق layer دیگری می‌رسد، شاید هم‌زمان از چند مسیر؟

آن مشتق وجود دارد. حساب‌کردنش با دست برای هر چیزی بزرگ‌تر از toy ناامیدکننده است، و حساب‌کردنش یکی‌یکی برای هر پارامتر در مقیاسی دیگر ناامیدکننده. چیزی که لازم است procedureای است که هر مشتق در network را از یک backward pass واحد روی همان graphی به دست آورد که forward pass همین الآن طی کرد.

این فصل 5 است، و engineی است که بقیه‌ی این دوره روی آن اجرا می‌شود.


خواندن این‌ها کنار این فصل هم ارزش دارد: Bishop, Pattern Recognition and Machine Learning §1.2, §1.5, §1.6 and §4.3، که probability، decision theory، information theory و linear classification را به همان ترتیبی پوشش می‌دهد که این فصل دنبال می‌کند؛ Murphy, Probabilistic Machine Learning: An Introduction, chapters 6 and 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 نقل‌شده در بالا نباید تنها عدد threshold-free باشد که وقتی 1.7 % قطعات معیوب‌اند به آن نگاه می‌کنید.

  1. Ma, T. and Ng, A. CS229 Lecture Notes, Stanford University, chapters 2 and 3. جایی که حذفی که pyp - y را تولید می‌کند دیگر شبیه شانس به نظر نمی‌رسد: توزیع exponential-family متناسب با خروجی را انتخاب کنید، canonical link آن را استفاده کنید، و گرادیان همیشه پیش‌بینی منهای حقیقت است.

  2. Olah, C. Visual Information Theory (2015), colah.github.io/posts/2015-09-Visual-Information. روشن‌ترین توضیح در دسترس از entropy، cross-entropy و KL divergence به‌عنوان هزینه‌هایی بر حسب بیت، نه صرفاً formula.

  3. Abu-Mostafa, Y. S., Magdon-Ismail, M. and Lin, H.-T. Learning From Data (AMLBook, 2012), lectures 13 and 17 of the Caltech course. Lecture 13 درباره‌ی validation است؛ lecture 17، درباره‌ی سه اصل یادگیری، جایی است که data snooping نام‌گذاری می‌شود. این دو با هم منبع discipline این فصل‌اند: هر نگاه به یک data set یک تصمیم fitting است، چه optimiser اجرا کرده باشید چه نه.

  4. James, G., Witten, D., Hastie, T. and Tibshirani, R. An Introduction to Statistical Learning, 2nd edition (Springer, 2021), chapters 2 and 5, برای bias–variance decomposition و resampling. جلد همراه جایی است که selection trap بی‌پرده بیان می‌شود: Hastie, Tibshirani and Friedman, The Elements of Statistical Learning, 2nd edition, §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. برخوردی formal با شکستی که بالا نشان داده شد، همراه با case studyهایی از رقابت‌هایی که مدلی برده بود که artefact نحوه‌ی assembled شدن داده را یاد گرفته بود.

  6. Wilson, E. B. Probable Inference, the Law of Succession, and Statistical Inference. Journal of the American Statistical Association 22(158), pp. 209–212 (1927). score interval استفاده‌شده در wilson() بالا، که هنوز default درست برای یک proportion است. interval کتاب درسی p^±zp^(1p^)/n\hat{p} \pm z\sqrt{\hat{p}(1-\hat{p})/n} همان چیزی است که باید از آن پرهیز کنید: نزدیک 0 و 1 بی‌معنی می‌شود و در nn کوچک به‌شدت undercovers می‌کند.

  7. Efron, B. Bootstrap Methods: Another Look at the Jackknife. The Annals of Statistics 7(1), pp. 1–26 (1979). ایده‌ای که اجازه می‌دهد روی هر statisticی که می‌توانید محاسبه کنید interval بگذارید، از جمله آن‌هایی که sampling theory ندارند.


تهیه‌شده توسط

David Vicente Campos

بنیان‌گذار NeuraLIA Labs و هم‌بنیان‌گذار MyRealFood

من مهندس کامپیوتر و فارغ‌التحصیل دانشگاه لئون هستم. هم‌بنیان‌گذار MyRealFood بودم، جایی که به‌عنوان مدیر ارشد فناوری اپلیکیشنی را ساختم که میلیون‌ها نفر برای سالم‌تر غذا خوردن از آن استفاده کرده‌اند، و NeuraLIA Labs را بنیان‌گذاری کردم؛ جایی که محصولات هوش مصنوعی می‌سازم. اینجا از چیزهایی می‌نویسم که در طول مسیر باید می‌فهمیدم، همان‌طور که دوست داشتم کسی برایم توضیح می‌داد.

بیشتر درباره نویسنده

منتشرشده توسط NeuraLIA Labs.

پست‌های جدید را در ایمیل خود دریافت کنید

اخبار AI، راهنماها و به‌روزرسانی‌های محصول — هر وقت چیزی ارزشمند منتشر کنیم، یک ایمیل کوتاه می‌فرستیم.

فهرست دوره

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev12 دقیقه مطالعه

مدل هوش مصنوعی Jev برای تصمیم ساخته شده، نه نثر

Jev از TypeSafe AI توجه‌ها را جلب کرده چون هوشمندی نرم‌افزار را مسئله‌ای احتمالاتی می‌بیند: شاخه درست را انتخاب کنید، میزان اطمینان را کنار آن بگذارید، و وقتی کد به یک تصمیم نیاز دارد برای نوشتن متن به یک LLM پول ندهید.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering13 دقیقه مطالعه

مهندسی کانتکست برای عامل‌های AI بلندافق

عامل‌های طولانی‌اجرا فقط به‌خاطر کوچک بودن پنجره شکست نمی‌خورند. وقتی فایل‌ها، خروجی ابزارها و تاریخچهٔ کهنه وظیفه‌ای را که عامل قرار بود تمام کند کنار می‌زنند، شکست رخ می‌دهد.

آماده‌اید انتخاب مدل را به LIA بسپارید؟

با همه مدل‌های هوش مصنوعی در یک جا بسازید — همین امروز رایگان شروع کنید.