بسیاری از برنامهنویسان یادگیری ماشین کار خود را مستقیماً با ابزارهایی مانند PyTorch یا TensorFlow آغاز میکنند؛ اما به دلیل ماهیت لایههای زیرین، این فریمورکها برای آنها مانند جعبهای سیاه رفتار میکنند. کپی و پیست کردن کدهای پراکنده برای ساخت لایهها، مدیریت گرادیانها و نوشتن دستی حلقهی آموزش، نهتنها نگهداری پروژهها را دشوار میکند، بلکه مانع از فهم دقیق منطق دستورهایی مثل model.train() یا optimizer.zero_grad() میشود.
در این درس تمام اجزایی را که تاکنون جداگانه ساختهاید—شامل لایهها، توابع فعالسازی، نرمالسازی دستهای، dropout و بهینهسازها—در یک معماری منسجم و حدود 500 خط پایتون خالص بدون نیاز به کتابخانههای خارجی یکپارچه میکنید. این پیادهسازی مستقل روی پردازندهی استاندارد هر رایانهای به شکل محلی اجرا میشود و شما را به درکی عمیق از طراحی شیگرای موتورهای مدرن یادگیری عمیق میرساند.
تا اینجا نورونها، لایهها، شبکهها، پسانتشار (backpropagation)، توابع فعالسازی، تابع زیان (loss)، بهینهسازها (optimizers)، منظمسازی (regularization)، مقداردهی اولیه (initialization) و برنامههای زمانبندی نرخ یادگیری را پیادهسازی کردهاید؛ اما همه را در قالب قطعاتی مجزا. حالا وقت آن است که همهی آنها را در قالب یک فریمورک به هم متصل کنید. نه PyTorch، نه TensorFlow؛ فریمورک اختصاصی خودتان.
اهداف یادگیری
- ساخت یک فریمورک یادگیری عمیق کامل (حدود 500 خط) شامل کلاسهای Module، Linear، ReLU، Sigmoid، Dropout، BatchNorm، Sequential، توابع زیان، بهینهسازها و DataLoader
- درک مفهوم انتزاع Module (متدهای forward و backward و parameters) و دلیل نیاز به جابهجایی بین حالت آموزش و ارزیابی (train/eval mode)
- اتصال تمام اجزا در یک حلقهی آموزش کارآمد برای آموزش یک شبکهی 4 لایه روی مسئلهی دستهبندی دایرهها
- تطبیق هر جزء از فریمورک خود با معادل آن در PyTorch (مانند nn.Module، nn.Sequential، optim.Adam و DataLoader)
صورت مسئله
حاصل ده درس گذشته، قطعاتی از کد است که در فایلهای جداگانه پراکندهاند: کلاس Value در یک فایل، حلقهی آموزش در فایلی دیگر، مقداردهی اولیهی وزنها جایی دیگر و برنامهی زمانبندی نرخ یادگیری (learning rate schedule) در فایلی دیگر. برای آموزش یک شبکه، مجبورید کدهای پنج درس مختلف را کپی و پیست کنید و آنها را دستی به هم وصل کنید.
فریمورکها دقیقاً همین مشکل را حل میکنند. PyTorch ابزارهایی مثل nn.Module، nn.Sequential، optim.Adam، DataLoader و یک الگوی استاندارد برای حلقهی آموزش در اختیارتان میگذارد تا همهی اینها را به هم پیوند دهد. TensorFlow هم keras.Layer، keras.Sequential و keras.optimizers.Adam را ارائه میدهد. این ابزارها جادو نمیکنند؛ الگوهای سازماندهی کدی هستند که تعریف، آموزش و ارزیابی شبکهها را ممکن میسازند تا نیازی نباشد هر بار چرخ را از نو بسازید.
قرار است همین ساختار را در حدود 500 خط پایتون پیاده کنید؛ بدون numpy و بدون هیچ وابستگی خارجی. فریمورکی که بتواند هر شبکهی پیشخور (feedforward) را تعریف کند، آن را با SGD یا Adam آموزش دهد، دادهها را دستهبندی (batch) کند، dropout و نرمالسازی دستهای (batch normalization) را اعمال کند، از هر تابع فعالسازی دلخواهی بهره ببرد و نرخ یادگیری را طبق برنامه تغییر دهد.
پس از پایان این درس، وقتی در PyTorch مینویسید model = nn.Sequential(...)، دقیقاً میدانید در پشت صحنه چه میگذرد. میفهمید چرا متدهای model.train() و model.eval() وجود دارند. درک میکنید چرا optimizer.zero_grad() باید در یک فراخوانی جداگانه اجرا شود. همهچیز را خواهید فهمید، چون صفر تا صد آن را خودتان ساختهاید.
مفاهیم پایه
مفهوم انتزاعی Module
تمام لایهها در PyTorch از کلاس nn.Module ارثبری میکنند. یک Module سه وظیفهی اصلی دارد:
forward()-- محاسبهی خروجی بر اساس ورودیهاparameters()-- برگرداندن تمام وزنهای قابل آموزشbackward()-- محاسبهی گرادیانها (در PyTorch با autograd مدیریت میشود، اما در فریمورک ما کاملاً صریح پیاده خواهد شد)
لایهی Linear یک Module است. تابع فعالسازی ReLU یک Module است. لایهی dropout و لایهی نرمالسازی دستهای نیز هر کدام یک Module هستند. رابط کاربری (interface) همهی آنها کاملاً یکسان است.
نگهدارندهی Sequential
کلاس nn.Sequential چند Module را پشت سر هم زنجیر میکند. گذر رو به جلو (forward pass): دادهها را از Module اول، سپس دوم و بعد سوم عبور میدهد. گذر رو به عقب (backward pass): این زنجیره را برعکس طی میکند. خود این نگهدارنده نیز یک Module است؛ یعنی متدهای forward()، parameters() و backward() را دارد. این همان الگوی طراحی مرکب (composite pattern) است: زنجیرهای از Moduleها خودش یک Module به حساب میآید.
حالت آموزش در برابر حالت ارزیابی
لایه Dropout در زمان آموزش، برخی نورونها را تصادفی صفر میکند، اما در زمان ارزیابی، همهی دادهها را بدون تغییر عبور میدهد. لایه BatchNorm در زمان آموزش از آمارههای دستهی جاری استفاده میکند، اما در زمان ارزیابی، میانگینها و واریانسهای متحرکِ انباشتهشده را به کار میگیرد. متدهای train() و eval() این رفتار را تغییر میدهند. هر Module یک پرچم به نام training دارد.
بهینهساز
بهینهساز پارامترها را با استفاده از گرادیان آنها بهروزرسانی میکند. در SGD رابطه به شکل param -= lr * grad است. در Adam، تخمینی از تکانه (momentum) و واریانس نگهداری و سپس پارامتر بهروزرسانی میشود. بهینهساز هیچ اطلاعی از معماری شبکه ندارد؛ تنها یک لیست تخت از پارامترها و گرادیانهای آنها را میبیند.
کلاس DataLoader
دستهبندی دادهها به دو دلیل اهمیت دارد: اول اینکه در مسائل بزرگ، کل مجموعهداده در حافظه جا نمیشود. دوم اینکه گرادیان کاهشی با دستهی کوچک (mini-batch gradient descent) نویزی ایجاد میکند که به فرار مدل از کمینههای محلی (local minima) کمک میکند. کلاس DataLoader دادهها را به دستهها تقسیم میکند و در صورت نیاز، پیش از هر دور (epoch) آنها را برمیزند.
معماری فریمورک
حلقهی آموزش
سلسلهمراتب Module
پیادهسازی
گام 1: کلاس پایهی Module
رابط کاربری انتزاعی که تمام لایهها آن را پیادهسازی میکنند.
class Module:
def __init__(self):
self.training = True
def forward(self, x):
raise NotImplementedError
def backward(self, grad):
raise NotImplementedError
def parameters(self):
return []
def train(self):
self.training = True
def eval(self):
self.training = False
گام 2: لایهی Linear
بلوک ساختمانی اصلی شبکه. وزنها و بایاسها را ذخیره میکند، در گذر رو به جلو فرمول Wx + b را محاسبه کرده و در گذر رو به عقب گرادیان ورودیها و وزنها را به دست میآورد.
import math
import random
class Linear(Module):
def __init__(self, fan_in, fan_out):
super().__init__()
std = math.sqrt(2.0 / fan_in)
self.weights = [[random.gauss(0, std) for _ in range(fan_in)] for _ in range(fan_out)]
self.biases = [0.0] * fan_out
self.weight_grads = [[0.0] * fan_in for _ in range(fan_out)]
self.bias_grads = [0.0] * fan_out
self.fan_in = fan_in
self.fan_out = fan_out
self.input = None
def forward(self, x):
self.input = x
output = []
for i in range(self.fan_out):
val = self.biases[i]
for j in range(self.fan_in):
val += self.weights[i][j] * x[j]
output.append(val)
return output
def backward(self, grad):
input_grad = [0.0] * self.fan_in
for i in range(self.fan_out):
self.bias_grads[i] += grad[i]
for j in range(self.fan_in):
self.weight_grads[i][j] += grad[i] * self.input[j]
input_grad[j] += grad[i] * self.weights[i][j]
return input_grad
def parameters(self):
params = []
for i in range(self.fan_out):
for j in range(self.fan_in):
params.append((self.weights, i, j, self.weight_grads))
params.append((self.biases, i, None, self.bias_grads))
return params
گام 3: ماژولهای فعالسازی
پیادهسازی ReLU، Sigmoid و Tanh در قالب Module. هر کدام دادههای لازم برای گذر رو به عقب را در خود کش میکنند.
class ReLU(Module):
def __init__(self):
super().__init__()
self.mask = None
def forward(self, x):
self.mask = [1.0 if v > 0 else 0.0 for v in x]
return [max(0.0, v) for v in x]
def backward(self, grad):
return [g * m for g, m in zip(grad, self.mask)]
class Sigmoid(Module):
def __init__(self):
super().__init__()
self.output = None
def forward(self, x):
self.output = []
for v in x:
v = max(-500, min(500, v))
self.output.append(1.0 / (1.0 + math.exp(-v)))
return self.output
def backward(self, grad):
return [g * o * (1 - o) for g, o in zip(grad, self.output)]
class Tanh(Module):
def __init__(self):
super().__init__()
self.output = None
def forward(self, x):
self.output = [math.tanh(v) for v in x]
return self.output
def backward(self, grad):
return [g * (1 - o * o) for g, o in zip(grad, self.output)]
گام 4: ماژول Dropout
در زمان آموزش، مقادیر ورودی را به شکل تصادفی صفر میکند. مقادیر باقیمانده را با ضریب 1/(1-p) مقیاسبندی میکند تا امید ریاضی آنها ثابت بماند. در حالت ارزیابی هیچ تغییری روی دادهها ایجاد نمیکند.
class Dropout(Module):
def __init__(self, p=0.5):
super().__init__()
self.p = p
self.mask = None
def forward(self, x):
if not self.training:
return x
self.mask = [0.0 if random.random() < self.p else 1.0 / (1 - self.p) for _ in x]
return [v * m for v, m in zip(x, self.mask)]
def backward(self, grad):
if self.mask is None:
return grad
return [g * m for g, m in zip(grad, self.mask)]
گام 5: ماژول BatchNorm
فعالسازیها را به ازای هر ویژگی در سراسر دسته نرمال میکند تا میانگین به صفر و واریانس به یک برسد. آمارههای متحرک را برای استفاده در حالت ارزیابی نگهداری میکند.
class BatchNorm(Module):
def __init__(self, size, momentum=0.1, eps=1e-5):
super().__init__()
self.size = size
self.gamma = [1.0] * size
self.beta = [0.0] * size
self.gamma_grads = [0.0] * size
self.beta_grads = [0.0] * size
self.running_mean = [0.0] * size
self.running_var = [1.0] * size
self.momentum = momentum
self.eps = eps
self.x_norm = None
self.std_inv = None
self.batch_input = None
def forward_batch(self, batch):
batch_size = len(batch)
output_batch = []
if self.training:
mean = [0.0] * self.size
for sample in batch:
for j in range(self.size):
mean[j] += sample[j]
mean = [m / batch_size for m in mean]
var = [0.0] * self.size
for sample in batch:
for j in range(self.size):
var[j] += (sample[j] - mean[j]) ** 2
var = [v / batch_size for v in var]
self.std_inv = [1.0 / math.sqrt(v + self.eps) for v in var]
self.x_norm = []
self.batch_input = batch
for sample in batch:
normed = [(sample[j] - mean[j]) * self.std_inv[j] for j in range(self.size)]
self.x_norm.append(normed)
output = [self.gamma[j] * normed[j] + self.beta[j] for j in range(self.size)]
output_batch.append(output)
for j in range(self.size):
self.running_mean[j] = (1 - self.momentum) * self.running_mean[j] + self.momentum * mean[j]
self.running_var[j] = (1 - self.momentum) * self.running_var[j] + self.momentum * var[j]
else:
std_inv = [1.0 / math.sqrt(v + self.eps) for v in self.running_var]
for sample in batch:
normed = [(sample[j] - self.running_mean[j]) * std_inv[j] for j in range(self.size)]
output = [self.gamma[j] * normed[j] + self.beta[j] for j in range(self.size)]
output_batch.append(output)
return output_batch
def forward(self, x):
result = self.forward_batch([x])
return result[0]
def backward(self, grad):
if self.x_norm is None:
return grad
for j in range(self.size):
self.gamma_grads[j] += self.x_norm[0][j] * grad[j]
self.beta_grads[j] += grad[j]
return [grad[j] * self.gamma[j] * self.std_inv[j] for j in range(self.size)]
def parameters(self):
params = []
for j in range(self.size):
params.append((self.gamma, j, None, self.gamma_grads))
params.append((self.beta, j, None, self.beta_grads))
return params
گام 6: نگهدارندهی Sequential
ماژولها را زنجیروار به هم متصل میکند. در گذر رو به جلو از چپ به راست و در گذر رو به عقب از راست به چپ حرکت میکند.
class Sequential(Module):
def __init__(self, *modules):
super().__init__()
self.modules = list(modules)
def forward(self, x):
for module in self.modules:
x = module.forward(x)
return x
def backward(self, grad):
for module in reversed(self.modules):
grad = module.backward(grad)
return grad
def parameters(self):
params = []
for module in self.modules:
params.extend(module.parameters())
return params
def train(self):
self.training = True
for module in self.modules:
module.train()
def eval(self):
self.training = False
for module in self.modules:
module.eval()
گام 7: توابع زیان
پیادهسازی MSE و آنتروپی متقاطع دودویی (Binary Cross-Entropy). هر کدام مقدار زیان را محاسبه کرده و با اجرای متد backward() گرادیان را برمیگردانند.
class MSELoss:
def __call__(self, predicted, target):
self.predicted = predicted
self.target = target
n = len(predicted)
self.loss = sum((p - t) ** 2 for p, t in zip(predicted, target)) / n
return self.loss
def backward(self):
n = len(self.predicted)
return [2 * (p - t) / n for p, t in zip(self.predicted, self.target)]
class BCELoss:
def __call__(self, predicted, target):
self.predicted = predicted
self.target = target
eps = 1e-7
n = len(predicted)
self.loss = 0
for p, t in zip(predicted, target):
p = max(eps, min(1 - eps, p))
self.loss += -(t * math.log(p) + (1 - t) * math.log(1 - p))
self.loss /= n
return self.loss
def backward(self):
eps = 1e-7
n = len(self.predicted)
grads = []
for p, t in zip(self.predicted, self.target):
p = max(eps, min(1 - eps, p))
grads.append((-t / p + (1 - t) / (1 - p)) / n)
return grads
گام 8: بهینهسازهای SGD و Adam
هر دو بهینهساز لیستی از پارامترها را میگیرند و وزنها را با استفاده از گرادیانها بهروزرسانی میکنند.
class SGD:
def __init__(self, parameters, lr=0.01):
self.params = parameters
self.lr = lr
def step(self):
for container, i, j, grad_container in self.params:
if j is not None:
container[i][j] -= self.lr * grad_container[i][j]
else:
container[i] -= self.lr * grad_container[i]
def zero_grad(self):
for container, i, j, grad_container in self.params:
if j is not None:
grad_container[i][j] = 0.0
else:
grad_container[i] = 0.0
class Adam:
def __init__(self, parameters, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
self.params = parameters
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.eps = eps
self.t = 0
self.m = [0.0] * len(parameters)
self.v = [0.0] * len(parameters)
def step(self):
self.t += 1
for idx, (container, i, j, grad_container) in enumerate(self.params):
if j is not None:
g = grad_container[i][j]
else:
g = grad_container[i]
self.m[idx] = self.beta1 * self.m[idx] + (1 - self.beta1) * g
self.v[idx] = self.beta2 * self.v[idx] + (1 - self.beta2) * g * g
m_hat = self.m[idx] / (1 - self.beta1 ** self.t)
v_hat = self.v[idx] / (1 - self.beta2 ** self.t)
update = self.lr * m_hat / (math.sqrt(v_hat) + self.eps)
if j is not None:
container[i][j] -= update
else:
container[i] -= update
def zero_grad(self):
for container, i, j, grad_container in self.params:
if j is not None:
grad_container[i][j] = 0.0
else:
grad_container[i] = 0.0
گام 9: کلاس DataLoader
دادهها را به دستههای کوچک تقسیم میکند و میتواند در هر دور آنها را بربزند.
class DataLoader:
def __init__(self, data, batch_size=32, shuffle=True):
self.data = data
self.batch_size = batch_size
self.shuffle = shuffle
def __iter__(self):
indices = list(range(len(self.data)))
if self.shuffle:
random.shuffle(indices)
for start in range(0, len(indices), self.batch_size):
batch_indices = indices[start:start + self.batch_size]
batch = [self.data[i] for i in batch_indices]
inputs = [item[0] for item in batch]
targets = [item[1] for item in batch]
yield inputs, targets
def __len__(self):
return (len(self.data) + self.batch_size - 1) // self.batch_size
گام 10: آموزش یک شبکهی 4 لایه روی مسئلهی دستهبندی دایرهها
اتصال تمام اجزا به یکدیگر. مدل را تعریف کنید، تابع زیان و بهینهساز را برگزینید و حلقهی آموزش را اجرا کنید.
def make_circle_data(n=500, seed=42):
random.seed(seed)
data = []
for _ in range(n):
x = random.uniform(-2, 2)
y = random.uniform(-2, 2)
label = 1.0 if x * x + y * y < 1.5 else 0.0
data.append(([x, y], [label]))
return data
def train():
random.seed(42)
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)
data = make_circle_data(500)
split = int(len(data) * 0.8)
train_data = data[:split]
test_data = data[split:]
loader = DataLoader(train_data, batch_size=16, shuffle=True)
model.train()
for epoch in range(100):
total_loss = 0
total_correct = 0
total_samples = 0
for batch_inputs, batch_targets in loader:
batch_loss = 0
for x, t in zip(batch_inputs, batch_targets):
pred = model.forward(x)
loss = criterion(pred, t)
batch_loss += loss
optimizer.zero_grad()
grad = criterion.backward()
model.backward(grad)
optimizer.step()
predicted_class = 1.0 if pred[0] >= 0.5 else 0.0
if predicted_class == t[0]:
total_correct += 1
total_samples += 1
total_loss += batch_loss
avg_loss = total_loss / total_samples
accuracy = total_correct / total_samples * 100
if epoch % 10 == 0 or epoch == 99:
print(f"Epoch {epoch:3d} | Loss: {avg_loss:.6f} | Train Accuracy: {accuracy:.1f}%")
model.eval()
correct = 0
for x, t in test_data:
pred = model.forward(x)
predicted_class = 1.0 if pred[0] >= 0.5 else 0.0
if predicted_class == t[0]:
correct += 1
test_accuracy = correct / len(test_data) * 100
print(f"\nTest Accuracy: {test_accuracy:.1f}% ({correct}/{len(test_data)})")
return model, test_accuracy
استفاده از ابزارهای واقعی
در ادامه معادل کدی که ساختید را در PyTorch میبینید:
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
model = nn.Sequential(
nn.Linear(2, 16),
nn.ReLU(),
nn.Linear(16, 16),
nn.ReLU(),
nn.Linear(16, 8),
nn.ReLU(),
nn.Linear(8, 1),
nn.Sigmoid(),
)
criterion = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
for epoch in range(100):
model.train()
for inputs, targets in dataloader:
optimizer.zero_grad()
predictions = model(inputs)
loss = criterion(predictions, targets)
loss.backward()
optimizer.step()
model.eval()
with torch.no_grad():
test_predictions = model(test_inputs)
ساختار این کد کاملاً یکسان است: Sequential، Linear، ReLU، Sigmoid، BCELoss، Adam، zero_grad، backward، step، train، eval. تمام مفاهیم یکبهیک نظیر هم هستند. تفاوت در این است که PyTorch گرادیانگیری خودکار دارد (نیازی به پیادهسازی backward() در هر ماژول نیست)، روی GPU اجرا میشود و سالها بهینهسازی فنی را پشت سر گذاشته است؛ اما ساختار اصلی همان است.
حالا با دیدن هر کدی در PyTorch، دقیقاً میدانید در هر خط چه اتفاقی میافتد و این درک عمیق، همان هدف اصلی این درس است.
خروجی این درس
از این درس خروجی زیر را به دست میآورید:
outputs/prompt-framework-architect.md-- پرامپتی برای طراحی معماری شبکههای عصبی با استفاده از مفاهیم انتزاعی فریمورک
تمرینها
-
یک کلاس
SoftmaxCrossEntropyLossبرای دستهبندی چندکلاسه بنویسید. روی پیشبینیها تابع سافتمکس (Softmax) را اعمال کنید، تابع زیان آنتروپی متقاطع (Cross-Entropy) را به دست آورید و گذر رو به عقبِ ترکیبی آن را پیادهسازی کنید. مدل را روی مجموعهدادهی مارپیچ 3 کلاسه آزمایش کنید. -
برنامهریزی نرخ یادگیری را در بهینهساز پیاده کنید: یک متد
set_lr()بیفزایید و برنامهی کسینوسی درس 09 را به آن متصل کنید. دستهبند دایرهها را با پیشگرمایش (warmup) به همراه برنامهی کسینوسی آموزش دهید و نتایج را با نرخ یادگیری ثابت مقایسه کنید. -
دو متد
save()وload()به Sequential بیفزایید که تمام وزنها را در یک فایل JSON ذخیره و بازیابی کند. بررسی کنید که آیا مدل بارگذاریشده دقیقاً همان پیشبینیهای مدل اولیه را تولید میکند یا خیر. -
کاهش وزن (weight decay به عنوان منظمسازی L2) را در بهینهساز Adam پیاده کنید. پارامتر
weight_decayرا اضافه کنید تا وزنها در هر گام به سمت صفر فشرده شوند. آموزش را در دو حالت decay=0 و decay=0.01 مقایسه کنید. -
حلقهی آموزش تکنمونهای را با انباشت گرادیان (Gradient Accumulation) دستهای مناسب جایگزین کنید: گرادیانها را در تمام نمونههای یک دسته جمعآوری کنید، سپس آنها را بر اندازهی دسته تقسیم کرده و یک گام بهینهسازی بردارید. بررسی کنید که آیا این کار سرعت همگرایی را تغییر میدهد یا خیر.
اصطلاحات کلیدی
| اصطلاح | در گفتگوی عامیانه | معنای دقیق فنی |
|---|---|---|
| ماژول (Module) | «یک لایه» | مفهوم پایهای انتزاعی در یک فریمورک؛ هر ساختاری که متدهای forward()، backward() و parameters() را داشته باشد |
| ترتیبی (Sequential) | «چیدن لایهها پشت سر هم» | نگهدارندهای که ماژولها را زنجیر میکند و ورودی را در گذر رو به جلو به ترتیب و در گذر رو به عقب به صورت معکوس اعمال مینماید |
| گذر رو به جلو (Forward pass) | «اجرای شبکه» | محاسبهی خروجی از طریق عبور دادن دادههای ورودی از تمام ماژولها به ترتیب |
| گذر رو به عقب (Backward pass) | «محاسبهی گرادیانها» | بازانتشار گرادیانِ تابع زیان به صورت معکوس در طول ماژولها برای محاسبهی گرادیان پارامترها |
| پارامترها (Parameters) | «وزنهای قابل آموزش» | تمام مقادیر عددی در شبکه که بهینهساز توانایی بهروزرسانی آنها را دارد؛ یعنی وزنها و بایاسها |
| بهینهساز (Optimizer) | «چیزی که وزنها را تغییر میدهد» | الگوریتمی برای بهروزرسانی پارامترها با استفاده از گرادیانها، مانند SGD یا Adam |
| بارگذار داده (DataLoader) | «چیزی که دادهها را میخواند» | یک تکرارکننده (iterator) که مجموعهداده را به دستههای کوچک تقسیم میکند و در صورت نیاز بین دورهها آن را برمیزند |
| حالت آموزش (Training mode) | «دستور model.train()» | پرچمی که رفتارهای تصادفی مانند dropout و نرمالسازی با استفاده از آمارههای دستهی جاری را فعال میکند |
| حالت ارزیابی (Evaluation mode) | «دستور model.eval()» | پرچمی که dropout را خاموش کرده و در نرمالسازی دستهای، آمارههای متحرکِ ذخیرهشده را به کار میگیرد |
| صفر کردن گرادیان (Zero grad) | «پاک کردن گرادیانها» | ریست کردن گرادیان تمام پارامترها به صفر، پیش از شروع محاسبهی گرادیانها برای دستهی جدید |
برای مطالعهی بیشتر
- مقالهی پاشکه و همکاران، «PyTorch: An Imperative Style, High-Performance Deep Learning Library» (سال 2019) -- مقالهای که تصمیمات طراحی معماری PyTorch را توضیح میدهد
- شوله، «Deep Learning with Python, Second Edition» (سال 2021) -- فصل 3 به سازوکار داخلی Keras با همین ساختار انتزاعی ماژول/لایه میپردازد
- جانسون، ریپازیتوری «Tiny-DNN» در آدرس (https://github.com/tiny-dnn/tiny-dnn) -- یک فریمورک یادگیری عمیق در زبان ++C تنها با فایلهای هدر، مناسب برای درک ساختار داخلی فریمورکها
