Kararlı sigmoid
Zorluk: Kolay Çözüldüaktivasyon fonksiyonları
Sigmoid, her gerçek sayıyı 0 ile 1 arasına sıkıştıran bir fonksiyondur. Bir modelin ham skorunu (logit) olasılığa çevirmek için kullanırsın:
- Skor çok büyükse olasılık 1’e yaklaşır.
- Skor çok negatifse olasılık 0’a yaklaşır.
sigmoid(z) fonksiyonunu yaz. z, ham skorlardan oluşan bir dizidir (array). Şu formülü eleman bazında (element-wise) uygula ve girdiyle aynı şekle (shape) sahip bir array döndür:
Formülü olduğu gibi yazarsan büyük negatif girdilerde taşma (overflow) olur. için olur. Bu sayı float64’ün alabileceği en büyük sayıyı aşar ve np.exp inf döndürür.
Bu girdide sonuç yine de çıkar. Ama formülün başka bir yazılışı inf / inf = nan üretir.
Amaç sayısal kararlılıktır (numerical stability). Üs alma (np.exp, np.e ** ... ya da başka bir yazılış) hiçbir girdide inf üretmemeli. Sonuç her girdide doğru çıkmalı.
Örnekler
Girdi: z = [-2.0, -0.5, 0.0, 0.5, 2.0]
Çıktı: array([0.11920292, 0.37754067, 0.5 , 0.62245933, 0.88079708])
Girdi: z = [-1000.0, -50.0, 50.0, 1000.0]
Çıktı: array([0.00000000e+00, 1.92874985e-22, 1.00000000e+00, 1.00000000e+00])
Girdi: z = [-6.0, -3.0, -0.1, 0.1, 3.0, 6.0]
Çıktı: array([0.00247262, 0.04742587, 0.47502081, 0.52497919, 0.95257413,
0.99752738])
İpucu 1
yalnızca çok büyükken taşar. İki durum vardır:
- çok negatifse çok büyüktür ve taşar.
- pozitifse sıfıra yaklaşır ve güvenlidir. Formülün o tarafı olduğu gibi kalabilir.
İpucu 2
Formülün hem payını hem paydasını ile çarparsan aynı fonksiyonu başka biçimde elde edersin:
Böylece iki formülün olur:
- için ilk formülü kullan.
- için bunu kullan: küçüktür ve taşmaz.
İpucu 3
np.exp(-np.abs(z)) her girdide güvenlidir, çünkü argümanı hiçbir zaman pozitif olmaz. İki formülü de bu değerle yaz ve np.where(z >= 0, ..., ...) ile birini seç.
np.where seçilmeyen dalı da hesaplar. O dalda np.exp(z) ya da np.exp(-z) gibi taşabilecek bir ifade varsa o dal yine taşar.
Gereksinimler
zherhangi bir shape’te bir array’dir. Tam sayılardan da oluşabilir.- Girdiyle aynı shape’te bir
floatarray döndür. - Üs alma (
np.exp,np.e ** ...ya da başka bir yazılış) hiçbir girdideinfüretmemeli. ve gibi değerlernanya dainfolmadan, sırasıyla ‘a ve ‘e yakın çıkmalı. z’yi değiştirme. Fonksiyon bittiğinde onu çağıran kodun array’i aynı kalmalı.
Sigmoid, logit’i 0 ile 1 arasında bir olasılığa çevirir. Lojistik regresyon (logistic regression) ve sinir ağlarının çıkış katmanı onunla çalışır.
Formül matematikte her için tanımlıdır. Ama float64 sayıları yaklaşık ‘e kadar çıkabilir. bu sınırı aşar ve sonuç inf olur.
inf tek başına her zaman bir felaket değildir: doğru sonuçtur. Asıl tehlike, inf’in başka bir işlemle karşılaşmasıdır.
ve nan verir. nan sonraki her işleme yayılır. Eğitim sırasında tek bir nan weight’lerin hepsini bozabilir.
Çözüm, sayıyı hiç taşmayacak biçimde yazmaktır. negatifken ilk ifade yerine ikincisini kullanmak aynı değeri verir:
İkincisinde ‘nin üssü hiçbir zaman pozitif olmaz. Aynı türden yeniden yazımlar numerical stability’nin temel yöntemidir. Softmax’ta da karşına çıkacak.
Daha fazlası için Lojistik regresyon dersine bak.
Çözüm iki adımdır:
- Her girdide güvenli olan ‘yi hesapla.
- ‘nin işaretine göre iki formülden birini seç.
Kodda bu adımlar şöyle görünür:
import numpy as npdef sigmoid(z: np.ndarray) -> np.ndarray: e = np.exp(-np.abs(z)) return np.where(z >= 0, 1 / (1 + e), e / (1 + e))
Sık yapılan hata, iki dalı da np.where içinde doğrudan yazmaktır: np.where(z >= 0, 1 / (1 + np.exp(-z)), np.exp(z) / (1 + np.exp(z))).
np.where seçilmeyen dalı da hesaplar. İki dal taşar:
- ‘de
np.exp(-z)taşar. - ‘de
np.exp(z) / (1 + np.exp(z))inf / infolur.
Seçilmeyen dalın sonucu atılsa bile o dal taşmıştır. Çözümde e her iki dal için de güvenli olduğundan hiçbir dal taşmaz.
Bu problemde bir sorun mu var?