Asosiy qismga o'tish

Cost funksiya va gradient descent

13-bob 16 kod bloki 3504 so'z

Bu bobdagi kod shu fayllar bilan ishlaydio'z kompyuteringizda ishga tushiring

Sahna: vaznlar qayerdan keladi

11-bobda men vaznlarni qo'lda yozdim:

python1 qator
w = np.array([55.0, 7.5, -28.0, 90.0])

Qayerdan oldim? O'ylab topdim. Va bashorat yaqin chiqdi, lekin aniq emas.

Savol: model bu sonlarni qanday o'zi topadi?

Javob ikki qadamdan iborat:

  1. Xatoni o'lchaydigan funksiya yozamiz (cost function)
  2. Shu funksiyani eng kichik qiladigan vaznlarni izlaymiz (gradient descent)

Bu bob — fit() ning ichidagi mexanizm.

1-qadam: xatoni o'lchaymiz

python19 qator
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.read_csv(DATA / "uy_ijara.csv")
son = ["maydon_m2"]                      # avval BITTA xususiyat — ko'rish osonroq
X = df[son].to_numpy(dtype=float)
y = df["narx_usd"].to_numpy(dtype=float)

def bashorat(X, w, b):
    return X @ w + b

def mse(X, y, w, b):
    xato = bashorat(X, w, b) - y
    return float(np.mean(xato ** 2))

# ikki xil taxmin
for w_t, b_t in [(np.array([5.0]), 200.0), (np.array([7.5]), 180.0), (np.array([12.0]), 50.0)]:
    print(f"w={w_t[0]:>5.1f}  b={b_t:>5.0f}  ->  MSE = {mse(X, y, w_t, b_t):>12,.0f}".replace(",", " "))
Chiqish
w=  5.0  b=  200  ->  MSE =      105 898
w=  7.5  b=  180  ->  MSE =       41 789
w= 12.0  b=   50  ->  MSE =       14 025

MSE (Mean Squared Error) — o'rtacha kvadratik xato:

Nega kvadrat? Ikki sabab:

python5 qator
show(pd.DataFrame([
    {"Sabab": "Ishorani yo'qotish", "Izoh": "+50 va -50 xato bir-birini yo'qotmasligi kerak"},
    {"Sabab": "Katta xatoni ko'proq jazolash", "Izoh": "10 birlik xato -> 100; 100 birlik -> 10 000"},
    {"Sabab": "Hosilasi oson", "Izoh": "Kvadratning hosilasi chiziqli — gradient hisoblash qulay"},
]))
SababIzoh
Ishorani yo'qotish+50 va -50 xato bir-birini yo'qotmasligi kerak
Katta xatoni ko'proq jazolash10 birlik xato -> 100; 100 birlik -> 10 000
Hosilasi osonKvadratning hosilasi chiziqli — gradient hisoblash qulay

MSE chetki qiymatlarga juda sezgir

Kvadrat katta xatoni kuchaytiradi. Agar ma'lumotda bitta narx = 999999 bo'lsa, u butun MSE ni va butun modelni tortib ketadi.

Shu holatda MAE (mutlaq xato) yoki HuberRegressor ishlatiladi. 14-bobda taqqoslaymiz.

Cost funksiya — bu kosa

Bitta vazn uchun MSE ni chizib ko'raylik. b ni qotirib, faqat w ni o'zgartiramiz.

python15 qator
w_oraliq = np.linspace(0, 20, 200)
xarajat = [mse(X, y, np.array([wi]), 180.0) for wi in w_oraliq]
eng_yaxshi_i = int(np.argmin(xarajat))

rasm, ax = plt.subplots(figsize=(9.5, 5))
ax.plot(w_oraliq, xarajat, "b-", lw=2)
ax.plot(w_oraliq[eng_yaxshi_i], xarajat[eng_yaxshi_i], "ro", ms=11,
        label=f"eng past: w={w_oraliq[eng_yaxshi_i]:.2f}")
ax.set_xlabel("w (maydon_m2 ning vazni)")
ax.set_ylabel("MSE (xarajat)")
ax.set_title("Cost funksiya — kosa shaklida", fontweight="bold")
ax.legend(); ax.grid(alpha=.3)
fig("kosa.png", caption="MSE kosa shaklida. Eng past nuqta — eng yaxshi vazn. Gradient descent shu nuqtaga tushadi.")

print(f"Eng past nuqta: w = {w_oraliq[eng_yaxshi_i]:.3f},  MSE = {xarajat[eng_yaxshi_i]:,.0f}".replace(",", " "))
Chiqish
Eng past nuqta: w = 10.352   MSE = 11 057

MSE kosa shaklida. Eng past nuqta — eng yaxshi vazn. Gradient descent shu nuqtaga tushadi.

MSE kosa shaklida. Eng past nuqta — eng yaxshi vazn. Gradient descent shu nuqtaga tushadi.

Mana vazifa aniq bo'ldi: kosaning tubini topish.

Bitta vazn bo'lsa, shunchaki 200 ta qiymatni sinab ko'rish mumkin. Lekin 4 vazn bo'lsa —

milliard variant. 100 vazn bo'lsa — imkonsiz.

Shuning uchun boshqa yo'l kerak.

2-qadam: hosila — qaysi tomonga qarab tushish

Tasavvur qiling: siz tog'dasiz, tuman qalin, pastga tushmoqchisiz. Butun vodiyni ko'rmaysiz — lekin oyog'ingiz ostidagi qiyalikni sezasiz.

Qiyalik qaysi tomonga pasaysa — o'sha tomonga qadam qo'yasiz. Takrorlaysiz. Tubga tushasiz.

Hosila — bu matematik qiyalik.

python12 qator
def gradient(X, y, w, b):
    """MSE ning w va b bo'yicha hosilalari."""
    n = len(y)
    xato = bashorat(X, w, b) - y          # shakli (n,)
    dw = (2 / n) * (X.T @ xato)           # shakli (m,)
    db = (2 / n) * np.sum(xato)
    return dw, db

for wi in [3.0, 7.5, 15.0]:
    dw, db = gradient(X, y, np.array([wi]), 180.0)
    yonalish = "w ni OSHIRISH kerak" if dw[0] < 0 else "w ni KAMAYTIRISH kerak"
    print(f"w={wi:>5.1f}  ->  dw={dw[0]:>12,.1f}   {yonalish}".replace(",", " "))
Chiqish
w=  3.0  ->  dw=   -54 337.2   w ni OSHIRISH kerak
w=  7.5  ->  dw=   -21 257.9   w ni OSHIRISH kerak
w= 15.0  ->  dw=    33 874.3   w ni KAMAYTIRISH kerak

Gradient belgisi yo'nalishni aytadi, kattaligi qiyalik tikligini aytadi.

Formulalar (chiziqli model va MSE uchun):

E'tibor bering: X.T @ xato — bu 11-bobdagi amal. Yangi narsa yo'q.

Gradient descent: qoida bitta qatorda

So'z bilan: vazndan gradientni ayir, lekin oldin uni kichik songa ko'paytir. O'sha kichik son — alpha, ya'ni learning rate.

python13 qator
def gradient_descent(X, y, alpha=0.0001, qadamlar=200):
    w = np.zeros(X.shape[1])
    b = 0.0
    tarix = []
    for i in range(qadamlar):
        dw, db = gradient(X, y, w, b)
        w = w - alpha * dw
        b = b - alpha * db
        tarix.append({"qadam": i, "w": float(w[0]), "b": b, "mse": mse(X, y, w, b)})
    return w, b, pd.DataFrame(tarix)

w_fin, b_fin, tarix = gradient_descent(X, y, alpha=0.0002, qadamlar=400)
show(tarix.iloc[[0, 1, 2, 5, 20, 100, 399]].round(3))
qadamwbmse
019.3950.31156 913.295
110.2690.17746 637.126
214.5630.25322 220.079
513.0450.25515 350.696
2013.1860.38715 269.7
10013.1761.07815 239.823
39913.1363.64115 130.008
python7 qator
print(f"Boshlanish : w=0.000     MSE={tarix.mse.iloc[0]:>12,.0f}".replace(",", " "))
print(f"Yakun      : w={w_fin[0]:.3f}   MSE={tarix.mse.iloc[-1]:>12,.0f}".replace(",", " "))
print()
from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(X, y)
print(f"sklearn javobi: w={lr.coef_[0]:.3f}  b={lr.intercept_:.2f}")
print(f"Bizning javob : w={w_fin[0]:.3f}  b={b_fin:.2f}")
Chiqish
Boshlanish : w=0.000     MSE=     156 913
Yakun      : w=13.136   MSE=      15 130

sklearn javobi: w=10.132  b=196.68
Bizning javob : w=13.136  b=3.64

Raqamlarga qarang. Ular mos kelmadi:

  • w: biz 13.136, sklearn 10.132
  • b: biz 3.64, sklearn 196.68

b ayniqsa dahshatli orqada — u 0 dan boshlanib, atigi 3.64 ga yetdi, 196.68 ga esa yaqin ham bormadi.

Nega? 400 qadam yetarli emas. Lekin "yetarli emas" degani qancha? O'lchaymiz.

python9 qator
print("Shkalalanmagan ma'lumotda qancha qadam kerak:")
print(f"{'qadamlar':>10} {'w':>9} {'b':>10} {'MSE':>12}")
print("-" * 44)
for n in [400, 5000, 50000, 300000]:
    w_t, b_t, tr_t = gradient_descent(X, y, alpha=0.0002, qadamlar=n)
    mse_matn = f"{tr_t.mse.iloc[-1]:,.0f}".replace(",", " ")
    print(f"{n:>10} {w_t[0]:>9.3f} {b_t:>10.2f} {mse_matn:>12}")
print()
print(f"sklearn:   w={lr.coef_[0]:.3f}  b={lr.intercept_:.2f}")
Chiqish
Shkalalanmagan ma'lumotda qancha qadam kerak:
  qadamlar         w          b          MSE
--------------------------------------------
       400    13.136       3.64       15 130
      5000    12.584      39.08       13 760
     50000    10.469     175.02       11 072
    300000    10.132     196.68       11 021

sklearn:   w=10.132  b=196.68

300 000 qadam kerak bo'ldi. Bitta xususiyat va 1200 qator uchun.

Bu — qabul qilinmaydigan narx. Va sabab ma'lumotning shkalasida: maydon_m2 22 dan 220 gacha, shuning uchun alpha juda kichik bo'lishga majbur, va b ning gradienti ham o'sha kichik alpha ga ko'paytiriladi.

Yechim esa keyingi bo'limda.

python14 qator
rasm, axes = plt.subplots(1, 2, figsize=(14, 4.8))
axes[0].plot(tarix.qadam, tarix.mse, "b-", lw=2)
axes[0].set_xlabel("Qadam"); axes[0].set_ylabel("MSE")
axes[0].set_title("Xarajat qadamma-qadam tushadi", fontweight="bold")
axes[0].grid(alpha=.3)

axes[1].plot(w_oraliq, xarajat, "k-", lw=1.5, alpha=.5, label="cost funksiya")
qadamlar_ko = tarix.iloc[::12]
axes[1].plot(qadamlar_ko.w, [mse(X, y, np.array([wi]), 180.0) for wi in qadamlar_ko.w],
             "ro-", ms=5, lw=1, label="gradient descent yo'li")
axes[1].set_xlabel("w"); axes[1].set_ylabel("MSE")
axes[1].set_title("Kosa bo'ylab tushish", fontweight="bold")
axes[1].legend(); axes[1].grid(alpha=.3)
fig("gd_yoli.png", caption="Chapda: xarajat har qadamda kamayadi. O'ngda: shu qadamlar kosa ustida qanday ko'rinadi.")

Chapda: xarajat har qadamda kamayadi. O'ngda: shu qadamlar kosa ustida qanday ko'rinadi.

Chapda: xarajat har qadamda kamayadi. O'ngda: shu qadamlar kosa ustida qanday ko'rinadi.

Learning rate — eng muhim sozlama

alpha juda kichik bo'lsa — sekin. Juda katta bo'lsa — kosadan otib chiqadi va portlaydi.

python14 qator
print(f"{'alpha':>10} {'400 qadamdan keyin w':>22} {'MSE':>16} {'holat':>14}")
print("-" * 68)
for a in [0.000001, 0.00002, 0.0002, 0.001, 0.01]:
    w_t, b_t, tr = gradient_descent(X, y, alpha=a, qadamlar=400)
    m = tr.mse.iloc[-1]
    if not np.isfinite(m) or m > 1e12:
        holat, m_matn = "PORTLADI", "inf"
    elif abs(w_t[0] - lr.coef_[0]) < 0.5:
        holat, m_matn = "yaqinlashdi", f"{m:,.0f}".replace(",", " ")
    else:
        holat, m_matn = "juda sekin", f"{m:,.0f}".replace(",", " ")
    print(f"{a:>10} {w_t[0]:>22.3f} {m_matn:>16} {holat:>14}")
print()
print(f"sklearn ning javobi: w = {lr.coef_[0]:.3f}")
Chiqish
     alpha   400 qadamdan keyin w              MSE          holat
--------------------------------------------------------------------
     1e-06                 12.500           17 022     juda sekin
     2e-05                 13.184           15 263     juda sekin
    0.0002                 13.136           15 130     juda sekin
     0.001                    nan              inf       PORTLADI
      0.01                    nan              inf       PORTLADI

sklearn ning javobi: w = 10.132

Jadval uchta holatni ko'rsatadi: juda sekin, to'g'ri, portlagan. Oraliq keng emas.

Nega scaling gradient descent uchun SHART

Yuqoridagi alpha qiymatlari juda kichik — 0.0002. Nega?

Chunki maydon_m2 22 dan 220 gacha. Gradient shu kattalikka proporsional bo'ladi, shuning uchun qadam juda kichik bo'lishi kerak.

Agar ustunlar turli shkalada bo'lsa, bitta alpha hammasiga mos kelmaydi: bittasi uchun juda katta, boshqasi uchun juda kichik. Kosa "cho'zilgan" bo'ladi va gradient descent zigzag qiladi.

StandardScaler dan keyin esa kosa dumaloq bo'ladi va bitta alpha hammasiga yetadi. Bu — 4-bobdagi "scaling kerak" qoidasining haqiqiy sababi.

python8 qator
from sklearn.preprocessing import StandardScaler
Xs = StandardScaler().fit_transform(X)
print("Shkalalangan ma'lumotda ANCHA katta alpha ishlaydi:")
print(f"{'alpha':>8} {'w':>10} {'MSE':>14}")
print("-" * 34)
for a in [0.001, 0.01, 0.1, 0.5]:
    w_t, b_t, tr = gradient_descent(Xs, y, alpha=a, qadamlar=400)
    print(f"{a:>8} {w_t[0]:>10.2f} {tr.mse.iloc[-1]:>13,.0f}".replace(",", " "))
Chiqish
Shkalalangan ma'lumotda ANCHA katta alpha ishlaydi:
   alpha          w            MSE
----------------------------------
   0.001     112.41       140 823
    0.01     203.93        11 021
     0.1     203.99        11 021
     0.5     203.99        11 021

Shkalalangandan keyin alpha=0.1 ham ishlaydi — shkalalashdan oldin bu portlardi.

Ko'p xususiyat bilan

python13 qator
son4 = ["xonalar", "maydon_m2", "metro_km", "mebel"]
X4 = StandardScaler().fit_transform(df[son4].to_numpy(dtype=float))
w4, b4, tr4 = gradient_descent(X4, y, alpha=0.1, qadamlar=1500)

lr4 = LinearRegression().fit(X4, y)
taqqos = pd.DataFrame({
    "xususiyat": son4,
    "biz topdik": w4.round(2),
    "sklearn": lr4.coef_.round(2),
})
show(taqqos)
print(f"bias: biz {b4:.2f}  |  sklearn {lr4.intercept_:.2f}")
print(f"MSE : biz {tr4.mse.iloc[-1]:,.0f}  |  sklearn {mse(X4, y, lr4.coef_, lr4.intercept_):,.0f}".replace(",", " "))
Chiqish
bias: biz 776.10  |  sklearn 776.10
MSE : biz 6 567  |  sklearn 6 567
xususiyatbiz topdiksklearn
xonalar62.0262.02
maydon_m2147.32147.32
metro_km-44.55-44.55
mebel43.3643.36

Kodimizda hech narsa o'zgarmadi — faqat X ning ustunlari ko'paydi. gradient() funksiyasi matritsa amallari bilan yozilgani uchun u har qanday ustun soni bilan ishlaydi.

Uch xil gradient descent

Hozirgacha har qadamda butun ma'lumotni ishlatdik. Boshqa variantlar ham bor.

python8 qator
show(pd.DataFrame([
    {"Turi": "Batch GD", "Har qadamda": "HAMMA qator", "Tezlik": "Sekin",
     "Yo'li": "Silliq", "Qachon": "Kichik ma'lumot"},
    {"Turi": "Stochastic GD (SGD)", "Har qadamda": "BITTA qator", "Tezlik": "Tez",
     "Yo'li": "Juda shovqinli", "Qachon": "Juda katta ma'lumot, onlayn o'qitish"},
    {"Turi": "Mini-batch GD", "Har qadamda": "32-256 qator", "Tezlik": "Tez",
     "Yo'li": "O'rtacha shovqinli", "Qachon": "AMALDA ENG KO'P ishlatiladi"},
]))
TuriHar qadamdaTezlikYo'liQachon
Batch GDHAMMA qatorSekinSilliqKichik ma'lumot
Stochastic GD (SGD)BITTA qatorTezJuda shovqinliJuda katta ma'lumot, onlayn o'qitish
Mini-batch GD32-256 qatorTezO'rtacha shovqinliAMALDA ENG KO'P ishlatiladi
python18 qator
def mini_batch_gd(X, y, alpha=0.1, epoxalar=30, paket=64, seed=42):
    r = np.random.default_rng(seed)
    w, b = np.zeros(X.shape[1]), 0.0
    tarix = []
    n = len(y)
    for e in range(epoxalar):
        tartib = r.permutation(n)
        for boshi in range(0, n, paket):
            idx = tartib[boshi:boshi + paket]
            dw, db = gradient(X[idx], y[idx], w, b)
            w, b = w - alpha * dw, b - alpha * db
        tarix.append(mse(X, y, w, b))
    return w, b, tarix

w_mb, b_mb, tar_mb = mini_batch_gd(X4, y)
print(f"Batch GD      (1500 qadam): MSE = {tr4.mse.iloc[-1]:>10,.0f}".replace(",", " "))
print(f"Mini-batch GD (30 epoxa)  : MSE = {tar_mb[-1]:>10,.0f}".replace(",", " "))
print(f"sklearn                   : MSE = {mse(X4, y, lr4.coef_, lr4.intercept_):>10,.0f}".replace(",", " "))
Chiqish
Batch GD      (1500 qadam): MSE =      6 567
Mini-batch GD (30 epoxa)  : MSE =      6 584
sklearn                   : MSE =      6 567

Mini-batch kamroq hisob bilan shunga yaqin natijaga keldi. Neyron tarmoqlarda (29–30-bob) aynan shu usul ishlatiladi.

Nega bu bob muhim

python7 qator
show(pd.DataFrame([
    {"Qayerda": "Linear regression (14-bob)", "Gradient descent roli": "Vaznlarni topadi"},
    {"Qayerda": "Logistic regression (15-bob)", "Gradient descent roli": "Xuddi shu, boshqa cost funksiya bilan"},
    {"Qayerda": "Ridge / Lasso (16-bob)", "Gradient descent roli": "Cost ga jarima qo'shiladi"},
    {"Qayerda": "Neyron tarmoq (30-bob)", "Gradient descent roli": "Backpropagation — bu zanjir bo'yicha gradient"},
    {"Qayerda": "XGBoost (19-bob)", "Gradient descent roli": "Nomidan ko'rinadi: Gradient Boosting"},
]))
QayerdaGradient descent roli
Linear regression (14-bob)Vaznlarni topadi
Logistic regression (15-bob)Xuddi shu, boshqa cost funksiya bilan
Ridge / Lasso (16-bob)Cost ga jarima qo'shiladi
Neyron tarmoq (30-bob)Backpropagation — bu zanjir bo'yicha gradient
XGBoost (19-bob)Nomidan ko'rinadi: Gradient Boosting

Siz hozir bitta mexanizmni o'rgandingiz, va u kitobning yarmida qayta uchraydi. Daraxtlar (17-bob) bundan mustasno — ular butunlay boshqa yo'l bilan ishlaydi.

Bugun nima o'rgandik

python10 qator
show(pd.DataFrame([
    {"Tushuncha": "Cost funksiya", "Bitta jumla": "Xatoni bitta son bilan o'lchaydi (MSE)"},
    {"Tushuncha": "Nega kvadrat", "Bitta jumla": "Ishorani yo'qotadi, katta xatoni jazolaydi, hosilasi oson"},
    {"Tushuncha": "Cost — kosa", "Bitta jumla": "Vazifa: kosaning tubini topish"},
    {"Tushuncha": "Gradient", "Bitta jumla": "Qiyalik: belgisi yo'nalishni, kattaligi tiklikni aytadi"},
    {"Tushuncha": "GD qoidasi", "Bitta jumla": "w := w - alpha * gradient"},
    {"Tushuncha": "Learning rate", "Bitta jumla": "Kichik - sekin, katta - portlaydi; oraliq tor"},
    {"Tushuncha": "Nega scaling kerak", "Bitta jumla": "Kosani dumaloq qiladi, bitta alpha hammasiga yetadi"},
    {"Tushuncha": "Mini-batch", "Bitta jumla": "Amalda eng ko'p ishlatiladigan variant"},
]))
TushunchaBitta jumla
Cost funksiyaXatoni bitta son bilan o'lchaydi (MSE)
Nega kvadratIshorani yo'qotadi, katta xatoni jazolaydi, hosilasi oson
Cost — kosaVazifa: kosaning tubini topish
GradientQiyalik: belgisi yo'nalishni, kattaligi tiklikni aytadi
GD qoidasiw := w - alpha * gradient
Learning rateKichik - sekin, katta - portlaydi; oraliq tor
Nega scaling kerakKosani dumaloq qiladi, bitta alpha hammasiga yetadi
Mini-batchAmalda eng ko'p ishlatiladigan variant

Keyingi bobda: chiziqli regressiya uchun gradient descent kerak emas ekan. Javobni bitta formula bilan ham topish mumkin — va qachon qaysi birini tanlash kerakligini ko'ramiz.

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar