6-bo‘lim

Transformer bloki

LayerNorm, qoldiq ulanish, FFN va ularning tartibi; pre-norm va post-norm farqi hamda parametrlarning qayerda joylashgani.

🕑 9 daqiqa o‘qish 📄 644 so‘z 👁 3 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. LayerNorm
  2. Nega BatchNorm emas
  3. Qoldiq ulanish
  4. FFN
  5. To'liq blok
  6. Parametrlar qayerda
  7. Pre-norm va post-norm
  8. Xulosa

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 #

Python
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))
Natija
  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:

Python
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()])
Natija
  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
BatchNormLayerNorm
Nima bo'yicha normallaydiButun paket bo'yichaHar namuna bo'yicha
Paket hajmiga bog'liqmiHaYo'q
Har xil uzunlikdagi matnMuammoliMuammosiz
train/eval farqiBorYo'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.

Python
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}")
Natija
  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.

Gradient uchun ham xuddi shunday

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:

Python
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()))
Natija
  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.

QismVazifasi
E'tiborSo'zlar orasida ma'lumot almashadi
FFNHar so'zni alohida qayta ishlaydi

Ikkalasi birga kerak: e'tibor yig'adi, FFN o'ylaydi.

To'liq blok #

Python
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
Natija
  kirish: (2, 6, 64) -> chiqish: (2, 6, 64)
  blok parametrlari: 49,984
Pre-norm blok: x + attn(norm(x)), keyin x + ffn(norm(x)) kirish x LayerNorm Multi-head attention + qoldiq LayerNorm FFN (4x kengaytirish) + Parametrlar e'tibor: 33.3% FFN: 66.2% LayerNorm: 0.5%
Ikkita qoldiq ulanish - biri e'tibor, biri FFN atrofida

Parametrlar qayerda #

Python
attn_p = sum(p.numel() for p in blok.attn.parameters())
ffn_p = sum(p.numel() for p in blok.ffn.parameters())
Natija
  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:

UslubFormulaHolati
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 ni tanlang

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.

Amaliy topshiriq
  1. nn.LayerNorm ni qo'llab, o'rtacha va og'ish qanday o'zganini tekshiring.
  2. Bir xil namunani 4 ta va 1 ta paketda normallab, natijani solishtiring.
  3. LayerNorm va BatchNorm farqini jadval qilib yozing.
  4. Matnda nega BatchNorm noqulay ekanini tushuntiring.
  5. 20 qatlamni qoldiqsiz va qoldiq bilan o'tkazib, signal kattaligini o'lchang.
  6. Qoldiq ulanish gradientga qanday yordam berishini yozing.
  7. 4x kengaytirishli FFN yaratib, parametrlarini sanang.
  8. E'tibor va FFN ning vazifalari farqini tushuntiring.
  9. To'liq blok yozib, kirish va chiqish shakli bir xilligini tekshiring.
  10. 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.

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.