IlmHamroh
Data Science va sun'iy intellekt/ML asoslari6/10-dars19 daqiqa
Mundarija (22)

12.6-dars: Birinchi to'liq model

12-QISM — MACHINE LEARNING ASOSLARI · 6-dars


1. Kirish va motivatsiya

Oldingi beshta darsda nazariyani yig'dik: ML nima 12.1-bob, vazifani qanday qo'yish 12.2-bob, qanday ajratish 12.3-bob, overfitting va bias-variance (12.4-12.5). Endi ularni bitta ishchi oqimga birlashtiramiz: ma'lumotdan boshlab, baholangan modelgacha.

Bu dars — amaliy shablon: ma'lumotni yuklash va ajratish, belgilarni tayyorlash (pipeline ichida), baza modeldan boshlash, bir necha modelni cross-validation bilan solishtirish, giperparametrni sozlash, yakuniy modelni bir marta test to'plamida baholash va natijani talqin qilish. Shu shablon keyingi barcha loyihalarda takrorlanadi — faqat modellar va belgilar o'zgaradi.

Real vaziyat. Yangi Data Scientist birinchi vazifasini oladi: uy narxini bashorat qilish. U darhol Random Forest o'qitadi, R^2 = 0.86 oladi va hisobot yozadi. Katta hamkasbi savol beradi: "Baza model qancha? Oddiy chiziqli regressiya-chi? Natijaning noaniqligi qancha? Xato qaysi uylarda katta?". Javoblar yo'q. Qayta ishlangan tahlil: baza (o'rtacha) MAE = 42 ming, chiziqli regressiya 18 ming, o'rmon 14 ming ± 1.2 (CV SD); eng katta xatolar — juda katta va noyob uylarda. Endi hisobot to'liq va ishonchli.

Bu darsda to'liq ML oqimini quramiz.

Bu darsda:

  • To'liq oqim: 8 qadam
  • Pipeline va ColumnTransformer
  • Baza modeldan boshlash
  • Modellarni cross-validation bilan solishtirish
  • Giperparametrni sozlash (GridSearchCV)
  • Yakuniy baho va xatolarni tahlil qilish
  • Oqim tuzoqlari
  • Amaliy: uy narxi modeli

ℹ Misollar real numpy/pandas/sklearn bilan (Python 3.14).


2. Nazariya — chuqur tushuntirish

2.1. To'liq oqim

text
1. MA'LUMOT      — yuklash, tekshirish, dublikatlarni olib tashlash
2. AJRATISH      — test to'plamini darhol ajratib, yopib qo'yish 12.3-bob
3. EDA (o'quvda) — taqsimotlar, yetishmovchilik, outlierlar (8-qism)
4. TAYYORLASH    — pipeline: imputer, scaler, one-hot 12.9-bob
5. BAZA          — DummyRegressor / DummyClassifier 12.1-bob
6. MODELLAR      — bir nechta nomzodni CV bilan solishtirish
7. SOZLASH       — GridSearchCV / RandomizedSearchCV (validatsiyada)
8. YAKUNIY BAHO  — test to'plamida BIR MARTA + xatolar tahlili

Bu tartib muhim: test to'plami 2-qadamda ajratiladi va 8-qadamgacha tegilmaydi 12.3-bob; EDA va barcha qarorlar faqat o'quv ma'lumotida (aks holda leakage — 12.2). Har qadamda natija yoziladi: baza, har model CV balli (± SD), tanlangan giperparametrlar, yakuniy test balli.

2.2. Pipeline va ColumnTransformer

python
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder, StandardScaler

son = ["maydon", "yosh"]; kategoriya = ["hudud", "tur"]

tayyorlash = ColumnTransformer([
    ("son", Pipeline([("imp", SimpleImputer(strategy="median")),
                      ("sc", StandardScaler())]), son),
    ("kat", Pipeline([("imp", SimpleImputer(strategy="most_frequent")),
                      ("oh", OneHotEncoder(handle_unknown="ignore"))]), kategoriya),
])

model = Pipeline([("tayyor", tayyorlash), ("model", Ridge(alpha=1.0))])
model.fit(X_train, y_train)      # barcha qadamlar faqat o'quvda fit bo'ladi

Pipeline — barcha tayyorlash qadamlari va modelni bitta obyekt qiladi. Bu uch muammoni hal qiladi: (1) leakage — fit faqat o'quv qismida bajariladi, CV da ham har bo'lakda alohida 12.9-bob; (2) takrorlanuvchanlik — bir xil qadamlar test va ishlab chiqarishda; (3) soddalik — giperparametrlarni butun oqim bo'ylab sozlash mumkin (model__alpha).

2.3. Baza modeldan boshlash

Baza 12.1-bob — har qanday natijaning o'lchovi: regressiyada DummyRegressor(strategy="mean") yoki "oddiy qoida" (masalan, narx = maydon × o'rtacha kvadrat metr narxi); klassifikatsiyada DummyClassifier(strategy="most_frequent"/"stratified"). Agar murakkab model bazadan sezilarli yaxshi bo'lmasa — muammo belgilarda yoki vazifada 12.2-bob. Baza natijasi hisobotda majburiy: menejer "MAE 14 ming" ni faqat "42 ming" bilan solishtirganda tushunadi.

2.4. Modellarni solishtirish

text
Nomzodlar (jadval ma'lumoti uchun odatiy tartib):
  1. Chiziqli (Ridge / LogisticRegression) — tez, talqin qilinadi
  2. KNN — oddiy, masofaga asoslangan (masshtablash shart)
  3. Qaror daraxti — nochiziqli, talqin qilinadi
  4. Random Forest / Gradient Boosting — odatda eng kuchli

Solishtirish: bir xil CV bo'linishi, bir xil metrika, ± SD bilan
cross_validate(model, X_tr, y_tr, cv=5, scoring="neg_mean_absolute_error")

Modellarni bir xil sharoitda solishtirish kerak: bir xil CV bo'linishi (cv obyektini bir marta yaratib, hammasiga bering), bir xil metrika, bir xil tayyorlash. Natijani ± SD bilan bering: farq SD dan kichik bo'lsa, u ishonchli emas 11.1-bob. Jadval ma'lumotida ko'pincha gradient boosting yutadi, lekin chiziqli model tezligi va talqin qilinishi bilan qimmatli.

2.5. Giperparametrni sozlash

python
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV

setka = {"model__alpha": [0.01, 0.1, 1, 10, 100]}
qidiruv = GridSearchCV(pipeline, setka, cv=5, scoring="neg_mean_absolute_error", n_jobs=-1)
qidiruv.fit(X_train, y_train)
qidiruv.best_params_, -qidiruv.best_score_
qidiruv.best_estimator_        # eng yaxshi parametrlar bilan qayta o'qitilgan model

GridSearchCV — barcha kombinatsiyalarni CV bilan sinaydi va eng yaxshisini tanlaydi; RandomizedSearchCV — tasodifiy nomzodlar (ko'p parametrda tezroq — 9.2 dagi kombinatorik portlash). Muhim: qidiruv faqat o'quv ma'lumotida; best_score_ — CV balli, u yakuniy baho emas (11.9 — tanlov optimizmi). Yakuniy baho — test to'plamida.

2.6. Yakuniy baho va xatolar tahlili

text
1. Yakuniy modelni butun o'quv to'plamida qayta o'qitish (GridSearchCV buni o'zi qiladi)
2. TEST to'plamida BIR MARTA baholash
3. Xatolar tahlili:
     eng katta xatolar qayerda? (segmentlar, diapazonlar)
     xato taqsimoti (qiyshiqmi?)
     qoldiqlar va bashorat grafigi 10.6-bob
4. Talqin: koeffitsiyentlar yoki belgi muhimligi (ehtiyotkorlik bilan — 10.5)

Xatolar tahlili — modelni yaxshilashning eng samarali yo'li: xatolar qayerda to'planganini ko'rish (masalan, katta uylarda, yangi hududlarda, kam ma'lumotli segmentlarda) yangi belgilar yoki alohida model g'oyasini beradi. Yakuniy hisobotda: baza, CV natijalari, test natijasi, xatolar tahlili va cheklovlar 8.9-bob.

2.7. Oqim tuzoqlari

Asosiy tuzoqlar: pipeline'siz tayyorlash (leakage — 12.9); test bilan tanlov 12.3-bob; bazani o'tkazib yuborish; turli CV bo'linishlarida solishtirish; SD siz taqqoslash; metrikani natijadan keyin tanlash 8.8-bob; best_score_ ni yakuniy natija deb e'lon qilish; xatolar tahlilini qilmaslik; random_state yo'qligi (takrorlanmaydi).

2.8. To'liq oqim — takrorlanadigan shablon

Oqim: ma'lumot → test ajratish → EDA (o'quvda) → pipeline bilan tayyorlash → baza → nomzod modellarni CV bilan solishtirish (± SD, bir xil bo'linish) → giperparametr sozlash (GridSearch/RandomizedSearch, faqat o'quvda) → test'da bir marta baho → xatolar tahlili va hisobot. Pipeline leakage'dan himoya qiladi va butun oqimni bitta obyektga jamlaydi. Keyingi dars — klassifikatsiya metrikalari: aniqlik nega yetarli emas va nimalar bilan almashtiriladi.


3. Tez ma'lumotnoma

python
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.dummy import DummyRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.impute import SimpleImputer
from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV, KFold, cross_validate, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=0)

tayyor = ColumnTransformer([
    ("son", Pipeline([("i", SimpleImputer(strategy="median")), ("s", StandardScaler())]), son_ust),
    ("kat", OneHotEncoder(handle_unknown="ignore"), kat_ust),
])
pipe = Pipeline([("t", tayyor), ("m", Ridge())])

cv = KFold(5, shuffle=True, random_state=0)
cross_validate(pipe, X_tr, y_tr, cv=cv, scoring="neg_mean_absolute_error")

gs = GridSearchCV(pipe, {"m__alpha": [0.1, 1, 10]}, cv=cv,
                  scoring="neg_mean_absolute_error", n_jobs=-1).fit(X_tr, y_tr)
yakuniy = gs.best_estimator_
QOIDA: pipeline · bir xil cv · baza · SD bilan · test oxirida bir marta

To'liq oqim xulosasi

1 ma'lumot → 2 test ajratish → 3 EDA (o'quvda) → 4 pipeline
5 baza → 6 modellar (CV, ± SD) → 7 sozlash → 8 test (bir marta) + xatolar tahlili
Pipeline — leakage himoyasi va takrorlanuvchanlik
best_score_ — CV balli, yakuniy natija emas
Hisobot: baza, CV, test, xatolar, cheklovlar

4. Batafsil misollar

Misollar real numpy/pandas/sklearn bilan (Python 3.14). Barcha misollar bir xil yarat() generatoridan foydalanadi.

Misol 1 — Ma'lumot, ajratish va pipeline

python
"""To'liq oqim: ma'lumot, test ajratish, pipeline bilan tayyorlash (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.linear_model import Ridge
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler


def yarat(seed: int = 7) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    n = 3000
    hudud = rng.choice(["markaz", "shimol", "janub", "chekka"], n, p=[0.3, 0.25, 0.25, 0.2])
    tur = rng.choice(["yangi", "ikkilamchi"], n, p=[0.35, 0.65])
    maydon = rng.lognormal(np.log(65), 0.35, n).clip(25, 250)
    xona = np.clip((maydon / 24).round(), 1, 7)
    yosh = np.where(tur == "yangi", rng.integers(0, 4, n), rng.integers(4, 45, n))
    hudud_koef = pd.Series(hudud).map({"markaz": 1.45, "shimol": 1.05,
                                       "janub": 1.0, "chekka": 0.8}).to_numpy()
    narx = (1.55 * maydon + 5 * xona - 0.6 * yosh) * hudud_koef + rng.normal(0, 11, n)
    df = pd.DataFrame({"hudud": hudud, "tur": tur, "maydon": maydon.round(1),
                       "xona": xona, "yosh": yosh, "narx": narx.round(1)})
    df.loc[rng.choice(n, 90, replace=False), "yosh"] = np.nan          # yetishmovchilik
    return df


def main() -> None:
    df = yarat()
    X = df.drop(columns=["narx"])
    y = df["narx"]

    print("=== 1. Ma'lumot ===")
    print(f"  {df.shape[0]} qator, {df.shape[1]} ustun; dublikatlar: {df.duplicated().sum()}")
    print(f"  yetishmovchilik: {df.isna().sum().loc[lambda s: s > 0].to_dict()}")

    print("\n=== 2. Test to'plamini darhol ajratish ===")
    X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=0)
    print(f"  o'quv {len(X_tr)}, test {len(X_te)} (test yopiladi)")

    print("\n=== 3. EDA (faqat o'quvda) ===")
    print(f"  narx: median {y_tr.median():.1f}, IQR "
          f"{y_tr.quantile(0.25):.1f}..{y_tr.quantile(0.75):.1f}")
    print(f"  hudud bo'yicha median narx: "
          f"{X_tr.assign(narx=y_tr).groupby('hudud')['narx'].median().round(1).to_dict()}")

    print("\n=== 4. Pipeline ===")
    son = ["maydon", "xona", "yosh"]
    kat = ["hudud", "tur"]
    tayyor = ColumnTransformer([
        ("son", Pipeline([("imp", SimpleImputer(strategy="median")),
                          ("sc", StandardScaler())]), son),
        ("kat", OneHotEncoder(handle_unknown="ignore"), kat),
    ])
    pipe = Pipeline([("tayyor", tayyor), ("model", Ridge(alpha=1.0))])
    pipe.fit(X_tr, y_tr)
    print(f"  qadamlar: {[nom for nom, _ in pipe.steps]}")
    print(f"  belgilar soni (one-hot dan keyin): "
          f"{pipe.named_steps['tayyor'].transform(X_tr.head(5)).shape[1]}")
    print(f"  o'quv R^2 = {pipe.score(X_tr, y_tr):.3f}")
    print("  ⭐ Pipeline — tayyorlash va model bitta obyektda")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  3000 qator, 6 ustun; dublikatlar: 0
  yetishmovchilik: {'yosh': 90}

=== 2. Test to'plamini darhol ajratish ===
  o'quv 2400, test 600 (test yopiladi)

=== 3. EDA (faqat o'quvda) ===
  narx: median 113.7, IQR 83.0..150.6
  hudud bo'yicha median narx: {'chekka': 87.8, 'janub': 106.9, 'markaz': 151.5, 'shimol': 107.7}

=== 4. Pipeline ===
  qadamlar: ['tayyor', 'model']
  belgilar soni (one-hot dan keyin): 9
  o'quv R^2 = 0.927
  ⭐ Pipeline — tayyorlash va model bitta obyektda

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — Baza va modellarni solishtirish

python
"""Baza modeldan boshlab, nomzodlarni bir xil CV bilan solishtirish (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.dummy import DummyRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.impute import SimpleImputer
from sklearn.linear_model import Ridge
from sklearn.model_selection import KFold, cross_validate, train_test_split
from sklearn.neighbors import KNeighborsRegressor
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.tree import DecisionTreeRegressor


def yarat(seed: int = 7) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    n = 3000
    hudud = rng.choice(["markaz", "shimol", "janub", "chekka"], n, p=[0.3, 0.25, 0.25, 0.2])
    tur = rng.choice(["yangi", "ikkilamchi"], n, p=[0.35, 0.65])
    maydon = rng.lognormal(np.log(65), 0.35, n).clip(25, 250)
    xona = np.clip((maydon / 24).round(), 1, 7)
    yosh = np.where(tur == "yangi", rng.integers(0, 4, n), rng.integers(4, 45, n))
    hudud_koef = pd.Series(hudud).map({"markaz": 1.45, "shimol": 1.05,
                                       "janub": 1.0, "chekka": 0.8}).to_numpy()
    narx = (1.55 * maydon + 5 * xona - 0.6 * yosh) * hudud_koef + rng.normal(0, 11, n)
    df = pd.DataFrame({"hudud": hudud, "tur": tur, "maydon": maydon.round(1),
                       "xona": xona, "yosh": yosh, "narx": narx.round(1)})
    df.loc[rng.choice(n, 90, replace=False), "yosh"] = np.nan
    return df


def tayyorlash() -> ColumnTransformer:
    return ColumnTransformer([
        ("son", Pipeline([("imp", SimpleImputer(strategy="median")),
                          ("sc", StandardScaler())]), ["maydon", "xona", "yosh"]),
        ("kat", OneHotEncoder(handle_unknown="ignore"), ["hudud", "tur"]),
    ])


def main() -> None:
    df = yarat()
    X, y = df.drop(columns=["narx"]), df["narx"]
    X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=0)

    cv = KFold(5, shuffle=True, random_state=0)
    nomzodlar = {
        "baza (o'rtacha)": DummyRegressor(strategy="mean"),
        "Ridge": Pipeline([("t", tayyorlash()), ("m", Ridge(alpha=1.0))]),
        "KNN (k=10)": Pipeline([("t", tayyorlash()), ("m", KNeighborsRegressor(10))]),
        "daraxt (d=6)": Pipeline([("t", tayyorlash()),
                                  ("m", DecisionTreeRegressor(max_depth=6, random_state=0))]),
        "o'rmon": Pipeline([("t", tayyorlash()),
                            ("m", RandomForestRegressor(n_estimators=200, random_state=0,
                                                        min_samples_leaf=2, n_jobs=-1))]),
    }

    print("=== CV natijalari (MAE, ming $) ===")
    print(f"  {'model':<18} {'CV MAE':>10} {'SD':>7} {'o\"quv MAE':>11}")
    for nom, m in nomzodlar.items():
        r = cross_validate(m, X_tr, y_tr, cv=cv, scoring="neg_mean_absolute_error",
                           return_train_score=True)
        cv_mae = -r["test_score"].mean()
        tr_mae = -r["train_score"].mean()
        print(f"  {nom:<18} {cv_mae:>10.2f} {r['test_score'].std():>7.2f} {tr_mae:>11.2f}")

    print("\n  baza bilan solishtirish — har qanday natijaning o'lchovi")
    print("  ⭐ Bir xil CV bo'linishi, bir xil metrika, SD bilan")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== CV natijalari (MAE, ming $) ===
  model                  CV MAE      SD   o"quv MAE
  baza (o'rtacha)         42.93    0.89       42.92
  Ridge                   11.76    0.21       11.72
  KNN (k=10)              10.42    0.29        9.42
  daraxt (d=6)            11.55    0.34        9.95
  o'rmon                   9.96    0.17        5.55

  baza bilan solishtirish — har qanday natijaning o'lchovi
  ⭐ Bir xil CV bo'linishi, bir xil metrika, SD bilan

Nima ko'rsatdi: 2.3, 2.4-bo'limlar.

Misol 3 — Giperparametrni sozlash

python
"""GridSearchCV bilan sozlash: qidiruv faqat o'quv ma'lumotida (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestRegressor
from sklearn.impute import SimpleImputer
from sklearn.model_selection import GridSearchCV, KFold, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler


def yarat(seed: int = 7) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    n = 3000
    hudud = rng.choice(["markaz", "shimol", "janub", "chekka"], n, p=[0.3, 0.25, 0.25, 0.2])
    tur = rng.choice(["yangi", "ikkilamchi"], n, p=[0.35, 0.65])
    maydon = rng.lognormal(np.log(65), 0.35, n).clip(25, 250)
    xona = np.clip((maydon / 24).round(), 1, 7)
    yosh = np.where(tur == "yangi", rng.integers(0, 4, n), rng.integers(4, 45, n))
    hudud_koef = pd.Series(hudud).map({"markaz": 1.45, "shimol": 1.05,
                                       "janub": 1.0, "chekka": 0.8}).to_numpy()
    narx = (1.55 * maydon + 5 * xona - 0.6 * yosh) * hudud_koef + rng.normal(0, 11, n)
    df = pd.DataFrame({"hudud": hudud, "tur": tur, "maydon": maydon.round(1),
                       "xona": xona, "yosh": yosh, "narx": narx.round(1)})
    df.loc[rng.choice(n, 90, replace=False), "yosh"] = np.nan
    return df


def main() -> None:
    df = yarat()
    X, y = df.drop(columns=["narx"]), df["narx"]
    X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=0)

    tayyor = ColumnTransformer([
        ("son", Pipeline([("imp", SimpleImputer(strategy="median")),
                          ("sc", StandardScaler())]), ["maydon", "xona", "yosh"]),
        ("kat", OneHotEncoder(handle_unknown="ignore"), ["hudud", "tur"]),
    ])
    pipe = Pipeline([("t", tayyor),
                     ("m", RandomForestRegressor(random_state=0))])

    setka = {
        "m__n_estimators": [100, 300],
        "m__max_depth": [6, 12, None],
        "m__min_samples_leaf": [1, 5],
    }

    print("=== 1. Qidiruv hajmi ===")
    print(f"  kombinatsiyalar: {2 * 3 * 2} × 5 fold = {2 * 3 * 2 * 5} ta o'qitish")

    gs = GridSearchCV(pipe, setka, cv=KFold(5, shuffle=True, random_state=0),
                      scoring="neg_mean_absolute_error", n_jobs=-1)
    gs.fit(X_tr, y_tr)

    print("\n=== 2. Eng yaxshi natija ===")
    print(f"  parametrlar: {gs.best_params_}")
    print(f"  CV MAE = {-gs.best_score_:.2f}")

    print("\n=== 3. Eng yaxshi 3 kombinatsiya ===")
    nat = pd.DataFrame(gs.cv_results_)
    nat = nat.sort_values("rank_test_score").head(3)
    for _, q in nat.iterrows():
        print(f"  MAE {-q['mean_test_score']:.2f} ± {q['std_test_score']:.2f}: "
              f"depth={q['param_m__max_depth']}, leaf={q['param_m__min_samples_leaf']}, "
              f"n={q['param_m__n_estimators']}")

    print("\n=== 4. best_score_ — yakuniy natija emas ===")
    print("  u CV bahosi va tanlov tufayli biroz optimistik 11.9-bob")
    print("  ⭐ Yakuniy baho — test to'plamida (Misol 4)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Qidiruv hajmi ===
  kombinatsiyalar: 12 × 5 fold = 60 ta o'qitish

=== 2. Eng yaxshi natija ===
  parametrlar: {'m__max_depth': 12, 'm__min_samples_leaf': 5, 'm__n_estimators': 300}
  CV MAE = 9.85

=== 3. Eng yaxshi 3 kombinatsiya ===
  MAE 9.85 ± 0.16: depth=12, leaf=5, n=300
  MAE 9.85 ± 0.16: depth=12, leaf=5, n=100
  MAE 9.85 ± 0.16: depth=None, leaf=5, n=100

=== 4. best_score_ — yakuniy natija emas ===
  u CV bahosi va tanlov tufayli biroz optimistik 11.9-bob
  ⭐ Yakuniy baho — test to'plamida (Misol 4)

Nima ko'rsatdi: 2.5-bo'lim.

Misol 4 — Yakuniy baho va xatolar tahlili

python
"""Test to'plamida bir martalik baho va xatolar tahlili (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.dummy import DummyRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.impute import SimpleImputer
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_absolute_error, r2_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler


def yarat(seed: int = 7) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    n = 3000
    hudud = rng.choice(["markaz", "shimol", "janub", "chekka"], n, p=[0.3, 0.25, 0.25, 0.2])
    tur = rng.choice(["yangi", "ikkilamchi"], n, p=[0.35, 0.65])
    maydon = rng.lognormal(np.log(65), 0.35, n).clip(25, 250)
    xona = np.clip((maydon / 24).round(), 1, 7)
    yosh = np.where(tur == "yangi", rng.integers(0, 4, n), rng.integers(4, 45, n))
    hudud_koef = pd.Series(hudud).map({"markaz": 1.45, "shimol": 1.05,
                                       "janub": 1.0, "chekka": 0.8}).to_numpy()
    narx = (1.55 * maydon + 5 * xona - 0.6 * yosh) * hudud_koef + rng.normal(0, 11, n)
    df = pd.DataFrame({"hudud": hudud, "tur": tur, "maydon": maydon.round(1),
                       "xona": xona, "yosh": yosh, "narx": narx.round(1)})
    df.loc[rng.choice(n, 90, replace=False), "yosh"] = np.nan
    return df


def tayyor() -> ColumnTransformer:
    return ColumnTransformer([
        ("son", Pipeline([("imp", SimpleImputer(strategy="median")),
                          ("sc", StandardScaler())]), ["maydon", "xona", "yosh"]),
        ("kat", OneHotEncoder(handle_unknown="ignore"), ["hudud", "tur"]),
    ])


def main() -> None:
    df = yarat()
    X, y = df.drop(columns=["narx"]), df["narx"]
    X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=0)

    modellar = {
        "baza": DummyRegressor(strategy="mean"),
        "Ridge": Pipeline([("t", tayyor()), ("m", Ridge(alpha=1.0))]),
        "o'rmon": Pipeline([("t", tayyor()),
                            ("m", RandomForestRegressor(n_estimators=300, max_depth=12,
                                                        min_samples_leaf=1, random_state=0,
                                                        n_jobs=-1))]),
    }

    print("=== 1. Test natijalari (bir marta) ===")
    bashoratlar = {}
    for nom, m in modellar.items():
        m.fit(X_tr, y_tr)
        p = m.predict(X_te)
        bashoratlar[nom] = p
        print(f"  {nom:<8}: MAE {mean_absolute_error(y_te, p):6.2f}, R^2 {r2_score(y_te, p):6.3f}")

    print("\n=== 2. Xatolar tahlili (o'rmon) ===")
    xato = np.abs(y_te.to_numpy() - bashoratlar["o'rmon"])
    te = X_te.assign(xato=xato, haqiqiy=y_te.to_numpy())
    print(f"  xato: median {np.median(xato):.2f}, 90-persentil {np.percentile(xato, 90):.2f}, "
          f"maks {xato.max():.2f}")

    print("\n=== 3. Segmentlar bo'yicha o'rtacha xato ===")
    print(f"  hudud: {te.groupby('hudud')['xato'].mean().round(2).to_dict()}")
    te["maydon_guruh"] = pd.cut(te["maydon"], [0, 50, 80, 120, 300],
                                labels=["<50", "50-80", "80-120", ">120"])
    print(f"  maydon: {te.groupby('maydon_guruh', observed=True)['xato'].mean().round(2).to_dict()}")

    print("\n=== 4. Xulosa ===")
    print("  eng katta xatolar — katta maydonli uylarda (kam ma'lumot, yuqori dispersiya)")
    print("  keyingi qadam: shu segment uchun ko'proq ma'lumot yoki alohida model")
    print("  ⭐ Xatolar tahlili — yaxshilash yo'nalishini beradi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Test natijalari (bir marta) ===
  baza    : MAE  44.14, R^2 -0.000
  Ridge   : MAE  11.95, R^2  0.926
  o'rmon  : MAE   9.66, R^2  0.953

=== 2. Xatolar tahlili (o'rmon) ===
  xato: median 7.80, 90-persentil 19.41, maks 48.73

=== 3. Segmentlar bo'yicha o'rtacha xato ===
  hudud: {'chekka': 9.63, 'janub': 9.1, 'markaz': 10.27, 'shimol': 9.57}
  maydon: {'<50': 9.28, '50-80': 9.34, '80-120': 10.45, '>120': 11.42}

=== 4. Xulosa ===
  eng katta xatolar — katta maydonli uylarda (kam ma'lumot, yuqori dispersiya)
  keyingi qadam: shu segment uchun ko'proq ma'lumot yoki alohida model
  ⭐ Xatolar tahlili — yaxshilash yo'nalishini beradi

Nima ko'rsatdi: 2.6, 2.7-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Tayyorlashni oldin, ajratishni keyin" Avval ajratish (leakage)
"Scaler'ni butun X da fit qilsa bo'ladi" Faqat o'quvda (pipeline)
"Baza model — vaqt isrofi" Har natijaning o'lchovi
"best_score_ — yakuniy natija" CV balli (optimistik)
"Eng murakkab model — eng yaxshi" CV bilan tekshiring
"Model tayyor — ish tugadi" Xatolar tahlili kerak
"SD muhim emas" Farq SD dan kichik bo'lishi mumkin
"GridSearch har doim kerak" Kichik setka yoki Randomized

6. Keng tarqalgan xatolar va yechimlari

1. Pipeline'siz tayyorlash

python
X_scaled = StandardScaler().fit_transform(X)   # butun ma'lumot      # ⚠️
Pipeline([("sc", StandardScaler()), ("m", Ridge())])                 # ✅

2. Bazani o'tkazib yuborish

python
print("MAE:", mean_absolute_error(y_te, p))                          # ⚠️
print("baza MAE:", mean_absolute_error(y_te, DummyRegressor()...))   # ✅

3. Turli CV bo'linishlari

python
cross_val_score(m1, X, y, cv=5); cross_val_score(m2, X, y, cv=5)     # ⚠️ (shuffle yo'q/har xil)
cv = KFold(5, shuffle=True, random_state=0)   # bir marta yarating    # ✅

4. best_score_ ni hisobot qilish

python
print("model natijasi:", -gs.best_score_)                            # ⚠️
print("test natijasi:", mean_absolute_error(y_te, gs.predict(X_te))) # ✅

5. One-hot da yangi kategoriya

python
OneHotEncoder()                        # testda yangi qiymat → xato  # ⚠️
OneHotEncoder(handle_unknown="ignore")                               # ✅

6. Xatolar tahlilisiz

python
print("R^2:", r2_score(y_te, p))                                     # ⚠️
# segmentlar bo'yicha xato, eng katta xatolar ro'yxati                # ✅

7. random_state yo'q

python
RandomForestRegressor(n_estimators=300)                              # ⚠️
RandomForestRegressor(n_estimators=300, random_state=0)              # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 12.1-12.5-darslar (o'tilgan): Vazifa, ajratish, overfitting
  • 6-qism (o'tilgan): Tozalash, kodlash, masshtablash
  • 12.7-12.8-darslar: Metrikalar
  • 12.9-dars: Pipeline va leakage chuqurroq
  • 12.10-dars: To'liq loyiha

8. Eng yaxshi amaliyotlar

  1. Test to'plamini birinchi qadamda ajrating.

  2. Barcha tayyorlashni pipeline ichida qiling.

  3. Bazadan boshlang.

  4. Bir xil CV bo'linishida solishtiring.

  5. Natijani ± SD bilan bering.

  6. Sozlashni faqat o'quvda qiling.

  7. Testda bir marta baholang.

  8. Xatolar tahlilini o'tkazing.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # oqimning birinchi qadami?
2.  # test qachon ajratiladi?
3.  # EDA qayerda qilinadi?
4.  # pipeline nima uchun?
5.  # baza model nima?
6.  # modellarni qanday solishtirish kerak?
7.  # GridSearchCV nima qiladi?
8.  # best_score_ nima?
9.  # yakuniy baho qayerda?
10. # xatolar tahlili nima beradi?
11. # handle_unknown nima uchun?
12. # ColumnTransformer nima uchun?
Javoblar
  1. Ma'lumotni yuklash va tekshirish
  2. Darhol (2-qadam)
  3. Faqat o'quv ma'lumotida
  4. Leakage himoyasi va takrorlanuvchanlik
  5. Oddiy qoida/o'rtacha
  6. Bir xil CV, bir xil metrika, SD bilan
  7. Giperparametr kombinatsiyalarini CV bilan sinaydi
  8. Eng yaxshi CV balli
  9. Test to'plamida (bir marta)
  10. Yaxshilash yo'nalishini
  11. Testdagi yangi kategoriyalar uchun
  12. Turli ustun turlariga turli tayyorlash

Vazifa 2: Xatolarni tuzating

python
1.  X_s = StandardScaler().fit_transform(X); train_test_split(X_s, y)

2.  print("natija:", -gs.best_score_)   # yakuniy hisobot

3.  cross_val_score(m1, X, y, cv=5); cross_val_score(m2, X, y, cv=KFold(5, shuffle=True))

4.  print("MAE:", mae)   # bazasiz

5.  OneHotEncoder().fit(X_tr[["hudud"]])   # testda yangi hudud bor
Javoblar
python
1.  pipe = Pipeline([("sc", StandardScaler()), ("m", model)]); pipe.fit(X_tr, y_tr)

2.  print("test:", mean_absolute_error(y_te, gs.predict(X_te)))

3.  cv = KFold(5, shuffle=True, random_state=0)   # ikkalasiga bir xil

4.  print("baza MAE:", ...); print("model MAE:", mae)

5.  OneHotEncoder(handle_unknown="ignore")

Vazifa 3: To'liq oqim

Modellang:

  1. O'z ma'lumotingiz (yoki sklearn dataset)
  2. 8 qadam
  3. Har qadamda natija
  4. Yakuniy hisobot

Vazifa 4: Modellar jadvali

Modellang:

  1. 5 nomzod model
  2. Bir xil CV
  3. MAE ± SD jadvali
  4. Tanlov asosi

Vazifa 5: Sozlash

Modellang:

  1. GridSearchCV (kichik setka)
  2. RandomizedSearchCV (katta setka)
  3. Vaqt va natija solishtiruvi
  4. Tavsiya

Vazifa 6: Integratsiya

Modellang:

  1. Tozalash (6-qism)
  2. CV (12.3)
  3. Overfitting tashxisi (12.4)
  4. Xatolar tahlili

Vazifa 7: O'ylash

Ko'p boshlovchi Data Scientistlar modelni tanlashga va giperparametrlarni sozlashga ko'p vaqt sarflaydi, lekin natija kam yaxshilanadi. Tajribali mutaxassislar esa ko'proq vaqtni ma'lumot va belgilarga sarflaydi. Nima uchun bu farq bor va vaqtni qanday taqsimlash kerak?

Javob

Qisqa javob: model tanlash odatda bir necha foiz yaxshilanish beradi, ma'lumot sifati va belgilar esa o'nlab foiz. Bundan tashqari, model almashtirish oson va "qiziqarli", ma'lumot bilan ishlash esa mashaqqatli — shuning uchun boshlovchilar oson yo'ldan boradi.

1. Odatiy hissa (jadval ma'lumoti)

Manba Taxminiy ta'sir
Vazifani to'g'ri qo'yish (12.2) Juda katta (noto'g'ri bo'lsa — hammasi behuda)
Ma'lumot sifati va hajmi Katta
Belgi muhandisligi Katta
Model turi O'rtacha
Giperparametr sozlash Kichik-o'rtacha

2. Nega shunday

  • Zamonaviy modellar (boosting) standart parametrlar bilan ham kuchli
  • Ma'lumotdagi xato yoki leakage hech qanday model bilan tuzatilmaydi
  • Yangi belgi modelga yangi ma'lumot beradi, sozlash esa faqat mavjudini optimallashtiradi

3. Vaqtni taqsimlash tavsiyasi

  1. Vazifa va metrikani aniqlash — 15%
  2. Ma'lumot tekshiruvi, tozalash, leakage ovi — 30%
  3. Belgi muhandisligi — 25%
  4. Model tanlash — 15%
  5. Sozlash va yakuniy baho — 15%

4. Data Scientist qanday

  • Oddiy model + yaxshi belgilar bilan boshlaydi
  • Xatolar tahlilidan yangi belgi g'oyalarini oladi
  • Sozlashni oxirida, cheklangan byudjet bilan qiladi

5. Xulosa

  1. Model tanlash — eng kichik hissa
  2. Ma'lumot va belgilar — eng katta hissa
  3. Xatolar tahlili — yaxshilash kompasi
  4. Sozlash — oxirgi, kichik qadam

Nimani mustahkamlaydi: 2.3, 2.6-bo'limlar.


Xulosa

Bu darsda to'liq ML oqimini qurdik.

Eng muhim uch fikr:

  1. Sakkiz qadam. Ma'lumot → test ajratish (darhol) → EDA (faqat o'quvda) → pipeline bilan tayyorlash → baza → nomzodlarni CV bilan solishtirish → sozlash → test'da bir marta baho va xatolar tahlili.

  2. Pipeline. ColumnTransformer + Pipeline barcha tayyorlash qadamlarini modelga bog'laydi: fit faqat o'quv qismida bajariladi (CV da ham har bo'lakda alohida) — bu leakage'dan himoya 12.9-bob va takrorlanuvchanlikni ta'minlaydi; giperparametrlar butun oqim bo'ylab sozlanadi (m__alpha).

  3. Halol taqqoslash va tahlil. Modellar bir xil CV bo'linishida, bir xil metrikada, ± SD bilan solishtiriladi; best_score_ — CV balli, yakuniy natija emas. Yakuniy hisobot: baza, CV natijalari, test natijasi, xatolar tahlili (qaysi segmentlarda xato katta) va cheklovlar.

Keyingi darsda klassifikatsiya metrikalarini o'rganamiz: aniqlik nega aldaydi, precision/recall/F1, ROC va PR egri chiziqlari, chegarani tanlash va nomutanosib sinflar.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
12.6-dars: Birinchi to'liq model — IlmHamroh