Çok sınıflı çapraz entropi
Zorluk: Zor Çözüldükayıp fonksiyonları
Çok sınıflı bir sınıflandırma (classification) modeli, her örnek (sample) için sınıfın her birine bir skor verir. Bu ham skorlar (logits) herhangi bir gerçek sayı olabilir. Softmax onları toplamı olan olasılıklara (probability) çevirir.
Çapraz entropi (cross-entropy) kaybı (loss), modelin doğru sınıfa verdiği olasılığın ne kadar düşük olduğunu ölçer:
- Olasılık ‘e yakınsa kayıp ‘a yakındır.
- Olasılık ‘a yakınsa kayıp büyür.
cross_entropy(logits, y) fonksiyonunu yaz. Girdiler şunlardır:
logits: şekli (shape)(n, k)olan bir dizi (array). Her satır bir örneğin logits’idir.y:(n,)shape’inde bir tam sayı array’i. Her örneğin doğru sınıfını, ile arasında bir etiket (label) olarak verir.
Fonksiyon örneklerin loss’unun ortalamasını döndürmeli:
Burada:
- : logits.
- : softmax’ın olasılıkları.
- : . örneğin doğru sınıfa verdiği olasılık.
- : doğal logaritma.
Formülü bu sırayla hesaplamak işe yaramaz. Sonuç, logits çok büyük ya da birbirinden çok uzak olsa da sonlu ve doğru çıkmalı.
Örneğin logits = [[1000.0, 0.0]] ve y = [1] için loss 1000’dir. Önce olasılıkları hesaplayıp sonra logaritma almak ona ulaşamaz.
Fonksiyon tek bir Python float döndürmeli.
Örnekler
Girdi: logits = [[2.0, 1.0, 0.1], [0.5, 2.5, 0.3], [1.2, 0.2, 3.0], [0.1, 0.2, 0.3]], y = [0, 1, 2, 0]
Çıktı: 0.5107170220038892
Girdi: logits = [[1000.0, 0.0, -1000.0], [0.0, 500.0, 1000.0]], y = [2, 0]
Çıktı: 1500.0
Girdi: logits = [[-1.0, -2.0, -0.5, -3.0], [0.0, 0.0, 0.0, 0.0], [-5.0, 1.0, 1.0, -4.0]], y = [3, 0, 1]
Çıktı: 1.7440187236679012
İpucu 1
logits[np.arange(n), y], her satırdan kendi doğru sınıfının logit’ini seçer. np.arange(n) satır numaralarını (0, 1, ..., n - 1), y sütun numaralarını verir. İndeksleme bunları eşleştirir ve (0, y[0]), (1, y[1]) gibi elemanları (n,) shape’inde bir array olarak seçer.
logits[:, y] ise bütün satırlardan bütün etiketleri seçer ve (n, n) verir.
İpucu 2
Önce olasılığı bulup sonra log almak yerine logaritmayı formülün içine al:
Böylece küçük bir olasılığın 0’a yuvarlanıp log(0) vermesi sorunu ortadan kalkar. Geriye bir toplamın logaritması kalır.
İpucu 3
Her satırdan kendi maksimumunu çıkar. Burada satırın maksimumudur:
Çıkardıktan sonra exp’in en büyük argümanı olur. Toplam en az kalır ve log güvenlidir.
logits.max(axis=1) (n,) shape’inde bir vektör verir. Onu logits’ten çıkarırsan NumPy vektörü sütunlara hizalar, satırlara değil. Bu işleme yayınlama (broadcasting) denir.
Maksimumu logits.max(axis=1, keepdims=True) ya da logits.max(axis=1).reshape(-1, 1) ile (n, 1) shape’inde bir sütun olarak al. Toplamı da aynı biçimde, .sum(axis=1, keepdims=True) ile al.
Gereksinimler
logits(n, k)shape’inde bir float array’i,y(n,)shape’inde bir tam sayı array’idir. Hery[i],0ilek - 1arasındadır.- Dönüş değeri tek bir
float: array ya da liste değil.np.mean’in verdiğinp.float64de kabul edilir.float(...)ile çevirmek zorunda değilsin. - Örnekler üzerinde ortalama al, toplam değil.
- Sonuç, logits ne kadar büyük olursa olsun sonlu ve doğru olmalı. Doğru sınıfın olasılığı
0’a yuvarlansa bile lossinfya danançıkmamalı. logits’i vey’yi değiştirme. Fonksiyon bittiğinde çağıran kodun array’leri aynı kalmalı.- Örnekler üzerinde döngü kullanma: birkaç NumPy ifadesi yeter.
Doğru sınıfa verilen olasılık düştükçe loss hızla artar. Üç sınıflı bir örneğin logits’i olsun. Softmax bunları yaklaşık olasılıklarına çevirir.
Loss, doğru sınıfa göre değişir:
- Doğru sınıf ise loss olur.
- Doğru sınıf ise loss olur.
Olasılık ‘a giderken loss sonsuza gider. Birçok örnek olduğunda loss, her örneğin değerinin ortalamasıdır.
Lojistik regresyondaki ikili çapraz entropi (binary cross-entropy), olan bu formülün özel hâlidir.
Loss’u bu sırayla hesaplamak sayısal olarak kırılgandır. ve doğru sınıf olsun:
- Softmax verir.
- float’ta ‘a yuvarlanır.
- çıkar.
Oysa gerçek loss sonludur: .
Logaritmayı softmax’ın içinde hesaplarsan olasılık hiç oluşmaz:
Toplamın logaritması için kullanılan maksimumu çıkarma hilesine log-sum-exp denir. PyTorch’un cross_entropy’si ve Keras’ın from_logits=True seçeneği bu yüzden olasılığı değil logits’i alır.
Daha fazlası için Nöronlar ve aktivasyonlar dersine bak.
Logaritmayı softmax’ın içinde hesapla, böylece olasılık hiç oluşmaz.
- Her satırdan kendi maksimumunu çıkar.
- ‘yi, logaritmayı softmax’ın içinde hesaplayarak bul.
- Doğru sınıfların değerlerini seç.
- Ortalamalarının eksisini döndür.
keepdims=True maksimumu ve toplamı (n, 1) shape’inde bir sütun olarak tutar. .reshape(-1, 1) de aynı işi görür. log_probs[np.arange(n), y] ise her satırdan y[i]. sütunu seçer.
import numpy as npdef cross_entropy(logits: np.ndarray, y: np.ndarray) -> float: n = logits.shape[0] shifted = logits - logits.max(axis=1, keepdims=True) log_probs = shifted - np.log(np.exp(shifted).sum(axis=1, keepdims=True)) return float(-np.mean(log_probs[np.arange(n), y]))
Sık yapılan üç hata var:
- Softmax’ı hesaplayıp
np.log(probs[np.arange(n), y])almak: softmax kararlı bile olsa doğru sınıfın olasılığı0’a yuvarlanıncalog(0)loss’uinfyapar. - Olasılığa
1e-12gibi küçük bir sayı eklemek:inf’i önler ama değeri bozar.[[1000.0, 0.0]]vey = [1]için1000yerine yaklaşık 27,6 verir. - Maksimumu satır başına değil bütün array için çıkarmak: ölçeği farklı satırlarda küçük olan satır ‘a yuvarlanır ve yine
infçıkar.
Bu problemde bir sorun mu var?