6-bo‘lim

Yo'qotish funksiyasi

Xatoni bitta songa aylantirish - MSE, ikkilik krossentropiya va nima uchun tanlov muhim.

🕑 18 daqiqa o‘qish 📄 1 007 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Talab: xatoni o'lchash
  2. O'rtacha kvadratik xato (MSE)
  3. MSE tasniflash uchun yaramaydi
  4. Ikkilik krossentropiya
  5. Nima uchun krossentropiya sigmoid bilan mos
  6. Paket bo'yicha yo'qotish
  7. Yo'qotish o'qitish davomida
  8. Xulosa

Tarmoq qanchalik xato qilayotganini bilmasak, uni to'g'rilay olmaymiz. Yo'qotish funksiyasi (*loss function*) butun xatoni bitta songa siqadi.

Talab: xatoni o'lchash #

Python
import numpy as np


def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))
Python
haqiqiy   = np.array([1.0, 0.0, 1.0, 0.0])
bashorat  = np.array([0.9, 0.2, 0.6, 0.1])

farq = haqiqiy - bashorat
print("farq:", farq)
print("oddiy yig'indi:", float(farq.sum()))
Natija
farq: [ 0.1 -0.2  0.4 -0.1]
oddiy yig'indi: 0.19999999999999998
Farqlarni shunchaki qo'shish - ishlamaydi

Yig'indi 0.2 chiqdi. Lekin bu xatolar kichik degani emas: musbat va manfiy xatolar bir-birini qoplaydi.

Eng yomon holat:

Python
farq = np.array([100.0, -100.0])
farq.sum()   # 0.0 - "xato yo'q"?

Model dahshatli xato qilyapti, o'lchov esa nolni ko'rsatadi.

Ikkita to'g'ri yechim bor:

UsulFormulaNomi
Modulmean(abs(y - ŷ))MAE
Kvadratmean((y - ŷ)²)MSE

Ikkalasi ham ishorani yo'q qiladi. Farqi - katta xatolarga munosabatda.

Python
farq = np.array([100.0, -100.0])
print("yig'indi:", float(farq.sum()))
print("MAE:     ", float(np.abs(farq).mean()))
print("MSE:     ", float((farq ** 2).mean()))
Natija
yig'indi: 0.0
MAE:      100.0
MSE:      10000.0

O'rtacha kvadratik xato (MSE) #

Natija
MSE = (1/n) · Σ (y - ŷ)²
Python
def mse(haqiqiy, bashorat):
    return float(np.mean((haqiqiy - bashorat) ** 2))


haqiqiy = np.array([3.0, -0.5, 2.0, 7.0])
yaxshi  = np.array([2.9, -0.4, 2.1, 7.2])
yomon   = np.array([1.0,  2.0, 0.0, 3.0])

print("yaxshi model MSE:", round(mse(haqiqiy, yaxshi), 6))
print("yomon model MSE: ", round(mse(haqiqiy, yomon), 6))
Natija
yaxshi model MSE: 0.0175
yomon model MSE:  7.5625
MAE chiziqli, MSE kvadratik jazolaydi MAE = |xato| V shakli - burchak nolda MSE = xato² parabola - silliq, hosilasi uzluksiz Ikki barobar katta xato: MAE 2 barobar, MSE 4 barobar jazolaydi
Shakl gradient tushish uchun ham muhim
Python
for xato in [1.0, 2.0, 4.0, 10.0]:
    print(f"xato = {xato:>5.1f}   MAE = {xato:>6.1f}   MSE = {xato**2:>8.1f}")
Natija
xato =   1.0   MAE =    1.0   MSE =      1.0
xato =   2.0   MAE =    2.0   MSE =      4.0
xato =   4.0   MAE =    4.0   MSE =     16.0
xato =  10.0   MAE =   10.0   MSE =    100.0
MSE chetlangan qiymatlarga sezgir

Ma'lumotda bitta g'alati namuna bo'lsa (masalan noto'g'ri yozilgan narx), MSE uni kvadratga oshiradi va model o'sha bitta xato atrofida buriladi.

VaziyatTanlov
Ma'lumot toza, katta xato jiddiyMSE
Chetlangan qiymatlar borMAE yoki Huber
TasniflashKrossentropiya (pastda)

MSE ning afzalligi - silliqligi: hosilasi hamma joyda uzluksiz. MAE ning hosilasi esa nolda mavjud emas (V ning burchagi), bu gradient tushishni biroz qiyinlashtiradi.

Amalda regressiya uchun MSE standart tanlov, chetlangan qiymatlar muammo bo'lsa esa Huber yo'qotishi ishlatiladi - u kichik xatolarda MSE, katta xatolarda MAE kabi ishlaydi.

MSE tasniflash uchun yaramaydi #

Python
def mse(haqiqiy, bashorat):
    return float(np.mean((haqiqiy - bashorat) ** 2))


haqiqiy = np.array([1.0])
for bashorat in [0.9, 0.5, 0.1, 0.01]:
    b = np.array([bashorat])
    print(f"bashorat = {bashorat:<5} MSE = {mse(haqiqiy, b):.4f}")
Natija
bashorat = 0.9   MSE = 0.0100
bashorat = 0.5   MSE = 0.2500
bashorat = 0.1   MSE = 0.8100
bashorat = 0.01  MSE = 0.9801

Model 1 bo'lishi kerak bo'lgan javobga 0.01 berdi - ya'ni butunlay ishonch bilan xato qildi. Lekin MSE atigi 0.98, ya'ni 0.1 berganidan sal kattaroq.

Tasniflashda MSE jazoni yetarli kuchaytirmaydi

MSE ning chegarasi bor: yo'qotish hech qachon 1 dan oshmaydi (chunki xato eng ko'pi bilan 1).

Ishonch bilan qilingan xato va ikkilanib qilingan xato deyarli bir xil jazolanadi.

Bundan tashqari, sigmoid bilan birga MSE ishlatilganda gradient ikki marta kichrayadi: bir marta MSE dan, bir marta sigmoid hosilasidan (≤ 0.25).

Natijada butunlay xato qilgan model juda sekin o'qiydi - bu "sekin o'qish muammosi" deb ataladi.

Yechim - krossentropiya: u ishonch bilan qilingan xatoni cheksizlikka yaqin jazolaydi.

Ikkilik krossentropiya #

Natija
L = -(1/n) · Σ [ y·log(ŷ) + (1-y)·log(1-ŷ) ]
Python
def krossentropiya(haqiqiy, bashorat, eps=1e-12):
    b = np.clip(bashorat, eps, 1.0 - eps)
    return float(-np.mean(haqiqiy * np.log(b) + (1 - haqiqiy) * np.log(1 - b)))


haqiqiy = np.array([1.0])
for bashorat in [0.9, 0.5, 0.1, 0.01, 0.0001]:
    b = np.array([bashorat])
    print(f"bashorat = {bashorat:<8} KE = {krossentropiya(haqiqiy, b):>8.4f}")
Natija
bashorat = 0.9      KE =   0.1054
bashorat = 0.5      KE =   0.6931
bashorat = 0.1      KE =   2.3026
bashorat = 0.01     KE =   4.6052
bashorat = 0.0001   KE =   9.2103

Endi ishonch bilan qilingan xato keskin jazolanadi.

BashoratMSEKrossentropiya
0.90.010.11
0.50.250.69
0.10.812.30
0.010.984.61
0.0001~1.009.21

MSE 1 da to'xtaydi, krossentropiya esa cheksiz o'sadi.

log(0) - cheksizlik va nan

Formulada log(ŷ) bor. Agar model aynan 0 bashorat qilsa va haqiqiy javob 1 bo'lsa, log(0) = -inf chiqadi.

Shuning uchun kodda np.clip ishlatildi:

Python
b = np.clip(bashorat, eps, 1.0 - eps)

Bu bashoratni [1e-12, 1 - 1e-12] oralig'iga qisadi.

Bu shunchaki ehtiyot chorasi emas - majburiy. Usiz o'qitish jarayonida bir marta inf paydo bo'lsa, u gradientlar orqali tarqaladi va butun tarmoq nan ga aylanadi.

"Yo'qotish nan bo'ldi" - eng ko'p uchraydigan shikoyat, va sabablarning birinchisi aynan shu.

Python
xom = np.array([0.0])
haqiqiy = np.array([1.0])

with np.errstate(divide="ignore"):
    qiymat = -float(haqiqiy * np.log(xom))
print("clip siz:", qiymat)
print("cheksizmi:", bool(np.isinf(qiymat)))

b = np.clip(xom, 1e-12, 1 - 1e-12)
print("clip bilan:", round(-float(haqiqiy * np.log(b)), 4))
Natija
clip siz: inf
cheksizmi: True
clip bilan: 27.631
<stdin>:10: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
<stdin>:15: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)

Nima uchun krossentropiya sigmoid bilan mos #

Bu ikkisining birga ishlatilishi tasodifiy emas.

Python
def krossentropiya(haqiqiy, bashorat, eps=1e-12):
    b = np.clip(bashorat, eps, 1.0 - eps)
    return float(-np.mean(haqiqiy * np.log(b) + (1 - haqiqiy) * np.log(1 - b)))


h = 1e-6
for z_qiymat, y_qiymat in [(2.0, 1.0), (-1.0, 1.0), (0.5, 0.0), (3.0, 0.0)]:
    z = np.array([z_qiymat])
    y = np.array([y_qiymat])
    sonli = (krossentropiya(y, sigmoid(z + h))
             - krossentropiya(y, sigmoid(z - h))) / (2 * h)
    formula = (sigmoid(z) - y).item()
    print(f"z={z_qiymat:>5.1f} y={y_qiymat:.0f}  "
          f"sonli={sonli:>9.6f}  a-y={formula:>9.6f}  "
          f"mos={np.isclose(sonli, formula, atol=1e-5)}")
Natija
z=  2.0 y=1  sonli=-0.119203  a-y=-0.119203  mos=True
z= -1.0 y=1  sonli=-0.731059  a-y=-0.731059  mos=True
z=  0.5 y=0  sonli= 0.622459  a-y= 0.622459  mos=True
z=  3.0 y=0  sonli= 0.952574  a-y= 0.952574  mos=True

To'rt holatda ham sonli hosila a - y formulasiga aynan mos keldi.

dL/dz = ŷ - y - butun darslikdagi eng chiroyli natija

Sigmoid va krossentropiyaning hosilalari bir-birini qisqartiradi, va natija hayratlanarli darajada oddiy:

Natija
dL/dz = ŷ - y

Ya'ni chiqish qatlamidagi gradient - shunchaki bashorat bilan haqiqiy javob orasidagi farq. Sigmoid hosilasi (≤ 0.25) ham, logarifm hosilasi ham ko'rinmaydi.

Bu 9-bo'limdagi orqaga tarqalish kodini bir necha marta soddalashtiradi. Xuddi shu natija softmax + ko'p sinfli krossentropiya uchun ham o'rinli (18-bo'lim).

Va bu 4-bo'limdagi to'yinish muammosini ham hal qiladi: model butunlay xato qilganda ŷ - y katta bo'ladi, sigmoid hosilasi esa gradientni bo'g'ib qo'ymaydi - chunki u formulada umuman qatnashmaydi.

Aynan shu sabab tasniflashda doim sigmoid+krossentropiya juftligi ishlatiladi.

Paket bo'yicha yo'qotish #

Python
def krossentropiya(haqiqiy, bashorat, eps=1e-12):
    b = np.clip(bashorat, eps, 1.0 - eps)
    return float(-np.mean(haqiqiy * np.log(b) + (1 - haqiqiy) * np.log(1 - b)))


haqiqiy = np.array([1.0, 0.0, 1.0, 1.0, 0.0])
bashorat = np.array([0.95, 0.1, 0.8, 0.3, 0.4])

har_biri = -(haqiqiy * np.log(bashorat) + (1 - haqiqiy) * np.log(1 - bashorat))
for h, b, l in zip(haqiqiy, bashorat, har_biri):
    belgi = "  <- eng katta hissa" if l == har_biri.max() else ""
    print(f"y={h:.0f}  ŷ={b:.2f}  yo'qotish={l:.4f}{belgi}")

print(f"o'rtacha: {krossentropiya(haqiqiy, bashorat):.4f}")
Natija
y=1  ŷ=0.95  yo'qotish=0.0513
y=0  ŷ=0.10  yo'qotish=0.1054
y=1  ŷ=0.80  yo'qotish=0.2231
y=1  ŷ=0.30  yo'qotish=1.2040  <- eng katta hissa
y=0  ŷ=0.40  yo'qotish=0.5108
o'rtacha: 0.4189

Beshinchi namunadagi eng katta xato o'rtachaga eng katta hissa qo'shdi - va u o'qitishda ham eng kuchli signal beradi.

mean va sum - qaysi biri

Ko'p kodda yo'qotish sum bilan hisoblanadi, boshqalarda mean bilan. Farqi muhimmi?

UsulGradientga ta'siri
sumPaket kattalashsa gradient ham kattalashadi
meanGradient paket hajmidan mustaqil

mean afzal: paket hajmini 32 dan 128 ga o'zgartirganda o'qitish tezligini qayta sozlashingiz shart emas.

sum ishlatilsa, paketni to'rt barobar kattalashtirish gradientni ham to'rt barobar kattalashtiradi - va bu og'irliklarni portlatib yuborishi mumkin.

Darslikda hamma joyda mean ishlatiladi.

Yo'qotish o'qitish davomida #

Python
def krossentropiya(haqiqiy, bashorat, eps=1e-12):
    b = np.clip(bashorat, eps, 1.0 - eps)
    return float(-np.mean(haqiqiy * np.log(b) + (1 - haqiqiy) * np.log(1 - b)))


haqiqiy = np.array([1.0, 0.0, 1.0, 0.0])
bosqichlar = {
    "boshlanish (tasodifiy)": np.array([0.5, 0.5, 0.5, 0.5]),
    "biroz o'qidi":           np.array([0.6, 0.4, 0.7, 0.3]),
    "yaxshi o'qidi":          np.array([0.9, 0.1, 0.85, 0.05]),
    "deyarli mukammal":       np.array([0.99, 0.01, 0.98, 0.02]),
}
for nom, b in bosqichlar.items():
    print(f"{nom:<24} {krossentropiya(haqiqiy, b):.4f}")
Natija
boshlanish (tasodifiy)   0.6931
biroz o'qidi             0.4338
yaxshi o'qidi            0.1061
deyarli mukammal         0.0151
0.6931 - tasodifiy modelning "imzosi"

Birinchi qator 0.6931 - bu ln(2).

Model hech nima bilmasa va har javobga 0.5 bersa, krossentropiya aynan shu qiymatni beradi.

Bu juda foydali mo'ljal:

Yo'qotishXulosa
> 0.693Model tasodifdan yomonroq - kodda xato bor
≈ 0.693 va tushmayaptiO'qish boshlanmadi (15-bo'lim)
< 0.693 va tushmoqdaYaxshi

Ko'p sinfli holatda mo'ljal ln(sinflar_soni) bo'ladi: o'nta sinf uchun ln(10) ≈ 2.30.

O'qitishni boshlaganingizda birinchi yo'qotish shu qiymatga yaqin bo'lishi kerak. Agar u juda katta bo'lsa - og'irliklar juda katta boshlangan (13-bo'lim).

Python
print("ln(2)  =", round(float(np.log(2)), 4))
print("ln(10) =", round(float(np.log(10)), 4))
Natija
ln(2)  = 0.6931
ln(10) = 2.3026
Amaliy topshiriq
  1. Farqlarni shunchaki qo'shib, qoplash muammosini ko'rsating.
  2. MAE va MSE ni bir xil ma'lumotda hisoblang.
  3. Xato 1, 2, 4, 10 bo'lganda MSE qanday o'sishini jadval qiling.
  4. y=1 uchun turli bashoratlarda MSE ni hisoblang.
  5. Xuddi shu bashoratlar uchun krossentropiyani hisoblang.
  6. Ikkalasini jadvalda solishtiring.
  7. log(0) ni sinab, clip ning zarurligini ko'rsating.
  8. dL/dz = a - y ni sonli hosila bilan tekshiring.
  9. Paketdagi eng katta hissa qo'shgan namunani toping.
  10. ln(2) va ln(10) mo'ljallarini hisoblang.

Xulosa #

  • Farqlarni qo'shmang - musbat va manfiy bir-birini qoplaydi.
  • MAE chiziqli, MSE kvadratik jazolaydi.
  • MSE chetlangan qiymatlarga sezgir, lekin silliq.
  • Tasniflashda MSE yaramaydi - u 1 da to'xtaydi.
  • Krossentropiya ishonch bilan qilingan xatoni cheksiz jazolaydi.
  • log(0) uchun np.clip majburiy - aks holda nan.
  • Sigmoid + krossentropiya da dL/dz = ŷ - y - hammasi qisqaradi.
  • Yo'qotishda mean ishlating, sum emas.
  • Tasodifiy model uchun mo'ljal - ln(2) = 0.693.
  • Birinchi yo'qotish mo'ljaldan katta bo'lsa - boshlash noto'g'ri.

Keyingi bo'limda hosila va gradient tushunchasini o'rganamiz - xatoni kamaytirish yo'nalishini topamiz.

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.