Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Blok naqshi
- 2.2. Residual ulanish
- 2.3. Parametrlarni baham ko'rish
- 2.4. Murakkab forward
- 2.5. Konfiguratsiyadan qurish
- 2.6. Qatlam tartibi
- 2.7. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Blok va chuqurlik
- Misol 2 — Residual ulanish
- Misol 3 — Parametrlarni baham ko'rish va murakkab forward
- Misol 4 — Konfiguratsiyadan arxitektura
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
21.2-dars: O'z qatlamlaringiz va bloklar
21-QISM — PYTORCH · 2-dars
1. Kirish va motivatsiya
nn.Module ni tushundik. Endi undan foydalanamiz: o'z qatlamlaringiz, takrorlanuvchi bloklar va murakkab tuzilmalar quramiz.
Nima uchun bu kerak? Chunki zamonaviy arxitekturalarning hammasi blok tushunchasiga qurilgan. ResNet — bir xil residual blokning takrori. Transformer — bir xil attention blokining takrori. Siz bitta blokni to'g'ri yozsangiz, 50 qatlamli tarmoq to'rt qator kod bo'lib qoladi.
Bu darsda uchta muhim naqshni ko'ramiz. Birinchisi — residual ulanish (x + f(x)), chuqur tarmoqlarni o'rgatish mumkin qilgan bitta g'oya. Ikkinchisi — parametrlarni baham ko'rish: ikki joyda bir xil og'irliklarni ishlatish. Uchinchisi — konfiguratsiyadan arxitektura qurish, ya'ni modelni lug'at bilan ta'riflash.
Shuningdek forward ni murakkab holatlar uchun yozishni ko'ramiz: bir necha kirish, bir necha chiqish, shart-shariotli yo'llar.
Real vaziyat. Jamoa 12 qatlamli tarmoq yozdi — har qatlam qo'lda, 200 qator kod. Yangi qatlam qo'shish har safar uch joyni o'zgartirishni talab qilardi. Blokka ajratilgandan keyin model 20 qator bo'ldi va chuqurlik bitta son bilan boshqariladigan bo'ldi.
Bu darsda takrorlanuvchi bloklar quramiz.
Bu darsda:
- Blok naqshi
- Residual ulanish
- Parametrlarni baham ko'rish
- Murakkab forward
- Konfiguratsiyadan qurish
- Tuzoqlar
- Amaliy: o'z arxitekturangiz
ℹ Misollar real torch bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. Blok naqshi
BLOK = takrorlanadigan eng kichik mantiqiy birlik
class Blok(nn.Module):
def __init__(self, d, dropout=0.1):
super().__init__()
self.chiziqli = nn.Linear(d, d)
self.norm = nn.LayerNorm(d)
self.drop = nn.Dropout(dropout)
def forward(self, x):
return self.drop(torch.relu(self.norm(self.chiziqli(x))))
TARMOQ = bloklar ketma-ketligi:
self.bloklar = nn.ModuleList([Blok(d) for _ in range(chuqurlik)])
NIMA BERADI:
chuqurlik BITTA son bilan boshqariladi
har blok alohida sinaladi
state_dict kalitlari tartibli: "bloklar.0.chiziqli.weight"
bloklarni almashtirish osonBlokka ajratish — arxitektura kodining asosiy naqshi; chuqurlik bitta parametrga aylanadi.
2.2. Residual ulanish
ODDIY: x -> f(x)
RESIDUAL: x -> x + f(x)
NIMA UCHUN ISHLAYDI (He va b., 2015):
1. GRADIENT YO'LI: d(x + f(x))/dx = 1 + df/dx
-> gradient har doim KAMIDA 1 koeffitsiyent bilan o'tadi
-> yo'qolayotgan gradient muammosi keskin kamayadi
2. IDENTIKLIK OSON: f(x) = 0 bo'lsa blok hech narsa qilmaydi
-> ortiqcha qatlam ZARAR qilmaydi
SHAKL MOS KELISHI SHART:
x va f(x) bir xil o'lchamda bo'lishi kerak
bo'lmasa: proyeksiya qo'shiladi -> proj(x) + f(x)
PRE-NORM va POST-NORM:
post: x + f(norm siz) -> norm(x + f(x)) eski uslub
pre: x + f(norm(x)) barqarorroq, zamonaviy x + f(x) gradientga "qisqa yo'l" beradi — chuqur tarmoqlarni o'rgatish shu bilan mumkin bo'ldi.
2.3. Parametrlarni baham ko'rish
BIR XIL MODULNI IKKI JOYDA ISHLATISH:
self.umumiy = nn.Linear(d, d)
def forward(self, x):
return self.umumiy(self.umumiy(x)) # BIR XIL og'irliklar
-> parametrlar BIR MARTA saqlanadi
-> gradient IKKALA yo'ldan YIG'ILADI
QAYERDA ISHLATILADI:
siamese tarmoqlar (ikki kirishni bir xil koder bilan)
RNN (vaqt qadamlari bo'ylab bir xil og'irlik)
tied embeddings (kirish va chiqish embeddingi - 27-qism)
autoencoder (koder va dekoder transpozitsiyasi)
DIQQAT: bu NUSXA EMAS
self.a = nn.Linear(d, d)
self.b = nn.Linear(d, d) <- BOSHQA parametrlar
self.b = self.a <- BIR XIL parametrlarBir modulni ikki marta chaqirish — parametrlarni baham ko'rish, nusxalash emas.
2.4. Murakkab forward
BIR NECHA KIRISH:
def forward(self, x_son, x_kat): ...
BIR NECHA CHIQISH:
def forward(self, x):
return bosh1(z), bosh2(z) # tuple yoki dict
SHART-SHAROITLI YO'L:
def forward(self, x, rejim="tez"):
return self.tez(x) if rejim == "tez" else self.aniq(x)
MASKA / UZUNLIK:
def forward(self, x, maska=None): ...
DICT QAYTARISH ko'proq o'qiladigan:
return {"logits": ..., "embedding": ..., "attn": ...}
DIQQAT: forward ichida yangi Module YARATMANG
def forward(self, x):
return nn.Linear(4, 2)(x) # ⚠️ har chaqiruvda YANGI forward ichida Module yaratmang — u ro'yxatga olinmaydi va har chaqiruvda qaytadan boshlanadi.
2.5. Konfiguratsiyadan qurish
G'OYA: arxitekturani LUG'AT bilan ta'riflash
konfig = {"kirish": 20, "yashirin": 128, "chuqurlik": 4,
"dropout": 0.1, "chiqish": 3, "residual": True}
model = Tarmoq(**konfig)
NIMA BERADI:
konfigni model bilan birga SAQLASH -> qayta qurish oson
giperparametr qidiruvi tabiiy
tajribalarni taqqoslash aniq
kod bir joyda
SAQLASHDA:
torch.save({"konfig": konfig, "holat": model.state_dict()}, yol)
YUKLASHDA:
paket = torch.load(yol, weights_only=False)
model = Tarmoq(**paket["konfig"])
model.load_state_dict(paket["holat"]) Konfigni model bilan birga saqlang — usiz state_dict ni qayerga yuklashni bilmaysiz.
2.6. Qatlam tartibi
KLASSIK TARTIB:
Linear -> Norm -> Aktivatsiya -> Dropout
NIMA UCHUN SHU TARTIB:
Norm aktivatsiyadan OLDIN: chiziqli chiqishni markazlashtiradi
Dropout oxirida: allaqachon hisoblangan xususiyatlarni o'chiradi
BATCHNORM BILAN:
Linear(bias=False) -> BatchNorm -> ReLU
bias KERAK EMAS, chunki BatchNorm uni bekor qiladi
DROPOUT va BATCHNORM birga:
tartib muammoli (o'rgatish/inference statistikasi farq qiladi)
amalda: BatchNorm bo'lsa dropout ni kamaytiring yoki olib tashlang
PRE-NORM BLOK (zamonaviy):
x + Dropout(Linear(Aktivatsiya(Linear(Norm(x))))) BatchNorm dan oldingi bias bekor bo'ladi — bias=False qo'ying.
2.7. Tuzoqlar
Asosiy tuzoqlar: forward ichida Module yaratish; residual da shakl mos kelmasligi; self.b = self.a ni nusxa deb o'ylash; konfigni saqlamaslik; BatchNorm oldidan bias qoldirish; bloklarni list ga yig'ish; chuqurlikni qo'lda ko'chirib yozish; forward da in-place amallar (x += f(x)).
3. Tez ma'lumotnoma
import torch
import torch.nn as nn
class ResidualBlok(nn.Module):
def __init__(self, d, kengaytirish=4, dropout=0.1):
super().__init__()
self.norm = nn.LayerNorm(d)
self.tarmoq = nn.Sequential(
nn.Linear(d, d * kengaytirish), nn.GELU(),
nn.Dropout(dropout),
nn.Linear(d * kengaytirish, d))
def forward(self, x):
return x + self.tarmoq(self.norm(x)) # pre-norm
class Model(nn.Module):
def __init__(self, kirish, d=128, chuqurlik=4, chiqish=3):
super().__init__()
self.kirish = nn.Linear(kirish, d)
self.bloklar = nn.ModuleList(
[ResidualBlok(d) for _ in range(chuqurlik)])
self.norm = nn.LayerNorm(d)
self.bosh = nn.Linear(d, chiqish)
def forward(self, x):
x = self.kirish(x)
for blok in self.bloklar:
x = blok(x)
return self.bosh(self.norm(x))Bloklar xulosasi
blok = takrorlanadigan birlik
residual: x + f(x), shakllar mos bo'lsin
baham ko'rish: bir modulni ikki marta chaqirish
konfig: lug'at, model bilan birga saqlanadi
tartib: Linear -> Norm -> Aktivatsiya -> Dropout4. Batafsil misollar
Misollar real torch bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — Blok va chuqurlik
"""Blokka ajratish nima beradi (real torch)."""
import torch
import torch.nn as nn
class Blok(nn.Module):
def __init__(self, d, dropout=0.1):
super().__init__()
self.chiziqli = nn.Linear(d, d)
self.norm = nn.LayerNorm(d)
self.aktiv = nn.GELU()
self.drop = nn.Dropout(dropout)
def forward(self, x):
return self.drop(self.aktiv(self.norm(self.chiziqli(x))))
class Tarmoq(nn.Module):
def __init__(self, kirish, d=64, chuqurlik=3, chiqish=3,
dropout=0.1):
super().__init__()
self.kirish = nn.Linear(kirish, d)
self.bloklar = nn.ModuleList(
[Blok(d, dropout) for _ in range(chuqurlik)])
self.bosh = nn.Linear(d, chiqish)
def forward(self, x):
x = self.kirish(x)
for blok in self.bloklar:
x = blok(x)
return self.bosh(x)
def main() -> None:
torch.manual_seed(0)
print("=== 1. Chuqurlik bitta son bilan ===")
print(f" {'chuqurlik':>10} {'parametr':>10} {'modullar':>10}")
for ch in [1, 2, 4, 8]:
m = Tarmoq(20, chuqurlik=ch)
print(f" {ch:>10} {sum(p.numel() for p in m.parameters()):>10} "
f"{len(list(m.modules())):>10}")
print("\n=== 2. state_dict kalitlari tartibli ===")
m = Tarmoq(20, chuqurlik=2)
for kalit in list(m.state_dict())[:8]:
print(f" {kalit}")
print(f" ... jami {len(m.state_dict())} ta kalit")
print("\n=== 3. Blokni alohida sinash ===")
b = Blok(64)
x = torch.randn(32, 64)
b.eval()
with torch.no_grad():
chiqish = b(x)
print(f" kirish shakli: {tuple(x.shape)}")
print(f" chiqish shakli: {tuple(chiqish.shape)}")
print(f" kirish std: {x.std().item():.4f}")
print(f" chiqish std: {chiqish.std().item():.4f}")
print(" blok mustaqil sinalishi mumkin")
print("\n=== 4. Qatlamlar bo'ylab signal ===")
m = Tarmoq(20, d=64, chuqurlik=6)
m.eval()
X = torch.randn(256, 20)
with torch.no_grad():
h = m.kirish(X)
print(f" {'bosqich':<12} {'std':>10} {'o_rtacha':>10}")
print(f" {'kirish':<12} {h.std().item():>10.4f} "
f"{h.mean().item():>10.4f}")
for i, blok in enumerate(m.bloklar):
h = blok(h)
print(f" {f'blok {i}':<12} {h.std().item():>10.4f} "
f"{h.mean().item():>10.4f}")
print("\n=== 5. Blokni almashtirish ===")
class BoshqaBlok(nn.Module):
def __init__(self, d, dropout=0.1):
super().__init__()
self.a = nn.Linear(d, d * 2)
self.b = nn.Linear(d * 2, d)
self.norm = nn.LayerNorm(d)
def forward(self, x):
return self.norm(self.b(torch.relu(self.a(x))))
m2 = Tarmoq(20, d=64, chuqurlik=3)
m2.bloklar = nn.ModuleList([BoshqaBlok(64) for _ in range(3)])
print(f" {'variant':<16} {'parametr':>10} {'chiqish shakli':>16}")
for nom, mod in [("Blok", Tarmoq(20, d=64, chuqurlik=3)),
("BoshqaBlok", m2)]:
mod.eval()
with torch.no_grad():
sh = tuple(mod(X).shape)
print(f" {nom:<16} "
f"{sum(p.numel() for p in mod.parameters()):>10} "
f"{str(sh):>16}")
print(" ⭐ Bloklar almashtiriladigan bo'lsa tajriba oson")
print("\n=== 6. Dropout rejimi barcha blokka tarqaladi ===")
m3 = Tarmoq(20, chuqurlik=3, dropout=0.5)
m3.train()
print(f" train() da bloklar[0].drop.training: "
f"{m3.bloklar[0].drop.training}")
m3.eval()
print(f" eval() da: {m3.bloklar[0].drop.training}")
with torch.no_grad():
m3.train()
a1, a2 = m3(X[:4]), m3(X[:4])
m3.eval()
b1, b2 = m3(X[:4]), m3(X[:4])
print(f" train() da ikki yurish bir xilmi: "
f"{torch.allclose(a1, a2)}")
print(f" eval() da ikki yurish bir xilmi: "
f"{torch.allclose(b1, b2)}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Chuqurlik bitta son bilan ===
chuqurlik parametr modullar
1 5827 9
2 10115 14
4 18691 24
8 35843 44
=== 2. state_dict kalitlari tartibli ===
kirish.weight
kirish.bias
bloklar.0.chiziqli.weight
bloklar.0.chiziqli.bias
bloklar.0.norm.weight
bloklar.0.norm.bias
bloklar.1.chiziqli.weight
bloklar.1.chiziqli.bias
... jami 12 ta kalit
=== 3. Blokni alohida sinash ===
kirish shakli: (32, 64)
chiqish shakli: (32, 64)
kirish std: 0.9951
chiqish std: 0.5913
blok mustaqil sinalishi mumkin
=== 4. Qatlamlar bo'ylab signal ===
bosqich std o_rtacha
kirish 0.5771 0.0271
blok 0 0.5859 0.2855
blok 1 0.5772 0.2863
blok 2 0.5826 0.2844
blok 3 0.5767 0.2860
blok 4 0.5751 0.2823
blok 5 0.5733 0.2856
=== 5. Blokni almashtirish ===
variant parametr chiqish shakli
Blok 14403 (256, 3)
BoshqaBlok 51651 (256, 3)
⭐ Bloklar almashtiriladigan bo'lsa tajriba oson
=== 6. Dropout rejimi barcha blokka tarqaladi ===
train() da bloklar[0].drop.training: True
eval() da: False
train() da ikki yurish bir xilmi: False
eval() da ikki yurish bir xilmi: TrueNima ko'rsatdi: 2.1, 2.6-bo'limlar.
Misol 2 — Residual ulanish
"""x + f(x) chuqur tarmoqda nima o'zgartiradi (real torch)."""
import torch
import torch.nn as nn
class OddiyBlok(nn.Module):
def __init__(self, d):
super().__init__()
self.norm = nn.LayerNorm(d)
self.tarmoq = nn.Sequential(nn.Linear(d, d * 2), nn.GELU(),
nn.Linear(d * 2, d))
def forward(self, x):
return self.tarmoq(self.norm(x))
class ResidualBlok(nn.Module):
def __init__(self, d):
super().__init__()
self.norm = nn.LayerNorm(d)
self.tarmoq = nn.Sequential(nn.Linear(d, d * 2), nn.GELU(),
nn.Linear(d * 2, d))
def forward(self, x):
return x + self.tarmoq(self.norm(x))
def yasa(blok_sinfi, kirish=20, d=64, chuqurlik=12, chiqish=3, seed=0):
torch.manual_seed(seed)
class M(nn.Module):
def __init__(self):
super().__init__()
self.kirish = nn.Linear(kirish, d)
self.bloklar = nn.ModuleList(
[blok_sinfi(d) for _ in range(chuqurlik)])
self.bosh = nn.Linear(d, chiqish)
def forward(self, x):
x = self.kirish(x)
for b in self.bloklar:
x = b(x)
return self.bosh(x)
return M()
def main() -> None:
torch.manual_seed(0)
X = torch.randn(256, 20)
y = torch.randint(0, 3, (256,))
kriteriy = nn.CrossEntropyLoss()
print("=== 1. Gradient normalari qatlamlar bo'ylab ===")
print(f" {'blok':>6} {'oddiy':>14} {'residual':>14}")
normalar = {}
for nom, sinf in [("oddiy", OddiyBlok), ("residual", ResidualBlok)]:
m = yasa(sinf)
m.train()
kriteriy(m(X), y).backward()
normalar[nom] = [
b.tarmoq[0].weight.grad.norm().item() for b in m.bloklar]
for i in [0, 2, 5, 8, 11]:
print(f" {i:>6} {normalar['oddiy'][i]:>14.3e} "
f"{normalar['residual'][i]:>14.3e}")
print("\n=== 2. Birinchi va oxirgi blok nisbati ===")
print(f" {'variant':<12} {'1-blok':>13} {'oxirgi':>13} "
f"{'nisbat':>13}")
for nom in ["oddiy", "residual"]:
n = normalar[nom]
print(f" {nom:<12} {n[0]:>13.3e} {n[-1]:>13.3e} "
f"{n[0] / n[-1]:>13.3e}")
print(" residual da gradient pastki bloklarga ham YETADI")
print("\n=== 3. Signal masshtabi ===")
print(f" {'blok':>6} {'oddiy std':>13} {'residual std':>14}")
oqimlar = {}
for nom, sinf in [("oddiy", OddiyBlok), ("residual", ResidualBlok)]:
m = yasa(sinf)
m.eval()
with torch.no_grad():
h = m.kirish(X)
qator = []
for b in m.bloklar:
h = b(h)
qator.append(h.std().item())
oqimlar[nom] = qator
for i in [0, 2, 5, 8, 11]:
print(f" {i:>6} {oqimlar['oddiy'][i]:>13.4f} "
f"{oqimlar['residual'][i]:>14.4f}")
print("\n=== 4. O'rgatish tezligi ===")
print(f" {'variant':<12} {'boshlangich':>12}", end="")
for d in [20, 60, 150]:
print(f" {f'{d} qadam':>11}", end="")
print()
for nom, sinf in [("oddiy", OddiyBlok), ("residual", ResidualBlok)]:
m = yasa(sinf)
opt = torch.optim.AdamW(m.parameters(), lr=0.003)
m.train()
boshlangich = kriteriy(m(X), y).item()
chiqishlar = []
for qadam in range(1, 151):
opt.zero_grad()
loss = kriteriy(m(X), y)
loss.backward()
opt.step()
if qadam in (20, 60, 150):
chiqishlar.append(loss.item())
print(f" {nom:<12} {boshlangich:>12.4f}", end="")
for c in chiqishlar:
print(f" {c:>11.4f}", end="")
print()
print("\n=== 5. Chuqurlik ortganda ===")
print(f" {'chuqurlik':>10} {'oddiy (80 qadam)':>18} "
f"{'residual (80 qadam)':>21}")
for ch in [4, 12, 24]:
qiymatlar = []
for sinf in [OddiyBlok, ResidualBlok]:
m = yasa(sinf, chuqurlik=ch)
opt = torch.optim.AdamW(m.parameters(), lr=0.003)
m.train()
for _ in range(80):
opt.zero_grad()
loss = kriteriy(m(X), y)
loss.backward()
opt.step()
qiymatlar.append(loss.item())
print(f" {ch:>10} {qiymatlar[0]:>18.4f} {qiymatlar[1]:>21.4f}")
print(" chuqurlik ortganda farq KATTALASHADI")
print("\n=== 6. Shakl mos kelmasa ===")
class NotogriResidual(nn.Module):
def __init__(self, kirish, chiqish):
super().__init__()
self.f = nn.Linear(kirish, chiqish)
def forward(self, x):
return x + self.f(x)
try:
NotogriResidual(8, 16)(torch.randn(4, 8))
print(" xato chiqmadi (kutilmagan)")
except RuntimeError as xato:
print(f" RuntimeError: {str(xato)[:58]}")
class ProyeksiyaBilan(nn.Module):
def __init__(self, kirish, chiqish):
super().__init__()
self.f = nn.Linear(kirish, chiqish)
self.proj = (nn.Identity() if kirish == chiqish
else nn.Linear(kirish, chiqish, bias=False))
def forward(self, x):
return self.proj(x) + self.f(x)
p = ProyeksiyaBilan(8, 16)
print(f" proyeksiya bilan: {tuple(p(torch.randn(4, 8)).shape)}")
print(f" o'lcham teng bo'lsa proj turi: "
f"{type(ProyeksiyaBilan(8, 8).proj).__name__}")
print(" ⭐ Shakl mos kelmasa proyeksiya qo'shiladi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Gradient normalari qatlamlar bo'ylab ===
blok oddiy residual
0 8.445e-02 1.773e-01
2 1.038e-01 1.706e-01
5 1.260e-01 1.473e-01
8 1.342e-01 1.388e-01
11 1.651e-01 1.406e-01
=== 2. Birinchi va oxirgi blok nisbati ===
variant 1-blok oxirgi nisbat
oddiy 8.445e-02 1.651e-01 5.117e-01
residual 1.773e-01 1.406e-01 1.261e+00
residual da gradient pastki bloklarga ham YETADI
=== 3. Signal masshtabi ===
blok oddiy std residual std
0 0.2125 0.6444
2 0.2078 0.7085
5 0.2054 0.7671
8 0.2025 0.8471
11 0.2144 0.9394
=== 4. O'rgatish tezligi ===
variant boshlangich 20 qadam 60 qadam 150 qadam
oddiy 1.1069 1.0677 1.0876 1.0872
residual 1.1709 0.0039 0.0005 0.0000
=== 5. Chuqurlik ortganda ===
chuqurlik oddiy (80 qadam) residual (80 qadam)
4 0.0000 0.0000
12 1.0873 0.0001
24 1.0954 0.0000
chuqurlik ortganda farq KATTALASHADI
=== 6. Shakl mos kelmasa ===
RuntimeError: The size of tensor a (8) must match the size of tensor b (
proyeksiya bilan: (4, 16)
o'lcham teng bo'lsa proj turi: Identity
⭐ Shakl mos kelmasa proyeksiya qo'shiladiNima ko'rsatdi: 2.2-bo'lim.
Misol 3 — Parametrlarni baham ko'rish va murakkab forward
"""Bir xil og'irlik ikki joyda; bir necha kirish va chiqish."""
import torch
import torch.nn as nn
class Nusxa(nn.Module):
def __init__(self, d):
super().__init__()
self.a = nn.Linear(d, d)
self.b = nn.Linear(d, d)
def forward(self, x):
return self.b(torch.relu(self.a(x)))
class Baham(nn.Module):
def __init__(self, d):
super().__init__()
self.umumiy = nn.Linear(d, d)
def forward(self, x):
return self.umumiy(torch.relu(self.umumiy(x)))
class Siamese(nn.Module):
"""Ikki kirishni BIR XIL koder bilan ishlaydi."""
def __init__(self, kirish, d=32):
super().__init__()
self.koder = nn.Sequential(
nn.Linear(kirish, d), nn.ReLU(), nn.Linear(d, d))
def forward(self, x1, x2):
e1, e2 = self.koder(x1), self.koder(x2)
masofa = (e1 - e2).pow(2).sum(dim=1).sqrt()
return {"e1": e1, "e2": e2, "masofa": masofa}
class KopBosh(nn.Module):
"""Bir tana, uch bosh (ko'p vazifali)."""
def __init__(self, kirish, d=64):
super().__init__()
self.tana = nn.Sequential(
nn.Linear(kirish, d), nn.ReLU(), nn.Linear(d, d), nn.ReLU())
self.sinf = nn.Linear(d, 3)
self.regressiya = nn.Linear(d, 1)
self.embedding = nn.Linear(d, 8)
def forward(self, x, boshlar=("sinf",)):
z = self.tana(x)
natija = {}
if "sinf" in boshlar:
natija["logits"] = self.sinf(z)
if "regressiya" in boshlar:
natija["qiymat"] = self.regressiya(z).squeeze(-1)
if "embedding" in boshlar:
natija["embedding"] = self.embedding(z)
return natija
def main() -> None:
torch.manual_seed(0)
print("=== 1. Nusxa va baham ko'rish ===")
n, b = Nusxa(16), Baham(16)
print(f" {'model':<10} {'parametr':>10} {'kalitlar'}")
print(f" {'Nusxa':<10} {sum(p.numel() for p in n.parameters()):>10} "
f"{list(n.state_dict())}")
print(f" {'Baham':<10} {sum(p.numel() for p in b.parameters()):>10} "
f"{list(b.state_dict())}")
print(" baham ko'rishda parametrlar BIR MARTA saqlanadi")
print("\n=== 2. Gradient ikki yo'ldan yig'iladi ===")
x = torch.randn(8, 16)
y = torch.randn(8, 16)
b.zero_grad()
((b(x) - y) ** 2).mean().backward()
umumiy_grad = b.umumiy.weight.grad.norm().item()
bitta = Baham(16)
bitta.load_state_dict(b.state_dict())
bitta.zero_grad()
((bitta.umumiy(x) - y) ** 2).mean().backward()
print(f" ikki marta chaqirilganda grad normasi: "
f"{umumiy_grad:.6f}")
print(f" bir marta chaqirilganda: "
f"{bitta.umumiy.weight.grad.norm().item():.6f}")
print(" gradient IKKALA chaqiruvdan qo'shiladi")
print("\n=== 3. self.b = self.a ===")
class Biriktirilgan(nn.Module):
def __init__(self, d):
super().__init__()
self.a = nn.Linear(d, d)
self.b = self.a
def forward(self, x):
return self.b(torch.relu(self.a(x)))
bi = Biriktirilgan(16)
print(f" kalitlar: {list(bi.state_dict())}")
print(f" a va b bir obyektmi: {bi.a is bi.b}")
print(f" parametr soni: {sum(p.numel() for p in bi.parameters())}")
print(" torch takrorlanishni o'zi aniqlaydi")
print("\n=== 4. Siamese: ikki kirish ===")
s = Siamese(12)
x1, x2 = torch.randn(6, 12), torch.randn(6, 12)
natija = s(x1, x2)
print(f" {'kalit':<10} {'shakl':>12}")
for kalit, qiymat in natija.items():
print(f" {kalit:<10} {str(tuple(qiymat.shape)):>12}")
bir_xil = s(x1, x1)
print(f" bir xil kirishda masofa: "
f"{bir_xil['masofa'].abs().max().item():.6f}")
print(f" koder parametrlari: "
f"{sum(p.numel() for p in s.koder.parameters())}")
print("\n=== 5. Ko'p boshli model ===")
k = KopBosh(20)
X = torch.randn(10, 20)
print(f" {'so_ralgan boshlar':<32} {'qaytgan kalitlar'}")
for boshlar in [("sinf",), ("sinf", "regressiya"),
("sinf", "regressiya", "embedding")]:
chiqish = k(X, boshlar)
print(f" {str(boshlar):<32} {sorted(chiqish)}")
print("\n=== 6. Ko'p vazifali loss ===")
y_sinf = torch.randint(0, 3, (10,))
y_qiymat = torch.randn(10)
chiqish = k(X, ("sinf", "regressiya"))
l1 = nn.CrossEntropyLoss()(chiqish["logits"], y_sinf)
l2 = nn.MSELoss()(chiqish["qiymat"], y_qiymat)
print(f" {'komponent':<20} {'qiymat':>10}")
print(f" {'klassifikatsiya':<20} {l1.item():>10.4f}")
print(f" {'regressiya':<20} {l2.item():>10.4f}")
for vazn in [0.1, 0.5, 1.0]:
print(f" jami (vazn={vazn}): {(l1 + vazn * l2).item():.4f}")
print(" ⭐ Vaznlarni tanlash - ko'p vazifali o'rgatishning kaliti")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Nusxa va baham ko'rish ===
model parametr kalitlar
Nusxa 544 ['a.weight', 'a.bias', 'b.weight', 'b.bias']
Baham 272 ['umumiy.weight', 'umumiy.bias']
baham ko'rishda parametrlar BIR MARTA saqlanadi
=== 2. Gradient ikki yo'ldan yig'iladi ===
ikki marta chaqirilganda grad normasi: 0.466916
bir marta chaqirilganda: 1.006205
gradient IKKALA chaqiruvdan qo'shiladi
=== 3. self.b = self.a ===
kalitlar: ['a.weight', 'a.bias', 'b.weight', 'b.bias']
a va b bir obyektmi: True
parametr soni: 272
torch takrorlanishni o'zi aniqlaydi
=== 4. Siamese: ikki kirish ===
kalit shakl
e1 (6, 32)
e2 (6, 32)
masofa (6,)
bir xil kirishda masofa: 0.000000
koder parametrlari: 1472
=== 5. Ko'p boshli model ===
so_ralgan boshlar qaytgan kalitlar
('sinf',) ['logits']
('sinf', 'regressiya') ['logits', 'qiymat']
('sinf', 'regressiya', 'embedding') ['embedding', 'logits', 'qiymat']
=== 6. Ko'p vazifali loss ===
komponent qiymat
klassifikatsiya 1.1359
regressiya 0.5146
jami (vazn=0.1): 1.1874
jami (vazn=0.5): 1.3932
jami (vazn=1.0): 1.6505
⭐ Vaznlarni tanlash - ko'p vazifali o'rgatishning kalitiNima ko'rsatdi: 2.3, 2.4-bo'limlar.
Misol 4 — Konfiguratsiyadan arxitektura
"""Model lug'at bilan ta'riflanadi va u bilan saqlanadi."""
import shutil
import tempfile
from pathlib import Path
import torch
import torch.nn as nn
class Blok(nn.Module):
def __init__(self, d, kengaytirish=2, dropout=0.1, residual=True,
aktivatsiya="gelu"):
super().__init__()
self.residual = residual
aktivlar = {"relu": nn.ReLU, "gelu": nn.GELU, "silu": nn.SiLU}
self.norm = nn.LayerNorm(d)
self.tarmoq = nn.Sequential(
nn.Linear(d, d * kengaytirish),
aktivlar[aktivatsiya](),
nn.Dropout(dropout),
nn.Linear(d * kengaytirish, d))
def forward(self, x):
chiqish = self.tarmoq(self.norm(x))
return x + chiqish if self.residual else chiqish
class Tarmoq(nn.Module):
def __init__(self, kirish, chiqish, d=64, chuqurlik=3,
kengaytirish=2, dropout=0.1, residual=True,
aktivatsiya="gelu"):
super().__init__()
self.konfig = {"kirish": kirish, "chiqish": chiqish, "d": d,
"chuqurlik": chuqurlik,
"kengaytirish": kengaytirish,
"dropout": dropout, "residual": residual,
"aktivatsiya": aktivatsiya}
self.kirish_qatlam = nn.Linear(kirish, d)
self.bloklar = nn.ModuleList(
[Blok(d, kengaytirish, dropout, residual, aktivatsiya)
for _ in range(chuqurlik)])
self.norm = nn.LayerNorm(d)
self.bosh = nn.Linear(d, chiqish)
self.apply(self._boshla)
@staticmethod
def _boshla(m):
if isinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight, nonlinearity="relu")
if m.bias is not None:
nn.init.zeros_(m.bias)
def forward(self, x):
x = self.kirish_qatlam(x)
for blok in self.bloklar:
x = blok(x)
return self.bosh(self.norm(x))
def main() -> None:
torch.manual_seed(0)
papka = Path(tempfile.mkdtemp(prefix="torch_konfig_"))
try:
print("=== 1. Konfiguratsiya variantlari ===")
variantlar = [
{"d": 32, "chuqurlik": 2},
{"d": 64, "chuqurlik": 4},
{"d": 64, "chuqurlik": 4, "kengaytirish": 4},
{"d": 128, "chuqurlik": 6, "residual": False},
]
X = torch.randn(64, 20)
print(f" {'konfig':<48} {'parametr':>10}")
for v in variantlar:
m = Tarmoq(20, 3, **v)
print(f" {str(v):<48} "
f"{sum(p.numel() for p in m.parameters()):>10}")
print("\n=== 2. Aktivatsiyani konfigdan tanlash ===")
print(f" {'aktivatsiya':<14} {'chiqish std':>13} "
f"{'parametr':>10}")
for a in ["relu", "gelu", "silu"]:
m = Tarmoq(20, 3, aktivatsiya=a)
m.eval()
with torch.no_grad():
print(f" {a:<14} {m(X).std().item():>13.4f} "
f"{sum(p.numel() for p in m.parameters()):>10}")
print("\n=== 3. Konfig model bilan saqlanadi ===")
model = Tarmoq(20, 3, d=64, chuqurlik=4, dropout=0.15)
yol = papka / "model.pt"
torch.save({"konfig": model.konfig,
"holat": model.state_dict()}, yol)
print(f" saqlangan konfig: {model.konfig}")
print(f" fayl hajmi: {yol.stat().st_size / 1024:.1f} KB")
print("\n=== 4. Qayta qurish ===")
paket = torch.load(yol, weights_only=False)
qayta = Tarmoq(**paket["konfig"])
natija = qayta.load_state_dict(paket["holat"])
print(f" missing_keys: {natija.missing_keys}")
print(f" unexpected_keys: {natija.unexpected_keys}")
model.eval()
qayta.eval()
with torch.no_grad():
print(f" chiqishlar bir xilmi: "
f"{torch.allclose(model(X), qayta(X))}")
print("\n=== 5. Konfigsiz nima bo'ladi ===")
notogri = Tarmoq(20, 3, d=32, chuqurlik=2)
try:
notogri.load_state_dict(paket["holat"])
print(" xato chiqmadi (kutilmagan)")
except RuntimeError as xato:
print(f" RuntimeError: {str(xato).splitlines()[0][:58]}")
print(" ⭐ Konfigsiz state_dict ni qayerga yuklashni bilmaysiz")
print("\n=== 6. Giperparametr qidiruvi tabiiy bo'ladi ===")
y = torch.randint(0, 3, (64,))
kriteriy = nn.CrossEntropyLoss()
print(f" {'konfig':<34} {'50 qadamdan keyin loss':>24}")
for v in [{"d": 32, "chuqurlik": 2},
{"d": 64, "chuqurlik": 4},
{"d": 64, "chuqurlik": 4, "residual": False}]:
torch.manual_seed(0)
m = Tarmoq(20, 3, **v)
opt = torch.optim.AdamW(m.parameters(), lr=0.005)
m.train()
for _ in range(50):
opt.zero_grad()
loss = kriteriy(m(X), y)
loss.backward()
opt.step()
print(f" {str(v):<34} {loss.item():>24.4f}")
finally:
shutil.rmtree(papka, ignore_errors=True)
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Konfiguratsiya variantlari ===
konfig parametr
{'d': 32, 'chuqurlik': 2} 9347
{'d': 64, 'chuqurlik': 4} 68483
{'d': 64, 'chuqurlik': 4, 'kengaytirish': 4} 134531
{'d': 128, 'chuqurlik': 6, 'residual': False} 400387
=== 2. Aktivatsiyani konfigdan tanlash ===
aktivatsiya chiqish std parametr
relu 1.2676 51779
gelu 1.3919 51779
silu 1.4129 51779
=== 3. Konfig model bilan saqlanadi ===
saqlangan konfig: {'kirish': 20, 'chiqish': 3, 'd': 64, 'chuqurlik': 4, 'kengaytirish': 2, 'dropout': 0.15, 'residual': True, 'aktivatsiya': 'gelu'}
fayl hajmi: 276.3 KB
=== 4. Qayta qurish ===
missing_keys: []
unexpected_keys: []
chiqishlar bir xilmi: True
=== 5. Konfigsiz nima bo'ladi ===
RuntimeError: Error(s) in loading state_dict for Tarmoq:
⭐ Konfigsiz state_dict ni qayerga yuklashni bilmaysiz
=== 6. Giperparametr qidiruvi tabiiy bo'ladi ===
konfig 50 qadamdan keyin loss
{'d': 32, 'chuqurlik': 2} 0.0035
{'d': 64, 'chuqurlik': 4} 0.0011
{'d': 64, 'chuqurlik': 4, 'residual': False} 0.0020Nima ko'rsatdi: 2.5-bo'lim.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Residual — kichik yaxshilanish" | Chuqur tarmoqni mumkin qilgan g'oya |
"self.b = self.a nusxa" |
Bir xil parametrlar |
| "Har blokni qo'lda yozish aniqroq" | Blok naqshi xatoni kamaytiradi |
"forward da Module yaratsa bo'ladi" |
Ro'yxatga olinmaydi |
| "Konfig ortiqcha" | Usiz modelni tiklab bo'lmaydi |
"BatchNorm oldidan bias zararsiz" |
Bekor bo'ladi, parametr behuda |
| "Residual shakl talab qilmaydi" | Bir xil o'lcham shart |
| "Pre-norm va post-norm bir xil" | Pre-norm barqarorroq |
6. Keng tarqalgan xatolar va yechimlari
1. forward da Module yaratish
def forward(self, x): return nn.Linear(4, 2)(x) # ⚠️
# __init__ da yarating # ✅2. Residual shakli
return x + self.f(x) # f: 8 -> 16 # ⚠️
return self.proj(x) + self.f(x) # ✅3. BatchNorm oldidan bias
nn.Linear(d, d), nn.BatchNorm1d(d) # ⚠️
nn.Linear(d, d, bias=False), nn.BatchNorm1d(d) # ✅4. in-place amal residualda
x += self.f(x) # ⚠️ autograd buziladi
x = x + self.f(x) # ✅5. Konfigni saqlamaslik
torch.save(model.state_dict(), yol) # ⚠️
torch.save({"konfig": k, "holat": ...}, yol) # ✅6. Bloklarni list ga
self.bloklar = [Blok(d) for _ in range(n)] # ⚠️
self.bloklar = nn.ModuleList([...]) # ✅7. Chuqurlikni qo'lda ko'chirish
self.b1 = Blok(d); self.b2 = Blok(d); ... # ⚠️
self.bloklar = nn.ModuleList([Blok(d) for _ in range(n)]) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 21.1-dars (o'tilgan):
nn.Module - 21.7-dars: Checkpoint va konfig
- 24-qism:
ResNetbloklari - 26-qism: Transformer bloki
- 27-qism: Tied embeddings
8. Eng yaxshi amaliyotlar
Takrorlanuvchini blokka ajrating.
Chuqur tarmoqda residual ishlating.
Shakl mos kelmasa proyeksiya qo'ying.
Konfigni model bilan saqlang.
BatchNormoldidanbias=False.forwarddaModuleyaratmang.Boshlang'ich qiymatni
applybilan.Blokni alohida sinang.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # blok nima?
2. # residual formulasi?
3. # nega residual ishlaydi?
4. # shakl mos kelmasa?
5. # baham ko'rish qanday qilinadi?
6. # gradient baham ko'rishda qanday?
7. # forward da Module yaratsa?
8. # konfig nima uchun?
9. # BatchNorm oldida bias?
10. # pre-norm nima?
11. # ko'p chiqish qanday qaytariladi?
12. # in-place nega yomon?Javoblar
- Takrorlanadigan mantiqiy birlik
x + f(x)- Gradientga qisqa yo'l
- Proyeksiya qo'shiladi
- Bir modulni ikki marta chaqirish
- Ikkala yo'ldan yig'iladi
- Ro'yxatga olinmaydi
- Modelni qayta qurish uchun
- Kerak emas (
bias=False) x + f(norm(x))dictyokituple- Autograd grafini buzadi
Vazifa 2: Xatolarni tuzating
1. def forward(self, x): return nn.Linear(4, 2)(x)
2. return x + self.f(x) # f: 8 -> 16
3. nn.Linear(d, d), nn.BatchNorm1d(d)
4. x += self.f(x)
5. self.bloklar = [Blok(d) for _ in range(n)]Javoblar
1. # __init__ da yarating
2. return self.proj(x) + self.f(x)
3. nn.Linear(d, d, bias=False), nn.BatchNorm1d(d)
4. x = x + self.f(x)
5. self.bloklar = nn.ModuleList([Blok(d) for _ in range(n)])Vazifa 3: Bloklar
Modellang:
- Chuqurlik
- Kalitlar
- Alohida sinash
- Signal
Vazifa 4: Residual
Modellang:
- Gradientlar
- Nisbat
- O'rgatish
- Shakl
Vazifa 5: Baham ko'rish
Modellang:
- Nusxa va baham
- Gradient
- Siamese
- Ko'p bosh
Vazifa 6: Konfig
Modellang:
- Variantlar
- Saqlash
- Qayta qurish
- Qidiruv
Vazifa 7: O'ylash
Residual bloklardan 40 qatlamli tarmoq qurdingiz, lekin u 4 qatlamlidan yomon ishlayapti. Residual bor bo'lsa ham nima noto'g'ri bo'lishi mumkin?
Javob
Residual gradient muammosini yechadi, lekin boshqa hamma narsani emas.
1. Ma'lumot yetarli emas
40 qatlamli tarmoqda parametrlar soni 10 barobar ko'p. Tabular vazifada 5000 namuna bilan bu shunchaki yod olish.
p = sum(x.numel() for x in model.parameters())
print(p, p / len(X_train)) # > 50 bo'lsa juda kattaBu eng ehtimolli sabab va uni tekshirish bir qator.
2. Residual to'g'ri joyda emas
# noto'g'ri: norm residualdan KEYIN
return self.norm(x + self.f(x)) # post-norm, chuqurda beqaror
# to'g'ri: pre-norm
return x + self.f(self.norm(x))Post-norm bilan 40 qatlam o'rgatish uchun warmup va ehtiyotkor lr kerak — bu Transformer maqolasidagi klassik muammo.
3. lr chuqurlikka moslanmagan
Chuqur tarmoqda bir xil lr juda katta bo'lishi mumkin. Har blok chiqishga qo'shilgani uchun signal yig'iladi:
# 40 ta blok x + f(x) -> chiqish dispersiyasi ~40 barobar
print([h.std().item() for h in oraliq_chiqishlar])Yechim: blok chiqishini masshtablash yoki lr ni kamaytirish.
self.masshtab = nn.Parameter(torch.zeros(1)) # noldan boshlanadi
return x + self.masshtab * self.f(x) # LayerScaleBu LayerScale usuli — blok boshida hech narsa qilmaydi va asta faollashadi.
4. Davrlar yetarli emas
40 qatlamli tarmoq 4 qatlamlidan sekinroq yaqinlashadi. 100 davrda 4 qatlamli tugagan bo'lsa, 40 qatlamliga 300 kerak bo'lishi mumkin.
Tekshiruv tartibi:
| Qadam | Tekshiruv | Nimani ko'rsatadi |
|---|---|---|
| 1 | parametr / namuna |
Yod olish xavfi |
| 2 | O'quv va val loss farqi | Yodlash yuz berganmi |
| 3 | Blok chiqishlari std |
Signal portlayaptimi |
| 4 | Birinchi blok gradienti | Gradient yetayaptimi |
| 5 | lr ni 3x kamaytirish |
Beqarorlik sabab bo'lganmi |
| 6 | Davrlarni 3x oshirish | Shunchaki sekinmi |
Eng muhim fikr: chuqurlik bepul emas. U ma'lumot, vaqt va ehtiyotkor sozlash talab qiladi. Tabular vazifada 40 qatlam deyarli hech qachon kerak emas — 2-4 qatlam yetadi (20.3-dars).
Residual ulanish sizga chuqur tarmoqni o'rgatish imkonini beradi, lekin u chuqurlik foydali ekanini kafolatlamaydi.
Nimani mustahkamlaydi: 2.1, 2.2-bo'limlar.
Xulosa
Bu darsda bloklar va arxitektura naqshlarini ko'rdik.
Eng muhim uch fikr:
Blok — arxitektura kodining asosiy birligi. Takrorlanuvchi qismni alohida
Modulega ajratsangiz, chuqurlik bitta songa aylanadi,state_dictkalitlari tartibli bo'ladi (bloklar.0.chiziqli.weight), blokni alohida sinash mumkin bo'ladi va uni boshqasiga almashtirish bir qator kodga tushadi.x + f(x)gradientga qisqa yo'l beradi. Hosila1 + df/dxbo'lgani uchun gradient har blokda kamida birlik koeffitsiyent bilan o'tadi. 2-misolda 12 qatlamli tarmoqda birinchi va oxirgi blok gradientlari nisbati residualsiz0.51, residual bilan1.26bo'ldi. Farqning asl ko'rinishi esa o'rgatishda chiqdi: residualsiz tarmoq 150 qadamda loss ni1.107dan atigi1.087ga tushirdi — ya'ni amalda o'rganmadi — residual bilan esa loss 20 qadamdayoq0.004ga tushdi. 4 qatlamda ikkalasi ham o'rgandi; farq chuqurlik ortganda paydo bo'ladi.Konfig modelning bir qismi.
state_dictfaqat tensorlarni saqlaydi — uni qayerga yuklashni bilmasangiz, u foydasiz. Arxitektura lug'atini model bilan birga saqlash modelni bir qatorda tiklash imkonini beradi va giperparametr qidiruvini tabiiy qiladi.
Keyingi darsda Dataset va DataLoader: ma'lumotni tarmoqqa uzatishning standart yo'li, batch yig'ish, aralashtirish, collate_fn va namuna olish strategiyalari.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!