Layer norm ve artık bağlantılar
Önkoşul:Pozisyonel kodlama
Kanca
Gerçek bir Transformer, 10-12. derste kurduğumuz attention bloğunu 12’den 96’ya kadar ÜST ÜSTE yığar. Bu kadar derin bir yığında, DL Görüntü kategorisindeki (5. ders) kaybolan gradyan sorunu YİNE karşımıza çıkar — ama bu sefer çözüm hem TANIDIK hem YENİ.
Sezgi
İki farklı fikir, BİRLİKTE çalışıyor: “artık bağlantı” (residual connection), gradyanın derin bir yığından ATLAYARAK geçebileceği bir kestirme yol açar (DL Görüntü kategorisindeki ResNet’in AYNI fikri); “layer norm” ise her katmandan SONRA büyüklüğü kontrol altında tutar. Bu ders, ikisinin NEDEN birlikte kullanıldığını sayılarla gösteriyor.
Mekanizma
Önce layer norm’u DL Görüntü kategorisindeki batch normalization’dan AYIRAN kritik farkı görelim:
Layer norm, batch büyüklüğünden BAĞIMSIZ
# DL Görüntü kategorisinde (24. ders) batch normalization'ı gördük: her ÖZELLİĞİ,
# BATCH boyunca (tüm örnekler arasında) normalize eder. NLP'de bu SORUNLU: cümleler
# FARKLI uzunlukta, batch istatistikleri KARARSIZ olur. Layer norm FARKLI bir eksende
# normalize eder: HER token'ın KENDİ özellik vektörü İÇİNDE.
X_batch32 = torch.randn(32, 8) * 3 + 5 # 32 örnek, 8 özellik
X_batch1 = X_batch32[:1] # AYNI ilk örnek, TEK başına
katman_norm = nn.LayerNorm(8)
cikti_batch32 = katman_norm(X_batch32)
cikti_batch1 = katman_norm(X_batch1)
fark = (cikti_batch32[0] - cikti_batch1[0]).abs().max().item()
print(f"Batch=32 içindeki 1. örneğin layer norm çıktısı ile, AYNI örneğin TEK BAŞINA")
print(f"(batch=1) layer norm çıktısı arasındaki fark: {fark:.2e}")
print(f"torch.allclose: {torch.allclose(cikti_batch32[0], cikti_batch1[0], atol=1e-5)}")
print("\nLayer norm, HER örneği DİĞERLERİNDEN bağımsız normalize ediyor -- batch büyüklüğü")
print("SONUCU DEĞİŞTİRMİYOR. Batch norm'da bu ASLA doğru olmazdı (batch istatistiklerine bağımlı).")Batch=32 içindeki 1. örneğin layer norm çıktısı ile, AYNI örneğin TEK BAŞINA
(batch=1) layer norm çıktısı arasındaki fark: 0.00e+00
torch.allclose: True
Layer norm, HER örneği DİĞERLERİNDEN bağımsız normalize ediyor -- batch büyüklüğü
SONUCU DEĞİŞTİRMİYOR. Batch norm'da bu ASLA doğru olmazdı (batch istatistiklerine bağımlı).Layer norm'un elle hesabı
# Layer norm'un yaptığı: her token vektörünü KENDİ ortalaması ve std'siyle normalize et.
ornek = torch.tensor([1.0, 5.0, 3.0, 9.0])
ort = ornek.mean()
std = ornek.std(unbiased=False)
elle_normalize = (ornek - ort) / (std + 1e-5)
katman_norm_4 = nn.LayerNorm(4)
katman_norm_4.weight.data.fill_(1.0)
katman_norm_4.bias.data.fill_(0.0)
pytorch_normalize = katman_norm_4(ornek)
print(f"\nGirdi: {ornek.tolist()}")
print(f"Elle hesaplanan (x - ortalama) / std: {[round(v,3) for v in elle_normalize.tolist()]}")
print(f"nn.LayerNorm çıktısı (ağırlık=1, bias=0): {[round(v,3) for v in pytorch_normalize.tolist()]}")
print(f"torch.allclose: {torch.allclose(elle_normalize, pytorch_normalize, atol=1e-4)}")
Girdi: [1.0, 5.0, 3.0, 9.0]
Elle hesaplanan (x - ortalama) / std: [-1.183, 0.169, -0.507, 1.521]
nn.LayerNorm çıktısı (ağırlık=1, bias=0): [-1.183, 0.169, -0.507, 1.521]
torch.allclose: TrueŞimdi 12-96 katmanlık bir yığında gradyanın NASIL davrandığını ölçelim (5-7. dersteki AYNI “backward sonrası .grad normu ölç” tekniğiyle):
Artık bağlantı, gradyan akışını KURTARIYOR
# İkinci sorun: 10-12. dersteki attention blokları ÜST ÜSTE yığılınca (gerçek
# Transformer'larda 12-96 blok), gradyan NASIL akıyor? 5-7. derste gördüğümüz
# "gradyan .backward() sonrası .grad normunu ölç" TEKNİĞİNİ burada tekrar kullanıyoruz.
def rastgele_alt_katman(boyut, seed, std=0.2):
torch.manual_seed(seed)
katman = nn.Linear(boyut, boyut)
nn.init.normal_(katman.weight, std=std)
return katman
def gradyan_olc(derinlik, artik_baglanti, boyut=16):
torch.manual_seed(1)
x = torch.randn(1, boyut, requires_grad=True)
h = x
for i in range(derinlik):
alt_katman = rastgele_alt_katman(boyut, seed=100 + i)
if artik_baglanti:
h = h + torch.tanh(alt_katman(h))
else:
h = torch.tanh(alt_katman(h))
kayip = h.sum()
kayip.backward()
return x.grad.norm().item()
print(f"\n{'Derinlik':<10} {'Artık bağlantı YOK':<22} {'Artık bağlantı VAR':<22}")
for derinlik in [5, 20, 50]:
g_yok = gradyan_olc(derinlik, artik_baglanti=False)
g_var = gradyan_olc(derinlik, artik_baglanti=True)
print(f"{derinlik:<10} {g_yok:<22.2e} {g_var:<22.2e}")
print("\nArtık bağlantı OLMADAN, 50 katman sonra girdinin gradyanı 1.23e-06'ya ÇÖKÜYOR -- 7-8.")
print("dersteki AYNI kaybolan gradyan sorunu, şimdi attention katmanları için. Artık bağlantı")
print("VARKEN gradyan 448'e kadar SAĞLIKLI (hatta BÜYÜK) kalıyor -- x + ... ifadesindeki '+x' terimi,")
print("gradyanın backprop'ta HİÇBİR ARA katmandan geçmeden DOĞRUDAN akabileceği bir 'kestirme yol' açıyor.")
Derinlik Artık bağlantı YOK Artık bağlantı VAR
5 5.05e-01 9.21e+00
20 3.70e-03 1.26e+01
50 1.23e-06 4.48e+02
Artık bağlantı OLMADAN, 50 katman sonra girdinin gradyanı 1.23e-06'ya ÇÖKÜYOR -- 7-8.
dersteki AYNI kaybolan gradyan sorunu, şimdi attention katmanları için. Artık bağlantı
VARKEN gradyan 448'e kadar SAĞLIKLI (hatta BÜYÜK) kalıyor -- x + ... ifadesindeki '+x' terimi,
gradyanın backprop'ta HİÇBİR ARA katmandan geçmeden DOĞRUDAN akabileceği bir 'kestirme yol' açıyor.Matematik
Artık bağlantı ve post-norm
| Sembol | Anlamı |
|---|---|
| Alt katmana giren aktivasyon | |
| Attention veya feed-forward (15. ders) alt katmanı | |
| ‘nin KENDİ boyutları üzerindeki ortalama ve varyansı (batch üzerinde DEĞİL) | |
| ÖĞRENİLEN ölçek ve kaydırma parametreleri |
ifadesindeki "" terimi, backprop sırasında gradyanın AltKatman’ın içinden geçmeden DOĞRUDAN ‘e akabileceği bir yol sağlar — zincir kuralı olur, EN KÖTÜ ihtimalle gradyan hâlâ katsayısıyla geçer.
Kod
İki fikri BİRLİKTE kullanan gerçek bir Transformer alt-katmanı kuralım:
Artık bağlantı + layer norm birlikte
# Gerçek bir Transformer bloğu: artık bağlantı + layer norm BİRLİKTE.
class TransformerAltKatmani(nn.Module):
def __init__(self, boyut, seed):
super().__init__()
self.alt_katman = rastgele_alt_katman(boyut, seed=seed)
self.norm = nn.LayerNorm(boyut)
def forward(self, x):
# Post-norm (orijinal "Attention is All You Need" makalesi): LayerNorm(x + Alt(x))
return self.norm(x + torch.tanh(self.alt_katman(x)))
torch.manual_seed(1)
x = torch.randn(1, 16)
normlar_birlikte = [x.norm().item()]
for i in range(50):
blok = TransformerAltKatmani(16, seed=100 + i)
x = blok(x)
normlar_birlikte.append(x.norm().item())
print(f"\nArtık bağlantı + layer norm BİRLİKTE, 50 katman sonra çıktı normu: {normlar_birlikte[-1]:.4f}")
print(f"İlk 5 katmandaki normlar: {[round(v,3) for v in normlar_birlikte[:5]]}")
print("Layer norm, her katmandan SONRA büyüklüğü SABİT bir aralığa 'sıfırlıyor' -- artık")
print("bağlantı bilgiyi KORURKEN, layer norm büyüklüğü KONTROL ALTINDA tutuyor.")
Artık bağlantı + layer norm BİRLİKTE, 50 katman sonra çıktı normu: 4.0000
İlk 5 katmandaki normlar: [3.778, 4.0, 4.0, 4.0, 4.0]
Layer norm, her katmandan SONRA büyüklüğü SABİT bir aralığa 'sıfırlıyor' -- artık
bağlantı bilgiyi KORURKEN, layer norm büyüklüğü KONTROL ALTINDA tutuyor.Nerede işe yarar
Bu iki fikir, SADECE Transformer’a özgü değil — ama Transformer’ın derinliğinde VAZGEÇİLMEZ:
- Artık bağlantı fikri İLK KEZ ResNet’te (DL Görüntü kategorisi) ortaya çıktı — Transformer, AYNI fikri attention ve feed-forward katmanlarının ETRAFINA uyguluyor.
- “Post-norm” (LayerNorm(x + Alt(x))) orijinal 2017 makalesindeydi; modern modeller (GPT-2 ve sonrası) genelde “pre-norm” (x + Alt(LayerNorm(x))) kullanır — pre-norm, ÇOK derin modellerde (96+ katman) eğitimi daha KARARLI hale getirir.
- Layer norm’un batch’ten bağımsız olması, DEĞİŞKEN uzunluktaki cümlelerle çalışan NLP için ÖZELLİKLE önemli — batch norm, farklı uzunluktaki dolgulu (padded) dizilerde istatistikleri BOZAR.
Bu 2 hatayı yaparsın:
- Artık bağlantıyı “isteğe bağlı bir performans ipucu” sanmak — 12+ katmanlı bir Transformer’da bu, HİÇ eğitilememek ile eğitilebilmek arasındaki farktır (bu dersteki 1.23e-06 vs 448 farkına bak).
- Layer norm ile batch norm’u KARIŞTIRMAK — ikisi de “normalizasyon” ama TAMAMEN farklı eksenlerde çalışırlar; biri (layer norm) tek bir örneğin İÇİNDE, diğeri (batch norm) örnekler ARASINDA.
Kendini test et
1. Notebook'ta 50 katmanlık bir yığında, artık bağlantı OLMADAN gradyan 1.23e-06'ya çökerken, artık bağlantı VARKEN 448'de kalıyor. Bu farkın matematiksel nedeni ne?
- Rastgele bir tesadüf
- y = x + AltKatman(x) türevi 1 + AltKatman'(x) olur -- 'artı 1' terimi, gradyanın AltKatman'ın içinden geçmeden DOĞRUDAN akabileceği bir yol sağlar, en kötü ihtimalle gradyan katsayı 1 ile korunur (doğru cevap)
- Artık bağlantı, öğrenme oranını otomatik ayarlar
- Layer norm bu farkı yaratıyor, artık bağlantının etkisi yok
Neden: MathBox'taki zincir kuralı türevinde ∂y/∂x = 1 + ∂AltKatman/∂x -- '+x' terimi backprop'ta GARANTİLİ bir minimum-1 katsayılı yol açar, bu da artik-baglanti-olmadan bloğundaki 448 vs 1.23e-06 farkını AÇIKLAR.
2. Layer norm, batch normalization'dan (DL Görüntü, 23. ders) HANGİ eksende FARKLIDIR?
- Hiçbir farkı yoktur, ikisi aynı işi yapar
- Batch norm her ÖZELLİĞİ batch boyunca (örnekler arasında) normalize eder; layer norm her ÖRNEĞİ (token'ı) KENDİ özellik boyutları içinde normalize eder -- bu yüzden batch büyüklüğünden bağımsızdır (doğru cevap)
- Layer norm sadece görüntülerde, batch norm sadece metinde çalışır
- Layer norm öğrenilebilir parametre içermez
Neden: batch-norm-vs-layer-norm bloğunda batch=32 içindeki bir örneğin çıktısı, AYNI örneğin batch=1'deki çıktısıyla TAM eşleşiyor (fark 0.00e+00) -- bu, layer norm'un normalizasyon ekseninin BATCH değil, ÖZELLİK boyutu olduğunu kanıtlıyor.
3. Layer norm'un batch büyüklüğünden bağımsız olması NEDEN özellikle NLP için (görüntüden daha fazla) önemlidir?
- Önemli değildir, ikisi için de eşit derecede kullanışlıdır
- Cümleler DEĞİŞKEN uzunlukta olduğu için, bir batch'teki diziler dolgu (padding) ile eşitlenir -- batch norm bu dolgulu batch istatistiklerinden BOZULUR, layer norm her token'ı KENDİ İÇİNDE normalize ettiği için bundan ETKİLENMEZ (doğru cevap)
- NLP modelleri her zaman batch=1 ile eğitilir
- Görüntülerde normalizasyona hiç gerek yoktur
Neden: kullanım bölümünde açıklandığı gibi, değişken uzunluktaki NLP dizilerinde batch istatistikleri dolgu nedeniyle güvenilmezdir; layer norm'un her örneği bağımsız normalize etmesi (batch-norm-vs-layer-norm bloğunda kanıtlandığı gibi) bu sorunu tamamen ortadan kaldırır.
Özet
Özet
- Layer norm, her örneği KENDİ özellik boyutları içinde normalize eder -- batch norm gibi örnekler ARASINDA değil, bu yüzden batch büyüklüğünden bağımsızdır.
- Artık bağlantı (y = x + AltKatman(x)), ResNet'teki (DL Görüntü) AYNI fikirdir -- backprop'ta gradyanın doğrudan akabileceği bir yol açar.
- Artık bağlantı OLMADAN, 50 katmanlık bir yığında gradyan 1.23e-06'ya çöküyor; VARKEN 448'de sağlıklı kalıyor.
- Artık bağlantı + layer norm BİRLİKTE, hem bilgiyi KORUYOR hem büyüklüğü SABİT (4.0) tutuyor.
- Orijinal makale "post-norm" kullandı; modern modeller genelde daha kararlı "pre-norm" tercih eder.