Geri Yayılım
Backpropagation
Sinir ağında her bir parametrenin kayba ne kadar etki ettiğini hesaplayan algoritma — modern derin öğrenmenin matematiksel kalbi.
Bir sinir ağı tahmin yaparken bilgiyi soldan sağa (girdi → çıktı) akıtır. Eğitim aşamasında ise hata geriye doğru, çıktıdan girdiye yayılır. Her bir ağırlığın "kaybı ne kadar etkilediğini" zincir kuralı (chain rule) ile katman katman hesaplamak işte backpropagation'dur. Bu kısmi türevler bilindikten sonra gradient descent her ağırlığı kendi etkisi oranında günceller.
Backprop tarihçesi 1980'lere dayanır ama gerçek devrim, GPU'ların güçlü matris çarpımı yapmaya başlaması ve büyük etiketli veri setlerinin (ImageNet) ortaya çıkmasıyla 2012'de geldi. Bugün PyTorch, TensorFlow, JAX gibi tüm modern çerçeveler arkada otomatik diferansiyasyon (autograd) ile backprop'u görünmez şekilde halleder; sen sadece ileri akışı yazarsın.
Önemli bir özellik: backprop verimli olmasa milyarlarca parametreli modelleri eğitmek imkânsız olurdu. Tek tek sonlu farkla türev almak parametre sayısı kadar ileri geçiş gerektirirdi — GPT seviyesi modellerde fizik olarak imkânsız. Backprop tek bir geri geçişle tüm türevleri hesaplar.
Bir restoranın yemek tarifindeki bir hatayı düzeltmeyi düşün. Müşteri "tuz fazla" diyor; restoranda tuzu kim ekledi? Aşçıbaşı mı, salata yapan eleman mı, sosu kaynatan mı? Şikâyet, mutfak zincirini geriye doğru takip ederek kaynağa götürülür: "tatlı sebze servisinde sorun yok, ana yemekte tuz az, sosta fazla". Her aşamada ne kadar düzeltileceği hesaplanır. Backprop tam olarak bunu yapar — kaybın sorumluluğunu zincirde geriye doğru dağıtır.
PyTorch'ta üç katmanlı bir sinir ağı eğitiyorsun. Her bir mini-batch için şu olur:
1. İleri geçiş: girdi → katman 1 → katman 2 → çıktı → kayıp.
2. Geri geçiş: kayıptan başlayarak, her katmanın çıktısının
kayba etkisi (dL/d(çıktı)), oradan girdiye etkisi
(dL/d(girdi)), oradan ağırlığa etkisi (dL/d(W)). Zincir
kuralı bu üçünü çarparak her ağırlık için tek bir gradient
sayısı çıkarır.
3. Güncelleme: her ağırlık W -= learning_rate × dL/dW.
Sen bu adımları açık yazmazsın — loss.backward() arka planda
autograd grafından otomatik türetir. Tek bir satır, milyarlarca
parametre. Modern derin öğrenmenin temel yapı taşı.
import torch
import torch.nn as nn
# Basit ağ
model = nn.Sequential(
nn.Linear(10, 32),
nn.ReLU(),
nn.Linear(32, 1),
)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
loss_fn = nn.MSELoss()
# Sahte veri
X = torch.randn(64, 10)
y = torch.randn(64, 1)
# Eğitim adımı
optimizer.zero_grad() # eski gradient'leri temizle
preds = model(X) # ileri geçiş
loss = loss_fn(preds, y)
loss.backward() # backpropagation — autograd burada çalışır
optimizer.step() # ağırlıkları gradient'lere göre güncelle
# Bir parametrenin gradient'ine bakmak istersen
for name, p in model.named_parameters():
print(name, p.grad.shape if p.grad is not None else None)- Sinir ağı eğitiyorsan kaçınılmaz — tüm modern derin öğrenmenin temeli
- Custom loss veya custom layer yazıyorsan — gradient akışını anlamak şart
- Eğitim sırasında patlayan/ölen gradient sorunlarını teşhis ederken
- Klasik ML (rastgele orman, lojistik regresyon vb.) — onların kendi optimizasyonları var
- Gradient hesaplanamayan diferansiyellenebilir olmayan operasyonlar — alternatif yöntemlere ihtiyaç vardır
Patlayan/Ölen gradient'ler
Çok derin ağlarda gradient'ler ya çok büyür (NaN'a gider) ya da sıfıra yaklaşır (hiç öğrenme yok). Çözümler: uygun activation (ReLU), batch normalization, residual bağlantılar (ResNet), gradient clipping.
Eski gradient'leri temizlememek
PyTorch gradient'leri biriktirir; her batch'ten önce optimizer.zero_grad() çağırmazsan eski türevler birikir ve eğitim bozulur.
Eğitim modunu unutmak
Dropout ve batch norm gibi katmanlar model.train() ve model.eval() arasında farklı davranır. Inference'da eval moduna geçmemek tutarsız sonuç verir.