چیتشیت
مبانی شبکه عصبی
Neural Network Basics
پرسپترون، گذر رو به جلو، پسانتشار، انتخاب تابع زیان و یک مینیفریمورک. فرمولها، ابعاد، مقادیر پیشفرض و راهحلها.
فهرست مطالب
گذر رو به جلو و عقب در یک شبکه 2 لایه
معمارییک گام آموزش
گامبهگام- DataLoaderدستهای از ورودیها و اهداف را تحویل میدهد. در هر دور (epoch) بر میزند.
- Modelگذر رو به جلو را لایه به لایه اجرا میکند. هر لایه ورودی و خروجی خود را ذخیره میکند.
- Lossپیشبینیها را با اهداف مقایسه کرده و یک عدد اسکالر میدهد.
- Optimizerمتد
optimizer.zero_grad()همهی گرادیانها را صفر میکند. - Lossمتد
loss.backward()گرادیانها را از راست به چپ در گراف ارسال میکند. - Optimizerمتد
optimizer.step()هر پارامتر را بهروزرسانی میکند؛ برای SGD به شکلparam -= lr * grad. - Modelفراخوانی
model.eval()پیش از آزمون. استنتاج فقط گذر رو به جلو را اجرا میکند.
فرمولها
مفهومها- پله (Step)
- اگر $z \ge 0$ باشد $\text{step}(z) = 1$، وگرنه 0.
- قاعده پرسپترون
w_i = w_i + learning_rate * error * x_i، با error = y_true منهای y_pred.- سیگموئید
- $\sigma(z) = \frac{1}{1 + e^{-z}}$، بازه (0, 1). مشتق آن $a(1 - a)$ است.
- لایه
- $z = W x + b$، سپس $a = \text{sigmoid}(z)$.
- MSE
- برابر با $\frac{1}{n} \sum (y_{\text{pred}} - y_{\text{true}})^2$، گرادیان $\frac{2}{n}(y_{\text{pred}} - y_{\text{true}})$.
- BCE
- برابر با $-\left(y \log(p) + (1 - y) \log(1 - p)\right)$
- CCE
- برابر با $-\sum_i y_i \log(p_i)$. فقط کلاس واقعی سهم دارد.
- گرادیان سافتمکس به همراه CE
- احتمال منهای 1 برای کلاس واقعی، و خود احتمال برای سایر کلاسها.
وظایف در کد
کار و کد| وظیفه | کد |
|---|---|
| آموزش پرسپترون روی AND | `p_and = Perceptron(2)` then `p_and.train(and_data)` |
| زنجیرهسازی لایهها | Network([hidden, output]) |
| شبکه مشتقگیری خودکار، 2-4-1 | net = Network([2, 4, 1]) |
| پسانتشار از تابع زیان | total_loss.backward() |
| تابع زیان پایدار چندکلاسه | F.cross_entropy(logits, labels) |
| اهداف هموارشده | label_smoothing=0.1 |
| انتخاب بهینهساز | Adam(model.parameters(), lr=0.01) |
| دستهبندی دادهها | DataLoader(train_data, batch_size=16, shuffle=True) |
گره Value با backward
کدpython
out = Value(self.data + other.data, (self, other), '+')
self.grad += out.grad # عملگر add گرادیان را عبور میدهد
other.grad += out.grad
out = Value(self.data * other.data, (self, other), '*')
self.grad += other.data * out.grad # عملگر mul ورودیها را جابهجا میکند
other.grad += self.data * out.grad
self.grad += (s * (1 - s)) * out.grad # sigmoid reuses s
build_topo(self) # مرتبسازی توپولوژیک گراف
self.grad = 1.0 # dL/dL = 1
for v in reversed(topo):
v._backward() # ارسال (push) گرادیانها به فرزندان
حلقهی آموزش مینیفریمورک
کدpython
model = Sequential(Linear(2, 16), ReLU(), Linear(16, 16), ReLU(),
Linear(16, 8), ReLU(), Linear(8, 1), Sigmoid())
criterion = BCELoss()
optimizer = Adam(model.parameters(), lr=0.01)
model.train() # روشن بودن دراپاوت و آمارهای دسته
for batch_inputs, batch_targets in loader:
for x, t in zip(batch_inputs, batch_targets):
pred = model.forward(x)
loss = criterion(pred, t)
optimizer.zero_grad()
grad = criterion.backward() # dL/dpred
model.backward(grad) # ماژولها به ترتیب معکوس
optimizer.step()
model.eval() # آمارهای جاری، بدون دراپاوت
کدام تابع زیان
دستورها| دستور | کارکرد |
|---|---|
F.mse_loss | رگرسیون. جریمهی مربعی دادههای پرت |
MAE / Huber | رگرسیون مقاوم در برابر دادههای پرت |
F.binary_cross_entropy | دستهبندی دوکلاسه روی احتمالات |
F.cross_entropy | چندکلاسه. Log-softmax و NLL در یک گام |
Label smoothing | افزودن در زمان بیشاطمینانی CE چندکلاسه |
Focal Loss | کلاسهای نامتوازن. وزندهی کمتر به نمونههای ساده |
Triplet Loss | لنگر، مثبت، منفی با حاشیه (margin) |
InfoNCE | امبدینگهای خودنظارتی دستهبزرگ |
پیشفرضها در کد درس
پیشفرضها| تنظیم | پیشفرض | نکته |
|---|---|---|
Perceptron `learning_rate` | 0.1 | epochs=100 |
Adam `lr` | 0.001 | beta1=0.9، beta2=0.999، eps=1e-8 |
SGD `lr` | 0.01 | param -= lr * grad |
Dropout `p` | 0.5 | مقیاسبندی باقیماندهها با 1/(1-p) |
BatchNorm | momentum=0.1 | eps=1e-5، آمارهای جاری برای eval |
BCE clip `eps` | 1e-15 | BCELoss در فریمورک از 1e-7 استفاده میکند |
alpha | 0.1 | هموارسازی برچسبها، هدف 0.91 برای 10 کلاس |
temperature | 0.07 | تابع زیان تقابلی، پیشفرض SimCLR |
اعدادی که باید به خاطر سپرد
نکتهها- مشتق سیگموئید در $a = 0.5$ به اوج 0.25 میرسد.
- ده لایه سیگموئید گرادیان را حداکثر با ضریب $0.25^{10} = 0.000001$ تضعیف میکنند.
- تابع MSE که در وظیفهی دوکلاسه 50/50 عدد 0.5 را پیشبینی کند، زیان 0.25 داده و چیزی یاد نمیگیرد.
- آنتروپی متقاطع مقادیر $-\log(0.5) = 0.693$ و $-\log(0.99) = 0.01$ را میدهد.
- حدس اشتباهِ بااطمینان $p = 0.01$ هزینهای معادل $-\log(0.01) = 4.6$ دارد.
- حدس تصادفی روی 10 کلاس هزینهای معادل $-\log(0.1) = 2.3$ دارد.
- تابع زیان کانونی با $\gamma = 2$ به نمونهی ساده وزن 0.01 و به نمونهی سخت وزن 0.81 میدهد.
- یک گام رو به عقب همهی گرادیانها را میدهد. تغییر جزئی هر وزن نیازمند یک گذر رو به جلو به ازای هر وزن است.
عیبیابی
عیبیابیپرسپترون روی XOR هرگز همگرا نمیشود
- علت
- مسئله XOR تفکیکپذیر خطی نیست
- راهحل
- افزودن یک لایهی پنهان
خروجی طبقهبند برای همه 0.5 است
- علت
- تابع زیان MSE برای طبقهبندی
- راهحل
- استفاده از آنتروپی متقاطع
لایههای اولیه یاد نمیگیرند
- علت
- سیگموئید گرادیانها را در هر لایه کوچک میکند
- راهحل
- استفاده از ReLU (درس 04)، مقداردهی اولیهی مقیاسشده
تابع زیان inf یا NaN است
- علت
- $\log(0) = -\infty$
- راهحل
- محدود کردن (clip) مقدار p در بازه [eps, 1 - eps]
سرریز
math.exp(1000)- علت
- ورودی بزرگ به سیگموئید
- راهحل
- محدود کردن (clamp) در بازه [-500, 500]
گرادیان نادرست برای مقدار بازاستفادهشده
- علت
=روی مسیرهای دیگر بازنویسی میکند- راهحل
- تجمع گرادیان با
+=
رشد گرادیانها در هر گام
- علت
- باقی ماندن گرادیانهای قبلی
- راهحل
- ابتدا فراخوانی
zero_grad()
تغییر نتایج آزمون در هر بار اجرا
- علت
- دراپاوت همچنان روشن است
- راهحل
- فراخوانی
model.eval()
پرسشها
پرسش و پاسخ- ابعاد W چیست؟
- سطرها مطابق با لایه فعلی؛ ستونها مطابق با لایه قبلی.
- چرا برای آموزش از سیگموئید استفاده میشود نه پله؟
- سیگموئید هموار است، بنابراین گرادیان وجود دارد.
- چرا اکتیویشنها ذخیره میشوند؟
- گام رو به عقب به آنها نیاز دارد. حافظه هزینه میشود تا تنها یک گام نیاز باشد.
- چرا مرتبسازی توپولوژیک؟
- هر گره قبل از انتقال گرادیان به بعدی، کل گرادیان خود را دریافت میکند.
- چرا
F.cross_entropy، و نه سافتمکس و سپس لگاریتم؟ - یک عملیات واحد از نظر محاسباتی پایدار است.
- چرا
train()وeval()؟ - دراپاوت و BatchNorm در هر حالت رفتار متفاوتی دارند.
- چرا
zero_gradیک فراخوانی جداگانه است؟ - گرادیانها با
+=انباشته میشوند مگر اینکه آنها را بازنشانی کنید.