Sıradaki

Kararlı softmax

Zorluk: Kolay

aktivasyon fonksiyonları

Bir sınıflandırma (classification) modeli, her örnek (sample) için KK sınıfın her birine bir skor verir. Bu ham skorlar (logits) herhangi bir gerçek sayı olabilir.

Softmax, bunları toplamı 11 olan olasılıklara (probability) çeviren fonksiyondur. Skoru büyük olan sınıfın olasılığı da büyük olur.

softmax(logits) fonksiyonunu yaz. logits, şekli (shape) (n, k) olan bir dizidir (array); her satır bir örneğin KK sınıf için logits’idir.

Softmax’ı her satıra ayrı uygula:

Pij=eZij∑c=0K−1eZicP_{ij} = \frac{e^{Z_{ij}}}{\sum_{c=0}^{K-1} e^{Z_{ic}}}

Burada ZZ logits, PP döndüreceğin olasılıklardır.

Formülü olduğu gibi yazarsan büyük skorlarda çöker:

  1. e1000e^{1000}, float’un sınırını aşar.
  2. Bu taşma (overflow) inf verir.
  3. inf / inf, nan olur.

Fonksiyon, skorlar çok büyük ya da çok küçük olduğunda da nan ya da inf içermeyen, doğru olasılıklar döndürmeli. Yani sayısal kararlılığı (numerical stability) korumalı.

Dönüş değeri (n, k) shape’inde bir array olmalı. Her satırın toplamı 11 olmalı.

Örnekler

Girdi: logits = [[2.0, 1.0, 0.1], [0.5, 0.5, 0.5]]
Çıktı: array([[0.65900114, 0.24243297, 0.09856589], [0.33333333, 0.33333333, 0.33333333]])

Girdi: logits = [[1000.0, 1001.0, 1002.0], [-1000.0, -1001.0, -1002.0], [3.0, 3.0, 3.0]]
Çıktı: array([[0.09003057, 0.24472847, 0.66524096], [0.66524096, 0.24472847, 0.09003057], [0.33333333, 0.33333333, 0.33333333]])

Girdi: logits = [[-1.0, 0.0, 2.5, -3.0], [4.0, -4.0, 0.0, 1.0]]
Çıktı: array([[2.70496397e-02, 7.35285442e-02, 8.95761045e-01, 3.66077066e-03], [9.35945596e-01, 3.13974769e-04, 1.71424416e-02, 4.65979874e-02]])

İpucu 1

Taşmaya, exp’in büyük bir argümanla çağrılması yol açar. Tüm skorlardan aynı cc sayısını çıkarırsan pay da payda da e−ce^{-c} ile çarpılır. Bu çarpan sadeleşir ve olasılıklar değişmez.

cc‘yi exp’in argümanı hiç pozitif olmayacak biçimde seç.

İpucu 2

Her satırın maksimumunu (n, 1) shape’inde bir sütun olarak al. logits.max(axis=1) her satırın en büyük skorunu verir, ama (n,) shape’inde bir vektör olarak.

Bu vektörü logits’ten çıkarırsan NumPy onu son eksene (axis), yani sütunlara hizalar. Bu işleme yayınlama (broadcasting) denir. n = k ise hata bile vermez, yanlış sonuç verir.

Maksimumu sütuna çevirmenin iki yolu var:

  • logits.max(axis=1, keepdims=True) yaz. keepdims=True, indirgenen axis’i boyutu 1 olarak korur.
  • logits.max(axis=1).reshape(-1, 1) yaz.
İpucu 3

Maksimumu çıkarıp np.exp aldıktan sonra her satırı kendi toplamına böl. Toplamı da maksimum gibi .sum(axis=1, keepdims=True) ya da .sum(axis=1).reshape(-1, 1) ile sütun olarak al. Böylece bölme her satıra kendi toplamını uygular.

Gereksinimler

  • logits, (n, k) shape’inde bir float array’idir. Her satır bir örnektir.
  • Dönüş değeri logits ile aynı (n, k) shape’inde bir NumPy array’idir. Her satırın toplamı 11 olur, sütunların değil.
  • Büyük girdilerde (1000 gibi) ve çok küçük girdilerde (-1000 gibi) sonuç nan ya da inf içermemeli.
  • Satırlar birbirinden bağımsızdır. Bir satırın sonucu yalnızca o satırdaki skorlara bağlıdır. Bir satır 1000 civarında, başka bir satır -1000 civarında olabilir.
  • logits’i değiştirme. Fonksiyon bittiğinde çağıran kodun array’i aynı kalmalı.
  • Satırlar üzerinde döngü kullanma: birkaç NumPy ifadesi yeter.

Softmax, her skorun üstelini alır ve sonucu toplama böler. Üç sınıflı bir örneğin logits’i (2,1,0)(2, 1, 0) olsun:

  1. Üstel değerleri bul: e2≈7,39e^2 \approx 7{,}39, e1≈2,72e^1 \approx 2{,}72 ve e0=1e^0 = 1.
  2. Toplamlarını al: yaklaşık 11,1111{,}11.
  3. Her değeri toplama böl: yaklaşık 0,6650{,}665, 0,2450{,}245 ve 0,0900{,}090.

Softmax’ın özellikleri şunlardır:

  • Skor ne kadar büyükse olasılık o kadar büyüktür.
  • Hiçbir olasılık 00 olmaz ve toplamları 11 kalır.
  • İki sınıf olduğunda softmax, iki skorun farkına uygulanan sigmoid’e (değerleri 00 ile 11 arasına sıkıştıran fonksiyon) dönüşür.

Tüm skorlara aynı sayıyı eklesen ya da onlardan çıkarsan olasılıklar değişmez. Pay ve paydadaki ortak çarpan sadeleşir. Kararlı sürüm bu özelliğe dayanır.

En büyük skoru çıkarırsan:

  • O skor 00 olur ve e0=1e^0 = 1 kalır.
  • Geri kalanlar ee‘nin negatif üslerine döner. En kötü ihtimalle 00‘a yuvarlanır, taşmaz.
  • Payda her zaman en az 11 olur, bu yüzden bölme güvenlidir.

Çıkarılan sayıyı her satır için ayrı seç. Bir satırın skorları ötekilerden çok daha küçükse, bütün array’in maksimumunu çıkarmak o satırın bütün üstel değerlerini 00‘a yuvarlar. Sonuç 0/00 / 0 olur.

Daha fazlası için Nöronlar ve aktivasyonlar dersine bak.

Üstelden önce satır maksimumunu çıkarmak softmax’ı kararlı yapar.

  1. Her satırdan kendi maksimumunu çıkar.
  2. Sonucun üstelini al.
  3. Her satırı kendi toplamına böl.

keepdims=True, maksimumu ve toplamı (n, 1) shape’inde bir sütun olarak tutar. .reshape(-1, 1) de aynı işi görür.

import numpy as npdef softmax(logits: np.ndarray) -> np.ndarray:    shifted = logits - logits.max(axis=1, keepdims=True)    exps = np.exp(shifted)    return exps / exps.sum(axis=1, keepdims=True)

Sık yapılan üç hata var:

  • Maksimumu hiç çıkarmamak: küçük skorlarla doğru çalışır, ilk büyük skorda nan verir.
  • logits.max() ile bütün array’in maksimumunu çıkarmak: satırların ölçeği farklıysa küçük satır nan olur.
  • Maksimumu ya da toplamı sütuna çevirmeyi (keepdims=True ya da reshape(-1, 1)) unutmak: n ile k farklıysa shape hatası verir. Aynıysa hata vermeden sütunları normalize eder.

Bu problemde bir sorun mu var?

solution.py
import numpy as npdef softmax(logits: np.ndarray) -> np.ndarray:    """Her satırı ayrı ayrı olasılıklara çevirir; sonucun şekli logits ile aynıdır."""    # Kodunu buraya yaz.    return np.zeros_like(logits)

İçindekiler

Geri bildirim

Tür