Ders 05 / 24
Geri Yayılım
Zincir kuralının ağ üzerinde elle uygulanması ve elde edilen eğimin sayısal türevle doğrulanması: 113 parametrenin tamamında en büyük mutlak fark ilklendirmede 2,193e-10, yüz tur eğitimden sonra 6,441e-11. Adım büyüklüğü 0,001 seçildiğinde eğitilmiş ağda fark 2,518e-01'e fırlıyor; bu bir hesap hatası değil, doğrultulmuş birimin kırığıdır. Maliyet: geri yayılım 113 eğimin hepsini örnek başına bir ileri ve bir geri geçişle çıkarıyor, sayısal türev aynı iş için 4.520 geçiş istiyor ve oran parametre sayısıyla büyüyor: 58, 226, 898. Üçlü değişmiyor — taban çizgisi 0,7579, ağın sayısı 0,7857, ikinci koşum 0,7698.
İçindekiler
Dört derste eğim hep hazır geldi. Çıkış katmanındaki sayının nasıl bulunduğu 04’te yazıldı, ama
o sayının ara katmanın ağırlıklarına nasıl dağıtıldığı hiç açılmadı. Bu ders o dağıtımı yazar ve
yazdığı şeyin doğru olduğunu ayrı bir yöntemle sınar.
Dağıtımın kuralı zincir kuralıdır. Bir ağırlığın kayba etkisi iki parçanın çarpımıdır: beslediği birime geriye gelen değer ve çarptığı girdi. Geriye gelen değer de bir çarpımdır — sonraki katmandan gelenlerin ağırlıklarla toplamı, çarpı etkinleştirmenin türevi. Tek bir kural katman katman uygulanınca bütün parametrelerin eğimi çıkar.
- SA29. Küme, bölme, tohum, on iki sütun ve 300 satıra küçültülmüş eğitim kümesi
01–04’teki gibidir; ağ 12–8–1, 113 parametre. Taban çizgisi sınama kümesinde 0,7579. - SA30. Kayıp çapraz düzensizliktir. Çıkış katmanına gelen değer ’dir, çünkü sigmoidin
türevi kaybın türeviyle sadeleşir;
04’te gösterildi. - SA31. Ara katmanın etkinleştirmesi doğrultulmuş doğrusal birimdir. Türevi girdisi pozitifken 1, değilse 0’dır; sıfır noktasında tanımsızdır ve kodda 0 seçilmiştir.
- SA32. Eğimler örnekler üzerinden toplanır. Sayısal türev ortalama kayıp üzerinden hesaplandığı için sonucu örnek sayısıyla çarpılır ve iki yöntem aynı ölçeğe gelir.
- SA33. Sayısal türev merkezi farktır: parametre bir yukarı bir aşağı oynatılır ve fark iki adıma bölünür. Tek yanlı farktan daha doğrudur, iki kat geçiş ister.
- SA34. Doğrulama 20 satırlık bir alt kümede yapılır; gerekçe geçiş sayısıdır.
- SA35. Geçiş sayısı ileri geçiş cinsindendir. Geri yayılımın geri geçişi bir ileri geçişle aynı mertebededir ve ayrı sayılmaz; oran bu yüzden bir alt sınırdır.
Zincir Kuralı Ağ Üzerinde
Geri yayılım iki koşudan oluşur. İleri geçişte ara değerler saklanır, çünkü ağırlık eğimi onlara ihtiyaç duyar. Geri geçişte çıkıştan başlanır ve her katmanda iki iş yapılır: o katmanın eğimleri yazılır, sonra geriye gelen değer bir önceki katmana taşınır.
# geri_yayilim.py — MODELDIR. Ayni KURGU abone tablosu, ayni tohum ve ayni bolme; # egitim kumesi 01'deki gibi 300 satira kucultulmustur. import math import statistics TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF BOLGE = [("kuzey", 0.28, 21, 0.00), ("guney", 0.22, 17, -0.10), ("dogu", 0.18, 26, 0.30), ("bati", 0.14, 14, -0.05), ("merkez", 0.18, 23, 0.05)] def uretec(t): x = ((t ^ (t >> 16)) * 2246822507) & M32 x = ((x ^ (x >> 13)) * 3266489909) & M32 s = [(x ^ (x >> 16)) & M32] def sonraki(): s[0] = (s[0] * 1664525 + 1013904223) & M32 return s[0] / 4294967296 return sonraki def ayrik(u, w): t = 0.0 for i, x in enumerate(w): t += x if u < t: return i return len(w) - 1 ABONE, VERI = [], [] for i in range(HAM): r = uretec(TOHUM + i) b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])] hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05]) memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17]) if r() >= 0.046: ABONE.append({"no": 10001 + i, "bolge": b, "hane": hane, "memnuniyet": memnun}) for k in ABONE: r, v = uretec(TOHUM + 7000 + k["no"]), [] for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])): v.append(0.0 if r() < 0.038 else math.floor( k["bolge"][2] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100 + 0.5) / 100) r() if not v: continue ort, oyn, r = sum(v) / len(v), max(v) - min(v), uretec(TOHUM + 51000 + k["no"]) z = (0.052 * (ort - 20) + 0.85 * (min(v) == 0.0) + 0.026 * oyn + 0.24 * (k["hane"] >= 5) - 0.20 * (k["memnuniyet"] >= 4) + k["bolge"][3] + 0.9 * (k["no"] - 10001) / 1399 + (r() + r() + r() - 1.5) * 1.30) x = {"hane": k["hane"], "memnuniyet": k["memnuniyet"], "donem": len(v), "ort_tuketim": round(ort, 2), "oynaklik": round(oyn, 2), "sifir_okuma": int(min(v) == 0.0), "supheli": int(z > 1.35)} x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3) for b in BOLGE: x["b_" + b[0]] = int(k["bolge"][0] == b[0]) VERI.append(x) def karistir(veri, tohum): r, s = uretec(tohum), list(range(len(veri))) for i in range(len(s) - 1, 0, -1): j = int(r() * (i + 1)) s[i], s[j] = s[j], s[i] return [veri[i] for i in s] K = karistir(VERI, TOHUM + 90000) EGT, DOG, SIN = K[:756], K[756:1008], K[1008:] ALAN = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "sifir_okuma", "kisi_basi", "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"] ORTALAMA = {a: statistics.fmean(x[a] for x in EGT) for a in ALAN} SAPMA = {a: max(1e-9, statistics.pstdev([x[a] for x in EGT])) for a in ALAN} TABAN = sum(x["supheli"] == 0 for x in SIN) / len(SIN) def kume(veri): return ([[(x[a] - ORTALAMA[a]) / SAPMA[a] for a in ALAN] for x in veri], [float(x["supheli"]) for x in veri]) XE, YE = kume(EGT[:300]) # KUCULTULMUS egitim kumesi XD, YD = kume(DOG) XS, YS = kume(SIN) def sikistir(z): # S bicimli islev, tasma korumali if z >= 0: return 1.0 / (1.0 + math.exp(-z)) if z < 700 else 1.0 e = math.exp(z) if z > -700 else 0.0 return e / (1.0 + e) def dogrultulmus(z): return z if z > 0.0 else 0.0 def ilklendir(tohum, kat): # kazanc olcekli ilklendirme, yanma sifir r, ag = uretec(tohum), [] for a, b in zip(kat, kat[1:]): o = math.sqrt(2.0 / a) ag.append(([[(r() * 2 - 1) * o for _ in range(a)] for _ in range(b)], [0.0] * b)) return ag def dizey_carp(W, y, a): # DIZEY CARPIMI elle: her satir bir birim return [sum(w * v for w, v in zip(satir, a)) + yanma for satir, yanma in zip(W, y)] def ileri(ag, x): # ILERI GECIS: katman katman a = x for k, (W, y) in enumerate(ag): z = dizey_carp(W, y, a) a = [sikistir(z[0])] if k == len(ag) - 1 else [dogrultulmus(t) for t in z] return a def kayip(a, y): # capraz duzensizlik return -(math.log(max(a, 1e-12)) if y else math.log(max(1 - a, 1e-12))) def geri_yayilim(ag, X, Y, iz=False): # MODELDIR: zincir kurali katman katman gW = [[[0.0] * len(W[0]) for _ in W] for W, _ in ag] gy = [[0.0] * len(y) for _, y in ag] toplam = 0.0 for i in range(len(X)): a, ara = X[i], [X[i]] # ILERI GECIS: ara degerler saklanir for k, (W, y) in enumerate(ag): z = dizey_carp(W, y, a) a = [sikistir(z[0])] if k == len(ag) - 1 else [dogrultulmus(q) for q in z] ara.append(a) toplam += kayip(ara[-1][0], Y[i]) d = [ara[-1][0] - Y[i]] # cikis katmani: kayip ile sigmoit sadelesir for k in range(len(ag) - 1, -1, -1): W, a = ag[k][0], ara[k] # a: bu katmanin GIRDISI if iz: print(f" katman {k + 1}: geriye gelen {len(d)} deger, ilk iki " f"{[round(v, 5) for v in d[:2]]}") for j, dj in enumerate(d): # agirlik egimi = geriye gelen x girdi s = gW[k][j] for p in range(len(a)): s[p] += dj * a[p] gy[k][j] += dj # yanma egimi = geriye gelen if k: # onceki katmana tasi, etkinlestirme turevi ile onceki = [0.0] * len(a) for j, dj in enumerate(d): s = W[j] for p in range(len(a)): onceki[p] += dj * s[p] d = [onceki[p] if a[p] > 0 else 0.0 for p in range(len(a))] return gW, gy, toplam / len(X) ag = ilklendir(TOHUM + 1, [12, 8, 1]) print(f"tek abone, gercek etiket {int(YE[0])}, agin ciktisi {ileri(ag, XE[0])[0]:.5f}") gW, gy, k0 = geri_yayilim(ag, XE[:1], YE[:1], iz=True) print(f" kayip {k0:.5f}") print(f" cikis katmani agirlik egimi, ilk uc " f"{[round(v, 5) for v in gW[1][0][:3]]}") print(f" ara katman 1. birimin agirlik egimi, ilk uc " f"{[round(v, 5) for v in gW[0][0][:3]]}") print(f" ara katman yanma egimleri, ilk uc {[round(v, 5) for v in gy[0][:3]]}")
tek abone, gercek etiket 0, agin ciktisi 0.49369 katman 2: geriye gelen 1 deger, ilk iki [0.49369] katman 1: geriye gelen 8 deger, ilk iki [0.0, 0.00155] kayip 0.68061 cikis katmani agirlik egimi, ilk uc [0.0, 0.15433, 0.18243] ara katman 1. birimin agirlik egimi, ilk uc [0.0, 0.0, 0.0] ara katman yanma egimleri, ilk uc [0.0, 0.00155, -0.03503]
Çıkış katmanına gelen değer 0,49369, yani ağın çıktısı eksi gerçek etiket. Ara katmana sekiz değer
iniyor ve birincisi tam olarak 0,0. Nedeni 02’de ölçülmüştü: o birim bu abone için etkin değil,
doğrultulmuş işlev girdisini kesmiş, türevi 0 ve zincir orada kopuyor. Sonucu son iki satırda
görünüyor — birinci birimin on iki ağırlık eğimi de sıfır ve yanma eğimi de sıfır. Bu abone o
birime hiçbir şey öğretemiyor.
Sayısal Türevle Doğrulama
Elle yazılan bir türev sessizce yanlış olabilir: kod çalışır, kayıp düşer, sayı üretilir ve hata yalnız başarımın biraz düşük kalmasıyla kendini belli eder. Bağımsız sınama, kaybı parametrenin küçük bir oynamasıyla iki kez ölçüp farkı almaktır. Yavaştır, ama ağın yapısı hakkında hiçbir şey bilmediği için zincir kuralıyla aynı hatayı yapmaz.
def ortalama_kayip(ag, X, Y): return sum(kayip(ileri(ag, x)[0], y) for x, y in zip(X, Y)) / len(X) def sayisal_turev(ag, X, Y, eps): # merkezi fark, her parametre icin iki gecis gW = [[[0.0] * len(W[0]) for _ in W] for W, _ in ag] gy = [[0.0] * len(y) for _, y in ag] gecis = 0 for k, (W, y) in enumerate(ag): for j in range(len(W)): for p in range(len(W[j]) + 1): hedef, dizin = (W[j], p) if p < len(W[j]) else (y, j) eski = hedef[dizin] hedef[dizin] = eski + eps art = ortalama_kayip(ag, X, Y) hedef[dizin] = eski - eps eksi = ortalama_kayip(ag, X, Y) hedef[dizin] = eski deger = (art - eksi) / (2 * eps) * len(X) gecis += 2 * len(X) if p < len(W[j]): gW[k][j][p] = deger else: gy[k][j] = deger return gW, gy, gecis def kontrol(ag, X, Y, eps): # elle yazilan egim ile sayisal turev aW, ay, _ = geri_yayilim(ag, X, Y) sW, sy, gecis = sayisal_turev(ag, X, Y, eps) f = [] for k in range(len(ag)): for j in range(len(aW[k])): for p in range(len(aW[k][j])): f.append((abs(aW[k][j][p] - sW[k][j][p]), abs(aW[k][j][p]))) f.append((abs(ay[k][j] - sy[k][j]), abs(ay[k][j]))) return len(f), max(a for a, _ in f), max(a / max(b, 1e-12) for a, b in f), gecis def egit(ag, X, Y, adim, tur): # MODELDIR: tam toplu egim inisi n = len(X) for _ in range(tur): gW, gy, _ = geri_yayilim(ag, X, Y) for k, (W, y) in enumerate(ag): for j in range(len(W)): s, g = W[j], gW[k][j] for p in range(len(s)): s[p] -= adim * g[p] / n y[j] -= adim * gy[k][j] / n return ag ALT_X, ALT_Y = XE[:20], YE[:20] # denetim alt kumesi, 20 satir EGITILMIS = egit(ilklendir(TOHUM + 1, [12, 8, 1]), XE, YE, 0.3, 100) print(f"{'ag':<24} {'eps':>7} {'parametre':>10} {'en buyuk mutlak':>16} " f"{'en buyuk bagil':>15}") for etiket, a in (("ilklendirmede", ilklendir(TOHUM + 1, [12, 8, 1])), ("100 tur egitimden sonra", EGITILMIS)): for eps in (1e-3, 1e-5, 1e-7): n, mutlak, bagil, gecis = kontrol(a, ALT_X, ALT_Y, eps) print(f"{etiket:<24} {eps:>7g} {n:>10} {mutlak:>16.3e} {bagil:>15.3e}")
ag eps parametre en buyuk mutlak en buyuk bagil ilklendirmede 0.001 113 2.199e-07 1.583e-07 ilklendirmede 1e-05 113 2.193e-10 4.567e-08 ilklendirmede 1e-07 113 2.591e-08 4.932e-06 100 tur egitimden sonra 0.001 113 2.518e-01 2.881e+00 100 tur egitimden sonra 1e-05 113 6.441e-11 1.227e-09 100 tur egitimden sonra 1e-07 113 5.694e-09 2.697e-07
İlk üç satır sayısal türevin kendi hatasını gösteriyor. Adım büyükken kesme hatası baskındır, küçükken yuvarlama hatası; en iyi uyum ortada, 1e-05’te, 2,193e-10 ile. Yani 113 parametrenin hepsinde iki yöntem yaklaşık on anlamlı basamağa kadar aynı sayıyı veriyor. Bu, elle yazılan zincir kuralının doğru olduğunun kanıtıdır.
Dördüncü satır bir tuzaktır ve ölçülmeden geçilmemelidir. Eğitilmiş ağda, adım 0,001 seçildiğinde en büyük mutlak fark 2,518e-01’e, bağıl fark 2,881’e fırlıyor. Bu bir hesap hatası değildir; aynı ağ aynı adımda 1e-05’te 6,441e-11 veriyor. Neden doğrultulmuş birimin kırığıdır: 0,001’lik bir oynama, ağırlıklı toplamı sıfırın bir yanından öbür yanına geçirmeye yetiyor ve merkezi farkın iki ucu işlevin iki ayrı doğrusal parçasına düşüyor. Doğrulama başarısız olduğunda ilk bakılacak yer kod değil, adım büyüklüğüdür.
Maliyet: Kaç Geçiş
İki yöntem aynı sayıyı üretiyorsa hangisinin kullanılacağı maliyete bakar. Geri yayılım bir ileri ve bir geri geçişte bütün parametrelerin eğimini çıkarır. Sayısal türev her parametre için ayrı iki geçiş ister.
print(f"{'birim':>6} {'parametre':>10} {'geri yayilim':>13} {'sayisal turev':>14} " f"{'oran':>7}") for h in (2, 8, 32): kat = [12, h, 1] P = sum(a * b + b for a, b in zip(kat, kat[1:])) print(f"{h:>6} {P:>10} {len(ALT_X):>13} {2 * P * len(ALT_X):>14} {2 * P:>7}") print(f"\n{'kosum':>6} {'egt_kayip':>10} {'dogrulama':>10} {'sinama':>8}") SIN_SAYI = [] for kosum in (1, 2): ag = egit(ilklendir(TOHUM + kosum, [12, 8, 1]), XE, YE, 0.3, 100) _, _, k = geri_yayilim(ag, XE, YE) d = sum((ileri(ag, x)[0] > 0.5) == (y > 0.5) for x, y in zip(XD, YD)) / len(XD) s = sum((ileri(ag, x)[0] > 0.5) == (y > 0.5) for x, y in zip(XS, YS)) / len(XS) SIN_SAYI.append(s) print(f"{kosum:>6} {k:>10.4f} {d:>10.4f} {s:>8.4f}") print(f"taban cizgisi {TABAN:.4f}, agin sayisi {SIN_SAYI[0]:.4f}, " f"ikinci kosum {SIN_SAYI[1]:.4f}, fark " f"{round(abs(SIN_SAYI[0] - SIN_SAYI[1]) * len(XS))} abone")
birim parametre geri yayilim sayisal turev oran
2 29 20 1160 58
8 113 20 4520 226
32 449 20 17960 898
kosum egt_kayip dogrulama sinama
1 0.3597 0.8214 0.7857
2 0.3646 0.8056 0.7698
taban cizgisi 0.7579, agin sayisi 0.7857, ikinci kosum 0.7698, fark 4 abone
Geri yayılım sütunu üç satırda da 20’dir — örnek sayısı kadar. Sayısal türev sütunu parametre sayısıyla birlikte büyüyor: 1.160, 4.520, 17.960. Oran parametre sayısının iki katıdır ve 32 birimlik ağda 898’e çıkıyor. Geri yayılımın maliyeti parametre sayısından bağımsızdır; işi ileri geçişte saklanan ara değerler yapar. Sayısal türev bu yüzden bir eğitim yöntemi değil, bir denetim aracıdır.
Son satır dört dersin sayısını doğruluyor. Bu bölümde eğitim geri_yayilim ile yürütüldü ve üçlü
01’dekiyle aynı çıktı: 0,7579 — 0,7857 — 0,7698. Bunun nedeni açıktır; 01’den beri koşan kod
zaten geri yayılımdı, yalnız adı konmamıştı.
Özet
- Bir ağırlığın eğimi, beslediği birime geriye gelen değer ile o ağırlığın çarptığı girdinin çarpımıdır; geriye gelen değer sonraki katmandan taşınıp etkinleştirmenin türeviyle çarpılır.
- Etkin olmayan bir birimin bütün eğimleri sıfırdır: örnek abonede ara katmanın birinci biriminin on iki ağırlık eğimi de yanma eğimi de 0,0 çıkıyor.
- Merkezi farkla doğrulama 113 parametrenin hepsinde uyuyor: ilklendirmede en büyük mutlak fark 2,193e-10, yüz tur eğitimden sonra 6,441e-11. Adım çok büyük ya da çok küçük seçildiğinde sayısal türevin kendi hatası büyüyor.
- Eğitilmiş ağda adım 0,001’de fark 2,518e-01’e fırlıyor; bu doğrultulmuş birimin kırığıdır, hesap hatası değil. Aynı ağ 1e-05’te 6,441e-11 veriyor.
- Geri yayılım 113 eğimin hepsini örnek başına bir ileri ve bir geri geçişle çıkarıyor; sayısal türev aynı iş için 4.520 geçiş istiyor ve oran parametre sayısıyla büyüyor: 58, 226, 898.
Sonraki Adım
Eğim artık doğru hesaplanıyor ve ucuz. Ama onunla ne yapılacağı beş derste hiç sorulmadı: her turda eğim aynı sabit sayıyla çarpılıp ağırlıklardan çıkarıldı, hep aynı büyüklükte, önceki turların ne yaptığına hiç bakılmadan. Aynı eğimi başka türlü kullanmak — önceki adımların yönünü hatırlamak ya da her parametreye kendi adım büyüklüğünü vermek — aynı tur bütçesinde hangi kayba varır ve iki koşum arasında ne kadar oynar, ölçülmedi. Sonraki ders eğimi bir sonuç değil bir girdi olarak ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.