Sıradaki

İkili çapraz entropi

Zorluk: Orta

kayıp fonksiyonları

Bu problemde iki sınıflı bir sınıflandırmanın (classification) kaybını hesaplarsın. Model, her örnek (sample) için etiketin (label) 1 olma olasılığını tahmin eder. Bu tahminlerin (prediction) ne kadar iyi olduğunu ölçen kayıp fonksiyonu (loss function), ikili çapraz entropidir (binary cross-entropy).

binary_cross_entropy(p, y, eps=1e-12) fonksiyonunu yaz. Girdiler şunlardır:

  • p: modelin tahmin ettiği olasılıklar (p^i\hat{p}_i).
  • y: gerçek label’lar (0 ya da 1).

p ve y, şekli (shape) (n,) olan birer dizidir (array). Ortalama loss’u bir float olarak döndür:

L=−1N∑i=1N[ yiln⁡p^i+(1−yi)ln⁡(1−p^i) ]L = -\frac{1}{N}\sum_{i=1}^{N}\Big[\, y_i \ln \hat{p}_i + (1 - y_i)\ln(1 - \hat{p}_i) \,\Big]

Köşeli parantezde şunlar kalır:

  • Label 1 ise yalnızca ln⁡p^i\ln \hat{p}_i.
  • Label 0 ise yalnızca ln⁡(1−p^i)\ln(1 - \hat{p}_i).

Yani loss, doğru sınıfa verilen olasılığın negatif logaritmasıdır. Model doğru sınıfa ne kadar yüksek olasılık verirse loss o kadar küçük olur.

Sorun, p^i\hat{p}_i tam 00 ya da 11 olduğunda çıkar. ln⁡0=−∞\ln 0 = -\infty olur ve çarpan 00 olsa bile 0⋅(−∞)0 \cdot (-\infty) nan verir. Yani kesin ve doğru bir tahmin bile loss’u bozar.

Sayısal kararlılık (numerical stability) için p’yi önce [eps, 1 - eps] aralığına kırp (clip). Böylece logaritmaya hiçbir zaman 00 girmez.

Örnekler

Girdi: p = [0.9, 0.2, 0.8, 0.4], y = [1, 0, 1, 0]
Çıktı: 0.2656183105130591

Girdi: p = [0.0, 1.0, 0.5], y = [1, 0, 1]
Çıktı: 18.651737178127284

Girdi: p = [0.999, 0.001, 0.5, 0.95], y = [1, 1, 0, 0]
Çıktı: 2.649408808357414

İpucu 1

np.clip(p, eps, 1 - eps) iki uçta kırpar:

  • eps’ten küçük değerleri eps’e çeker.
  • 1 - eps’ten büyük değerleri 1 - eps’e çeker.

Bu işlem p’yi değiştirmez, yeni bir array döndürür. Sonucu bir değişkene ata ve hem ln⁡p^\ln \hat{p} hem ln⁡(1−p^)\ln(1 - \hat{p}) için onu kullan.

İpucu 2

İki terim şunu yapar:

  • y * np.log(p) yalnızca label’ı 1 olan örnekleri sayar.
  • (1 - y) * np.log(1 - p) yalnızca label’ı 0 olanları sayar.

Çünkü her örnekte iki çarpandan biri 00‘dır. İkisini topla.

İpucu 3

Logaritmalar negatif ya da sıfırdır. Bu yüzden terimlerin toplamı da öyledir. Şunları yap:

  1. Başına eksi koy.
  2. np.mean ile ortalamayı al.
  3. Sonucu float(...) ile sade bir Python float’una çevir.

Gereksinimler

  • p ve y aynı (n,) shape’inde array’lerdir ve en az bir örnek içerir.
  • p’nin değerleri [0,1][0, 1] aralığındadır (uçlar dahil). y’nin değerleri 0 ya da 1’dir.
  • Dönüş değeri tek bir float: array ya da liste değil.
  • np.mean’in verdiği np.float64 de kabul edilir. float(...) ile çevirmek zorunda değilsin.
  • Kırpmayı iki uçta da yap, eps parametresiyle: p’yi [eps, 1 - eps] aralığına çek. p^i=0\hat{p}_i = 0 ya da p^i=1\hat{p}_i = 1 iken sonuç sonlu olmalı (nan ya da inf değil).
  • Doğal logaritma (np.log) kullan. Toplamı değil, ortalamayı döndür.
  • p’yi ve y’yi değiştirme. Fonksiyon bittiğinde onu çağıran kodun array’leri aynı kalmalı.

Binary cross-entropy, modelin emin olduğu hataları ağır cezalandırır. Label 1 iken loss şöyle değişir:

p^\hat{p}Loss
0,9−ln⁡0,9≈0,11-\ln 0{,}9 \approx 0{,}11
0,5ln⁡2≈0,69\ln 2 \approx 0{,}69
0,01−ln⁡0,01≈4,6-\ln 0{,}01 \approx 4{,}6

p^→0\hat{p} \to 0 giderken loss sonsuza büyür. Aynı hatanın ortalama kare hata (mean squared error, MSE) karşılığı en fazla 11‘dir.

Kırpma, sonsuz loss’u sınırlı bir sayıya çevirir. Label 1 iken p^=0\hat{p} = 0 ise loss −ln⁡ϵ-\ln \epsilon olur. ϵ=10−12\epsilon = 10^{-12} için bu yaklaşık 27,627{,}6‘dır.

Küçük bir ϵ\epsilon gerçekçi olasılıklarda sonucu neredeyse hiç değiştirmez. Çünkü modeller bu kadar uç olasılıkları nadiren verir.

Olasılıklar sigmoid’den (her sayıyı 0 ile 1 arasına sıkıştıran fonksiyon) geliyorsa binary cross-entropy’nin gradyanı (gradient) çok sade çıkar. Loss’un ham skora (logit) göre türevi p^−y\hat{p} - y olur.

Model emin ama yanlışken iki loss farklı davranır:

Loss (sigmoid ile)Emin ama yanlışken gradyan
Binary cross-entropyp^−y\hat{p} - y büyüktür, yani güncelleme de büyük olur.
MSESıfıra yaklaşır.

Daha fazlası için Lojistik regresyon dersine bak.

Çözüm üç adımdır:

  1. Olasılıkları [eps, 1 - eps] aralığına kırp.
  2. İki terimi topla.
  3. Toplamın negatif ortalamasını al.

Kodda bu adımlar şöyle görünür:

import numpy as npdef binary_cross_entropy(p: np.ndarray, y: np.ndarray, eps: float = 1e-12) -> float:    p = np.clip(p, eps, 1 - eps)    return float(-np.mean(y * np.log(p) + (1 - y) * np.log(1 - p)))

Sık yapılan hata, kırpmayı yalnızca bir uçta yapmaktır: np.clip(p, eps, 1).

Bu p^=0\hat{p} = 0‘ı düzeltir. Ama p^=1\hat{p} = 1 iken np.log(1 - p) yine ln⁡0\ln 0 verir ve loss inf ya da nan olur.

Kırpmayı np.log(p + eps) gibi bir toplamla değiştirmek de aynı sonucu vermez. Toplam değerleri 11‘in üstüne taşırır ve kırpmanın tanımladığı sınırı tutmaz.

Bu problemde bir sorun mu var?

solution.py
import numpy as npdef binary_cross_entropy(p: np.ndarray, y: np.ndarray, eps: float = 1e-12) -> float:    """Tahmin edilen olasılıklar ile 0 ya da 1 olan etiketler arasındaki ortalama ikili çapraz entropiyi döndürür."""    # Kodunu buraya yaz.    return 0.0

İçindekiler

Geri bildirim

Tür