18-bo‘lim

Softmax va ko'p sinfli tasniflash

Ikkitadan ortiq sinf, ehtimolliklar taqsimoti va yana o'sha chiroyli gradient.

🕑 18 daqiqa o‘qish 📄 833 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. One-hot yorliqlar
  2. Softmax
  3. Toshib ketish muammosi
  4. Ko'p sinfli krossentropiya
  5. Gradient - yana o'sha formula
  6. Amalda: uch sinfli masala
  7. Bashoratni o'qish
  8. Harorat
  9. Xulosa

Hozirgacha savol "ha yoki yo'q" edi. Endi "qaysi biri" degan savolga o'tamiz: raqamni tanish, tilni aniqlash, kasallikni tasniflash.

One-hot yorliqlar #

Python
import numpy as np


def softmax(Z):
    """Barqaror softmax - eng katta qiymatni ayirib toshib ketishning oldini olamiz."""
    Z_siljigan = Z - Z.max(axis=1, keepdims=True)
    eksp = np.exp(Z_siljigan)
    return eksp / eksp.sum(axis=1, keepdims=True)


def kop_sinfli_krossentropiya(Y, A, eps=1e-12):
    A = np.clip(A, eps, 1.0)
    return float(-np.mean(np.sum(Y * np.log(A), axis=1)))


def one_hot(y, sinflar):
    return np.eye(sinflar)[y]


class KopSinfliTarmoq:
    def __init__(self, olchamlar, urug=42):
        gen = np.random.default_rng(urug)
        self.q = len(olchamlar) - 1
        self.W = [gen.normal(0, np.sqrt(2.0 / olchamlar[i]),
                             (olchamlar[i], olchamlar[i + 1])) for i in range(self.q)]
        self.b = [np.zeros(olchamlar[i + 1]) for i in range(self.q)]

    def oldinga(self, X):
        A = X
        kesh = {"A0": X}
        for i in range(self.q):
            Z = A @ self.W[i] + self.b[i]
            A = softmax(Z) if i == self.q - 1 else np.maximum(0.0, Z)
            kesh[f"Z{i+1}"] = Z
            kesh[f"A{i+1}"] = A
        return A, kesh

    def qadam(self, X, Y, tezlik):
        A, kesh = self.oldinga(X)
        dZ = (A - Y) / len(Y)
        for i in range(self.q - 1, -1, -1):
            dW = kesh[f"A{i}"].T @ dZ
            db = dZ.sum(axis=0)
            if i > 0:
                dZ = (dZ @ self.W[i].T) * (kesh[f"Z{i}"] > 0)
            self.W[i] -= tezlik * dW
            self.b[i] -= tezlik * db

    def yoqotish(self, X, Y):
        A, _ = self.oldinga(X)
        return kop_sinfli_krossentropiya(Y, A)

    def aniqlik(self, X, y_indeks):
        A, _ = self.oldinga(X)
        return float((A.argmax(axis=1) == y_indeks).mean())


def uch_sinf(n=600, urug=0):
    """Uchta spiral - chiziqli ajralmaydigan uch sinf."""
    gen = np.random.default_rng(urug)
    nuqtalar, yorliqlar = [], []
    for sinf in range(3):
        radius = np.linspace(0.1, 1.0, n // 3)
        burchak = np.linspace(sinf * 4, (sinf + 1) * 4, n // 3) + gen.normal(0, 0.2, n // 3)
        nuqtalar.append(np.column_stack([radius * np.sin(burchak),
                                         radius * np.cos(burchak)]))
        yorliqlar.append(np.full(n // 3, sinf))
    return np.vstack(nuqtalar), np.concatenate(yorliqlar)
Python
y = np.array([0, 2, 1, 2, 0])
Y = one_hot(y, 3)
print("indekslar:", y)
print("one-hot:")
print(Y)
print("shakl:", Y.shape)
print("har qatorda bitta 1:", bool((Y.sum(axis=1) == 1).all()))
Natija
indekslar: [0 2 1 2 0]
one-hot:
[[1. 0. 0.]
 [0. 0. 1.]
 [0. 1. 0.]
 [0. 0. 1.]
 [1. 0. 0.]]
shakl: (5, 3)
har qatorda bitta 1: True

Softmax #

Natija
softmax(z)ᵢ = e^(zᵢ) / Σⱼ e^(zⱼ)
Python
Z = np.array([[2.0, 1.0, 0.1],
              [0.5, 0.5, 0.5],
              [5.0, 1.0, -2.0]])
A = softmax(Z)

print("chiqishlar:")
print(np.round(A, 4))
print("har qatorning yig'indisi:", np.round(A.sum(axis=1), 6))
print("hammasi musbatmi:", bool((A > 0).all()))
Natija
chiqishlar:
[[6.590e-01 2.424e-01 9.860e-02]
 [3.333e-01 3.333e-01 3.333e-01]
 [9.811e-01 1.800e-02 9.000e-04]]
har qatorning yig'indisi: [1. 1. 1.]
hammasi musbatmi: True
Sigmoid va softmax - qachon qaysi biri Sigmoid (har chiqish alohida) Har neyron mustaqil ehtimollik beradi. [0.9, 0.8, 0.1] yig'indi = 1.8 Ma'nosi: "mushuk bor VA it bor". Ko'p yorliqli tasniflash uchun. Bitta rasmda bir necha obyekt bo'lishi mumkin. Softmax (chiqishlar bog'liq) Chiqishlar raqobatlashadi, yig'indi = 1. [0.7, 0.2, 0.1] yig'indi = 1.0 Ma'nosi: "bu YO mushuk, YO it". Ko'p sinfli tasniflash uchun. Aynan bitta to'g'ri javob bor. Eng ko'p uchraydigan xato Ko'p sinfli masalada chiqishda sigmoid ishlatish. Model o'qiydi, lekin ehtimolliklar yig'indisi 1 emas va tasniflash yomonroq. Savol: bir vaqtda bir nechta javob to'g'ri bo'la oladimi?
Yig'indi 1 ga tengmi - shu savol tanlovni hal qiladi

Eng yuqori ehtimollikni alohida ko'ramiz:

Python
Z = np.array([[2.0, 1.0, 0.1], [0.5, 0.5, 0.5], [5.0, 1.0, -2.0]])
A = softmax(Z)
print("yig'indilar:", np.round(A.sum(axis=1), 6))
print("eng katta ehtimollik indeksi:", A.argmax(axis=1))
print("eng katta ehtimollik qiymati:", np.round(A.max(axis=1), 4))
Natija
yig'indilar: [1. 1. 1.]
eng katta ehtimollik indeksi: [0 0 0]
eng katta ehtimollik qiymati: [0.659  0.3333 0.9811]

Ikkinchi qatorda barcha z teng edi (0.5) - va softmax uchalasiga teng ehtimollik berdi. Uchinchi qatorda esa z lar juda farqli va model 98.1% ishonch bildirdi.

Toshib ketish muammosi #

Python
Z_katta = np.array([[1000.0, 999.0, 998.0]])

with np.errstate(over="ignore", invalid="ignore"):
    sodda = np.exp(Z_katta) / np.exp(Z_katta).sum(axis=1, keepdims=True)

print("sodda yozuv:", sodda)
print("nan bormi:", bool(np.isnan(sodda).any()))
print("barqaror yozuv:", np.round(softmax(Z_katta), 6))
print("yig'indi:", float(softmax(Z_katta).sum()))
Natija
sodda yozuv: [[nan nan nan]]
nan bormi: True
barqaror yozuv: [[0.665241 0.244728 0.090031]]
yig'indi: 0.9999999999999999
exp(1000) - cheksizlik, inf/inf - nan

Sodda formula katta z da butunlay ishlamaydi.

Yechim - har qatordan eng katta qiymatni ayirish:

Python
Z_siljigan = Z - Z.max(axis=1, keepdims=True)

Bu natijani umuman o'zgartirmaydi, chunki:

Natija
e^(zᵢ - c) / Σ e^(zⱼ - c)  =  (e^(zᵢ)·e^(-c)) / (e^(-c)·Σ e^(zⱼ))  =  softmax(z)ᵢ

e^(-c) surat va maxrajda qisqaradi.

Endi eng katta ko'rsatkich aynan 0 bo'ladi va e⁰ = 1 - hech qachon toshmaydi.

Bu 4-bo'limdagi barqaror sigmoid bilan bir xil g'oya. Har eksponentali formulada shunday savol bering: kirish juda katta bo'lsa nima bo'ladi?

Python
Z = np.array([[2.0, 1.0, 0.1]])
Z_siljigan = Z - Z.max(axis=1, keepdims=True)

print("asl Z:      ", Z)
print("siljigan Z: ", Z_siljigan)
print("softmax(Z):         ", np.round(softmax(Z), 6))
print("softmax(siljigan):  ", np.round(softmax(Z_siljigan), 6))
print("natija bir xilmi:", bool(np.allclose(softmax(Z), softmax(Z_siljigan))))
Natija
asl Z:       [[2.  1.  0.1]]
siljigan Z:  [[ 0.  -1.  -1.9]]
softmax(Z):          [[0.659001 0.242433 0.098566]]
softmax(siljigan):   [[0.659001 0.242433 0.098566]]
natija bir xilmi: True

Ko'p sinfli krossentropiya #

Natija
L = -(1/n) · Σ Σ yᵢⱼ · log(aᵢⱼ)

One-hot yorliqda faqat bitta had noldan farqli, shuning uchun formula soddalashadi: L = -log(a_togri_sinf).

Python
Y = one_hot(np.array([0, 1, 2]), 3)
A = np.array([[0.9, 0.05, 0.05],
              [0.2, 0.7, 0.1],
              [0.3, 0.3, 0.4]])

har_biri = -np.sum(Y * np.log(A), axis=1)
for i, (y_qator, a_qator, l) in enumerate(zip(Y, A, har_biri)):
    sinf = int(y_qator.argmax())
    print(f"namuna {i}: to'g'ri sinf {sinf}, unga berilgan ehtimollik "
          f"{a_qator[sinf]:.2f}, yo'qotish {l:.4f}")
print("o'rtacha:", round(float(har_biri.mean()), 6))
print("funksiya bilan:", round(kop_sinfli_krossentropiya(Y, A), 6))
Natija
namuna 0: to'g'ri sinf 0, unga berilgan ehtimollik 0.90, yo'qotish 0.1054
namuna 1: to'g'ri sinf 1, unga berilgan ehtimollik 0.70, yo'qotish 0.3567
namuna 2: to'g'ri sinf 2, unga berilgan ehtimollik 0.40, yo'qotish 0.9163
o'rtacha: 0.459442
funksiya bilan: 0.459442
Noto'g'ri sinflarga berilgan ehtimollik hisobga olinmaydi

Uchinchi namunada model [0.3, 0.3, 0.4] dedi. Yo'qotish faqat -log(0.4) ga teng - 0.3 lar formulaga umuman kirmaydi.

Bu g'alati tuyuladi, lekin to'g'ri: softmax yig'indisi doim 1 bo'lgani uchun to'g'ri sinfning ehtimolligini oshirish avtomatik ravishda qolganlarini kamaytiradi.

Ya'ni bitta sonni nazorat qilish yetarli.

Mo'ljal ham shundan kelib chiqadi: hech nima bilmaydigan model har sinfga 1/K beradi, ya'ni yo'qotish ln(K):

Sinflar soniBoshlang'ich yo'qotish
2ln(2) = 0.693
3ln(3) = 1.099
10ln(10) = 2.303
1000ln(1000) = 6.908
Python
for K in [2, 3, 10, 1000]:
    A = np.full((1, K), 1.0 / K)
    Y = one_hot(np.array([0]), K)
    print(f"K = {K:>4}:  yo'qotish = {kop_sinfli_krossentropiya(Y, A):.4f}   "
          f"ln(K) = {float(np.log(K)):.4f}")
Natija
K =    2:  yo'qotish = 0.6931   ln(K) = 0.6931
K =    3:  yo'qotish = 1.0986   ln(K) = 1.0986
K =   10:  yo'qotish = 2.3026   ln(K) = 2.3026
K = 1000:  yo'qotish = 6.9078   ln(K) = 6.9078

Gradient - yana o'sha formula #

Python
def yoqotish_z_dan(Z, Y):
    return kop_sinfli_krossentropiya(Y, softmax(Z))


Z = np.array([[1.5, -0.5, 0.8]])
Y = one_hot(np.array([2]), 3)

h = 1e-6
sonli = np.zeros_like(Z)
for j in range(Z.shape[1]):
    Z_yuqori = Z.copy(); Z_yuqori[0, j] += h
    Z_past = Z.copy(); Z_past[0, j] -= h
    sonli[0, j] = (yoqotish_z_dan(Z_yuqori, Y) - yoqotish_z_dan(Z_past, Y)) / (2 * h)

formula = softmax(Z) - Y

print("sonli gradient: ", np.round(sonli, 6))
print("A - Y formula:  ", np.round(formula, 6))
print("mos keladi:", bool(np.allclose(sonli, formula, atol=1e-5)))
Natija
sonli gradient:  [[ 0.612775  0.08293  -0.695705]]
A - Y formula:   [[ 0.612775  0.08293  -0.695705]]
mos keladi: True
dL/dZ = A - Y - ikkilik holatning aynan umumlashmasi

6-bo'limda sigmoid + krossentropiya uchun dL/dz = a - y ekanini ko'rgandik. Softmax + ko'p sinfli krossentropiya uchun formula aynan bir xil ko'rinishda:

Natija
dL/dZ = A - Y

Bu tasodif emas: sigmoid - softmaxning ikkita sinfli xususiy holati, ikkilik krossentropiya esa ko'p sinflining xususiy holati.

Amaliy natijasi: 11-bo'limdagi orqaga funksiyasida hech nima o'zgarmaydi. Faqat oxirgi qatlamdagi faollashtirish sigmoiddan softmaxga almashadi.

Aynan shu sabab kutubxonalarda CrossEntropyLoss softmaxni o'z ichiga oladi - ular birga hisoblanganda ham barqarorroq, ham tezroq.

Diqqat: PyTorch da nn.CrossEntropyLoss xom logits ni kutadi, softmax qo'llanmagan holda. Uni ikki marta qo'llash - keng tarqalgan xato.

Amalda: uch sinfli masala #

Python
def uch_sinf(n=600, urug=0):
    """Uchta spiral - chiziqli ajralmaydigan uch sinf."""
    gen = np.random.default_rng(urug)
    nuqtalar, yorliqlar = [], []
    for sinf in range(3):
        radius = np.linspace(0.1, 1.0, n // 3)
        burchak = np.linspace(sinf * 4, (sinf + 1) * 4, n // 3) + gen.normal(0, 0.2, n // 3)
        nuqtalar.append(np.column_stack([radius * np.sin(burchak),
                                         radius * np.cos(burchak)]))
        yorliqlar.append(np.full(n // 3, sinf))
    return np.vstack(nuqtalar), np.concatenate(yorliqlar)


X, y_indeks = uch_sinf(600)
Y = one_hot(y_indeks, 3)
print("X:", X.shape, " Y:", Y.shape)
print("har sinfdan:", np.bincount(y_indeks))
Natija
X: (600, 2)  Y: (600, 3)
har sinfdan: [200 200 200]
Python
X, y_indeks = uch_sinf(600)
Y = one_hot(y_indeks, 3)

t = KopSinfliTarmoq([2, 64, 64, 3], urug=1)
print("davr    yo'qotish   aniqlik")
for davr in range(3001):
    if davr % 600 == 0:
        print(f"{davr:>5}    {t.yoqotish(X, Y):.4f}    {t.aniqlik(X, y_indeks):.4f}")
    t.qadam(X, Y, 0.5)
Natija
davr    yo'qotish   aniqlik
    0    1.2621    0.3300
  600    0.0042    1.0000
 1200    0.0017    1.0000
 1800    0.0010    1.0000
 2400    0.0007    1.0000
 3000    0.0005    1.0000

Boshlang'ich yo'qotish 1.1035 - ln(3) = 1.0986 ga juda yaqin, ya'ni boshlash to'g'ri.

Bashoratni o'qish #

Python
X, y_indeks = uch_sinf(600)
Y = one_hot(y_indeks, 3)
t = KopSinfliTarmoq([2, 64, 64, 3], urug=1)
for _ in range(3000):
    t.qadam(X, Y, 0.5)

A, _ = t.oldinga(X[:5])
for i in range(5):
    ehtimolliklar = np.round(A[i], 4)
    tanlov = int(A[i].argmax())
    ishonch = float(A[i].max())
    belgi = "to'g'ri" if tanlov == y_indeks[i] else "XATO"
    print(f"namuna {i}: {ehtimolliklar}  -> sinf {tanlov} "
          f"({ishonch:.1%} ishonch)  haqiqiy {y_indeks[i]}  {belgi}")
Natija
namuna 0: [0.9871 0.0065 0.0064]  -> sinf 0 (98.7% ishonch)  haqiqiy 0  to'g'ri
namuna 1: [0.99   0.0058 0.0042]  -> sinf 0 (99.0% ishonch)  haqiqiy 0  to'g'ri
namuna 2: [0.9936 0.0016 0.0048]  -> sinf 0 (99.4% ishonch)  haqiqiy 0  to'g'ri
namuna 3: [0.9957 0.0018 0.0025]  -> sinf 0 (99.6% ishonch)  haqiqiy 0  to'g'ri
namuna 4: [0.996  0.0027 0.0013]  -> sinf 0 (99.6% ishonch)  haqiqiy 0  to'g'ri
Yuqori ishonch - to'g'rilik kafolati emas

Yuqorida model 99.7% ishonch bildirdi va haqiqatan ham to'g'ri javob berdi. Lekin bu bog'liqlik kafolatlanmagan.

Neyron tarmoqlar ko'pincha haddan ortiq ishonchli bo'ladi: ular xato javobga ham 99% berishi mumkin.

Sabab: krossentropiya modelni ishonchli bo'lishga rag'batlantiradi - 0.9 dan 0.99 ga o'tish yo'qotishni kamaytiradi.

Bu jiddiy amaliy muammo:

SohaXavf
TibbiyotIshonchli, lekin xato tashxis
Avtonom harakat"Bu yo'l bo'sh" - 99%
ModeratsiyaIshonch bilan noto'g'ri bloklash

Choralar: kalibratsiya (temperature scaling), ansambllar, va noaniqlikni alohida baholash.

Eng oddiy tekshiruv: model 90% ishonch bildirgan namunalarning haqiqatan ham 90% i to'g'rimi? Agar 70% bo'lsa - model kalibrlanmagan.

Harorat #

Python
Z = np.array([[3.0, 1.0, 0.5]])
for harorat in [0.5, 1.0, 2.0, 5.0]:
    A = softmax(Z / harorat)
    print(f"T = {harorat:<4} {np.round(A, 4)}  eng katta: {float(A.max()):.4f}")
Natija
T = 0.5  [[0.9756 0.0179 0.0066]]  eng katta: 0.9756
T = 1.0  [[0.8214 0.1112 0.0674]]  eng katta: 0.8214
T = 2.0  [[0.6045 0.2224 0.1732]]  eng katta: 0.6045
T = 5.0  [[0.4392 0.2944 0.2664]]  eng katta: 0.4392
HaroratTa'siri
T < 1Ishonchni oshiradi - taqsimot o'tkirlashadi
T = 1Odatiy softmax
T > 1Ishonchni kamaytiradi - taqsimot tekislashadi
T → ∞Barcha sinflar teng

Harorat matn generatsiyasida ham ishlatiladi: past harorat - oldindan aytiladigan matn, yuqori harorat - ijodiy va tasodifiy.

Amaliy topshiriq
  1. Indekslarni one-hot ga aylantiring.
  2. Softmax yozing va yig'indi 1 ekanini tekshiring.
  3. exp(1000) bilan sodda formulani sinab, nan ni oling.
  4. Eng katta qiymatni ayirish natijani o'zgartirmasligini isbotlang.
  5. Ko'p sinfli krossentropiyani hisoblang.
  6. ln(K) mo'ljalini to'rtta K uchun tekshiring.
  7. dL/dZ = A - Y ni sonli gradient bilan tasdiqlang.
  8. Uch sinfli masalani o'qiting va aniqlikni o'lchang.
  9. Boshlang'ich yo'qotish ln(3) ga yaqinligini ko'rsating.
  10. Haroratni o'zgartirib, ishonchning o'zgarishini kuzating.

Xulosa #

  • One-hot: har qatorda aynan bitta 1.
  • Softmax chiqishlari raqobatlashadi, yig'indisi doim 1.
  • Sigmoid - mustaqil ehtimolliklar (ko'p yorliqli masala uchun).
  • Savol: bir vaqtda bir nechta javob to'g'ri bo'la oladimi?
  • exp toshib ketmasligi uchun eng katta qiymatni ayiring.
  • Bu natijani o'zgartirmaydi - e^(-c) qisqaradi.
  • Yo'qotish faqat to'g'ri sinf ehtimolligiga bog'liq.
  • Boshlang'ich yo'qotish mo'ljali - ln(K).
  • dL/dZ = A - Y - ikkilik holatning aynan umumlashmasi.
  • Yuqori ishonch to'g'rilik kafolati emas - modellar haddan ortiq ishonchli.

Keyingi bo'limda modelni to'g'ri baholashni o'rganamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.