cover2_compressed

بازپس‌انتشار خطا از صفر – بخش دوم: محاسبه گرادیان و آموزش الگوریتم در شبکه چندلایه

5. — Backpropagation یک نورون؛ از محاسبه خطا تا اصلاح وزن‌ها

در بخش چهارم، قاعده زنجیره‌ای را برای گراف‌های محاسباتی و شبکه‌های عصبی بررسی کردیم. اکنون می‌خواهیم از این دانش برای حل یک مسئله عملی استفاده کنیم: چگونه یک نورون مصنوعی با مشاهده خطای پیش‌بینی، مقدار وزن‌ها و بایاس خود را اصلاح می‌کند؟

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

هدف این است که فرایند یادگیری، بدون وابستگی به PyTorch، TensorFlow یا ابزارهای مشتق‌گیری خودکار، کاملاً قابل‌فهم و قابل‌بازتولید باشد.

۵.۱. مسئله کاربردی: پیش‌بینی یک شاخص عملکرد

فرض کنید یک شرکت از دو شاخص عملیاتی برای پیش‌بینی یک امتیاز عملکرد استفاده می‌کند:

  • x₁: شاخص نرمال‌شده فعالیت بازاریابی؛
  • x₂: شاخص نرمال‌شده کیفیت خدمات؛
  • y: امتیاز عملکرد هدف، در بازه صفر تا یک.

برای این مسئله آموزشی، یک نورون با تابع فعال‌سازی Sigmoid در نظر می‌گیریم:

z=w1x1+w2x2+ba=σ(z)=11+e−z\begin{align} z&=w_1x_1+w_2x_2+b \\ a&=\sigma(z)=\frac{1}{1+e^{-z}} \end{align}

که در آن (a) پیش‌بینی نورون است.

برای سنجش اختلاف پیش‌بینی و مقدار هدف، از تابع زیان مربعی نصف‌شده استفاده می‌کنیم:

ℒ=12(a−y)2\begin{equation} \mathcal{L}=\frac{1}{2}(a-y)^2 \end{equation}

این انتخاب برای آموزش محاسبات مشتق مناسب است. در مسائل احتمالاتی طبقه‌بندی دودویی، معمولاً زیان آنتروپی متقاطع دودویی انتخاب مناسب‌تری است؛ اما در این مثال تمرکز ما بر سازوکار عمومی Backpropagation است.

داده‌ها و پارامترهای اولیه

متغیرمقدارنقش
x₁۱ورودی اول
x₂۲ورودی دوم
w₁۰٫۲وزن اول
w₂۰٫۳وزن دوم
(b)۰٫۱بایاس
(y)۱مقدار هدف
η۰٫۵نرخ یادگیری

این مقادیر برای نمایش آموزشی انتخاب شده‌اند و داده واقعی یک شرکت نیستند.

۵.۲. مرحله اول: انتشار رو‌به‌جلو (Forward Propagation)

در انتشار رو‌به‌جلو، ابتدا جمع وزن‌دار ورودی‌ها محاسبه می‌شود.

z=w1x1+w2x2+bz=w_1x_1+w_2x_2+b

با جایگذاری مقادیر:

z=(0.2)(1)+(0.3)(2)+0.1z=(0.2)(1)+(0.3)(2)+0.1

محاسبات میانی:

w1x1=0.2w_1x_1=0.2
w2x2=0.6w_2x_2=0.6

بنابراین:

z=0.2+0.6+0.1=0.9\boxed{z=0.2+0.6+0.1=0.9}

اکنون تابع Sigmoid را اعمال می‌کنیم:

a=11+e−0.9a=\frac{1}{1+e^{-0.9}}

چون:

e−0.9≈0.406570e^{-0.9}\approx0.406570

پس:

a≈0.7109495\boxed{a\approx0.7109495}

پیش‌بینی نورون تقریباً ۰٫۷۱۱ است؛ درحالی‌که مقدار هدف برابر یک است.

اختلاف پیش‌بینی و هدف:

e=a−y=0.7109495−1e≈−0.2890505\begin{aligned} e&=a-y=0.7109495-1\\ \boxed{e}&\boxed{\approx-0.2890505} \end{aligned}

اکنون مقدار زیان:

ℒ=12(−0.2890505)2ℒ≈0.0417751\begin{equation} \begin{aligned} \mathcal{L}&=\frac{1}{2}(-0.2890505)^2\\ \boxed{\mathcal{L}}&\boxed{\approx0.0417751} \end{aligned} \end{equation}

شکل ۵-۱. انتشار رو‌به‌جلو با تمام مقادیر میانی

در این مرحله فقط مقدار خروجی و زیان محاسبه شده است. هنوز هیچ پارامتری تغییر نکرده است.

۵.۳. مرحله دوم: انتشار رو‌به‌عقب (Backward Propagation)

اکنون باید مشخص کنیم هر پارامتر چه سهمی در زیان دارد.

هدف، محاسبه سه مشتق زیر است:

∂ℒ∂w1,∂ℒ∂w2,∂ℒ∂b\frac{\partial\mathcal{L}}{\partial w_1},\qquad \frac{\partial\mathcal{L}}{\partial w_2},\qquad \frac{\partial\mathcal{L}}{\partial b}

برای جلوگیری از محاسبات تکراری، Backpropagation را از آخرین عملیات آغاز می‌کنیم.

۵.۳.۱. مشتق زیان نسبت به خروجی نورون

تابع زیان:

ℒ=12(a−y)2\mathcal{L}=\frac{1}{2}(a-y)^2

مشتق نسبت به (a):

∂ℒ∂a=a−y\frac{\partial\mathcal{L}}{\partial a}=a-y

با جایگذاری:

∂ℒ∂a≈−0.2890505(5-5)\begin{equation} \boxed{\frac{\partial\mathcal{L}}{\partial a}\approx-0.2890505} \tag{5-5} \end{equation}

علامت منفی بیان می‌کند که افزایش کوچک خروجی (a)، در این نقطه موجب کاهش زیان می‌شود.

۵.۳.۲. مشتق تابع فعال‌سازی

برای Sigmoid:

∂a∂z=a(1−a)\frac{\partial a}{\partial z}=a(1-a)

بنابراین:

∂a∂z=0.7109495(1−0.7109495)∂a∂z≈0.2055003\begin{equation} \begin{aligned} \frac{\partial a}{\partial z}&=0.7109495(1-0.7109495)\\ \boxed{\frac{\partial a}{\partial z}}&\boxed{\approx0.2055003} \end{aligned} \end{equation}

این مشتق نشان می‌دهد تغییر کوچک مقدار (z) با چه ضریبی به خروجی نورون منتقل می‌شود.

۵.۳.۳. محاسبه گرادیان زیان نسبت به (z)

طبق قاعده زنجیره‌ای:

∂ℒ∂z=∂ℒ∂a∂a∂z\frac{\partial\mathcal{L}}{\partial z}=\frac{\partial\mathcal{L}}{\partial a}\frac{\partial a}{\partial z}

پس:

∂ℒ∂z≈(−0.2890505)(0.2055003)δ=∂ℒ∂z≈−0.0593999\begin{equation} \begin{aligned} \frac{\partial\mathcal{L}}{\partial z}&\approx(-0.2890505)(0.2055003)\\ \boxed{\delta=\frac{\partial\mathcal{L}}{\partial z}}&\boxed{\approx-0.0593999} \end{aligned} \end{equation}

این مقدار را سیگنال خطای محلی نورون می‌نامیم.

نکته مهم آن است که δ یک بار محاسبه می‌شود و سپس برای به دست آوردن گرادیان تمام وزن‌ها و بایاس مورد استفاده قرار می‌گیرد.

۵.۳.۴. گرادیان وزن اول

چون:

z=w1x1+w2x2+bz=w_1x_1+w_2x_2+b

داریم:

∂z∂w1=x1\frac{\partial z}{\partial w_1}=x_1

بنابراین:

∂ℒ∂w1=∂ℒ∂z∂z∂w1=δx1\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_1}&=\frac{\partial\mathcal{L}}{\partial z}\frac{\partial z}{\partial w_1}\\ &=\delta x_1 \end{aligned}

و چون x₁ = ۱:

∂ℒ∂w1≈−0.0593999\begin{equation} \boxed{\frac{\partial\mathcal{L}}{\partial w_1}\approx-0.0593999} \end{equation}

۵.۳.۵. گرادیان وزن دوم

برای وزن دوم:

∂z∂w2=x2=2\frac{\partial z}{\partial w_2}=x_2=2

پس:

∂ℒ∂w2=δx2=(−0.0593999)(2)∂ℒ∂w2≈−0.1187998\begin{equation} \begin{aligned} \frac{\partial\mathcal{L}}{\partial w_2}&=\delta x_2\\ &=(-0.0593999)(2)\\ \boxed{\frac{\partial\mathcal{L}}{\partial w_2}}&\boxed{\approx-0.1187998} \end{aligned} \end{equation}

۵.۳.۶. گرادیان بایاس

چون:

∂z∂b=1\frac{\partial z}{\partial b}=1

در نتیجه:

∂ℒ∂b=δ≈−0.0593999\begin{equation} \boxed{\frac{\partial\mathcal{L}}{\partial b}=\delta\approx-0.0593999} \end{equation}

اکنون گرادیان تمام پارامترهای قابل‌آموزش را در اختیار داریم.

شکل ۵-۲. محاسبات کامل انتشار رو‌به‌عقب

در شکل ۵-۲. سیگنال خطای محلی (delta) تنها یک بار محاسبه می‌شود. سپس با ضرب آن در هر ورودی، گرادیان وزن متناظر به دست می‌آید.

۵.۴. مرحله سوم: به‌روزرسانی پارامترها

اکنون باید از گرادیان‌های محاسبه‌شده برای اصلاح پارامترهای نورون استفاده کنیم.

برای این منظور از یک گام گرادیان کاهشی (Gradient Descent) استفاده می‌کنیم:

θnew=θold−η∂ℒ∂θ\begin{equation} \theta_{\mathrm{new}}=\theta_{\mathrm{old}}-\eta\frac{\partial\mathcal{L}}{\partial\theta} \end{equation}

در این رابطه:

  • • θ یکی از پارامترهای مدل است؛
    • η نرخ یادگیری است؛
    • ∂ℒ/∂θ گرادیان زیان نسبت به آن پارامتر است.

نرخ یادگیری را برابر ۰٫۵ انتخاب کرده‌ایم:

η=0.5\eta=0.5

۵.۴.۱. به‌روزرسانی وزن اول

w1new=w1−η∂ℒ∂w1=0.2−0.5(−0.0593999)w1new≈0.2297000\begin{aligned} w_1^{\mathrm{new}}&=w_1-\eta\frac{\partial\mathcal{L}}{\partial w_1}\\ &=0.2-0.5(-0.0593999)\\ \boxed{w_1^{\mathrm{new}}}&\boxed{\approx0.2297000} \end{aligned}

۵.۴.۲. به‌روزرسانی وزن دوم

w2new=0.3−0.5(−0.1187998)w2new≈0.3593999\begin{aligned} w_2^{\mathrm{new}}&=0.3-0.5(-0.1187998)\\ \boxed{w_2^{\mathrm{new}}}&\boxed{\approx0.3593999} \end{aligned}

۵.۴.۳. به‌روزرسانی بایاس

bnew=0.1−0.5(−0.0593999)bnew≈0.1297000\begin{aligned} b^{\mathrm{new}}&=0.1-0.5(-0.0593999)\\ \boxed{b^{\mathrm{new}}}&\boxed{\approx0.1297000} \end{aligned}

جدول زیر تغییر پارامترها را نشان می‌دهد.

پارامترمقدار اولیهگرادیانمقدار جدید
(w₁)۰٫۲۰۰۰۰۰−۰٫۰۵۹۴۰۰۰٫۲۲۹۷۰۰
(w₂)۰٫۳۰۰۰۰۰−۰٫۱۱۸۸۰۰۰٫۳۵۹۴۰۰
(b)۰٫۱۰۰۰۰۰−۰٫۰۵۹۴۰۰۰٫۱۲۹۷۰۰

در این مثال، تمام گرادیان‌ها منفی‌اند. بنابراین کم‌کردن مقدار منفی از پارامترهای اولیه موجب افزایش آن‌ها شده است.

اما منفی‌بودن گرادیان همیشه به معنی افزایش وزن نیست؛ جهت تغییر هر پارامتر به علامت گرادیان همان پارامتر بستگی دارد.

۵.۵. مرحله چهارم: آیا زیان واقعاً کاهش یافته است؟

محاسبه گرادیان و اصلاح پارامترها به‌تنهایی کافی نیست. باید یک انتشار رو‌به‌جلوی جدید انجام دهیم و زیان پس از به‌روزرسانی را محاسبه کنیم.

با پارامترهای جدید:

znew=w1newx1+w2newx2+bnewznew≈0.2297000(1)+0.3593999(2)+0.1297000znew≈1.0781998\begin{aligned} z_{\mathrm{new}}&=w_1^{\mathrm{new}}x_1+w_2^{\mathrm{new}}x_2+b^{\mathrm{new}}\\ z_{\mathrm{new}}&\approx0.2297000(1)+0.3593999(2)+0.1297000\\ \boxed{z_{\mathrm{new}}}&\boxed{\approx1.0781998} \end{aligned}

خروجی جدید:

anew=σ(1.0781998)anew≈0.746154\begin{aligned} a_{\mathrm{new}}&=\sigma(1.0781998)\\ \boxed{a_{\mathrm{new}}}&\boxed{\approx0.746154} \end{aligned}

زیان جدید:

ℒnew=12(0.746154−1)2ℒnew≈0.032219\begin{aligned} \mathcal{L}_{\mathrm{new}}&=\frac{1}{2}(0.746154-1)^2\\ \boxed{\mathcal{L}_{\mathrm{new}}}&\boxed{\approx0.032219} \end{aligned}

در مقایسه با زیان اولیه:

ℒold≈0.041775\mathcal{L}_{\mathrm{old}}\approx0.041775

بنابراین:

Δℒ=0.032219−0.041775Δℒ≈−0.009556\begin{aligned} \Delta\mathcal{L}&=0.032219-0.041775\\ \boxed{\Delta\mathcal{L}}&\boxed{\approx-0.009556} \end{aligned}

درصد کاهش زیان:

0.041775−0.0322190.041775×100≈22.87%\frac{0.041775-0.032219}{0.041775}\times100\approx22.87\%

پس در این مثال، تنها با یک گام به‌روزرسانی، زیان حدود ۲۲٫۹ درصد کاهش یافته است.

زیان پیش از یادگیری

0.041775

پیش‌بینی: 0.710950

زیان پس از یک گام

0.032219

پیش‌بینی: 0.746154

شکل ۵-۳. مقایسه زیان پیش و پس از یک گام یادگیری

این نتیجه نشان می‌دهد جهت گرادیان برای گام انتخاب‌شده مفید بوده است.

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

۵.۶. چرا گرادیان وزن دوم بزرگ‌تر است؟

در مثال ما:

x1=1,x2=2x_1=1,\qquad x_2=2

و:

∂ℒ∂wi=δxi\frac{\partial\mathcal{L}}{\partial w_i}=\delta x_i

بنابراین:

∂ℒ∂w2=2∂ℒ∂w1\frac{\partial\mathcal{L}}{\partial w_2}=2\frac{\partial\mathcal{L}}{\partial w_1}

این نتیجه از آنجا ناشی می‌شود که ورودی دوم دو برابر ورودی اول است.

اگر مقدار ورودی دوم به‌جای ۲ برابر ۱۰ باشد، با ثابت‌ماندن مقدار (delta) به‌صورت فرضی، گرادیان وزن دوم نیز پنج برابر می‌شود.

این نکته یکی از دلایل اهمیت مقیاس ویژگی‌ها در آموزش شبکه‌های عصبی است.

بااین‌حال، در یک شبکه واقعی تغییر مقدار ورودی معمولاً مقدار (z)، خروجی و در نتیجه (delta) را نیز تغییر می‌دهد. بنابراین نمی‌توان بدون درنظرگرفتن این وابستگی‌ها، نتیجه گرفت که افزایش پنج‌برابری یک ورودی همیشه گرادیان نهایی را دقیقاً پنج برابر می‌کند.

۵.۷. مثال کاربردی دوم: نورون خطی برای پیش‌بینی فروش

برای نشان‌دادن اینکه Backpropagation فقط به Sigmoid محدود نیست، یک نورون خطی را بررسی می‌کنیم.

فرض کنید یک مدل آموزشی، فروش روزانه را از دو ویژگی پیش‌بینی می‌کند:

y^=w1x1+w2x2+b\hat{y}=w_1x_1+w_2x_2+b

مقادیر:

x1=2,x2=3,w1=1,w2=0.5,b=1,y=6x_1=2,\quad x_2=3,\qquad w_1=1,\quad w_2=0.5,\quad b=1,\qquad y=6

خروجی اولیه:

y^=1(2)+0.5(3)+1=4.5\hat{y}=1(2)+0.5(3)+1=4.5

زیان:

ℒ=12(4.5−6)2=1.125\mathcal{L}=\frac{1}{2}(4.5-6)^2=1.125

چون تابع فعال‌سازی همانی است:

a=za=z

داریم:

∂a∂z=1\frac{\partial a}{\partial z}=1

در نتیجه:

δ=y^−y=4.5−6=−1.5\delta=\hat{y}-y=4.5-6=-1.5

گرادیان‌ها:

∂ℒ∂w1=−1.5(2)=−3∂ℒ∂w2=−1.5(3)=−4.5∂ℒ∂b=−1.5\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_1}&=-1.5(2)=-3\\ \frac{\partial\mathcal{L}}{\partial w_2}&=-1.5(3)=-4.5\\ \frac{\partial\mathcal{L}}{\partial b}&=-1.5 \end{aligned}

اگر نرخ یادگیری را ۰٫۱ انتخاب کنیم:

w1new=1−0.1(−3)=1.3w2new=0.5−0.1(−4.5)=0.95bnew=1−0.1(−1.5)=1.15\begin{aligned} w_1^{\mathrm{new}}&=1-0.1(-3)=1.3\\ w_2^{\mathrm{new}}&=0.5-0.1(-4.5)=0.95\\ b^{\mathrm{new}}&=1-0.1(-1.5)=1.15 \end{aligned}

خروجی جدید:

y^new=1.3(2)+0.95(3)+1.15=2.6+2.85+1.15=6.6\begin{aligned} \hat{y}_{\mathrm{new}}&=1.3(2)+0.95(3)+1.15\\ &=2.6+2.85+1.15=6.6 \end{aligned}

زیان جدید:

ℒnew=12(6.6−6)2=0.18\mathcal{L}_{\mathrm{new}}=\frac{1}{2}(6.6-6)^2=0.18

پس:

1.125⟶0.18\boxed{1.125\longrightarrow0.18}

زیان کاهش یافته است؛ اما پیش‌بینی از مقدار هدف عبور کرده و به ۶٫۶ رسیده است. این مثال نشان می‌دهد که حتی با کاهش زیان، اندازه گام یادگیری بر رفتار مدل تأثیر می‌گذارد.

۵.۸. پیاده‌سازی Backpropagation یک نورون بدون Autograd

اکنون مثال اصلی Sigmoid را با پایتون پیاده‌سازی می‌کنیم.

این کد فقط از کتابخانه استاندارد math استفاده می‌کند و هیچ ابزار مشتق‌گیری خودکاری در آن وجود ندارد.

import math

def sigmoid(z):
    return 1.0 / (1.0 + math.exp(-z))

def forward(x1, x2, y, w1, w2, b):
    z = w1 * x1 + w2 * x2 + b
    a = sigmoid(z)
    loss = 0.5 * (a - y) ** 2
    return z, a, loss

# داده آموزشی
x1 = 1.0
x2 = 2.0
y = 1.0

# پارامترهای اولیه
w1 = 0.2
w2 = 0.3
b = 0.1

learning_rate = 0.5

# مرحله اول: Forward
z, a, loss_before = forward(
    x1, x2, y, w1, w2, b
)

# مرحله دوم: Backward
dL_da = a - y
da_dz = a * (1.0 - a)

delta = dL_da * da_dz

dL_dw1 = delta * x1
dL_dw2 = delta * x2
dL_db = delta

# مرحله سوم: به‌روزرسانی پارامترها
w1_new = w1 - learning_rate * dL_dw1
w2_new = w2 - learning_rate * dL_dw2
b_new = b - learning_rate * dL_db

# مرحله چهارم: محاسبه مجدد زیان
z_new, a_new, loss_after = forward(
    x1, x2, y,
    w1_new, w2_new, b_new
)

print("Initial prediction:", a)
print("Initial loss:", loss_before)

print("Gradient w1:", dL_dw1)
print("Gradient w2:", dL_dw2)
print("Gradient b:", dL_db)

print("Updated w1:", w1_new)
print("Updated w2:", w2_new)
print("Updated b:", b_new)

print("New prediction:", a_new)
print("New loss:", loss_after)

assert loss_after < loss_before

این برنامه چهار وظیفه مستقل را انجام می‌دهد: محاسبه خروجی، محاسبه گرادیان‌ها، به‌روزرسانی پارامترها و ارزیابی زیان جدید.

استفاده از assert در انتهای برنامه صرفاً بررسی می‌کند که در این مثال مشخص، زیان کاهش یافته است. این شرط برای همه داده‌ها و همه نرخ‌های یادگیری تضمین‌شده نیست.

۵.۸.۱. گسترش برنامه به چند تکرار یادگیری

برای مشاهده رفتار تدریجی یادگیری، می‌توان فرایند را در یک حلقه تکرار کرد:

import math

def sigmoid(z):
    if z >= 0:
        return 1.0 / (1.0 + math.exp(-z))
    ez = math.exp(z)
    return ez / (1.0 + ez)

x1, x2, y = 1.0, 2.0, 1.0
w1, w2, b = 0.2, 0.3, 0.1
lr = 0.5

history = []

for epoch in range(101):
    # Forward
    z = w1 * x1 + w2 * x2 + b
    a = sigmoid(z)
    loss = 0.5 * (a - y) ** 2

    history.append((epoch, loss, a))

    if epoch == 100:
        break

    # Backward
    delta = (a - y) * a * (1.0 - a)

    grad_w1 = delta * x1
    grad_w2 = delta * x2
    grad_b = delta

    # Gradient descent
    w1 -= lr * grad_w1
    w2 -= lr * grad_w2
    b -= lr * grad_b

for epoch, loss, prediction in history:
    if epoch % 20 == 0:
        print(
            f"Epoch {epoch:3d} | "
            f"Loss={loss:.8f} | "
            f"Prediction={prediction:.6f}"
        )

در این برنامه، برای هر تکرار ابتدا خروجی و زیان محاسبه می‌شود، سپس گرادیان‌ها به دست می‌آیند و پارامترها اصلاح می‌شوند.

تابع Sigmoid در نسخه دوم به‌شکلی نوشته شده است که در ورودی‌های مثبت و منفی بزرگ، از محاسبه نماییِ غیرضروری با خطر سرریز عددی جلوگیری کند.

این پیاده‌سازی از یک نمونه آموزشی استفاده می‌کند. بنابراین کاهش زیان روی همین نمونه، به‌تنهایی نشان‌دهنده توانایی تعمیم مدل به داده‌های جدید نیست.

۵.۹. سه خطای رایج هنگام پیاده‌سازی Backpropagation

خطای اول: اشتباه‌گرفتن خطای پیش‌بینی با سیگنال خطای محلی

در نورون Sigmoid با زیان مربعی:

e=a−ye=a-y

اما:

δ=(a−y)a(1−a)\delta=(a-y)a(1-a)

بنابراین:

e=δe=\delta

مگر در شرایط خاص.

اگر به‌جای (delta)، مستقیماً از (e) برای گرادیان وزن‌ها استفاده کنیم، مشتق تابع فعال‌سازی را نادیده گرفته‌ایم.

خطای دوم: استفاده از پارامترهای به‌روزشده در محاسبه گرادیان‌های همان گام

تمام گرادیان‌های یک گام استاندارد گرادیان کاهشی باید بر اساس یک مجموعه مشترک از پارامترهای فعلی محاسبه شوند.

برای مثال، ابتدا باید هر سه مقدار زیر محاسبه شوند:

g1=∂ℒ∂w1,g2=∂ℒ∂w2,gb=∂ℒ∂bg_1=\frac{\partial\mathcal{L}}{\partial w_1},\qquad g_2=\frac{\partial\mathcal{L}}{\partial w_2},\qquad g_b=\frac{\partial\mathcal{L}}{\partial b}

سپس به‌روزرسانی انجام شود.

اگر وزن اول را تغییر دهیم و بعد با آن مقدار جدید، گرادیان وزن دوم را برای همان گام محاسبه کنیم، دیگر یک گام گرادیان کاهشی هم‌زمان مطابق تعریف اولیه انجام نداده‌ایم.

خطای سوم: انتخاب نرخ یادگیری نامناسب

نرخ یادگیری بسیار کوچک ممکن است پیشرفت را کند کند.

نرخ یادگیری بسیار بزرگ ممکن است موجب نوسان یا افزایش زیان شود.

برای نمونه، در مثال نورون خطی بخش ۵.۷، نرخ یادگیری ۰٫۱ موجب کاهش زیان شد؛ اما برای همان داده و همان گرادیان اولیه، نرخ یادگیری ۱ مقدار پیش‌بینی را به‌شدت از هدف دور می‌کند.

زیرا:

w1new=1−1(−3)=4w2new=0.5−1(−4.5)=5bnew=1−1(−1.5)=2.5\begin{aligned} w_1^{\mathrm{new}}&=1-1(-3)=4\\ w_2^{\mathrm{new}}&=0.5-1(-4.5)=5\\ b^{\mathrm{new}}&=1-1(-1.5)=2.5 \end{aligned}

و:

y^new=4(2)+5(3)+2.5=25.5\hat{y}_{\mathrm{new}}=4(2)+5(3)+2.5=25.5

در نتیجه:

ℒnew=12(25.5−6)2=190.125\mathcal{L}_{\mathrm{new}}=\frac{1}{2}(25.5-6)^2=190.125

درحالی‌که زیان اولیه تنها ۱٫۱۲۵ بود.

پس جهت منفی گرادیان، یک جهت کاهش محلی است؛ اما تضمین نمی‌کند هر گام با هر اندازه‌ای موجب کاهش زیان شود.

۵.۱۰. تمرین‌ها و مسائل بخش پنجم

  • تمرین ۱ ـ نورون خطی: نورونی با (x=3)، (w=2)، (b=1) و (y=10) در نظر بگیرید. زیان مربعی نصف‌شده، گرادیان وزن و بایاس و پارامترهای جدید را با نرخ یادگیری ۰٫۰۱ محاسبه کنید.
  • تمرین ۲ ـ نورون Sigmoid: برای (x=2)، (w=0.5)، (b=0) و (y=0)، خروجی Sigmoid، زیان و گرادیان وزن را محاسبه کنید.
  • تمرین ۳ ـ تحلیل علامت گرادیان: توضیح دهید چرا در مثال اصلی این بخش، هر سه پارامتر پس از یک گام یادگیری افزایش یافتند.
  • تمرین ۴ ـ نرخ یادگیری: مثال نورون خطی بخش ۵.۷ را با نرخ‌های یادگیری ۰٫۰۱، ۰٫۰۵، ۰٫۱ و ۰٫۵ تکرار کنید و مقدار زیان جدید را مقایسه کنید.
  • تمرین ۵ ـ برنامه‌نویسی: کد نورون Sigmoid را به‌گونه‌ای توسعه دهید که تعداد تکرارها، نرخ یادگیری و مقادیر اولیه وزن‌ها از ورودی برنامه دریافت شوند.
  • تمرین ۶ ـ تحلیل حساسیت: اگر در مثال اصلی x₂ از ۲ به ۴ تغییر کند، تمام محاسبات Forward و Backward را دوباره انجام دهید. بررسی کنید آیا گرادیان w₂ دقیقاً دو برابر می‌شود یا خیر.
  • تمرین ۷ ـ پرسش تحلیلی: چرا کاهش زیان روی یک نمونه آموزشی، برای اثبات عملکرد مناسب مدل روی داده‌های جدید کافی نیست؟

پاسخ تشریحی منتخب: تمرین ۱

داریم:

y^=wx+b=2(3)+1=7\hat{y}=wx+b=2(3)+1=7

زیان:

ℒ=12(7−10)2=4.5\mathcal{L}=\frac{1}{2}(7-10)^2=4.5

گرادیان وزن:

∂ℒ∂w=(y^−y)x=(−3)(3)=−9\frac{\partial\mathcal{L}}{\partial w}=(\hat{y}-y)x=(-3)(3)=-9

گرادیان بایاس:

∂ℒ∂b=−3\frac{\partial\mathcal{L}}{\partial b}=-3

با نرخ یادگیری ۰٫۰۱:

wnew=2−0.01(−9)=2.09bnew=1−0.01(−3)=1.03\begin{aligned} w_{\mathrm{new}}&=2-0.01(-9)=2.09\\ b_{\mathrm{new}}&=1-0.01(-3)=1.03 \end{aligned}

پیش‌بینی جدید:

y^new=2.09(3)+1.03=7.30\hat{y}_{\mathrm{new}}=2.09(3)+1.03=7.30

زیان جدید:

ℒnew=12(7.30−10)2ℒnew=3.645\begin{aligned} \mathcal{L}_{\mathrm{new}}&=\frac{1}{2}(7.30-10)^2\\ \boxed{\mathcal{L}_{\mathrm{new}}}&\boxed{=3.645} \end{aligned}

پس زیان از ۴٫۵ به ۳٫۶۴۵ کاهش یافته است.

منابع علمی بخش پنجم

Bishop, C. M. (2006). Pattern recognition and machine learning. Springer.

Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep learning. MIT Press.

Hagan, M. T., Demuth, H. B., Beale, M. H., & De Jesús, O. (2014). Neural network design (2nd ed.). Martin Hagan.

Haykin, S. (2009). Neural networks and learning machines (3rd ed.). Pearson.

Rumelhart, D. E., Hinton, G. E., & Williams, R. J. (1986). Learning representations by back-propagating errors. Nature, 323, 533–536. https://doi.org/10.1038/323533a0

6. بازپس‌انتشار خطا در یک لایه کامل شبکه عصبی

در بخش پنجم، یاد گرفتیم که چگونه یک نورون مصنوعی با محاسبه گرادیان تابع زیان نسبت به وزن‌ها و بایاس، پارامترهای خود را اصلاح می‌کند. اکنون یک گام مهم‌تر برمی‌داریم: چگونه می‌توان گرادیان تمام نورون‌های یک لایه را به‌صورت هم‌زمان محاسبه کرد؟

این مسئله نقطه اتصال محاسبات اسکالر به محاسبات برداری و ماتریسی است. در شبکه‌های عصبی واقعی، محاسبه جداگانه مشتق هر وزن نه‌تنها زمان‌بر است، بلکه پیاده‌سازی و نگهداری آن را نیز دشوار می‌کند.

هدف این بخش آن است که ابتدا Backpropagation یک لایه را با اعداد واقعی و محاسبات کامل بیاموزیم، سپس همان محاسبات را به زبان ماتریس‌ها بیان کنیم و در پایان، یک پیاده‌سازی مستقل و قابل‌بازتولید با NumPy ارائه دهیم.

۶.۱. از یک نورون به یک لایه

در بخش پنجم، یک نورون با دو ورودی داشتیم:

z=w1x1+w2x2+ba=φ(z)\begin{aligned} z&=w_1x_1+w_2x_2+b\\ a&=\varphi(z) \end{aligned}

اکنون فرض کنید یک لایه دارای سه نورون باشد و هر نورون همان دو ورودی را دریافت کند.

شکل ۶-۱. یک لایه با دو ورودی و سه نورون

هر نورون دو وزن مستقل و یک بایاس دارد. بنابراین این لایه در مجموع ۶ وزن و ۳ بایاس، یعنی ۹ پارامتر قابل‌آموزش، خواهد داشت.

روابط سه نورون به شکل زیر هستند:

z1=w11x1+w12x2+b1z2=w21x1+w22x2+b2z3=w31x1+w32x2+b3\begin{aligned} z_1&=w_{11}x_1+w_{12}x_2+b_1\\ z_2&=w_{21}x_1+w_{22}x_2+b_2\\ z_3&=w_{31}x_1+w_{32}x_2+b_3 \end{aligned}

در این نمادگذاری، wⱼᵢ وزن اتصال ورودی i به نورون j است.

اگر تابع فعال‌سازی هر سه نورون یکسان باشد:

aj=φ(zj),j=1,2,3a_j=\varphi(z_j),\qquad j=1,2,3

می‌توان این روابط را به‌شکل فشرده زیر نوشت:

𝐳=W𝐱+𝐛,𝐚=φ(𝐳)\boxed{\mathbf{z}=W\mathbf{x}+\mathbf{b}},\qquad \boxed{\mathbf{a}=\varphi(\mathbf{z})}

که در آن φ به‌صورت مؤلفه‌به‌مؤلفه اعمال می‌شود.

۶.۲. مثال اصلی: پیش‌بینی سه شاخص عملکرد

فرض کنید یک شرکت می‌خواهد با استفاده از دو ویژگی عملیاتی، سه شاخص عملکرد نرمال‌شده را پیش‌بینی کند.

ورودی‌ها:

𝐱=[12]\mathbf{x}= \begin{bmatrix} 1\\ 2 \end{bmatrix}

وزن‌های اولیه:

W=[0.20.30.4−0.1−0.20.5]W= \begin{bmatrix} 0.2&0.3\\ 0.4&-0.1\\ -0.2&0.5 \end{bmatrix}

بایاس‌ها:

𝐛=[0.10.2−0.1]\mathbf{b}= \begin{bmatrix} 0.1\\ 0.2\\ -0.1 \end{bmatrix}

مقادیر هدف:

𝐲=[101]\mathbf{y}= \begin{bmatrix} 1\\ 0\\ 1 \end{bmatrix}

برای هر نورون از Sigmoid استفاده می‌کنیم و تابع زیان کل را مجموع زیان‌های مربعی نصف‌شده در نظر می‌گیریم:

ℒ=12∑j=13(aj−yj)2\begin{equation} \boxed{\mathcal{L}=\frac{1}{2}\sum_{j=1}^{3}(a_j-y_j)^2} \end{equation}

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

۶.۲.۱. محاسبه خروجی خطی نورون اول

z1=0.2(1)+0.3(2)+0.1z1=0.9\begin{aligned} z_1&=0.2(1)+0.3(2)+0.1\\ \boxed{z_1}&\boxed{=0.9} \end{aligned}

۶.۲.۲. محاسبه خروجی خطی نورون دوم

z2=0.4(1)−0.1(2)+0.2z2=0.4−0.2+0.2z2=0.4\begin{aligned} z_2&=0.4(1)-0.1(2)+0.2\\ z_2&=0.4-0.2+0.2\\ \boxed{z_2}&\boxed{=0.4} \end{aligned}

۶.۲.۳. محاسبه خروجی خطی نورون سوم

z3=−0.2(1)+0.5(2)−0.1z3=−0.2+1−0.1z3=0.7\begin{aligned} z_3&=-0.2(1)+0.5(2)-0.1\\ z_3&=-0.2+1-0.1\\ \boxed{z_3}&\boxed{=0.7} \end{aligned}

بنابراین:

𝐳=[0.90.40.7]\boxed{\mathbf{z}= \begin{bmatrix} 0.9\\ 0.4\\ 0.7 \end{bmatrix}}

۶.۳. اعمال تابع فعال‌سازی

تابع Sigmoid:

σ(z)=11+e−z\sigma(z)=\frac{1}{1+e^{-z}}
  • برای نورون اول:
a1=σ(0.9)≈0.710950a_1=\sigma(0.9)\approx0.710950
  • برای نورون دوم:
a2=σ(0.4)≈0.598688a_2=\sigma(0.4)\approx0.598688
  • برای نورون سوم:
a3=σ(0.7)≈0.668188a_3=\sigma(0.7)\approx0.668188

پس:

𝐚=[0.7109500.5986880.668188]\begin{equation} \boxed{\mathbf{a}= \begin{bmatrix} 0.710950\\ 0.598688\\ 0.668188 \end{bmatrix}} \end{equation}

۶.۳.۱. محاسبه زیان هر نورون

  • برای نورون اول:
ℒ1=12(0.710950−1)2≈0.041775\mathcal{L}_1=\frac{1}{2}(0.710950-1)^2\approx0.041775
  • برای نورون دوم:
ℒ2=12(0.598688−0)2≈0.179214\mathcal{L}_2=\frac{1}{2}(0.598688-0)^2\approx0.179214
  • برای نورون سوم:
ℒ3=12(0.668188−1)2≈0.055050\mathcal{L}_3=\frac{1}{2}(0.668188-1)^2\approx0.055050

زیان کل:

ℒ=ℒ1+ℒ2+ℒ3ℒ≈0.276039\begin{equation} \begin{aligned} \mathcal{L}&=\mathcal{L}_1+\mathcal{L}_2+\mathcal{L}_3\\ \boxed{\mathcal{L}}&\boxed{\approx0.276039} \end{aligned} \end{equation}

شکل ۶-۲. انتشار رو‌به‌جلو در سه نورون با نمایش محاسبات

هر نورون جمع وزن‌دار، فعال‌سازی و سهم خود از زیان کل را محاسبه می‌کند. زیان کل حاصل جمع سه مقدار زیان است.

۶.۴. بازپس‌انتشار خطا در سه نورون

اکنون می‌خواهیم گرادیان زیان نسبت به تمام وزن‌ها و بایاس‌ها را محاسبه کنیم.

برای هر نورون:

δj=∂ℒ∂zj\begin{equation} \delta_j=\frac{\partial\mathcal{L}}{\partial z_j} \end{equation}

با استفاده از قاعده زنجیره‌ای:

δj=∂ℒ∂aj∂aj∂zj\delta_j=\frac{\partial\mathcal{L}}{\partial a_j}\frac{\partial a_j}{\partial z_j}

چون:

∂ℒ∂aj=aj−yj,∂aj∂zj=aj(1−aj)\frac{\partial\mathcal{L}}{\partial a_j}=a_j-y_j,\qquad \frac{\partial a_j}{\partial z_j}=a_j(1-a_j)

در نتیجه:

δj=(aj−yj)aj(1−aj)\begin{equation} \boxed{\delta_j=(a_j-y_j)a_j(1-a_j)} \end{equation}

۶.۴.۱. سیگنال خطای نورون اول

δ1=(0.710950−1)(0.710950)(1−0.710950)δ1≈−0.059400\begin{aligned} \delta_1&=(0.710950-1)(0.710950)(1-0.710950)\\ \boxed{\delta_1}&\boxed{\approx-0.059400} \end{aligned}

۶.۴.۲. سیگنال خطای نورون دوم

δ2=(0.598688−0)(0.598688)(1−0.598688)δ2≈0.143735\begin{aligned} \delta_2&=(0.598688-0)(0.598688)(1-0.598688)\\ \boxed{\delta_2}&\boxed{\approx0.143735} \end{aligned}

۶.۴.۳. سیگنال خطای نورون سوم

δ3=(0.668188−1)(0.668188)(1−0.668188)δ3≈−0.073579\begin{aligned} \delta_3&=(0.668188-1)(0.668188)(1-0.668188)\\ \boxed{\delta_3}&\boxed{\approx-0.073579} \end{aligned}

پس بردار سیگنال‌های خطای محلی تقریباً برابر است با:

𝜹=[−0.0594000.143735−0.073579]\begin{equation} \boxed{\boldsymbol{\delta}= \begin{bmatrix} -0.059400\\ 0.143735\\ -0.073579 \end{bmatrix}} \end{equation}

مقادیر در متن تا شش رقم اعشار گرد شده‌اند؛ در محاسبات برنامه‌نویسی باید از مقادیر با دقت کامل استفاده شود.

۶.۴.۴. تفسیر علامت سیگنال‌های خطا

  • در نورون اول، مقدار پیش‌بینی کمتر از هدف است؛ بنابراین سیگنال خطا منفی شده است.
  • در نورون دوم، مقدار هدف صفر است اما نورون خروجی مثبتی تولید کرده است؛ بنابراین سیگنال خطا مثبت است.
  • در نورون سوم نیز پیش‌بینی کمتر از هدف است و سیگنال خطا منفی است.

این علامت‌ها در تعیین جهت به‌روزرسانی وزن‌ها نقش دارند.

۶.۵. محاسبه گرادیان تمام وزن‌ها

برای هر وزن wⱼᵢ:

∂ℒ∂wji=δjxi\begin{equation} \frac{\partial\mathcal{L}}{\partial w_{ji}}=\delta_j x_i \end{equation}

از آنجا که:

x1=1,x2=2x_1=1,\qquad x_2=2

گرادیان‌های نورون اول:

∂ℒ∂w11=δ1(1)≈−0.059400∂ℒ∂w12=δ1(2)≈−0.118800\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_{11}}&=\delta_1(1)\approx-0.059400\\ \frac{\partial\mathcal{L}}{\partial w_{12}}&=\delta_1(2)\approx-0.118800 \end{aligned}

گرادیان‌های نورون دوم:

∂ℒ∂w21=δ2(1)≈0.143735∂ℒ∂w22=δ2(2)≈0.287470\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_{21}}&=\delta_2(1)\approx0.143735\\ \frac{\partial\mathcal{L}}{\partial w_{22}}&=\delta_2(2)\approx0.287470 \end{aligned}

گرادیان‌های نورون سوم:

∂ℒ∂w31=δ3(1)≈−0.073579∂ℒ∂w32=δ3(2)≈−0.147158\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_{31}}&=\delta_3(1)\approx-0.073579\\ \frac{\partial\mathcal{L}}{\partial w_{32}}&=\delta_3(2)\approx-0.147158 \end{aligned}

بنابراین ماتریس گرادیان وزن‌ها برابر است با:

∇Wℒ≈[−0.059400−0.1188000.1437350.287470−0.073579−0.147158]\begin{equation} \boxed{\nabla_W\mathcal{L}\approx \begin{bmatrix} -0.059400&-0.118800\\ 0.143735&0.287470\\ -0.073579&-0.147158 \end{bmatrix}} \end{equation}

گرادیان بایاس‌ها نیز برابر است با:

∇𝐛ℒ=𝜹\begin{equation} \boxed{\nabla_{\mathbf{b}}\mathcal{L}=\boldsymbol{\delta}} \end{equation}

۶.۶. استخراج رابطه ماتریسی Backpropagation

اکنون می‌خواهیم نشان دهیم که ماتریس گرادیان وزن‌ها را می‌توان با یک ضرب ماتریسی محاسبه کرد.

بردار سیگنال خطا:

𝜹=[δ1δ2δ3]\boldsymbol{\delta}= \begin{bmatrix} \delta_1\\ \delta_2\\ \delta_3 \end{bmatrix}

بردار ورودی:

𝐱=[x1x2]\mathbf{x}= \begin{bmatrix} x_1\\ x_2 \end{bmatrix}

ترانهاده ورودی:

𝐱⊤=[x1x2]\mathbf{x}^{\top}= \begin{bmatrix} x_1&x_2 \end{bmatrix}

حاصل‌ضرب بیرونی:

𝜹𝐱⊤=[δ1x1δ1x2δ2x1δ2x2δ3x1δ3x2]\boldsymbol{\delta}\mathbf{x}^{\top}= \begin{bmatrix} \delta_1x_1&\delta_1x_2\\ \delta_2x_1&\delta_2x_2\\ \delta_3x_1&\delta_3x_2 \end{bmatrix}

این ماتریس دقیقاً شامل تمام مشتق‌های وزن‌هاست.

بنابراین:

∇Wℒ=𝜹𝐱⊤\begin{equation} \boxed{\nabla_W\mathcal{L}=\boldsymbol{\delta}\mathbf{x}^{\top}} \end{equation}

این رابطه یکی از مهم‌ترین نتایج Backpropagation در یک لایه است.

بررسی ابعاد ماتریس‌ها

𝜹∈ℝ3×1,𝐱⊤∈ℝ1×2\boldsymbol{\delta}\in\mathbb{R}^{3\times1},\qquad \mathbf{x}^{\top}\in\mathbb{R}^{1\times2}

بنابراین:

(3×1)(1×2)=(3×2),∇Wℒ∈ℝ3×2(3\times1)(1\times2)=(3\times2),\qquad \nabla_W\mathcal{L}\in\mathbb{R}^{3\times2}

این دقیقاً با ابعاد ماتریس وزن‌ها سازگار است.

شکل ۶-۳. ضرب بیرونی و تشکیل ماتریس گرادیان

۶.۷. گرادیان نسبت به ورودی لایه

تا اینجا گرادیان وزن‌ها و بایاس‌ها را محاسبه کردیم. اما اگر این لایه، لایه خروجی یک شبکه چندلایه نباشد، باید گرادیان زیان نسبت به ورودی‌های آن را نیز به دست آوریم.

این گرادیان برای آموزش لایه‌های پیشین ضروری است.

رابطه:

∇𝐱ℒ=W⊤𝜹\begin{equation} \boxed{\nabla_{\mathbf{x}}\mathcal{L}=W^{\top}\boldsymbol{\delta}} \end{equation}

برای مثال ما:

W⊤=[0.20.4−0.20.3−0.10.5]W^{\top}= \begin{bmatrix} 0.2&0.4&-0.2\\ 0.3&-0.1&0.5 \end{bmatrix}

بنابراین:

∇𝐱ℒ=[0.20.4−0.20.3−0.10.5][−0.0594000.143735−0.073579]\nabla_{\mathbf{x}}\mathcal{L}= \begin{bmatrix} 0.2&0.4&-0.2\\ 0.3&-0.1&0.5 \end{bmatrix} \begin{bmatrix} -0.059400\\ 0.143735\\ -0.073579 \end{bmatrix}

مؤلفه اول:

∂ℒ∂x1=0.2(−0.059400)+0.4(0.143735)−0.2(−0.073579)≈−0.011880+0.057494+0.014716∂ℒ∂x1≈0.060330\begin{aligned} \frac{\partial\mathcal{L}}{\partial x_1}&=0.2(-0.059400)+0.4(0.143735)-0.2(-0.073579)\\ &\approx-0.011880+0.057494+0.014716\\ \boxed{\frac{\partial\mathcal{L}}{\partial x_1}}&\boxed{\approx0.060330} \end{aligned}

مؤلفه دوم:

∂ℒ∂x2=0.3(−0.059400)−0.1(0.143735)+0.5(−0.073579)≈−0.017820−0.014374−0.036790∂ℒ∂x2≈−0.068984\begin{aligned} \frac{\partial\mathcal{L}}{\partial x_2}&=0.3(-0.059400)-0.1(0.143735)+0.5(-0.073579)\\ &\approx-0.017820-0.014374-0.036790\\ \boxed{\frac{\partial\mathcal{L}}{\partial x_2}}&\boxed{\approx-0.068984} \end{aligned}

پس:

∇𝐱ℒ≈[0.060330−0.068984]\boxed{\nabla_{\mathbf{x}}\mathcal{L}\approx \begin{bmatrix} 0.060330\\ -0.068984 \end{bmatrix}}

این گرادیان به ما می‌گوید اگر ورودی‌های لایه، خروجی یک لایه قبلی باشند، زیان نسبت به آن خروجی‌ها چه حساسیتی دارد.

در بخش هفتم، از همین رابطه برای انتقال گرادیان از لایه خروجی به لایه پنهان استفاده خواهیم کرد.

۶.۸. به‌روزرسانی هم‌زمان وزن‌ها و بایاس‌ها

برای انجام یک گام گرادیان کاهشی:

Wnew=W−η∇Wℒ𝐛new=𝐛−η∇𝐛ℒ\begin{aligned} W_{\mathrm{new}}&=W-\eta\nabla_W\mathcal{L}\\ \mathbf{b}_{\mathrm{new}}&=\mathbf{b}-\eta\nabla_{\mathbf{b}}\mathcal{L} \end{aligned}

نرخ یادگیری را برابر ۰٫۱ انتخاب می‌کنیم:

η=0.1\eta=0.1

در نتیجه، ماتریس وزن‌های جدید تقریباً برابر است با:

Wnew≈[0.2059400.3118800.385627−0.128747−0.1926420.514716]\boxed{W_{\mathrm{new}}\approx \begin{bmatrix} 0.205940&0.311880\\ 0.385627&-0.128747\\ -0.192642&0.514716 \end{bmatrix}}

و بایاس‌های جدید:

𝐛new≈[0.1059400.185627−0.092642]\boxed{\mathbf{b}_{\mathrm{new}}\approx \begin{bmatrix} 0.105940\\ 0.185627\\ -0.092642 \end{bmatrix}}

برای ارزیابی نتیجه، دوباره انتشار رو‌به‌جلو را با پارامترهای جدید انجام می‌دهیم.

به‌طور تقریبی:

𝐳new≈[0.9356400.3137600.744148],𝐚new≈[0.71820.57780.6779]\mathbf{z}_{\mathrm{new}}\approx \begin{bmatrix} 0.935640\\ 0.313760\\ 0.744148 \end{bmatrix}, \qquad \mathbf{a}_{\mathrm{new}}\approx \begin{bmatrix} 0.7182\\ 0.5778\\ 0.6779 \end{bmatrix}

زیان جدید تقریباً:

ℒnew≈0.2585\boxed{\mathcal{L}_{\mathrm{new}}\approx0.2585}

در مقایسه با زیان اولیه:

ℒold≈0.2760\mathcal{L}_{\mathrm{old}}\approx0.2760

بنابراین زیان در این گام کاهش یافته است.

۶.۹. پیاده‌سازی کامل یک لایه بدون Autograd

در این بخش، محاسبات را با NumPy پیاده‌سازی می‌کنیم.

NumPy تنها برای انجام عملیات برداری و ماتریسی استفاده می‌شود. تمام مشتق‌ها به‌صورت دستی محاسبه شده‌اند.

import numpy as np

def sigmoid(z):
    positive = z >= 0
    out = np.empty_like(z, dtype=float)
    out[positive] = 1 / (1 + np.exp(-z[positive]))
    ez = np.exp(z[~positive])
    out[~positive] = ez / (1 + ez)
    return out

# داده‌ها
x = np.array([[1.0], [2.0]])
y = np.array([[1.0], [0.0], [1.0]])

# پارامترهای اولیه
W = np.array([
    [0.2,  0.3],
    [0.4, -0.1],
    [-0.2, 0.5]
])

b = np.array([
    [0.1],
    [0.2],
    [-0.1]
])

learning_rate = 0.1

# ---------- Forward ----------
z = W @ x + b
a = sigmoid(z)

loss_before = 0.5 * np.sum((a - y) ** 2)

# ---------- Backward ----------
dL_da = a - y
da_dz = a * (1 - a)

delta = dL_da * da_dz

grad_W = delta @ x.T
grad_b = delta.copy()

# گرادیان نسبت به ورودی
grad_x = W.T @ delta

# ---------- Update ----------
W_new = W - learning_rate * grad_W
b_new = b - learning_rate * grad_b

# ---------- New Forward ----------
z_new = W_new @ x + b_new
a_new = sigmoid(z_new)

loss_after = 0.5 * np.sum((a_new - y) ** 2)

print("Initial z:n", z)
print("Initial a:n", a)
print("Initial loss:", loss_before)

print("Delta:n", delta)
print("Gradient W:n", grad_W)
print("Gradient b:n", grad_b)
print("Gradient x:n", grad_x)

print("Updated W:n", W_new)
print("Updated b:n", b_new)
print("New loss:", loss_after)

assert grad_W.shape == W.shape
assert grad_b.shape == b.shape
assert grad_x.shape == x.shape
assert loss_after < loss_before

۶.۹.۱. بررسی ابعاد در برنامه

متغیرابعادتوضیح
x(2×1)بردار ورودی
W(3×2)ماتریس وزن‌ها
b(3×1)بایاس‌ها
z(3×1)خروجی خطی نورون‌ها
a(3×1)خروجی فعال‌سازی
delta(3×1)سیگنال خطای محلی
grad_W(3×2)گرادیان وزن‌ها
grad_b(3×1)گرادیان بایاس‌ها
grad_x(2×1)گرادیان نسبت به ورودی

در پیاده‌سازی شبکه‌های عصبی، کنترل ابعاد یکی از مهم‌ترین ابزارهای جلوگیری از خطاهای محاسباتی است.

۶.۱۰. تعمیم به یک دسته از نمونه‌های آموزشی (Mini-batch)

تا اینجا فقط یک نمونه آموزشی داشتیم. در عمل، معمولاً چند نمونه را هم‌زمان پردازش می‌کنیم.

فرض کنید (B) نمونه آموزشی داریم و هر ستون ماتریس (X) یک نمونه باشد:

X∈ℝn×BX\in\mathbb{R}^{n\times B}

در این صورت:

Z=WX+𝐛𝟏B⊤A=φ(Z)\begin{equation} \begin{aligned} Z&=WX+\mathbf{b}\mathbf{1}_B^{\top} A&=\varphi(Z) \end{aligned} \end{equation}

که در آن 𝟏_B بردار ستونی شامل B عدد یک است.

اگر زیان کل، میانگین زیان نمونه‌ها باشد و ماتریس (Delta) شامل مشتق زیان هر نمونه نسبت به پیش‌فعال‌سازی‌های آن، پیش از اعمال ضریب میانگین‌گیری باشد، آنگاه:

∇Wℒ=1BΔX⊤,∇𝐛ℒ=1BΔ𝟏B\begin{align} \boxed{\nabla_W\mathcal{L}=\frac{1}{B}\Delta X^{\top}} , \boxed{\nabla_{\mathbf{b}}\mathcal{L}=\frac{1}{B}\Delta\mathbf{1}_B} \end{align}

عامل (1/B) به تعریف تابع زیان وابسته است. اگر مشتق‌های داخل (Delta) از ابتدا بر اساس زیان میانگین محاسبه شده باشند، نباید دوباره بر (B) تقسیم کنیم.

این تفاوت در پیاده‌سازی بسیار مهم است؛ زیرا اعمال دوباره ضریب میانگین، اندازه گرادیان را اشتباه تغییر می‌دهد.

مثال کوتاه: دو نمونه آموزشی

فرض کنید یک نورون خطی داریم:

y^=wx+b\hat{y}=wx+b

با:

w=1,b=0w=1,\qquad b=0

دو نمونه:

(x1,y1)=(1,2),(x2,y2)=(2,3)(x_1,y_1)=(1,2),\qquad (x_2,y_2)=(2,3)

پیش‌بینی‌ها:

y^1=1,y^2=2\hat{y}_1=1,\qquad \hat{y}_2=2

خطاها:

e1=−1,e2=−1e_1=-1,\qquad e_2=-1

زیان میانگین مربعی نصف‌شده:

ℒ=12B∑k=1B(y^k−yk)2\mathcal{L}=\frac{1}{2B}\sum_{k=1}^{B}(\hat{y}_k-y_k)^2

با (B=2):

ℒ=14(1+1)=0.5\mathcal{L}=\frac{1}{4}(1+1)=0.5

گرادیان وزن:

∂ℒ∂w=12[e1x1+e2x2]=12[(−1)(1)+(−1)(2)]∂ℒ∂w=−1.5\begin{aligned} \frac{\partial\mathcal{L}}{\partial w}&=\frac{1}{2}\left[e_1x_1+e_2x_2\right]\\ &=\frac{1}{2}\left[(-1)(1)+(-1)(2)\right]\\ \boxed{\frac{\partial\mathcal{L}}{\partial w}}&\boxed{=-1.5} \end{aligned}

گرادیان بایاس:

∂ℒ∂b=12(e1+e2)∂ℒ∂b=−1\begin{aligned} \frac{\partial\mathcal{L}}{\partial b}&=\frac{1}{2}(e_1+e_2)\\ \boxed{\frac{\partial\mathcal{L}}{\partial b}}&\boxed{=-1} \end{aligned}

این مثال نشان می‌دهد گرادیان یک دسته آموزشی، از تجمیع سهم نمونه‌ها به دست می‌آید.

۶.۱۱. خطاهای رایج در Backpropagation یک لایه

  • خطای اول: جابه‌جایی ترتیب ضرب ماتریسی. رابطه صحیح برای یک نمونه، δ𝐱^T است، نه 𝐱δ^T. این دو ضرب در حالت کلی ابعاد و معنای متفاوتی دارند.
  • خطای اول: جابه‌جایی ترتیب ضرب ماتریسی. رابطه صحیح برای یک نمونه، δ𝐱^T است، نه 𝐱δ^T. این دو ضرب در حالت کلی ابعاد و معنای متفاوتی دارند.
  • خطای سوم: فراموش‌کردن گرادیان بایاس. هر نورون علاوه بر وزن‌ها، بایاس قابل‌آموزش دارد. گرادیان بایاس در یک نمونه برابر سیگنال خطای محلی همان نورون است.
  • خطای چهارم: استفاده از وزن‌های جدید برای محاسبه گرادیان ورودی در همان گام. گرادیان ورودی باید با وزن‌های مربوط به همان انتشار رو‌به‌جلویی محاسبه شود که گرادیان‌ها از آن به دست آمده‌اند.
  • خطای پنجم: میانگین‌گیری ناهماهنگ از زیان و گرادیان‌ها. اگر تابع زیان میانگین نمونه‌ها باشد، ضریب میانگین‌گیری باید دقیقاً یک بار و به‌شکلی سازگار در مشتق‌ها لحاظ شود.

۶.۱۲. تمرین‌ها و مسائل بخش ششم

  • تمرین ۱ ـ محاسبه Forward یک لایه: لایه‌ای با دو ورودی و دو نورون در نظر بگیرید:
W=[0.50.2−0.30.4],𝐛=[0.10.2],𝐱=[21]W= \begin{bmatrix} 0.5&0.2\\ -0.3&0.4 \end{bmatrix}, \quad \mathbf{b}= \begin{bmatrix} 0.1\\ 0.2 \end{bmatrix}, \qquad \mathbf{x}= \begin{bmatrix} 2\\ 1 \end{bmatrix}

مقادیر z₁، z₂ و خروجی Sigmoid هر نورون را محاسبه کنید.

  • تمرین ۲ ـ محاسبه Backward: برای داده‌های تمرین اول، بردار هدف را 𝐲 = [۱، ۰]^T در نظر بگیرید. زیان کل، سیگنال‌های خطای محلی و گرادیان وزن‌ها و بایاس‌ها را محاسبه کنید.
  • تمرین ۳ ـ گرادیان ورودی: برای همان لایه، مقدار W^T δ را محاسبه و ابعاد آن را بررسی کنید.
  • تمرین ۴ ـ به‌روزرسانی: پارامترهای تمرین دوم را با نرخ یادگیری ۰٫۱ اصلاح کرده و زیان جدید را محاسبه کنید.
  • تمرین ۵ ـ پیاده‌سازی: کد NumPy این بخش را برای لایه‌ای با ۵ ورودی و ۴ نورون تعمیم دهید. داده‌ها و وزن‌ها را با مقادیر دلخواه کوچک مقداردهی کنید و ابعاد تمام گرادیان‌ها را بررسی کنید.
  • تمرین ۶ ـ Mini-batch: یک نورون خطی را با سه نمونه آموزشی و زیان میانگین مربعی نصف‌شده در نظر بگیرید. گرادیان وزن و بایاس را یک بار با جمع مشتق‌های نمونه‌ها و بار دیگر با ضرب ماتریسی محاسبه کنید.
  • تمرین ۷ ـ تحلیل نظری: توضیح دهید چرا رابطه ∇_𝐱 ℒ = W^T δ برای Backpropagation در شبکه‌های چندلایه ضروری است.

پاسخ تشریحی منتخب: تمرین ۱

برای نورون اول:

z1=0.5(2)+0.2(1)+0.1z1=1.3\begin{aligned} z_1&=0.5(2)+0.2(1)+0.1\\ \boxed{z_1}&\boxed{=1.3} \end{aligned}

برای نورون دوم:

z2=−0.3(2)+0.4(1)+0.2z2=0\begin{aligned} z_2&=-0.3(2)+0.4(1)+0.2\\ \boxed{z_2}&\boxed{=0} \end{aligned}

خروجی Sigmoid نورون اول:

a1=σ(1.3)≈0.785835a_1=\sigma(1.3)\approx0.785835

خروجی نورون دوم:

a2=σ(0)=0.5a_2=\sigma(0)=0.5

بنابراین:

𝐚=[0.7858350.5]\boxed{\mathbf{a}= \begin{bmatrix} 0.785835\\ 0.5 \end{bmatrix}}

منابع علمی بخش ششم

Bishop, C. M. (2006). Pattern recognition and machine learning. Springer.

Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep learning. MIT Press.

Hagan, M. T., Demuth, H. B., Beale, M. H., & De Jesús, O. (2014). Neural network design (2nd ed.). Martin Hagan.

Haykin, S. (2009). Neural networks and learning machines (3rd ed.). Pearson.

Rumelhart, D. E., Hinton, G. E., & Williams, R. J. (1986). Learning representations by back-propagating errors. Nature, 323, 533–536. https://doi.org/10.1038/323533a0

7. بازپس‌انتشار خطا در شبکه عصبی چندلایه (Multilayer Backpropagation)

در بخش ششم، محاسبه گرادیان‌های یک لایه کامل را آموختیم. اکنون به مهم‌ترین مرحله فصل می‌رسیم: آموزش شبکه‌ای که علاوه بر لایه خروجی، یک یا چند لایه پنهان دارد.

تفاوت اساسی این است که برای نورون‌های لایه خروجی، مقدار هدف را در اختیار داریم؛ اما برای نورون‌های لایه پنهان، مقدار هدف مستقلی تعریف نشده است. بنابراین نمی‌توان خطای هر نورون پنهان را مستقیماً از اختلاف خروجی آن با یک مقدار هدف محاسبه کرد.

راه‌حل Backpropagation این است که گرادیان زیان را از لایه خروجی به لایه‌های پنهان منتقل کنیم. به این ترتیب، هر وزن، حتی در نخستین لایه شبکه، می‌تواند بر اساس تأثیری که بر زیان نهایی دارد اصلاح شود.

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

۷.۱. معماری شبکه مورد مطالعه

برای جلوگیری از پیچیدگی غیرضروری، شبکه‌ای با معماری زیر در نظر می‌گیریم:

شکل ۷-۱. معماری شبکه ۲–۲–۱

دو ورودی به دو نورون لایه پنهان متصل می‌شوند و خروجی این دو نورون به نورون نهایی منتقل می‌شود. تمام اتصال‌ها دارای وزن مستقل هستند.

این شبکه دارای دو لایه قابل‌آموزش است:

  • لایه اول: دو ورودی و دو نورون پنهان؛
  • لایه دوم: دو ورودی از لایه پنهان و یک نورون خروجی.

در مجموع:

Nparameters=(2×2+2)+(1×2+1)=9N_{\mathrm{parameters}}=(2\times2+2)+(1\times2+1)=9

بنابراین شبکه دارای ۹ پارامتر قابل‌آموزش است: شش وزن و سه بایاس.

قرارداد نمادگذاری

برای جلوگیری از ابهام، از اندیس بالانویس برای شماره لایه استفاده می‌کنیم:

W(1),𝐛(1)W^{(1)},\mathbf{b}^{(1)}

برای وزن‌ها و بایاس‌های لایه پنهان، و:

W(2),b(2)W^{(2)},b^{(2)}

برای پارامترهای لایه خروجی.

همچنین:

𝐚(0)=𝐱\mathbf{a}^{(0)}=\mathbf{x}

یعنی خروجی لایه صفر همان ورودی شبکه است.

۷.۲. تعریف مسئله و مقادیر اولیه

فرض کنید شبکه قرار است یک امتیاز عملکرد نرمال‌شده را از دو ویژگی عددی پیش‌بینی کند.

داده آموزشی:

𝐱=[12],y=1\mathbf{x}= \begin{bmatrix} 1\\ 2 \end{bmatrix}, \qquad y=1

وزن‌های لایه پنهان:

W(1)=[0.20.30.4−0.1]W^{(1)}= \begin{bmatrix} 0.2&0.3\\ 0.4&-0.1 \end{bmatrix}

بایاس‌های لایه پنهان:

𝐛(1)=[0.10.2]\mathbf{b}^{(1)}= \begin{bmatrix} 0.1\\ 0.2 \end{bmatrix}

وزن‌های لایه خروجی:

W(2)=[0.5−0.4]W^{(2)}= \begin{bmatrix} 0.5&-0.4 \end{bmatrix}

بایاس خروجی:

b(2)=0.1b^{(2)}=0.1

تابع فعال‌سازی هر دو لایه Sigmoid است:

σ(z)=11+e−z\sigma(z)=\frac{1}{1+e^{-z}}

و تابع زیان:

ℒ=12(y^−y)2\begin{equation} \mathcal{L}=\frac{1}{2}(\hat{y}-y)^2 \end{equation}

این مقادیر ساختگی‌اند و صرفاً برای نمایش شفاف محاسبات انتخاب شده‌اند.

۷.۳. مرحله اول: انتشار رو‌به‌جلو در لایه پنهان

رابطه لایه اول:

𝐳(1)=W(1)𝐱+𝐛(1)\begin{equation} \mathbf{z}^{(1)}=W^{(1)}\mathbf{x}+\mathbf{b}^{(1)} \end{equation}

با جایگذاری:

𝐳(1)=[0.20.30.4−0.1][12]+[0.10.2]\mathbf{z}^{(1)}= \begin{bmatrix} 0.2&0.3\\ 0.4&-0.1 \end{bmatrix} \begin{bmatrix} 1\\ 2 \end{bmatrix} + \begin{bmatrix} 0.1\\ 0.2 \end{bmatrix}

محاسبه نورون پنهان اول

z1(1)=0.2(1)+0.3(2)+0.1z1(1)=0.9\begin{aligned} z_1^{(1)}&=0.2(1)+0.3(2)+0.1\\ \boxed{z_1^{(1)}}&\boxed{=0.9} \end{aligned}

خروجی فعال‌سازی:

a1(1)=σ(0.9)a1(1)≈0.710950\begin{aligned} a_1^{(1)}&=\sigma(0.9)\\ \boxed{a_1^{(1)}}&\boxed{\approx0.710950} \end{aligned}

محاسبه نورون پنهان دوم

z2(1)=0.4(1)−0.1(2)+0.2z2(1)=0.4\begin{aligned} z_2^{(1)}&=0.4(1)-0.1(2)+0.2\\ \boxed{z_2^{(1)}}&\boxed{=0.4} \end{aligned}

خروجی فعال‌سازی:

a2(1)=σ(0.4)a2(1)≈0.598688\begin{aligned} a_2^{(1)}&=\sigma(0.4)\\ \boxed{a_2^{(1)}}&\boxed{\approx0.598688} \end{aligned}

بنابراین:

𝐚(1)=[0.7109500.598688]\begin{equation} \boxed{\mathbf{a}^{(1)}= \begin{bmatrix} 0.710950\\ 0.598688 \end{bmatrix}} \end{equation}

این بردار، ورودی لایه خروجی خواهد بود.

۷.۴. مرحله دوم: انتشار رو‌به‌جلو در لایه خروجی

رابطه لایه دوم:

z(2)=W(2)𝐚(1)+b(2)\begin{equation} z^{(2)}=W^{(2)}\mathbf{a}^{(1)}+b^{(2)} \end{equation}

با جایگذاری:

z(2)=0.5(0.710950)−0.4(0.598688)+0.1z^{(2)}=0.5(0.710950)-0.4(0.598688)+0.1

محاسبات میانی:

0.5(0.710950)=0.355475,−0.4(0.598688)=−0.2394750.5(0.710950)=0.355475,\qquad -0.4(0.598688)=-0.239475

پس:

z(2)≈0.215999\boxed{z^{(2)}\approx0.215999}

اکنون خروجی نهایی:

y^=σ(z(2))y^≈0.553791\begin{aligned} \hat{y}&=\sigma(z^{(2)})\\ \boxed{\hat{y}}&\boxed{\approx0.553791} \end{aligned}

زیان:

ℒ=12(0.553791−1)2ℒ≈0.09955\begin{aligned} \mathcal{L}&=\frac{1}{2}(0.553791-1)^2\\ \boxed{\mathcal{L}}&\boxed{\approx0.09955} \end{aligned}

در این مرحله انتشار رو‌به‌جلو پایان یافته است.

۷.۵. مرحله سوم: بازپس‌انتشار خطا از لایه خروجی

اکنون می‌خواهیم گرادیان زیان نسبت به پارامترهای لایه دوم را محاسبه کنیم.

ابتدا سیگنال خطای خروجی:

δ(2)=∂ℒ∂z(2)\begin{equation} \delta^{(2)}=\frac{\partial\mathcal{L}}{\partial z^{(2)}} \end{equation}

طبق قاعده زنجیره‌ای:

δ(2)=∂ℒ∂y^∂y^∂z(2)\delta^{(2)}=\frac{\partial\mathcal{L}}{\partial\hat{y}}\frac{\partial\hat{y}}{\partial z^{(2)}}

از آنجا که:

∂ℒ∂y^=y^−y\frac{\partial\mathcal{L}}{\partial\hat{y}}=\hat{y}-y

و:

∂y^∂z(2)=y^(1−y^)\frac{\partial\hat{y}}{\partial z^{(2)}}=\hat{y}(1-\hat{y})

داریم:

δ(2)=(y^−y)y^(1−y^)\begin{equation} \boxed{\delta^{(2)}=(\hat{y}-y)\hat{y}(1-\hat{y})} \end{equation}

با جایگذاری:

δ(2)≈(0.553791−1)(0.553791)(1−0.553791)δ(2)≈−0.11027\begin{aligned} \delta^{(2)}&\approx(0.553791-1)(0.553791)(1-0.553791)\\ \boxed{\delta^{(2)}}&\boxed{\approx-0.11027} \end{aligned}

۷.۵.۱. گرادیان وزن اول لایه خروجی

∂ℒ∂w11(2)=δ(2)a1(1)≈(−0.11027)(0.710950)∂ℒ∂w11(2)≈−0.07840\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_{11}^{(2)}}&=\delta^{(2)}a_1^{(1)}\\ &\approx(-0.11027)(0.710950)\\ \boxed{\frac{\partial\mathcal{L}}{\partial w_{11}^{(2)}}}&\boxed{\approx-0.07840} \end{aligned}

۷.۵.۲. گرادیان وزن دوم لایه خروجی

∂ℒ∂w12(2)=δ(2)a2(1)≈(−0.11027)(0.598688)∂ℒ∂w12(2)≈−0.06602\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_{12}^{(2)}}&=\delta^{(2)}a_2^{(1)}\\ &\approx(-0.11027)(0.598688)\\ \boxed{\frac{\partial\mathcal{L}}{\partial w_{12}^{(2)}}}&\boxed{\approx-0.06602} \end{aligned}

۷.۵.۳. گرادیان بایاس خروجی

∂ℒ∂b(2)=δ(2)≈−0.11027\boxed{\frac{\partial\mathcal{L}}{\partial b^{(2)}}=\delta^{(2)}\approx-0.11027}

بنابراین:

∇W(2)ℒ≈[−0.07840−0.06602]\begin{equation} \boxed{\nabla_{W^{(2)}}\mathcal{L}\approx \begin{bmatrix} -0.07840&-0.06602 \end{bmatrix}} \end{equation}

تا اینجا محاسبات مشابه Backpropagation یک لایه بوده‌اند. تفاوت اصلی از مرحله بعد آغاز می‌شود.

۷.۶. مرحله چهارم: انتقال گرادیان به لایه پنهان

نورون‌های پنهان مقدار هدف مستقلی ندارند. پس چگونه می‌توان گرادیان وزن‌های آن‌ها را محاسبه کرد؟

پاسخ این است که خروجی هر نورون پنهان بر مقدار (z^{(2)}) اثر می‌گذارد و (z^{(2)}) نیز بر زیان تأثیر دارد.

بنابراین:

∂ℒ∂a1(1)=∂ℒ∂z(2)∂z(2)∂a1(1)\frac{\partial\mathcal{L}}{\partial a_1^{(1)}}=\frac{\partial\mathcal{L}}{\partial z^{(2)}}\frac{\partial z^{(2)}}{\partial a_1^{(1)}}

چون:

z(2)=w11(2)a1(1)+w12(2)a2(1)+b(2)z^{(2)}=w_{11}^{(2)}a_1^{(1)}+w_{12}^{(2)}a_2^{(1)}+b^{(2)}

داریم:

∂z(2)∂a1(1)=w11(2)=0.5\frac{\partial z^{(2)}}{\partial a_1^{(1)}}=w_{11}^{(2)}=0.5

پس:

∂ℒ∂a1(1)=(−0.11027)(0.5)∂ℒ∂a1(1)≈−0.05514\begin{aligned} \frac{\partial\mathcal{L}}{\partial a_1^{(1)}}&=(-0.11027)(0.5)\\ \boxed{\frac{\partial\mathcal{L}}{\partial a_1^{(1)}}}&\boxed{\approx-0.05514} \end{aligned}

برای نورون دوم:

∂ℒ∂a2(1)=δ(2)w12(2)=(−0.11027)(−0.4)∂ℒ∂a2(1)≈0.04411\begin{aligned} \frac{\partial\mathcal{L}}{\partial a_2^{(1)}}&=\delta^{(2)}w_{12}^{(2)}\\ &=(-0.11027)(-0.4)\\ \boxed{\frac{\partial\mathcal{L}}{\partial a_2^{(1)}}}&\boxed{\approx0.04411} \end{aligned}

این دو مقدار نشان می‌دهند زیان نسبت به خروجی هر نورون پنهان چه حساسیتی دارد.

اما برای محاسبه گرادیان وزن‌های لایه پنهان، باید مشتق تابع فعال‌سازی را نیز لحاظ کنیم.

۷.۶.۱. سیگنال خطای نورون پنهان اول

δ1(1)=∂ℒ∂z1(1)=∂ℒ∂a1(1)∂a1(1)∂z1(1)\begin{aligned} \delta_1^{(1)}&=\frac{\partial\mathcal{L}}{\partial z_1^{(1)}}\\ &=\frac{\partial\mathcal{L}}{\partial a_1^{(1)}}\frac{\partial a_1^{(1)}}{\partial z_1^{(1)}} \end{aligned}

مشتق Sigmoid:

σ′(0.9)=0.710950(1−0.710950)≈0.205500\sigma'(0.9)=0.710950(1-0.710950)\approx0.205500

پس:

δ1(1)≈(−0.05514)(0.205500)δ1(1)≈−0.01133\begin{aligned} \delta_1^{(1)}&\approx(-0.05514)(0.205500)\\ \boxed{\delta_1^{(1)}}&\boxed{\approx-0.01133} \end{aligned}

۷.۶.۲. سیگنال خطای نورون پنهان دوم

مشتق Sigmoid نورون دوم:

σ′(0.4)=0.598688(1−0.598688)≈0.240261\begin{aligned} \sigma'(0.4)&=0.598688(1-0.598688)\\ &\approx0.240261 \end{aligned}

پس:

δ2(1)≈(0.04411)(0.240261)δ2(1)≈0.01060\begin{aligned} \delta_2^{(1)}&\approx(0.04411)(0.240261)\\ \boxed{\delta_2^{(1)}}&\boxed{\approx0.01060} \end{aligned}

در نتیجه:

𝜹(1)≈[−0.011330.01060]\begin{equation} \boxed{\boldsymbol{\delta}^{(1)}\approx \begin{bmatrix} -0.01133\\ 0.01060 \end{bmatrix}} \end{equation}

رابطه کلیدی Backpropagation در لایه پنهان

تمام محاسبات بالا را می‌توان در یک رابطه ماتریسی خلاصه کرد:

𝜹(1)=(W(2)⊤𝜹(2))⊙σ′(𝐳(1))\begin{equation} \boxed{\boldsymbol{\delta}^{(1)}=\left(W^{(2)\top}\boldsymbol{\delta}^{(2)}\right)\odot\sigma'(\mathbf{z}^{(1)})} \end{equation}

که در آن ⊙ ضرب مؤلفه‌به‌مؤلفه است.

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

1. گرادیانی که از لایه بعد به عقب منتقل می‌شود؛

2. مشتق تابع فعال‌سازی لایه فعلی.

نکته بسیار مهم: در این رابطه از وزن‌های لایه دوم در زمان محاسبه Forward استفاده می‌کنیم، نه وزن‌هایی که بعداً به‌روزرسانی خواهند شد.

۷.۷. مرحله پنجم: محاسبه گرادیان وزن‌ها و بایاس‌های لایه پنهان

اکنون سیگنال خطای هر دو نورون پنهان را داریم:

𝜹(1)≈[−0.011329350.01059657]\boldsymbol{\delta}^{(1)}\approx \begin{bmatrix} -0.01132935\\ 0.01059657 \end{bmatrix}

با استفاده از رابطه بخش ششم:

∇W(1)ℒ=𝜹(1)𝐱⊤\begin{equation} \boxed{\nabla_{W^{(1)}}\mathcal{L}=\boldsymbol{\delta}^{(1)}\mathbf{x}^{\top}} \end{equation}

چون:

𝐱⊤=[12]\mathbf{x}^{\top}= \begin{bmatrix} 1&2 \end{bmatrix}

حاصل‌ضرب بیرونی برابر است با:

∇W(1)ℒ=[−0.011329350.01059657][12]\nabla_{W^{(1)}}\mathcal{L}= \begin{bmatrix} -0.01132935\\ 0.01059657 \end{bmatrix} \begin{bmatrix} 1&2 \end{bmatrix}

بنابراین:

∇W(1)ℒ≈[−0.01132935−0.022658700.010596570.02119315]\begin{equation} \boxed{\nabla_{W^{(1)}}\mathcal{L}\approx \begin{bmatrix} -0.01132935&-0.02265870\\ 0.01059657&0.02119315 \end{bmatrix}} \end{equation}

و گرادیان بایاس‌ها:

∇𝐛(1)ℒ=[−0.011329350.01059657]\begin{equation} \boxed{\nabla_{\mathbf{b}^{(1)}}\mathcal{L}= \begin{bmatrix} -0.01132935\\ 0.01059657 \end{bmatrix}} \end{equation}

محاسبه دستی تک‌تک وزن‌ها

برای نورون پنهان اول:

∂ℒ∂w11(1)=δ1(1)x1=(−0.01132935)(1)=−0.01132935∂ℒ∂w12(1)=δ1(1)x2=(−0.01132935)(2)=−0.02265870\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_{11}^{(1)}}&=\delta_1^{(1)}x_1=(-0.01132935)(1)=-0.01132935\\ \frac{\partial\mathcal{L}}{\partial w_{12}^{(1)}}&=\delta_1^{(1)}x_2=(-0.01132935)(2)=-0.02265870 \end{aligned}

برای نورون پنهان دوم:

∂ℒ∂w21(1)=(0.01059657)(1)=0.01059657∂ℒ∂w22(1)=(0.01059657)(2)=0.02119315\begin{aligned} \frac{\partial\mathcal{L}}{\partial w_{21}^{(1)}}&=(0.01059657)(1)=0.01059657\\ \frac{\partial\mathcal{L}}{\partial w_{22}^{(1)}}&=(0.01059657)(2)=0.02119315 \end{aligned}

اکنون گرادیان تمام ۹ پارامتر شبکه مشخص شده است.

شکل ۷-۳. جریان گرادیان از خروجی تا وزن‌های لایه پنهان

سیگنال خطای هر نورون پنهان از گرادیان دریافتی از لایه بعد و مشتق فعال‌سازی همان نورون ساخته می‌شود. سپس با ضرب آن در ورودی‌های لایه، گرادیان وزن‌ها محاسبه می‌شود.

۷.۸. مرحله ششم: به‌روزرسانی هم‌زمان تمام پارامترها

اکنون می‌توانیم تمام پارامترهای شبکه را اصلاح کنیم.

نرخ یادگیری:

η=0.1\eta=0.1

قانون گرادیان کاهشی برای هر لایه:

Wnew(l)=W(l)−η∇W(l)ℒ𝐛new(l)=𝐛(l)−η∇𝐛(l)ℒ\begin{align} W_{\mathrm{new}}^{(l)}&=W^{(l)}-\eta\nabla_{W^{(l)}}\mathcal{L} \\ \mathbf{b}_{\mathrm{new}}^{(l)}&=\mathbf{b}^{(l)}-\eta\nabla_{\mathbf{b}^{(l)}}\mathcal{L} \end{align}

۷.۸.۱. به‌روزرسانی لایه پنهان

وزن‌های اولیه:

W(1)=[0.20.30.4−0.1]W^{(1)}= \begin{bmatrix} 0.2&0.3\\ 0.4&-0.1 \end{bmatrix}

ماتریس گرادیان:

∇W(1)ℒ≈[−0.01132935−0.022658700.010596570.02119315]\nabla_{W^{(1)}}\mathcal{L}\approx \begin{bmatrix} -0.01132935&-0.02265870\\ 0.01059657&0.02119315 \end{bmatrix}

بنابراین:

Wnew(1)=[0.20.30.4−0.1]−0.1[−0.01132935−0.022658700.010596570.02119315]W_{\mathrm{new}}^{(1)}= \begin{bmatrix} 0.2&0.3\\ 0.4&-0.1 \end{bmatrix} -0.1 \begin{bmatrix} -0.01132935&-0.02265870\\ 0.01059657&0.02119315 \end{bmatrix}

نتیجه:

Wnew(1)≈[0.201132940.302265870.39894034−0.10211931]\begin{equation} \boxed{W_{\mathrm{new}}^{(1)}\approx \begin{bmatrix} 0.20113294&0.30226587\\ 0.39894034&-0.10211931 \end{bmatrix}} \end{equation}

بایاس‌های جدید:

𝐛new(1)≈[0.101132940.19894034]\begin{equation} \boxed{\mathbf{b}_{\mathrm{new}}^{(1)}\approx \begin{bmatrix} 0.10113294\\ 0.19894034 \end{bmatrix}} \end{equation}

۷.۸.۲. به‌روزرسانی لایه خروجی

گرادیان وزن‌های خروجی با استفاده از مقادیر دقیق‌تر برابر است با:

∇W(2)ℒ≈[−0.07839013−0.06601200]\nabla_{W^{(2)}}\mathcal{L}\approx \begin{bmatrix} -0.07839013&-0.06601200 \end{bmatrix}

پس:

Wnew(2)=[0.5−0.4]−0.1[−0.07839013−0.06601200]W_{\mathrm{new}}^{(2)}= \begin{bmatrix} 0.5&-0.4 \end{bmatrix} -0.1 \begin{bmatrix} -0.07839013&-0.06601200 \end{bmatrix}

بنابراین:

Wnew(2)≈[0.50783901−0.39339880]\begin{equation} \boxed{W_{\mathrm{new}}^{(2)}\approx \begin{bmatrix} 0.50783901&-0.39339880 \end{bmatrix}} \end{equation}

و:

bnew(2)=0.1−0.1(−0.11026117)bnew(2)≈0.11102612\begin{equation} \begin{aligned} b_{\mathrm{new}}^{(2)}&=0.1-0.1(-0.11026117)\\ \boxed{b_{\mathrm{new}}^{(2)}}&\boxed{\approx0.11102612} \end{aligned} \end{equation}

نکته اجرایی: ابتدا گرادیان همه لایه‌ها با پارامترهای اولیه محاسبه می‌شود؛ سپس به‌روزرسانی تمام پارامترها انجام می‌گیرد. تغییر وزن‌های خروجی پیش از محاسبه سیگنال خطای لایه پنهان، نتیجه یک گام استاندارد Backpropagation را تغییر می‌دهد.

۷.۹. مرحله هفتم: محاسبه مجدد خروجی و زیان

پس از اصلاح پارامترها، باید یک انتشار رو‌به‌جلوی جدید انجام دهیم.

برای لایه پنهان:

𝐳new(1)=Wnew(1)𝐱+𝐛new(1)\mathbf{z}_{\mathrm{new}}^{(1)}=W_{\mathrm{new}}^{(1)}\mathbf{x}+\mathbf{b}_{\mathrm{new}}^{(1)}

برای نورون اول:

z1,new(1)=0.20113294(1)+0.30226587(2)+0.10113294≈0.90679762\begin{aligned} z_{1,\mathrm{new}}^{(1)}&=0.20113294(1)+0.30226587(2)+0.10113294\\ &\approx0.90679762 \end{aligned}

برای نورون دوم:

z2,new(1)=0.39894034(1)−0.10211931(2)+0.19894034≈0.39364206\begin{aligned} z_{2,\mathrm{new}}^{(1)}&=0.39894034(1)-0.10211931(2)+0.19894034\\ &\approx0.39364206 \end{aligned}

پس از اعمال Sigmoid:

𝐚new(1)≈[0.7123440.597159]\boxed{\mathbf{a}_{\mathrm{new}}^{(1)}\approx \begin{bmatrix} 0.712344\\ 0.597159 \end{bmatrix}}

اکنون لایه خروجی:

znew(2)=0.50783901(0.712344)−0.39339880(0.597159)+0.11102612z_{\mathrm{new}}^{(2)}=0.50783901(0.712344)-0.39339880(0.597159)+0.11102612

و خروجی نهایی:

y^new≈0.559186\boxed{\hat{y}_{\mathrm{new}}\approx0.559186}

زیان جدید:

ℒnew=12(0.559186−1)2ℒnew≈0.097158\begin{equation} \begin{aligned} \mathcal{L}_{\mathrm{new}}&=\frac{1}{2}(0.559186-1)^2\\ \boxed{\mathcal{L}_{\mathrm{new}}}&\boxed{\approx0.097158} \end{aligned} \end{equation}

مقایسه:

زیان پیش از به‌روزرسانی

0.099551

پیش‌بینی: 0.553791

زیان پس از به‌روزرسانی

0.097158

پیش‌بینی: 0.559186

کاهش زیان:

Δℒ≈0.097158−0.099551=−0.002393\Delta\mathcal{L}\approx0.097158-0.099551=-0.002393

درصد کاهش:

0.099551−0.0971580.099551×100≈2.40%\frac{0.099551-0.097158}{0.099551}\times100\approx2.40\%

شکل ۷-۴. زیان شبکه چندلایه قبل و بعد از یک گام یادگیری

زیان نمونه آموزشی با نرخ یادگیری ۰٫۱ کاهش یافته است.

این کاهش نشان می‌دهد که در این مثال، اصلاح هم‌زمان وزن‌های لایه پنهان و خروجی در جهت کاهش زیان عمل کرده است. البته کاهش زیان یک نمونه، تضمین‌کننده عملکرد مناسب روی نمونه‌های دیده‌نشده نیست.

۷.۱۰. استخراج فرمول عمومی Backpropagation برای شبکه‌ای با (L) لایه

اکنون که محاسبات یک شبکه دو‌لایه را انجام دادیم، می‌توانیم الگوریتم را به شبکه‌ای با هر تعداد لایه قابل‌آموزش تعمیم دهیم.

فرض کنید شبکه دارای (L) لایه باشد. برای هر لایه (l):

𝐳(l)=W(l)𝐚(l−1)+𝐛(l)𝐚(l)=φ(l)(𝐳(l)) 𝐚(0)=𝐱\begin{align} \boxed{\mathbf{z}^{(l)}=W^{(l)}\mathbf{a}^{(l-1)}+\mathbf{b}^{(l)}} \\ \boxed{\mathbf{a}^{(l)}=\varphi^{(l)}(\mathbf{z}^{(l)})} \ \end{align} \mathbf{a}^{(0)}=\mathbf{x}

۷.۱۰.۱. سیگنال خطای لایه خروجی

در لایه خروجی:

𝜹(L)=∇𝐚(L)ℒ⊙φ(L)′(𝐳(L))\begin{equation} \boxed{\boldsymbol{\delta}^{(L)}=\nabla_{\mathbf{a}^{(L)}}\mathcal{L}\odot\varphi^{(L)\prime}(\mathbf{z}^{(L)})} \end{equation}

این رابطه برای تابع فعال‌سازی مؤلفه‌ای معتبر است. اگر فعال‌سازی خروجی مانند Softmax وابستگی بین مؤلفه‌ها ایجاد کند، باید از ژاکوبین مناسب یا مشتق ساده‌شده ترکیب فعال‌سازی و زیان استفاده شود.

۷.۱۰.۲. سیگنال خطای لایه‌های پنهان

برای هر لایه پنهان:

𝜹(l)=(W(l+1)⊤𝜹(l+1))⊙φ(l)′(𝐳(l))\begin{equation} \boxed{\boldsymbol{\delta}^{(l)}=\left(W^{(l+1)\top}\boldsymbol{\delta}^{(l+1)}\right)\odot\varphi^{(l)\prime}(\mathbf{z}^{(l)})} \end{equation}

که برای:

l=L−1,L−2,…,1l=L-1,L-2,\ldots,1

به‌ترتیب محاسبه می‌شود.

۷.۱۰.۳. گرادیان وزن‌ها و بایاس‌ها

برای هر لایه:

∇W(l)ℒ=𝜹(l)𝐚(l−1)⊤∇𝐛(l)ℒ=𝜹(l) \begin{align} \boxed{\nabla_{W^{(l)}}\mathcal{L}=\boldsymbol{\delta}^{(l)}\mathbf{a}^{(l-1)\top}} \\ \boxed{\nabla_{\mathbf{b}^{(l)}}\mathcal{L}=\boldsymbol{\delta}^{(l)}} \ \end{align}

این چهار رابطه، هسته ریاضی Backpropagation در شبکه‌های تمام‌متصل با یک نمونه آموزشی را تشکیل می‌دهند.

بررسی ابعاد

اگر لایه (l) دارای (n_l) نورون و لایه قبلی دارای (n_{l-1}) نورون باشد:

متغیرابعاد
W^(l)nₗ × nₗ₋₁
𝐚^(l−1)nₗ₋₁ × ۱
𝐳^(l)nₗ × ۱
δ^(l)nₗ × ۱
∇_{W^(l)} ℒnₗ × nₗ₋₁
∇_{𝐛^(l)} ℒnₗ × ۱

اگر ابعاد محاسبه‌شده با این جدول سازگار نباشند، احتمالاً ترتیب ضرب ماتریسی یا نحوه تعریف بردارها اشتباه است.

۷.۱۱. الگوریتم عمومی در قالب شبه‌کد

الگوریتم ۷-۱. Backpropagation یک نمونه در شبکه تمام‌متصل

Input:
    x, y
    W[1...L], b[1...L]
    learning_rate

Forward:
    a[0] = x

    for l = 1 to L:
        z[l] = W[l] @ a[l-1] + b[l]
        a[l] = activation[l](z[l])

    loss = Loss(a[L], y)

Backward:
    delta[L] =
        grad_output_loss(a[L], y)
        * activation_derivative[L](z[L])

    for l = L-1 down to 1:
        delta[l] =
            (W[l+1].T @ delta[l+1])
            * activation_derivative[l](z[l])

    for l = 1 to L:
        grad_W[l] = delta[l] @ a[l-1].T
        grad_b[l] = delta[l]

Update:
    for l = 1 to L:
        W[l] = W[l] - learning_rate * grad_W[l]
        b[l] = b[l] - learning_rate * grad_b[l]

Output:
    updated W, updated b, loss
  

نماد * در سطرهای مشتق فعال‌سازی به معنی ضرب مؤلفه‌ای است. به‌روزرسانی فقط پس از محاسبه تمام گرادیان‌ها انجام می‌شود.

۷.۱۲. پیاده‌سازی شبکه ۲–۲–۱ بدون Autograd

در این برنامه، تمام مشتق‌ها به‌صورت دستی نوشته شده‌اند. NumPy فقط عملیات ماتریسی را انجام می‌دهد.

import numpy as np

def sigmoid(z):
    out = np.empty_like(z, dtype=float)
    positive = z >= 0

    out[positive] = 1 / (1 + np.exp(-z[positive]))

    exp_z = np.exp(z[~positive])
    out[~positive] = exp_z / (1 + exp_z)

    return out


def forward(x, y, W1, b1, W2, b2):
    z1 = W1 @ x + b1
    a1 = sigmoid(z1)

    z2 = W2 @ a1 + b2
    a2 = sigmoid(z2)

    loss = 0.5 * np.sum((a2 - y) ** 2)

    cache = {
        "z1": z1,
        "a1": a1,
        "z2": z2,
        "a2": a2,
    }

    return loss, cache


# داده آموزشی
x = np.array([[1.0], [2.0]])
y = np.array([[1.0]])

# لایه پنهان
W1 = np.array([
    [0.2,  0.3],
    [0.4, -0.1]
])

b1 = np.array([
    [0.1],
    [0.2]
])

# لایه خروجی
W2 = np.array([[0.5, -0.4]])
b2 = np.array([[0.1]])

learning_rate = 0.1

# 1. Forward
loss_before, cache = forward(
    x, y, W1, b1, W2, b2
)

a1 = cache["a1"]
a2 = cache["a2"]

# 2. Backward: لایه خروجی
delta2 = (a2 - y) * a2 * (1 - a2)

grad_W2 = delta2 @ a1.T
grad_b2 = delta2.copy()

# 3. Backward: لایه پنهان
delta1 = (W2.T @ delta2) * a1 * (1 - a1)

grad_W1 = delta1 @ x.T
grad_b1 = delta1.copy()

# 4. Update: هم‌زمان پس از محاسبه گرادیان‌ها
W1_new = W1 - learning_rate * grad_W1
b1_new = b1 - learning_rate * grad_b1

W2_new = W2 - learning_rate * grad_W2
b2_new = b2 - learning_rate * grad_b2

# 5. Forward جدید
loss_after, new_cache = forward(
    x, y,
    W1_new, b1_new,
    W2_new, b2_new
)

print("Initial loss:", loss_before)
print("Initial prediction:", a2.item())

print("nDelta output:n", delta2)
print("Delta hidden:n", delta1)

print("nGradient W1:n", grad_W1)
print("Gradient b1:n", grad_b1)

print("nGradient W2:n", grad_W2)
print("Gradient b2:n", grad_b2)

print("nUpdated W1:n", W1_new)
print("Updated W2:n", W2_new)

print("nNew prediction:",
      new_cache["a2"].item())
print("New loss:", loss_after)

# بررسی ابعاد
assert grad_W1.shape == W1.shape
assert grad_b1.shape == b1.shape
assert grad_W2.shape == W2.shape
assert grad_b2.shape == b2.shape

# بررسی کاهش زیان برای این مثال مشخص
assert loss_after < loss_before

خروجی مورد انتظار

کمیتمقدار تقریبی
پیش‌بینی اولیه۰٫۵۵۳۷۹۱
زیان اولیه۰٫۰۹۹۵۵۱
سیگنال خطای خروجی−۰٫۱۱۰۲۶۱
سیگنال خطای نورون پنهان اول−۰٫۰۱۱۳۲۹
سیگنال خطای نورون پنهان دوم۰٫۰۱۰۵۹۷
پیش‌بینی جدید۰٫۵۵۹۱۸۶
زیان جدید۰٫۰۹۷۱۵۸

این برنامه یک گام کامل یادگیری را انجام می‌دهد. در بخش هشتم، ساختار برنامه را به یک پیاده‌سازی عمومی‌تر با تعداد لایه‌های قابل‌تنظیم، دسته‌های آموزشی و حلقه آموزش توسعه خواهیم داد.

۷.۱۳. چرا Backpropagation از محاسبه مستقل همه مشتق‌ها کارآمدتر است؟

فرض کنید شبکه‌ای دارای هزاران پارامتر باشد. اگر برای هر پارامتر، مشتق تابع زیان را از ابتدا و مستقل از پارامترهای دیگر محاسبه کنیم، بخش بزرگی از عملیات مشترک بارها تکرار خواهد شد.

Backpropagation از این تکرار جلوگیری می‌کند.

برای مثال، در شبکه آموزشی ما، مقدار:

δ(2)=∂ℒ∂z(2)\delta^{(2)}=\frac{\partial\mathcal{L}}{\partial z^{(2)}}

فقط یک بار محاسبه شد؛ اما در محاسبه گرادیان وزن‌های خروجی و هر دو نورون پنهان مورد استفاده قرار گرفت.

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

این استفاده مجدد از نتایج میانی، یکی از دلایل اصلی کارایی روش مشتق‌گیری خودکار در حالت معکوس (Reverse-mode Automatic Differentiation) است.

برای شبکه‌های تمام‌متصل متعارف، هزینه محاسباتی یک گذر Backward معمولاً از همان مرتبه هزینه گذر Forward است، نه متناسب با تعداد پارامترها ضرب‌در هزینه یک Forward مستقل. مقدار دقیق این هزینه به معماری و عملیات شبکه بستگی دارد (Griewank & Walther, 2008).

۷.۱۴. مثال مفهومی: چرا یک نورون پنهان با خروجی ظاهراً مناسب هم ممکن است اصلاح شود؟

فرض کنید نورون پنهان اول خروجی زیر را تولید کرده است:

a1(1)=0.710950a_1^{(1)}=0.710950

ممکن است تصور کنیم این مقدار به هدف نهایی یک نزدیک است و بنابراین نباید تغییر زیادی کند.

اما این برداشت نادرست است.

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

در مثال ما، وزن اتصال نورون پنهان اول به خروجی مثبت است:

w11(2)=0.5w_{11}^{(2)}=0.5

بنابراین افزایش خروجی این نورون، با ثابت‌ماندن سایر متغیرها، مقدار پیش‌فعال‌سازی خروجی را افزایش می‌دهد.

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

ترکیب این دو عامل باعث شد:

δ1(1)<0\delta_1^{(1)}<0

اما برای نورون پنهان دوم، وزن اتصال به خروجی منفی است:

w12(2)=−0.4w_{12}^{(2)}=-0.4

در نتیجه سیگنال خطای آن مثبت شد:

δ2(1)>0\delta_2^{(1)}>0

پس دو نورون پنهان ممکن است با وجود دریافت یک نمونه مشترک، در جهت‌های متفاوتی اصلاح شوند.

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

۷.۱۵. خطاهای رایج در Backpropagation شبکه چندلایه

خطاپیامدراه‌حل
استفاده از W^(l+1) به‌جای ترانهاده آنناسازگاری ابعاد یا گرادیان نادرستکنترل ابعاد ماتریسی
فراموش‌کردن مشتق فعال‌سازی لایه پنهانانتقال نادرست گرادیانضرب مؤلفه‌ای در φ′(z)
به‌روزرسانی زودهنگام وزن‌هااستفاده از پارامترهای ناسازگار در یک گاممحاسبه همه گرادیان‌ها قبل از Update
اشتباه‌گرفتن δ^(l) با خطای پیش‌بینیمشتق نادرست وزن‌های پنهانتعریف δ^(l) = ∂ℒ/∂𝐳^(l)
استفاده از خروجی لایه اشتباه در ضرب بیرونیگرادیان با ابعاد یا مقدار نادرستاستفاده از 𝐚^(l−1)
گردکردن زودهنگام محاسباتاختلاف عددی و تجمع خطانگهداری دقت کامل تا مرحله گزارش

۷.۱۶. تمرین‌های کاربردی و پژوهشی

  • تمرین ۱. بازسازی دستی مثال اصلی:تمام مراحل Forward و Backward شبکه ۲–۲–۱ این بخش را بدون استفاده از کد محاسبه کنید. نتایج را با جدول‌های بخش‌های ۷.۷ تا ۷.۹ مقایسه کنید.
  • تمرین ۲. تغییر تابع فعال‌سازی لایه پنهان:در همان شبکه، تابع فعال‌سازی لایه پنهان را از Sigmoid به Tanh تغییر دهید، اما تابع خروجی را Sigmoid نگه دارید.خروجی، زیان، سیگنال‌های خطا و گرادیان‌ها را مجدداً محاسبه کنید.
  • تمرین ۳. بررسی نقش وزن‌های خروجی:وزن دوم لایه خروجی را از (-0.4) به (+0.4) تغییر دهید.بررسی کنید سیگنال خطای نورون پنهان دوم چه تغییری می‌کند و آیا علامت آن عوض می‌شود یا خیر.
  • تمرین ۴. توسعه شبکه به معماری ۲–۳–۱:شبکه‌ای با دو ورودی، سه نورون پنهان و یک نورون خروجی تعریف کنید.تعداد وزن‌ها، بایاس‌ها و پارامترهای کل را محاسبه کرده و ابعاد همه ماتریس‌های Forward و Backward را مشخص کنید.
  • تمرین ۵. مقایسه نرخ‌های یادگیریبرنامه این بخش را با نرخ‌های یادگیری زیر اجرا کنید:
η∈{0.01,0.1,0.5,1,5}\eta\in\{0.01,0.1,0.5,1,5\}

زیان پس از یک گام را برای هر مقدار محاسبه کنید. بررسی کنید آیا نرخ یادگیری بزرگ‌تر همیشه کاهش زیان بیشتری ایجاد می‌کند.

  • تمرین ۶. اثر غیرفعال‌شدن ReLU:لایه پنهان را به ReLU تغییر دهید. وزن‌ها و بایاس‌ها را طوری انتخاب کنید که پیش‌فعال‌سازی یکی از نورون‌های پنهان منفی شود.بررسی کنید گرادیان وزن‌های ورودی آن نورون چه می‌شود و علت را توضیح دهید.
  • تمرین ۷. بررسی حساسیت به مقدار اولیه:پنج مجموعه وزن اولیه متفاوت برای شبکه ۲–۲–۱ انتخاب کنید.برای هر مجموعه، ۱۰۰ گام یادگیری انجام دهید و منحنی زیان را رسم کنید. تفاوت رفتار آموزش را تحلیل کنید.
  • تمرین ۸. استخراج رابطه عمومی:برای یک شبکه سه‌لایه قابل‌آموزش، روابط زیر را به‌صورت کامل استخراج کنید:
𝜹(3),𝜹(2),𝜹(1)\boldsymbol{\delta}^{(3)},\qquad \boldsymbol{\delta}^{(2)},\qquad \boldsymbol{\delta}^{(1)}

و گرادیان تمام ماتریس‌های وزن و بردارهای بایاس را بنویسید.

پاسخ تشریحی منتخب: تمرین ۴

برای معماری ۲–۳–۱:

لایه اول دارای سه نورون است که هرکدام دو ورودی دریافت می‌کنند:

NW(1)=3×2=6,Nb(1)=3N_{W^{(1)}}=3\times2=6,\qquad N_{b^{(1)}}=3

لایه خروجی یک نورون با سه ورودی دارد:

NW(2)=1×3=3,Nb(2)=1N_{W^{(2)}}=1\times3=3,\qquad N_{b^{(2)}}=1

پس تعداد پارامترها:

Nparameters=6+3+3+1=13\boxed{N_{\mathrm{parameters}}=6+3+3+1=13}

ابعاد:

W(1)∈ℝ3×2,𝐚(1)∈ℝ3×1,W(2)∈ℝ1×3,𝜹(2)∈ℝ1×1,𝜹(1)∈ℝ3×1W^{(1)}\in\mathbb{R}^{3\times2},\qquad \mathbf{a}^{(1)}\in\mathbb{R}^{3\times1},\qquad W^{(2)}\in\mathbb{R}^{1\times3},\qquad \boldsymbol{\delta}^{(2)}\in\mathbb{R}^{1\times1},\qquad \boldsymbol{\delta}^{(1)}\in\mathbb{R}^{3\times1}

بنابراین:

∇W(1)ℒ∈ℝ3×2,∇W(2)ℒ∈ℝ1×3\nabla_{W^{(1)}}\mathcal{L}\in\mathbb{R}^{3\times2},\qquad \nabla_{W^{(2)}}\mathcal{L}\in\mathbb{R}^{1\times3}

این ابعاد با ماتریس‌های وزن متناظر سازگارند.

۷.۱۷. جمع‌بندی بخش هفتم

در این بخش آموختیم که آموزش شبکه چندلایه، با وجود داشتن تعداد بیشتری نورون و پارامتر، همچنان بر همان اصولی استوار است که برای یک نورون و یک لایه بررسی کردیم.

تفاوت اصلی آن است که گرادیان زیان در لایه‌های پنهان، از لایه‌های بعدی دریافت می‌شود.

چهار رابطه اساسی Backpropagation در شبکه چندلایه

1. انتشار رو‌به‌جلو

𝐳(l)=W(l)𝐚(l−1)+𝐛(l)\mathbf{z}^{(l)}=W^{(l)}\mathbf{a}^{(l-1)}+\mathbf{b}^{(l)}

2. سیگنال خطای لایه پنهان

𝜹(l)=(W(l+1)⊤𝜹(l+1))⊙φ′(𝐳(l))\boldsymbol{\delta}^{(l)}=(W^{(l+1)\top}\boldsymbol{\delta}^{(l+1)})\odot\varphi'(\mathbf{z}^{(l)})

3. گرادیان وزن‌ها

∇W(l)ℒ=𝜹(l)𝐚(l−1)⊤\nabla_{W^{(l)}}\mathcal{L}=\boldsymbol{\delta}^{(l)}\mathbf{a}^{(l-1)\top}

4. گرادیان بایاس‌ها

∇𝐛(l)ℒ=𝜹(l)\nabla_{\mathbf{b}^{(l)}}\mathcal{L}=\boldsymbol{\delta}^{(l)}

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

منابع علمی

Baydin, A. G., Pearlmutter, B. A., Radul, A. A., & Siskind, J. M. (2018). Automatic differentiation in machine learning: A survey. Journal of Machine Learning Research, 18(153), 1–43.

Bishop, C. M. (2006). Pattern recognition and machine learning. Springer.

Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep learning. MIT Press.

Griewank, A., & Walther, A. (2008). Evaluating derivatives: Principles and techniques of algorithmic differentiation (2nd ed.). SIAM.

Hagan, M. T., Demuth, H. B., Beale, M. H., & De Jesús, O. (2014). Neural network design (2nd ed.). Martin Hagan.

Rumelhart, D. E., Hinton, G. E., & Williams, R. J. (1986). Learning representations by back-propagating errors. Nature, 323, 533–536. https://doi.org/10.1038/323533a0

Sidebar atefi
دکتر محمدرضا عاطفی

عضو هیئت علمی دانشگاه
رئیس هیئت مدیره گروه ناب
هم بنیان گذار شرکت دانش بنیان
مشاور شرکت ها و سازمان های بزرگ کشور

آنچه می خوانید

Cover3 compressed 300x169
هوش مصنوعی

بازپس‌انتشار خطا از صفر – بخش سوم: پیاده‌سازی با پایتون و بررسی گرادیان‌ها

8. پیاده‌سازی Backpropagation بدون Autograd؛ ساخت یک موتور یادگیری عصبی از پایه در بخش هفتم، بازپس‌انتشار خطا را در یک شبکه چندلایه با محاسبات عددی کامل بررسی کردیم. اکنون می‌خواهیم همان روابط ریاضی را به یک برنامه عملیاتی تبدیل کنیم؛ برنامه‌ای که بتواند شبکه‌هایی با تعداد لایه‌ها و نورون‌های متفاوت

توضیحات بیشتر »
Cover2 compressed 1 300x169
هوش مصنوعی

بازپس‌انتشار خطا از صفر – بخش دوم: محاسبه گرادیان و آموزش الگوریتم در شبکه چندلایه

5. — Backpropagation یک نورون؛ از محاسبه خطا تا اصلاح وزن‌ها در بخش چهارم، قاعده زنجیره‌ای را برای گراف‌های محاسباتی و شبکه‌های عصبی بررسی کردیم. اکنون می‌خواهیم از این دانش برای حل یک مسئله عملی استفاده کنیم: چگونه یک نورون مصنوعی با مشاهده خطای پیش‌بینی، مقدار وزن‌ها و بایاس خود

توضیحات بیشتر »
Cover1 compressed 1 300x169
هوش مصنوعی

بازپس‌انتشار خطا از صفر – بخش اول: گرادیان، گراف محاسباتی و قاعده زنجیره‌ای

۱. چرا شبکه عصبی باید گرادیان خطا را به عقب منتقل کند؟ شبکه عصبی در مرحله انتشار رو‌به‌جلو (Forward Propagation) مجموعه‌ای از محاسبات را روی داده‌های ورودی انجام می‌دهد و در نهایت خروجی تولید می‌کند. اما تولید خروجی، حتی اگر از نظر ریاضی کاملاً صحیح انجام شده باشد، به معنای

توضیحات بیشتر »
error: محتوا غیر قابل انتخاب و کپی است.