6-bo‘lim
Transformer bloki
LayerNorm, qoldiq ulanish, FFN va ularning tartibi; pre-norm va post-norm farqi hamda parametrlarning qayerda joylashgani.
Ushbu bo‘lim mundarijasi
E'tibor tayyor. Lekin undan model yasash uchun yana uchta qism kerak: normallash, qoldiq ulanish va FFN. Ular e'tibordan kam mashhur, lekin ularsiz chuqur tarmoq o'qimaydi.
LayerNorm #
import torch
import torch.nn as nn
x = torch.tensor([[1.0, 2.0, 3.0, 10.0]])
ln = nn.LayerNorm(4)
print(" kirish:", x.tolist()[0])
print(" o'rtacha:", round(x.mean().item(), 3), "| std:", round(x.std(unbiased=False).item(), 3))
y = ln(x)
print(" LayerNorm:", [round(v, 3) for v in y.tolist()[0]])
print(" yangi o'rtacha:", round(y.mean().item(), 4), "| std:", round(y.std(unbiased=False).item(), 3))
kirish: [1.0, 2.0, 3.0, 10.0]
o'rtacha: 4.0 | std: 3.536
LayerNorm: [-0.849, -0.566, -0.283, 1.697]
yangi o'rtacha: 0.0 | std: 1.0
Qiymatlar o'rtachasi 0, standart og'ishi 1 bo'lib qoldi. 10.0
hamon eng katta, lekin u endi boshqalarni bosib ketmaydi.
Nega BatchNorm emas #
PyTorch darsligining 20-bo'limida BatchNorm2d ishlatgan edik. Matnda
esa LayerNorm ishlatiladi. Farqi muhim:
b = torch.randn(4, 8)
print(" paket 4 ta bilan:", [round(v, 4) for v in nn.LayerNorm(8)(b)[0][:3].tolist()])
print(" paket 1 ta bilan:", [round(v, 4) for v in nn.LayerNorm(8)(b[:1])[0][:3].tolist()])
paket 4 ta bilan: [1.3737, 1.0601, 0.6418]
paket 1 ta bilan: [1.3737, 1.0601, 0.6418]
-> bir xil: LayerNorm paket hajmiga bog'liq emas
| BatchNorm | LayerNorm | |
|---|---|---|
| Nima bo'yicha normallaydi | Butun paket bo'yicha | Har namuna bo'yicha |
| Paket hajmiga bog'liqmi | Ha | Yo'q |
| Har xil uzunlikdagi matn | Muammoli | Muammosiz |
train/eval farqi | Bor | Yo'q |
Matnda jumlalar uzunligi har xil va paketda [PAD] tokenlar bor.
BatchNorm o'rtachani ular bo'ylab hisoblab, natijani buzardi.
Qoldiq ulanish #
G'oya bir qatorda: x + f(x) deb yozish, shunchaki f(x) emas.
d = 16
chuqur = [nn.Linear(d, d) for _ in range(20)]
x = torch.randn(1, 4, d)
v1 = x.clone()
for q in chuqur:
v1 = torch.tanh(q(v1))
print(f" 20 qatlam qoldiqsiz -> signal kattaligi: {v1.abs().mean():.6f}")
v2 = x.clone()
for q in chuqur:
v2 = v2 + torch.tanh(q(v2))
print(f" 20 qatlam qoldiq bilan -> signal kattaligi: {v2.abs().mean():.6f}")
20 qatlam qoldiqsiz -> signal kattaligi: 0.159266
20 qatlam qoldiq bilan -> signal kattaligi: 2.769192
Qoldiqsiz holatda signal 20 qatlamda so'nib ketdi. Qoldiq bilan u saqlanib qoldi.
Oldinga tarqalishda signal so'nsa, orqaga tarqalishda gradient ham so'nadi. Bu PyTorch darsligining 4-bo'limida ko'rgan zanjir qoidasining oqibati: ko'p marta kichik songa ko'paytirilsa, natija nolga intiladi.
x + f(x) yozuvida esa hosila 1 + f'(x) bo'ladi. O'sha 1 gradient
uchun to'g'ridan-to'g'ri yo'l ochib beradi - u qancha qatlam bo'lsa ham
so'nmaydi.
Aynan shu g'oya 2015-yilda ResNet ni yuzlab qatlamgacha chuqurlashtirishga imkon bergan edi.
FFN #
E'tibor so'zlar orasida ma'lumot almashtiradi. FFN esa har so'zni alohida qayta ishlaydi:
d_model, d_ff = 64, 256
ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model),
)
print(f" {d_model} -> {d_ff} -> {d_model} (kengaytirish nisbati {d_ff//d_model}x)")
print(" parametrlar:", sum(p.numel() for p in ffn.parameters()))
64 -> 256 -> 64 (kengaytirish nisbati 4x)
parametrlar: 33088
Kengaytirish nisbati deyarli har doim 4x. Model o'lchamni vaqtincha 4 barobar oshiradi, nochiziqlikni qo'llaydi, keyin qaytaradi.
| Qism | Vazifasi |
|---|---|
| E'tibor | So'zlar orasida ma'lumot almashadi |
| FFN | Har so'zni alohida qayta ishlaydi |
Ikkalasi birga kerak: e'tibor yig'adi, FFN o'ylaydi.
To'liq blok #
class Blok(nn.Module):
def __init__(self, d_model, n_head, d_ff):
super().__init__()
self.ln1 = nn.LayerNorm(d_model)
self.attn = nn.MultiheadAttention(d_model, n_head, batch_first=True)
self.ln2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff), nn.GELU(), nn.Linear(d_ff, d_model))
def forward(self, x, mask=None):
h = self.ln1(x)
a, _ = self.attn(h, h, h, attn_mask=mask, need_weights=False)
x = x + a
x = x + self.ffn(self.ln2(x))
return x
kirish: (2, 6, 64) -> chiqish: (2, 6, 64)
blok parametrlari: 49,984
Parametrlar qayerda #
attn_p = sum(p.numel() for p in blok.attn.parameters())
ffn_p = sum(p.numel() for p in blok.ffn.parameters())
e'tibor: 16,640 (33.3%)
FFN: 33,088 (66.2%)
LayerNorm: 256 ( 0.5%)
Bu kutilmagan natija. Transformer «e'tibor modeli» deb ataladi, lekin parametrlarning uchdan ikki qismi FFN da.
E'tibor - arxitekturaning yangi g'oyasi, lekin og'irlikning katta qismi oddiy ikki qatlamli tarmoqda yotadi.
Pre-norm va post-norm #
Asl 2017-yilgi maqolada LayerNorm qoldiqdan keyin turardi:
| Uslub | Formula | Holati |
|---|---|---|
| Post-norm (2017) | norm(x + attn(x)) | O'qitish qiyin, isitish (warmup) talab qiladi |
| Pre-norm (hozir) | x + attn(norm(x)) | Barqaror, zamonaviy modellarda standart |
Pre-norm da qoldiq yo'li butunlay toza qoladi - unda hech qanday normallash yo'q. Gradient boshdan oxirigacha to'sqinliksiz oqadi.
Post-norm da esa har qatlamda gradient LayerNorm dan o'tishga majbur.
Shuning uchun asl transformerni o'qitish uchun lr ni asta-sekin
oshiruvchi maxsus jadval kerak edi.
Yangi kod yozayotgan bo'lsangiz - pre-norm.
nn.LayerNormni qo'llab, o'rtacha va og'ish qanday o'zganini tekshiring.- Bir xil namunani 4 ta va 1 ta paketda normallab, natijani solishtiring.
- LayerNorm va BatchNorm farqini jadval qilib yozing.
- Matnda nega BatchNorm noqulay ekanini tushuntiring.
- 20 qatlamni qoldiqsiz va qoldiq bilan o'tkazib, signal kattaligini o'lchang.
- Qoldiq ulanish gradientga qanday yordam berishini yozing.
4xkengaytirishli FFN yaratib, parametrlarini sanang.- E'tibor va FFN ning vazifalari farqini tushuntiring.
- To'liq blok yozib, kirish va chiqish shakli bir xilligini tekshiring.
- Blokdagi parametrlarni qismlarga ajratib, ulushini hisoblang.
Xulosa #
- LayerNorm har namunani alohida normallaydi: o'rtacha 0, og'ish 1.
- U paket hajmiga bog'liq emas - shuning uchun matnda BatchNorm o'rniga ishlatiladi.
- Qoldiq ulanish
x + f(x)signalning so'nishiga yo'l qo'ymaydi. - Bizning o'lchovda 20 qatlamdan keyin signal 0.159 va 2.769 bo'ldi.
- Hosila
1 + f'(x)bo'lgani uchun gradient uchun to'g'ridan-to'g'ri yo'l ochiladi. - FFN har so'zni alohida qayta ishlaydi; kengaytirish nisbati odatda 4x.
- E'tibor so'zlar orasida ma'lumot almashadi, FFN esa uni qayta ishlaydi.
- Blokda ikkita qoldiq ulanish bor: e'tibor va FFN atrofida.
- Parametrlarning 66% i FFN da, atigi 33% i e'tiborda.
- Zamonaviy modellar pre-norm ishlatadi - qoldiq yo'li toza qoladi va o'qitish barqaror.
Keyingi bo'limda hal qilinmagan bitta muammoga qaytamiz: model hali ham so'z tartibini bilmaydi.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.