در این فصل چه یاد میگیری#
فصلِ قبل دو پارامترِ w و b را دستی نگه داشتی. برای یک خط قابلِ تحمل است؛ برای شبکهای با هزاران پارامتر نه. در این فصل nn.Module را میبینی — ظرفی که پارامترها و محاسبه را با هم نگه میدارد — و اولین شبکهٔ چندلایهات را میسازی.
و یک چیزِ آرامشبخش کشف میکنی: nn.Linear هیچ جادویی ندارد و دقیقاً همان فصلِ قبل است. خودت با دست ثابتش میکنی.

آخر این فصل میتوانی:
- یک
nn.Moduleبنویسی وforwardش را پر کنی - بگویی
nn.Linearدقیقاً چه حسابی میکند - پارامترهای مدلت را بشماری و شکلشان را توضیح بدهی
- خطای شکل را بخوانی و از رویش بفهمی کجا اشتباه شده
قبل از شروع#
از فصل ۳: حلقهٔ gradient descent و اینکه پارامتر چیست.
از سرنخ ترمِ ۵ فصل ۲: class، __init__، self — اینجا برای اولین بار واقعاً بهکارشان میبری.
📓 نوتبوک: نوتبوک این فصل را در Colab باز کن — همهٔ کدهای این فصل آماده و بهترتیب داخلش هست.
۱. nn.Linear چیست#
import torch
import torch.nn as nn
torch.manual_seed(0)
layer = nn.Linear(in_features=3, out_features=1)
print("weight:", layer.weight)
print("bias :", layer.bias)
print("shapes:", tuple(layer.weight.shape), tuple(layer.bias.shape))
weight: Parameter containing:
tensor([[-0.0043, 0.3097, -0.4752]], requires_grad=True)
bias : Parameter containing:
tensor([-0.4249], requires_grad=True)
shapes: (1, 3) (1,)
سه چیز را ببین:
- وزنها از قبل ساخته شدهاند، با مقادیرِ تصادفی. تو نساختیشان؛
nn.Linearدر__init__خودش ساخت. requires_grad=Trueخودکار است. هر چیزی کهParameterباشد، ردیابی میشود.- شکلِ
weightبرابرِ(out, in)است، نه(in, out). این ترتیب برعکسِ شهودِ اکثرِ آدمهاست و منبعِ خطای شکل است؛ الان حفظش کن تا بعداً وقت هدر ندهی.
۲. با دست ثابتش کن#
x = torch.tensor([[1.0, 2.0, 3.0]])
print("خروجی لایه :", layer(x))
print("دستی (x@W.T+b):", x @ layer.weight.T + layer.bias)
خروجی لایه : tensor([[-1.2353]], grad_fn=<AddmmBackward0>)
دستی (x@W.T+b): tensor([[-1.2353]], grad_fn=<AddBackward0>)
✅ چک کن: دو خروجی باید تا چهار رقمِ اعشار یکی باشند. اگر نبودند،
weight.Tرا جا انداختهای — و همان ترتیبِ(out, in)است که چند خط بالاتر گفتیم حفظش کن.
دقیقاً یک عدد. nn.Linear هیچ کارِ دیگری نمیکند: یک ضربِ ماتریسی و یک جمع — همان فصلِ ۳، فقط با چند ورودی بهجای یکی.
آن grad_fn متفاوت (AddmmBackward0 در برابر AddBackward0) فقط یعنی PyTorch نسخهٔ بهینهترِ «ضرب و جمع در یک عملیات» را بهکار برده. نتیجه یکی است، مسیرِ محاسبه بهینهتر.
🌱 ریشهاش کجاست:
x @ W.Tیک ضربِ ماتریسی است، و اینکه چرا این عمل اینطور تعریف شده و چه معنایی دارد، موضوعِ ریشه، ترم ۷ فصل ۵ است: ترکیبِ دو تبدیل. یک لایهٔ شبکه دقیقاً یک تبدیلِ خطی است، و شبکهٔ چندلایه ترکیبِ چندتاست — که در فصلِ بعد میبینی چرا بهتنهایی کافی نیست.
۳. اولین nn.Module خودت#
class TinyNet(nn.Module):
def __init__(self, n_features):
super().__init__()
self.hidden = nn.Linear(n_features, 8)
self.out = nn.Linear(8, 1)
def forward(self, x):
return self.out(torch.relu(self.hidden(x)))
torch.manual_seed(0)
net = TinyNet(3)
print(net)
TinyNet(
(hidden): Linear(in_features=3, out_features=8, bias=True)
(out): Linear(in_features=8, out_features=1, bias=True)
)
چهار قاعده که در هر nn.Moduleی که تا آخرِ دوره مینویسی تکرار میشوند:
super().__init__()اجباری است و اولین خط. بدونش PyTorch نمیتواند پارامترها را ثبت کند و مدلت بیصدا هیچ پارامتری نخواهد داشت.- لایهها در
__init__ساخته میشوند، نه درforward. ساختنشان درforwardیعنی هر بار وزنهای تازهٔ تصادفی — و مدل هرگز چیزی یاد نمیگیرد. forwardمسیرِ داده را میگوید، از ورودی تا خروجی.net(x)را صدا میزنی، نهnet.forward(x). ظاهراً یکیاند، ولی فراخوانیِ مستقیمِforwardقلابهای داخلیِ PyTorch را دور میزند و بعضی قابلیتها را میشکند.
آن torch.relu را فعلاً بپذیر؛ فصلِ بعد کاملاً دربارهٔ همان است و آنجا میبینی که بدونش این شبکهٔ دولایه هیچ تواناییای بیشتر از یک لایه ندارد.
۴. شمردنِ پارامترها#
total = sum(p.numel() for p in net.parameters())
print("تعداد کل پارامترها:", total)
for name, p in net.named_parameters():
print(f"{name:>14}: shape={tuple(p.shape)} -> {p.numel()}")
تعداد کل پارامترها: 41
hidden.weight: shape=(8, 3) -> 24
hidden.bias: shape=(8,) -> 8
out.weight: shape=(1, 8) -> 8
out.bias: shape=(1,) -> 1
. هیچ پارامترِ پنهانی وجود ندارد؛ هر عددی که این شبکه یاد میگیرد در همین چهار جاست.
این تابعِ سهخطی را حفظ کن — در کلِ دوره بارها بهکارش میبری. تعدادِ پارامتر اولین چیزی است که دربارهٔ ظرفیتِ یک مدل به تو میگوید، و در ترمِ ۲ میبینی که رابطهٔ مستقیمی با overfitting دارد.
📏 اندازه بگیر: قبل از آموزش، همیشه پارامترها را بشمار. اگر عددی که میبینی با انتظارت نمیخواند، مدلت آنی نیست که فکر میکنی — معمولاً یک لایه را در
forwardصدا نزدهای یاsuper().__init__()جا مانده. بشمردنش پنج ثانیه است و ساعتها اشکالزدایی را حذف میکند.
۵. batch، و خطای شکل#
batch = torch.randn(5, 3)
out = net(batch)
print("ورودی:", tuple(batch.shape), "-> خروجی:", tuple(out.shape))
try:
net(torch.randn(5, 4))
except RuntimeError as error:
print("RuntimeError:", str(error)[:78])
ورودی: (5, 3) -> خروجی: (5, 1)
RuntimeError: mat1 and mat2 shapes cannot be multiplied (5x4 and 3x8)
پنج نمونه با هم رفتند و پنج جواب برگشت. بُعدِ اول همیشه batch است و لایهها به آن کار ندارند — این قرارداد در کلِ PyTorch ثابت است.
و آن خطا را حرفبهحرف بخوان: (5x4 and 3x8). عددهای وسط باید بخوانند — ۴ با ۳ نمیخواند. این پیام دقیقاً میگوید کجا اشتباه است، و اگر یاد بگیری بخوانیاش، نودرصدِ خطاهای شکل را در ده ثانیه حل میکنی.
🔧 اگر کار نکرد: سه خطای رایج و علتشان.
mat1 and mat2 shapes cannot be multiplied: تعدادِ ویژگیهای ورودی باin_featuresنمیخواند.expected scalar type Float but found Long: ورودیint64است؛.float()بزن (فصلِ ۱ بخشِ ۲). مدل پارامتری ندارد:super().__init__()را جا انداختهای، یا لایهها را در یکlistمعمولی گذاشتهای بهجایnn.ModuleList.
🤖 از دستیارت بپرس: «چرا
nn.Linearوزن را به شکلِ(out_features, in_features)نگه میدارد و نه برعکس؟» و بعد جوابش را با کد بسنج:layer.weight.shapeرا ببین و خودتx @ W.Tرا حساب کن. اگر دستیارت توضیحِ قانعکنندهای نداد، کد جوابِ قطعی را میدهد — و همین عادت، تفاوتِ یادگیری از دستیار با باور کردنِ دستیار است.
واژههای تازهٔ این فصل#
| کلمه | تلفظ به حروف فارسی | یعنی چه |
|---|---|---|
| nn.Module | انان ماژول | کلاسِ پایهٔ هر مدلِ PyTorch |
| forward | فوروارد | مسیرِ داده از ورودی تا خروجی |
| parameter | پارامتر | tensorی که مدل یادش میگیرد |
| weight / bias | وِیت / بایاس | ضریبها و مقدارِ ثابتِ یک لایه |
| batch | بچ | چند نمونه که با هم پردازش میشوند |
| in_features / out_features | این/اوت فیچرز | تعدادِ ورودی و خروجیِ یک لایه |
تمرینها
اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.
در فصل بعد#
شبکهات دو لایه دارد، ولی هنوز یک راز باقی است: آن torch.relu وسطِ forward چه میکرد؟ فصلِ بعد نشان میدهد که بدونش، صد لایه هم دقیقاً بهاندازهٔ یک لایه توانایی دارند — و آن را با کد ثابت میکند، نه با ادعا.
به آخر این فصل رسیدی!
اگر ساختی و جواب داد، این دکمه مال توست.