Sıradaki

Kararlı sigmoid

Zorluk: Kolay

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:

σ(z)=11+e−z\sigma(z) = \frac{1}{1 + e^{-z}}

Formülü olduğu gibi yazarsan büyük negatif girdilerde taşma (overflow) olur. z=−1000z = -1000 için e−z=e1000e^{-z} = e^{1000} 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 00 çı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

e−ze^{-z} yalnızca −z-z çok büyükken taşar. İki durum vardır:

  • zz çok negatifse −z-z çok büyüktür ve e−ze^{-z} taşar.
  • zz pozitifse e−ze^{-z} 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ı eze^{z} ile çarparsan aynı fonksiyonu başka biçimde elde edersin:

σ(z)=ez1+ez\sigma(z) = \frac{e^{z}}{1 + e^{z}}

Böylece iki formülün olur:

  • z≥0z \ge 0 için ilk formülü kullan.
  • z<0z < 0 için bunu kullan: eze^{z} 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

  • z herhangi bir shape’te bir array’dir. Tam sayılardan da oluşabilir.
  • Girdiyle aynı shape’te bir float array döndür.
  • Üs alma (np.exp, np.e ** ... ya da başka bir yazılış) hiçbir girdide inf üretmemeli. σ(−1000)\sigma(-1000) ve σ(1000)\sigma(1000) gibi değerler nan ya da inf olmadan, sırasıyla 00‘a ve 11‘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 zz için tanımlıdır. Ama float64 sayıları yaklaşık 1,8⋅103081{,}8 \cdot 10^{308}‘e kadar çıkabilir. e710e^{710} bu sınırı aşar ve sonuç inf olur.

inf tek başına her zaman bir felaket değildir: 1/(1+∞)=01 / (1 + \infty) = 0 doğru sonuçtur. Asıl tehlike, inf’in başka bir işlemle karşılaşmasıdır.

∞/∞\infty / \infty ve 0⋅∞0 \cdot \infty 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. zz negatifken ilk ifade yerine ikincisini kullanmak aynı değeri verir:

11+e−z=ez1+ez\frac{1}{1 + e^{-z}} = \frac{e^{z}}{1 + e^{z}}

İkincisinde ee‘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:

  1. Her girdide güvenli olan e−∣z∣e^{-\lvert z \rvert}‘yi hesapla.
  2. zz‘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:

  • z=−1000z = -1000‘de np.exp(-z) taşar.
  • z=1000z = 1000‘de np.exp(z) / (1 + np.exp(z)) inf / inf olur.

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?

solution.py
import numpy as npdef sigmoid(z: np.ndarray) -> np.ndarray:    """Her ham skoru 0 ile 1 arasında bir olasılığa çevirir."""    # Kodunu buraya yaz.    return np.zeros_like(z, dtype=float)

İçindekiler

Geri bildirim

Tür