بسیاری از توسعهدهندگان، فریمورکهایی مانند PyTorch یا TensorFlow را بهعنوان جعبههای سیاهی میبینند که با یک فراخوانی سادهی backward() همهچیز را حل میکنند. اما وقتی مدلی همگرا نمیشود، گرادیان صفر میشود یا خطاهای ناآشنا در گراف محاسباتی رخ میدهد، نداشتن درک عمیق از سازوکار درونی موتورهای مشتقگیری خودکار عیبیابی را بسیار دشوار میکند.
در این درس با کنار گذاشتن تمام کتابخانههای سنگین، یک موتور autograd کمینه را از صفر با پایتون خالص پیادهسازی میکنید. یاد میگیرید چگونه قاعده زنجیرهای در قالب یک گراف جهتدار بدون دور (DAG) به کمک مرتبسازی توپولوژیک اجرا میشود و گرادیانها به شیوه پسرو (Reverse mode) منتشر میگردند.
پیادهسازی این درس کاملاً متکی بر منطق ریاضی و ساختار دادههای استاندارد پایتون است و تمام مراحل آن روی CPU معمولی سیستم شخصی شما اجرا میشود، بنابراین برای دنبالکردن آن هیچ نیازی به سرور ابری یا سختافزارهای پردازشی خاص ندارید.
قاعده زنجیرهای (Chain rule) موتور محرک هر شبکه عصبی است که چیزی یاد میگیرد.
اهداف یادگیری
- ساخت یک موتور مشتقگیری خودکار (Autograd engine) کمینه (کلاس Value) که عملیاتها را ثبت کرده و گرادیانها را با مشتقگیری خودکار پسرو (Reverse-mode autodiff) محاسبه کند
- پیادهسازی گذر رفت (Forward pass) و گذر برگشت (Backward pass) روی گراف محاسباتی با استفاده از مرتبسازی توپولوژیک (Topological sort)
- پیادهسازی و آموزش یک پرسپترون چندلایه (Multi-layer perceptron یا MLP) روی مسئله XOR، تنها با استفاده از موتور autograd دستساز خودتان
- بررسی صحت مشتقگیری خودکار از طریق بررسی گرادیان (Gradient checking) در مقایسه با تفاضل متناهی عددی (Numerical finite differences)
صورت مسئله
شما مشتق توابع ساده را بهراحتی حساب میکنید. اما شبکه عصبی یک تابع ساده نیست؛ ترکیبی از صدها تابع تودرتو است: ضرب ماتریسی، جمع با بایاس، اعمال تابع فعالساز، دوباره ضرب ماتریسی، سافتمکس (Softmax) و تابع زیان آنتروپی متقاطع (Cross-entropy loss). خروجی، تابعی از یک تابع دیگر از تابعی دیگر است.
برای آموزش شبکه، به گرادیانِ زیان نسبت به تکتک وزنها نیاز دارید. محاسبه دستی این گرادیانها برای میلیونها پارامتر غیرممکن است. محاسبه عددی آن (تفاضل متناهی) هم بیش از حد کند است.
قاعده زنجیرهای پایههای ریاضی را در اختیارتان میگذارد و مشتقگیری خودکار، الگوریتمِ کار را. ترکیب این دو به شما امکان میدهد گرادیان دقیق ترکیبهای دلخواه توابع را در زمانی متناسب با تنها یک گذر رفت محاسبه کنید.
کتابخانههایی مثل PyTorch، TensorFlow و JAX دقیقاً همینطور کار میکنند. در این درس، یک نسخه مینیاتوری از آن را از صفر خواهید ساخت.
مفاهیم پایه
قاعده زنجیرهای
اگر باشد، مشتق نسبت به برابر است با:
مشتقها را در امتداد زنجیره در هم ضرب کنید. هر حلقه، مشتق موضعی (Local derivative) خودش را وارد محاسبه میکند.
مثال:
برای توابع تودرتوی عمیقتر، این زنجیره گسترش پیدا میکند:
هر لایه در شبکه عصبی، یک حلقه از این زنجیره است.
گرافهای محاسباتی
گراف محاسباتی (Computational graph) قاعده زنجیرهای را شهودی و بصری میکند. هر عملیات به یک گره (Node) تبدیل میشود. دادهها در مسیر رفت در گراف جلو میروند و گرادیانها در مسیر برگشت به عقب میآیند.
گذر رفت (محاسبه مقادیر):
گذر برگشت (محاسبه گرادیانها):
گذر برگشت قاعده زنجیرهای را در تکتک گرهها اعمال میکند و گرادیانها را از خروجی به سمت ورودیها پیش میبرد.
حالت پیشرو در برابر حالت پسرو
برای اعمال قاعده زنجیرهای روی گراف دو روش وجود دارد:
حالت پیشرو (Forward mode) از ورودیها شروع میکند و مشتقها را به جلو میفرستد (push میکند). این روش را محاسبه کرده و در تکتک عملیاتها به جلو میبرد. این حالت برای مسائلی با ورودیهای کم و خروجیهای زیاد مناسب است.
Forward mode: seed dx/dx = 1, propagate forward
x = 2 (dx/dx = 1)
a = x^2 (da/dx = 2x = 4)
y = sin(a) (dy/dx = cos(a) * da/dx = cos(4) * 4 = -2.615)
حالت پسرو (Reverse mode) از خروجی شروع میکند و گرادیانها را به عقب میکشد. این روش را محاسبه کرده و در تکتک عملیاتها در جهت معکوس منتشر میکند. این حالت برای مسائلی با ورودیهای پرشمار و خروجیهای کم عالی است.
Reverse mode: seed dy/dy = 1, propagate backward
y = sin(a) (dy/dy = 1)
a = x^2 (dy/da = cos(a) = cos(4) = -0.654)
x = 2 (dy/dx = dy/da * da/dx = -0.654 * 4 = -2.615)
شبکههای عصبی میلیونها ورودی (وزنها) و تنها یک خروجی (زیان) دارند. حالت پسرو همه گرادیانها را تنها در یک گذر برگشت محاسبه میکند. به همین دلیل پسانتشار (Backpropagation) از حالت پسرو استفاده میکند.
| حالت | مقدار اولیه (Seed) | جهت | بهترین کاربرد |
|---|---|---|---|
| پیشرو | ورودی به خروجی | ورودی کم، خروجی زیاد | |
| پسرو | خروجی به ورودی | ورودی زیاد، خروجی کم (شبکههای عصبی) |
اعداد دوگانه برای حالت پیشرو
حالت پیشرو را میتوان خیلی تمیز با اعداد دوگانه (Dual numbers) پیادهسازی کرد. یک عدد دوگانه شکلی شبیه به دارد که در آن است.
Dual number: (value, derivative)
(2, 1) means: value is 2, derivative w.r.t. x is 1
Arithmetic rules:
(a, a') + (b, b') = (a+b, a'+b')
(a, a') * (b, b') = (a*b, a'*b + a*b')
sin(a, a') = (sin(a), cos(a)*a')
متغیر ورودی را با مشتق 1 مقداردهی اولیه کنید. مشتق بهطور خودکار در خلال تکتک عملیاتها منتشر میشود.
ساخت یک موتور Autograd
یک موتور autograd به سه بخش نیاز دارد:
- پوشش مقدار (Value wrapping): قرار دادن هر عدد درون شیئی که مقدار و گرادیان آن را ذخیره کند.
- ثبت گراف (Graph recording): هر عملیات باید ورودیهای خود و تابع گرادیان موضعی را ثبت کند.
- گذر برگشت (Backward pass): مرتبسازی توپولوژیک گراف و سپس پیمایش معکوس آن با اعمال قاعده زنجیرهای روی هر گره.
این دقیقاً همان کاری است که autograd در PyTorch انجام میدهد. کلاس torch.Tensor مقادیر را نگه میدارد، در صورت فعال بودن requires_grad=True عملیاتها را ثبت میکند و هنگام فراخوانی .backward() گرادیانها را به دست میآورد.
عملکرد درونی PyTorch Autograd
وقتی چنین کدی در PyTorch مینویسید:
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2 + 3 * x + 1
y.backward()
print(x.grad) # 7.0 = 2*x + 3 = 2*2 + 3
PyTorch در پشت صحنه کارهای زیر را انجام میدهد:
- یک گره
Tensorبرایxبا شرطrequires_grad=Trueمیسازد؛ - با هر عملیات (
**،*،+) گره جدیدی ایجاد کرده و تابع برگشت (Backward function) آن را ثبت میکند؛ - فراخوانی
y.backward()موتور autodiff حالت پسرو را روی گراف ثبتشده اجرا میکند؛ - مشخصهی
grad_fnدر هر گره، گرادیانهای موضعی را حساب کرده و به گرههای والد میفرستد؛ - گرادیانها از طریق جمع شدن (نه جایگزینی) در ویژگی
.gradروی هم انباشته میشوند.
این گراف پویا (تعریف در حین اجرا یا Define-by-run) است؛ یعنی در هر گذر رفت، گراف جدیدی از نو ساخته میشود. به همین دلیل PyTorch بهراحتی از ساختارهای کنترلی پایتون (حلقهها، if/else) درون مدلها پشتیبانی میکند.
پیادهسازی از صفر
گام 1: کلاس Value
class Value:
def __init__(self, data, children=(), op=''):
self.data = data
self.grad = 0.0
self._backward = lambda: None
self._prev = set(children)
self._op = op
def __repr__(self):
return f"Value(data={self.data:.4f}, grad={self.grad:.4f})"
هر شیء Value داده عددی، گرادیان (در ابتدا صفر)، یک تابع برگشت و ارجاعهایی به گرههای فرزندی که آن را ساختهاند نگه میدارد.
گام 2: عملیاتهای حسابی همراه با ردیابی گرادیان
def __add__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data + other.data, (self, other), '+')
def _backward():
self.grad += out.grad
other.grad += out.grad
out._backward = _backward
return out
def __mul__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data * other.data, (self, other), '*')
def _backward():
self.grad += other.data * out.grad
other.grad += self.data * out.grad
out._backward = _backward
return out
def relu(self):
out = Value(max(0, self.data), (self,), 'relu')
def _backward():
self.grad += (1.0 if out.data > 0 else 0.0) * out.grad
out._backward = _backward
return out
هر عملیات یک کلوژر میسازد که روش محاسبه گرادیانهای موضعی و ضرب آنها در گرادیان بالادست (out.grad) را میداند. علامت += برای حالتی است که از یک مقدار در چندین عملیات مختلف استفاده شده باشد.
گام 3: گذر برگشت
def backward(self):
topo = []
visited = set()
def build_topo(v):
if v not in visited:
visited.add(v)
for child in v._prev:
build_topo(child)
topo.append(v)
build_topo(self)
self.grad = 1.0
for v in reversed(topo):
v._backward()
مرتبسازی توپولوژیک تضمین میکند که گرادیان هر گره پیش از انتشار به فرزندانش بهطور کامل محاسبه شده باشد. مقدار اولیه گرادیان 1.0 است ().
گام 4: افزودن عملیاتهای بیشتر برای موتور کامل
کلاس پایهای Value جمع، ضرب و تابع relu را پوشش میدهد. اما یک موتور autograd واقعی به چیزهای بیشتری نیاز دارد. اینها عملیاتی هستند که برای ساخت شبکههای عصبی نیاز خواهید داشت:
def __neg__(self):
return self * -1
def __sub__(self, other):
return self + (-other)
def __radd__(self, other):
return self + other
def __rmul__(self, other):
return self * other
def __rsub__(self, other):
return other + (-self)
def __pow__(self, n):
out = Value(self.data ** n, (self,), f'**{n}')
def _backward():
self.grad += n * (self.data ** (n - 1)) * out.grad
out._backward = _backward
return out
def __truediv__(self, other):
return self * (other ** -1) if isinstance(other, Value) else self * (Value(other) ** -1)
def exp(self):
import math
e = math.exp(self.data)
out = Value(e, (self,), 'exp')
def _backward():
self.grad += e * out.grad
out._backward = _backward
return out
def log(self):
import math
out = Value(math.log(self.data), (self,), 'log')
def _backward():
self.grad += (1.0 / self.data) * out.grad
out._backward = _backward
return out
def tanh(self):
import math
t = math.tanh(self.data)
out = Value(t, (self,), 'tanh')
def _backward():
self.grad += (1 - t ** 2) * out.grad
out._backward = _backward
return out
دلیل اهمیت هر عملیات:
| عملیات | قاعده مشتقگیری | کاربرد |
|---|---|---|
__sub__ |
استفاده مجدد از جمع و منفیسازی | محاسبه تابع زیان (pred - target) |
__pow__ |
توابع فعالساز چندجملهای، MSE () | |
__truediv__ |
استفاده مجدد از ضرب و توان منفی یک | نرمالسازی، مقیاسدهی نرخ یادگیری |
exp |
تابع سافتمکس، درستنمایی لگاریتمی | |
log |
زیان آنتروپی متقاطع، احتمالهای لگاریتمی | |
tanh |
تابع فعالساز کلاسیک |
نکته هوشمندانه اینجاست: __sub__ و __truediv__ بر پایه همان عملیاتهای موجود تعریف شدهاند. این توابع گرادیان درست را رایگان به دست میآورند؛ چون قاعده زنجیرهای روی همان عملیاتهای پایهای جمع، ضرب و توان اعمال میشود.
گام 5: پیادهسازی پرسپترون چندلایه (MLP) از صفر
با کامل شدن کلاس Value، میتوانید یک شبکه عصبی بسازید. بدون نیاز به PyTorch یا NumPy؛ فقط با همین مقادیر Value و قاعده زنجیرهای.
import random
class Neuron:
def __init__(self, n_inputs):
self.w = [Value(random.uniform(-1, 1)) for _ in range(n_inputs)]
self.b = Value(0.0)
def __call__(self, x):
act = sum((wi * xi for wi, xi in zip(self.w, x)), self.b)
return act.tanh()
def parameters(self):
return self.w + [self.b]
class Layer:
def __init__(self, n_inputs, n_outputs):
self.neurons = [Neuron(n_inputs) for _ in range(n_outputs)]
def __call__(self, x):
return [n(x) for n in self.neurons]
def parameters(self):
return [p for n in self.neurons for p in n.parameters()]
class MLP:
def __init__(self, sizes):
self.layers = [Layer(sizes[i], sizes[i+1]) for i in range(len(sizes)-1)]
def __call__(self, x):
for layer in self.layers:
x = layer(x)
return x[0] if len(x) == 1 else x
def parameters(self):
return [p for layer in self.layers for p in layer.parameters()]
یک نورون (Neuron) مقدار tanh(w1*x1 + w2*x2 + ... + b) را حساب میکند. یک لایه (Layer) لیستی از این نورونهاست و MLP این لایهها را روی هم میچیند. تمام وزنها از جنس Value هستند؛ در نتیجه فراخوانی loss.backward() گرادیانها را به تکتک پارامترها میرساند.
آموزش روی XOR:
random.seed(42)
model = MLP([2, 4, 1]) # 2 inputs, 4 hidden neurons, 1 output
xs = [[0, 0], [0, 1], [1, 0], [1, 1]]
ys = [-1, 1, 1, -1] # XOR pattern (using -1/1 for tanh)
for step in range(100):
preds = [model(x) for x in xs]
loss = sum((p - y) ** 2 for p, y in zip(preds, ys))
for p in model.parameters():
p.grad = 0.0
loss.backward()
lr = 0.05
for p in model.parameters():
p.data -= lr * p.grad
if step % 20 == 0:
print(f"step {step:3d} loss = {loss.data:.4f}")
print("\nPredictions after training:")
for x, y in zip(xs, ys):
print(f" input={x} target={y:2d} pred={model(x).data:6.3f}")
این همان micrograd است؛ یک چرخهی کامل آموزش شبکه عصبی با پایتون خالص و مشتقگیری خودکار. تمام فریمورکهای تجاری یادگیری عمیق دقیقاً همین کار را در مقیاسی فوقالعاده بزرگ انجام میدهند.
گام 6: بررسی گرادیان
از کجا بدانیم autodiff درست کار میکند؟ آن را با مشتقهای عددی مقایسه میکنیم؛ به این کار بررسی گرادیان (Gradient checking) میگویند.
def gradient_check(build_expr, x_val, h=1e-7):
x = Value(x_val)
y = build_expr(x)
y.backward()
autodiff_grad = x.grad
y_plus = build_expr(Value(x_val + h)).data
y_minus = build_expr(Value(x_val - h)).data
numerical_grad = (y_plus - y_minus) / (2 * h)
diff = abs(autodiff_grad - numerical_grad)
return autodiff_grad, numerical_grad, diff
آزمودن روی یک عبارت پیچیده:
def expr(x):
return (x ** 3 + x * 2 + 1).tanh()
ad, num, diff = gradient_check(expr, 0.5)
print(f"Autodiff: {ad:.8f}")
print(f"Numerical: {num:.8f}")
print(f"Difference: {diff:.2e}")
# اختلاف باید کمتر از 1e-5 باشد
بررسی گرادیان هنگام پیادهسازی عملیاتهای جدید حیاتی است. اگر گذر برگشت شما باگی داشته باشد، بررسی عددی مچ آن را میگیرد. هر پیادهسازی جدی در یادگیری عمیق در طول توسعه حتماً بررسی گرادیان را اجرا میکند.
چه زمانی از بررسی گرادیان استفاده کنیم؟
| موقعیت | نیاز به بررسی گرادیان هست؟ |
|---|---|
| اضافه کردن یک عملیات جدید به موتور autograd | بله، همیشه |
| رفع اشکال چرخهای که همگرا نمیشود | بله، اول گرادیانها را بررسی کنید |
| آموزش در محیط پروداکشن | خیر، خیلی کند است (2 گذر رفت به ازای هر پارامتر) |
| آزمونهای واحد (Unit tests) برای کد autograd | بله، حتماً خودکارسازی شود |
گام 7: اعتبارسنجی با محاسبات دستی
x1 = Value(2.0)
x2 = Value(3.0)
a = x1 * x2 # a = 6.0
b = a + Value(1.0) # b = 7.0
y = b.relu() # y = 7.0
y.backward()
print(f"y = {y.data}") # 7.0
print(f"dy/dx1 = {x1.grad}") # 3.0 (= x2)
print(f"dy/dx2 = {x2.grad}") # 2.0 (= x1)
بررسی دستی: y = relu(x1*x2 + 1). چون x1*x2 + 1 = 7 > 0 است، تابع relu همان تابع همانی خواهد بود.
dy/dx1 = x2 = 3 و dy/dx2 = x1 = 2. خروجی موتور دقیقاً با محاسبات دستی همخوانی دارد.
نحوه استفاده
مقایسه با PyTorch
import torch
x1 = torch.tensor(2.0, requires_grad=True)
x2 = torch.tensor(3.0, requires_grad=True)
a = x1 * x2
b = a + 1.0
y = torch.relu(b)
y.backward()
print(f"PyTorch dy/dx1 = {x1.grad.item()}") # 3.0
print(f"PyTorch dy/dx2 = {x2.grad.item()}") # 2.0
گرادیانها کاملاً یکسان هستند. موتور شما دقیقاً همان خروجی PyTorch را تولید میکند، چون منطق ریاضی هر دو یکی است: مشتقگیری خودکار پسرو بر پایهی قاعده زنجیرهای.
یک عبارت محاسباتی پیچیدهتر
a = Value(2.0)
b = Value(-3.0)
c = Value(10.0)
f = (a * b + c).relu() # relu(2*(-3) + 10) = relu(4) = 4
f.backward()
print(f"df/da = {a.grad}") # -3.0 (= b)
print(f"df/db = {b.grad}") # 2.0 (= a)
print(f"df/dc = {c.grad}") # 1.0
خروجی نهایی
از این درس موارد زیر حاصل میشود:
outputs/skill-autodiff.md— مهارتی برای ساخت و رفع اشکال سیستمهای autogradcode/autodiff.py— یک موتور autograd کمینه که میتوانید گسترشش دهید
کلاس Value که در اینجا ساختید، پایه و اساس چرخهی آموزش شبکه عصبی در فاز 3 خواهد بود.
تمرینها
-
متد
__pow__را به کلاس Value اضافه کنید تا بتوانیدx ** nرا محاسبه کنید. بررسی کنید که آیا به ازای برابر با12.0میشود یا خیر. -
تابع
tanhرا بهعنوان یک تابع فعالساز اضافه کنید. درستی روابط و (بهصورت تقریبی) را بررسی کنید. -
گراف محاسباتی یک تکنورون را بسازید:
y = relu(w1*x1 + w2*x2 + b). هر پنج گرادیان را حساب کرده و درستیشان را با PyTorch بسنجید. -
حالت پیشروی مشتقگیری خودکار را با اعداد دوگانه پیادهسازی کنید. یک کلاس
Dualبسازید و بررسی کنید که آیا مشتقهای حاصل از آن با خروجی موتور پسرو همخوانی دارد یا نه.
راهنماییها
تمرین 1. کلاس Value از قبل متد __pow__ را تعریف کرده است. به پیادهسازی _backward آن نگاه کنید و مطمئن شوید که را روی گرادیان ورودی اعمال میکند. کدهای x = Value(2.0)، y = x ** 3 و y.backward() را اجرا کنید؛ مقدار x.grad باید دقیقاً 12.0 باشد.
تمرین 2. در _backward از فرمول استفاده کنید. نتیجه را با تفاضل متناهیِ خودتان بسنجید: مقدار باید در نقاط و همخوانی داشته باشد.
تمرین 3. عبارت w1*x1 + w2*x2 + b را با اشیاء Value بسازید، تابع relu را روی آن اعمال کرده و backward را صدا بزنید. قبل از مقایسه با PyTorch، دستی بررسی کنید: اگر ورودی relu مثبت باشد، گرادیان w1 برابر با x1 خواهد بود؛ اگر منفی باشد، هر پنج گرادیان 0 میشوند.
تمرین 4. در هر شیء Dual یک مقدار و یک مشتق ذخیره کنید و برای ضرب از قاعده ضرب استفاده کنید. متغیر ورودی را با مشتق 1 مقداردهی اولیه کرده و نتیجه را برای همان تابع با x.grad بهدستآمده از موتور پسرو مقایسه کنید.
اصطلاحات کلیدی
| اصطلاح | در گفتار عامیانه | معنای دقیق فنی |
|---|---|---|
| قاعده زنجیرهای (Chain rule) | «ضرب کردن مشتقها» | مشتق توابع تودرتو برابر است با حاصلضرب مشتق موضعی هر تابع که در نقطه مناسب محاسبه شده باشد |
| گراف محاسباتی (Computational graph) | «نمودار شبکه» | یک گراف جهتدار بدون دور (DAG) که در آن گرهها نمایانگر عملیاتها و یالها حامل مقادیر (در مسیر رفت) یا گرادیانها (در مسیر برگشت) هستند |
| حالت پیشرو (Forward mode) | «هدایت مشتق به جلو» | مشتقگیری خودکاری که مشتقها را از ورودیها به خروجیها میبرد؛ یک گذر به ازای هر متغیر ورودی |
| حالت پسرو (Reverse mode) | «پسانتشار» | مشتقگیری خودکاری که گرادیانها را از خروجیها به ورودیها منتقل میکند؛ یک گذر به ازای هر متغیر خروجی |
| مشتقگیری خودکار (Autograd) | «گرادیانهای خودکار» | سیستمی که عملیاتهای اعمالشده روی مقادیر را ثبت میکند، گرافی میسازد و گرادیانهای دقیق را بر اساس قاعده زنجیرهای به دست میآورد |
| اعداد دوگانه (Dual numbers) | «مقدار به همراه مشتق» | اعدادی به فرم (با شرط ) که اطلاعات مشتق را در حین محاسبات حسابی همراه خود منتقل میکنند |
| مرتبسازی توپولوژیک (Topological sort) | «ترتیب وابستگیها» | مرتب کردن گرههای گراف بهطوری که هر گره پس از تمام پیشنیازهایش قرار بگیرد؛ این کار برای انتشار صحیح گرادیان ضروری است |
| انباشت گرادیان (Gradient accumulation) | «جمع کن، جایگزین نکن» | وقتی یک مقدار به چند عملیات مختلف وارد میشود، گرادیان نهایی آن حاصل جمع تمام گرادیانهای ورودی به آن خواهد بود |
| گراف پویا (Dynamic graph) | «تعریف در حین اجرا» | گراف محاسباتی که در هر گذر رفت از نو ساخته میشود و امکان استفاده از جریانهای کنترلی پایتون را درون مدلها میدهد (رویکرد PyTorch) |
| بررسی گرادیان (Gradient checking) | «بررسی عددی صحت» | مقایسه گرادیانهای مشتقگیری خودکار با گرادیانهای عددی روش تفاضل متناهی جهت اطمینان از صحت کارکرد؛ ابزاری حیاتی برای اشکالزدایی |
| پرسپترون چندلایه (MLP) | «شبکه عصبی چندلایه» | یک شبکه عصبی با یک یا چند لایه پنهان از نورونها. هر نورون مجموع وزندار ورودیها را با بایاس جمع کرده و سپس تابع فعالساز را اعمال میکند |
| نورون (Neuron) | «مجموع وزندار + فعالساز» | واحد پایه محاسباتی: . وزنها و بایاس پارامترهای قابل یادگیری این واحد هستند |
برای مطالعه بیشتر
- 3Blue1Brown: حسابان پسانتشار — توضیح بصری قاعده زنجیرهای در شبکههای عصبی
- سازوکار Autograd در PyTorch — نحوه کارکرد سیستم واقعی در عمل
- مقاله Baydin و همکاران: مشتقگیری خودکار در یادگیری ماشین: یک پیمایش — مرجعی جامع و دقیق
