Zincir kuralı (backprop'un kalbi)
Önkoşul:Türev: değişim hızı
Kanca
Bir sinir ağının onlarca katmanı olduğunda, en baştaki bir parametrenin, en sondaki hatayı nasıl etkilediğini nasıl hesaplarsın? Cevap, aslında, ilkokuldan beri bildiğin bir fikrin genelleştirilmiş hâli.
Sezgi
Bazı fonksiyonlar, başka fonksiyonların içine yerleştirilmiştir. Zincir kuralı, böyle bir “iç içe” fonksiyonun türevini bulmanın yoludur: dış fonksiyonun türevini, iç fonksiyonun türeviyle çarp. Backpropagation, bu fikrin, bir sinir ağının tüm katmanlarına zincirleme uygulanmasından başka bir şey değildir.
Mekanizma
fonksiyonu, aslında iki fonksiyonun iç içe geçmesidir:
İç içe fonksiyonlar: h(x) = f(g(x))
# Bazı fonksiyonlar, başka bir fonksiyonun içine yerleştirilmiştir --
# h(x) = (3x+1)^2, aslında iki fonksiyonun birleşimidir: önce u=g(x)=3x+1,
# sonra h=f(u)=u^2.
def g(x):
return 3 * x + 1
def f(u):
return u ** 2
def h(x):
return f(g(x)) # h(x) = (3x+1)^2 -- iç içe fonksiyonlar
x0 = 1.0
print(f"x0 = {x0}")
print(f"g({x0}) = 3*{x0}+1 = {g(x0)} (iç fonksiyon)")
print(f"f(g({x0})) = {g(x0)}^2 = {f(g(x0))} (dış fonksiyon, iç fonksiyonun sonucuna uygulanıyor)")
print(f"h({x0}) = {h(x0)} (aynı sonuç, tek adımda)")x0 = 1.0
g(1.0) = 3*1.0+1 = 4.0 (iç fonksiyon)
f(g(1.0)) = 4.0^2 = 16.0 (dış fonksiyon, iç fonksiyonun sonucuna uygulanıyor)
h(1.0) = 16.0 (aynı sonuç, tek adımda)Şimdi bu iç içe fonksiyonun türevini zincir kuralıyla bulalım:
Zincir kuralı: dışın türevi çarpı için türevi
# Zincir kuralı: h(x)=f(g(x)) türevi, f'(g(x)) * g'(x)'tir -- yani "dış
# fonksiyonun türevi (iç fonksiyonun değerinde) çarpı iç fonksiyonun türevi".
def g_turev(x):
return 3 # g(x)=3x+1 -> g'(x)=3
def f_turev(u):
return 2 * u # f(u)=u^2 -> f'(u)=2u
zincir_sonucu = f_turev(g(x0)) * g_turev(x0)
print(f"\nZincir kuralı: h'(x0) = f'(g(x0)) * g'(x0) = f'({g(x0)}) * {g_turev(x0)} = {f_turev(g(x0))} * {g_turev(x0)} = {zincir_sonucu}")
# Sonlu fark ile doğrulama (9. dersteki gibi):
h_ = 0.0001
sonlu_fark = (h(x0 + h_) - h(x0)) / h_
print(f"Sonlu fark ile: (h({x0}+{h_}) - h({x0})) / {h_} = {sonlu_fark:.4f}")
print(f"Zincir kuralı ile sonlu fark aynı sonucu veriyor: {np.isclose(zincir_sonucu, sonlu_fark, atol=0.01)}")
Zincir kuralı: h'(x0) = f'(g(x0)) * g'(x0) = f'(4.0) * 3 = 8.0 * 3 = 24.0
Sonlu fark ile: (h(1.0+0.0001) - h(1.0)) / 0.0001 = 24.0009
Zincir kuralı ile sonlu fark aynı sonucu veriyor: TrueMatematik
Zincir kuralı
Bu, kelimenin tam anlamıyla bir zincirdir: her “halka” (katman), kendi yerel türevini hesaplar, ve hepsi birbirine çarpılarak bağlanır.
Kod
Şimdi bunu, minik bir “sinir ağı” üzerinde, tam olarak backpropagation’ın yaptığı gibi uygulayalım:
Backprop = zincir kuralının katman katman uygulanması
# Backpropagation, zincir kuralının, birden fazla katmandan geçerek tekrar
# tekrar uygulanmasıdır. Minik bir "sinir ağı": w (parametre) -> z=w*x -> a=z^2 -> Kayıp
girdi_x = 2.0
hedef = 10.0
w = 3.0
z = w * girdi_x # katman 1: doğrusal
a = z ** 2 # katman 2: "aktivasyon" (kare)
kayip = (a - hedef) ** 2 # katman 3: kayıp fonksiyonu
print(f"\nİleri yön (forward pass): w={w}, x={girdi_x}")
print(f" z = w*x = {w}*{girdi_x} = {z}")
print(f" a = z^2 = {z}^2 = {a}")
print(f" Kayıp = (a-hedef)^2 = ({a}-{hedef})^2 = {kayip}")
# Geri yön (backward pass): zincir kuralını katman katman geriye doğru uygula.
dKayip_da = 2 * (a - hedef) # Kayıp'ın a'ya göre türevi
da_dz = 2 * z # a'nın z'ye göre türevi
dz_dw = girdi_x # z'nin w'ya göre türevi
dKayip_dw = dKayip_da * da_dz * dz_dw # Zincir kuralı: hepsini çarp
print(f"\nGeri yön (backward pass), zincir kuralıyla:")
print(f" dKayıp/da = 2*(a-hedef) = 2*({a}-{hedef}) = {dKayip_da}")
print(f" da/dz = 2*z = 2*{z} = {da_dz}")
print(f" dz/dw = x = {dz_dw}")
print(f" dKayıp/dw = dKayıp/da * da/dz * dz/dw = {dKayip_da} * {da_dz} * {dz_dw} = {dKayip_dw}")
# Sonlu fark ile doğrulama:
def kayip_fonksiyonu(w_):
z_ = w_ * girdi_x
a_ = z_ ** 2
return (a_ - hedef) ** 2
h_ = 0.0001
sonlu_fark_dw = (kayip_fonksiyonu(w + h_) - kayip_fonksiyonu(w)) / h_
print(f"\nSonlu fark ile dKayıp/dw: {sonlu_fark_dw:.2f}")
print(f"Zincir kuralı ile sonlu fark aynı sonucu veriyor: {np.isclose(dKayip_dw, sonlu_fark_dw, rtol=0.01)}")
print("\nİşte backpropagation budur: her katmanın 'yerel' türevini hesaplayıp,")
print("zincir kuralıyla çarparak, en baştaki parametreye (w) kadar geri yaymak.")
İleri yön (forward pass): w=3.0, x=2.0
z = w*x = 3.0*2.0 = 6.0
a = z^2 = 6.0^2 = 36.0
Kayıp = (a-hedef)^2 = (36.0-10.0)^2 = 676.0
Geri yön (backward pass), zincir kuralıyla:
dKayıp/da = 2*(a-hedef) = 2*(36.0-10.0) = 52.0
da/dz = 2*z = 2*6.0 = 12.0
dz/dw = x = 2.0
dKayıp/dw = dKayıp/da * da/dz * dz/dw = 52.0 * 12.0 * 2.0 = 1248.0
Sonlu fark ile dKayıp/dw: 1248.08
Zincir kuralı ile sonlu fark aynı sonucu veriyor: True
İşte backpropagation budur: her katmanın 'yerel' türevini hesaplayıp,
zincir kuralıyla çarparak, en baştaki parametreye (w) kadar geri yaymak.Nerede işe yarar
Zincir kuralı, tüm derin öğrenmenin eğitim sürecinin motorudur:
- DL Görüntü ve DL NLP kategorilerinde, bir ağın “eğitilmesi”, tam olarak bu dersteki gibi, kayıptan geriye doğru, katman katman zincir kuralı uygulanmasıdır.
- Bir ağın 100 katmanı olsa bile, mantık değişmez — sadece zincirdeki halka sayısı artar.
- 11-12. derslerde (kısmi türev, gradyan inişi), bu zincirleme türev hesabının, birden fazla parametre için nasıl genelleştiği göreceğiz.
Bu 2 hatayı yaparsın:
- Zincir kuralını “iç ve dış fonksiyonların türevini toplamak” sanmak — hayır, toplanmaz, çarpılır.
- Backpropagation’ı “ayrı, gizemli bir algoritma” sanmak — hayır, sadece zincir kuralının, bir hesaplama grafiğinin tüm katmanlarına geriye doğru uygulanmasıdır.
Kendini test et
1. zincir-kurali-elle bloğunda h'(1.0)=24.0 çıkıyor ve sonlu fark da 24.0009 veriyor. Bu hesap nasıl yapıldı?
- f'(1.0) ile g'(1.0) toplanarak
- Dış fonksiyonun türevi iç fonksiyonun değerinde hesaplanıp (f'(g(1))=f'(4)=8), iç fonksiyonun türeviyle (g'(1)=3) çarpılarak: 8*3=24 (doğru cevap)
- Rastgele bir sayı seçilerek
- Sadece g(x) türevi alınarak
Neden: zincir-kurali-elle bloğunda bu adım adım gösteriliyor: h'(x0) = f'(g(x0)) * g'(x0) = 8.0 * 3 = 24.0.
2. backprop-mini-graf bloğunda dKayıp/dw, üç yerel türevin (dKayıp/da, da/dz, dz/dw) çarpılmasıyla bulunuyor. Bu neyi temsil ediyor?
- Rastgele bir hesaplama
- Backpropagation'ın tam olarak nasıl çalıştığını -- bir sinir ağının her katmanı kendi yerel türevini hesaplar, ve bunlar zincir kuralıyla çarpılarak en baştaki parametreye (w) kadar geri yayılır (doğru cevap)
- Sadece son katmanın önemli olduğunu
- w parametresinin etkisiz olduğunu
Neden: backprop-mini-graf bloğunun sonunda bu açıkça belirtiliyor: 'İşte backpropagation budur: her katmanın yerel türevini hesaplayıp, zincir kuralıyla çarparak ... geri yaymak'.
3. Bir sinir ağının 100 katmanı olsa, zincir kuralının uygulanma mantığı nasıl değişir?
- Tamamen farklı bir yöntem gerekir
- Mantık değişmez -- sadece zincirdeki yerel türev sayısı artar; her katman kendi yerel türevini hesaplar ve hepsi çarpılır, katman sayısı ne olursa olsun (doğru cevap)
- Zincir kuralı sadece 3 katmana kadar çalışır
- Türev alma işlemi gereksiz hale gelir
Neden: kullanım bölümünde bu açıkça belirtiliyor: 'Bir ağın 100 katmanı olsa bile, mantık değişmez -- sadece zincirdeki halka sayısı artar'.
Özet
Özet
- Zincir kuralı, iç içe geçmiş fonksiyonların (h(x)=f(g(x))) türevini bulmanın yoludur: f'(g(x)) · g'(x).
- Bu ders, dış fonksiyonun türevinin iç fonksiyonun türeviyle çarpıldığını (toplanmadığını) gösterdi.
- Backpropagation, zincir kuralının bir hesaplama grafiğinin tüm katmanlarına geriye doğru uygulanmasıdır.
- Bu dersteki mini "sinir ağı" örneğinde (w→z→a→Kayıp), üç yerel türev çarpılarak dKayıp/dw=1248 bulundu.
- Katman sayısı ne olursa olsun (2 ya da 200), mantık aynıdır: her katmanın yerel türevini çarp.