IlmHamroh
Data Science va sun'iy intellekt/Feature engineering1/10-dars18 daqiqa
Mundarija (22)

17.1-dars: Feature nima

17-QISM — FEATURE ENGINEERING · 1-dars


1. Kirish va motivatsiya

Modellar haqida ko'p gapirdik, lekin amaliyotdagi haqiqat boshqacha: model tanlash natijaning kichik qismini belgilaydi, belgilar esa kattasini. Bir xil ma'lumotda yaxshi belgilar bilan logistik regressiya, yomon belgilar bilan gradient boostingdan ustun chiqishi mumkin.

Belgi (feature) — modelga beriladigan har bir kirish o'zgaruvchisi. Feature engineering esa xom ma'lumotni modelga tushunarli shaklga keltirish: yangi belgilar yaratish, mavjudlarini o'zgartirish, keraksizlarini olib tashlash.

Bu — ma'lumot tahlilidagi eng domenga bog'liq qism: "oxirgi xariddan o'tgan kunlar" degan belgi hech qanday algoritmdan kelib chiqmaydi, u biznesni tushunishdan tug'iladi.

Bu darsda: belgi turlari, yaxshi belgi nimasi bilan yaxshi, modelning induktiv siljishi bilan bog'liqlik, belgi yaratishning asosiy manbalari, leakage xavfi va baholash tartibi.

Real vaziyat. Kredit skoringida jamoa uch hafta gradient boostingni sozladi — AUC 0.741 dan 0.748 ga ko'tarildi. Keyin bitta belgi qo'shildi: "so'nggi 3 oydagi kechikishlar soni / umumiy to'lovlar soni". AUC 0.783 ga chiqdi. Bitta belgi uch haftalik sozlashdan besh barobar ko'p berdi.

Bu darsda belgi tushunchasini o'rganamiz.

Bu darsda:

  • Belgi turlari
  • Yaxshi belgi nimasi bilan yaxshi
  • Model va belgi mosligi
  • Belgi yaratish manbalari
  • Leakage xavfi
  • Baholash tartibi
  • Tuzoqlar
  • Amaliy: birinchi belgilar

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


2. Nazariya — chuqur tushuntirish

2.1. Belgi turlari

text
SONLI (numerical):
  uzluksiz  - narx, harorat, masofa
  diskret   - xaridlar soni, xonalar soni

KATEGORIYALI (categorical):
  nominal   - hudud, rang, kanal (tartib yo'q)
  ordinal   - daraja (boshlang'ich < o'rta < yuqori) - TARTIB bor

IKKILIK (binary): ha/yo'q, erkak/ayol

VAQT: sana, vaqt belgisi, davomiylik

MATN: erkin matn, kategoriya nomi, izoh

MAXSUS: geografik (koordinata), tasvir, grafik, ketma-ketlik

Har tur uchun O'Z tayyorlash usuli bor (17.2-17.6)

Ordinal va nominalni ajratish muhim: ordinalga OrdinalEncoder (tartib saqlanadi), nominalga OneHotEncoder. Nominal belgiga tasodifiy raqam berish — modelga yolg'on tartib o'rgatadi.

2.2. Yaxshi belgi nimasi bilan yaxshi

text
1. MA'LUMOTLI: maqsad bilan bog'liq (lekin sabab bo'lishi shart emas)
2. MAVJUD: bashorat vaqtida ma'lum bo'ladi (leakage yo'q)
3. BARQAROR: vaqt o'tishi bilan ma'nosi o'zgarmaydi
4. TUSHUNARLI: nima ekanini tushuntirish mumkin
5. ARZON: hisoblash va yig'ish narxi oqlanadi
6. TOZA: yo'qolgan qiymatlar va xatolar kam

QO'SHIMCHA: model turiga MOS
  chiziqli model  -> chiziqlilashtirilgan belgilar kerak
  daraxtlar       -> monoton transformatsiyalar keraksiz (15.1)

"Mavjud" sharti eng ko'p buziladi: o'quv ma'lumotida bor bo'lgan belgi ishlab chiqarishda bashorat vaqtida mavjud bo'lmasligi mumkin. Har belgi uchun "bu qachon ma'lum bo'ladi?" degan savolni bering.

2.3. Model va belgi mosligi

text
                     Chiziqli model      Daraxtlar/ansambllar
masshtablash         MAJBURIY            keraksiz
log/kvadrat          ko'pincha zarur     keraksiz (monoton)
o'zaro ta'sir        QO'LDA qo'shiladi   o'zi topadi
one-hot              majburiy            ko'pincha kerak (yoki ichki)
chetlanishlar        juda ta'sirchan     chidamli
yo'qolgan qiymat     to'ldirish kerak    Hist*/XGB o'zi boshqaradi

XULOSA: bir xil ma'lumot uchun TURLI belgilar to'plami kerak bo'lishi mumkin

Daraxtlar log(x) dan foyda ko'rmaydi, chiziqli model esa ko'radi. Belgi muhandisligi modeldan ajralmagan: qaysi modelni ishlatishingizni bilmasdan optimal belgilar to'plamini tuzib bo'lmaydi.

2.4. Belgi yaratish manbalari

text
1. DOMEN BILIMI - eng qimmatli manba
   "yuk og'irligi / masofa" nisbati, "hafta oxiri" bayrog'i

2. AGREGATSIYA - guruh bo'yicha statistika (7-qism)
   mijozning o'rtacha cheki, hududning medianasi

3. NISBAT va FARQ - ikki belgi kombinatsiyasi
   narx / maydon, joriy / o'rtacha

4. VAQT - sana komponentlari, oynalar, lag lar 17.5-bob
   hafta kuni, oxirgi hodisadan o'tgan vaqt

5. MATN - uzunlik, so'zlar soni, TF-IDF 17.6-bob

6. NAZORATSIZ - klaster, PCA, anomaliya bali 16.11-bob

7. TASHQI - ob-havo, bayramlar, valyuta kursi

Nisbatlar — eng arzon va eng ko'p ishlaydigan usul: xarajat/daromad, kechikish/to'lovlar, narx/maydon. Ular modelga bitta belgida kontekst beradi.

2.5. Leakage xavfi

text
LEAKAGE - maqsad haqidagi ma'lumot belgiga "sizib kirishi" 12.9-bob

Ikki tur:
1. MAQSAD leakage: belgi maqsaddan hosil bo'lgan
   "to'lov summasi" - defolt bo'lgan mijozda 0
   "chegirma berildi" - buyurtma bekor qilingandan keyin

2. VAQT leakage: belgi bashorat vaqtida hali ma'lum emas
   "umumiy xaridlar soni" (butun davr bo'yicha)
   "keyingi oydagi faollik"

TEKSHIRUV:
  - belgi AJOYIB ishlayapti (AUC 0.99) -> shubhalaning
  - "bu qachon ma'lum bo'ladi?" savolini bering
  - vaqt bo'yicha validatsiya qiling

"Juda yaxshi natija" — leakage ning asosiy belgisi. AUC 0.98 chiqsa, avval quvonmang, tekshiring: real vazifalarda bunday natija kamdan-kam bo'ladi.

2.6. Baholash tartibi

text
Har yangi belgi uchun:
  1. BAZAVIY natijani qayd eting (belgilarsiz)
  2. Belgini QO'SHING va CV da o'lchang
  3. Yaxshilanish CV STD dan katta ekanini tekshiring
  4. Leakage yo'qligini tekshiring
  5. Ishlab chiqarishda hisoblash mumkinligini tasdiqlang

QO'SHIMCHA:
  - belgilar GURUHI bo'lib qo'shiladi (bitta-bitta emas)
  - permutation importance bilan tekshiring 15.11-bob
  - keraksizlarini olib tashlang (17.7)

Yaxshilanishni CV standart og'ishi bilan solishtiring 15.14-bob: 0.002 lik "yaxshilanish" std 0.008 bo'lsa — shovqin, belgi emas.

2.7. Tuzoqlar

Asosiy tuzoqlar: nominal belgiga tartib berish; leakage ni tekshirmaslik; belgini modelga moslamaslik (daraxtga log); yaxshilanishni CV std bilan solishtirmaslik; belgi ishlab chiqarishda hisoblanishini tekshirmaslik; juda ko'p belgi yaratib, tanlashni unutish; domen bilimini e'tiborsiz qoldirib, faqat avtomatik usullarga tayanish; tayyorlashni Pipeline tashqarisida qilish.

2.8. Belgi — modeldan muhimroq

Belgi — modelga beriladigan har bir kirish; feature engineering — xom ma'lumotni modelga tushunarli shaklga keltirish. Yaxshi belgi ma'lumotli, bashorat vaqtida mavjud, barqaror va tushunarli bo'ladi. Belgi modeldan ajralmagan: chiziqli modelga masshtablash va chiziqlilashtirish kerak, daraxtlarga esa yo'q. Eng qimmatli manba — domen bilimi, eng katta xavf — leakage. Keyingi dars — belgi yaratish.


3. Tez ma'lumotnoma

python
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, StandardScaler

df.dtypes                                  # belgi turlarini ko'rish
df.select_dtypes(include="number").columns
df.select_dtypes(include=["object", "category"]).columns
df.nunique()                               # kardinallik

tayyor = ColumnTransformer([
    ("nominal", OneHotEncoder(handle_unknown="ignore",
                                sparse_output=False), nominal),
    ("ordinal", OrdinalEncoder(categories=[["past", "o_rta", "yuqori"]]), ordinal),
    ("sonli", StandardScaler(), sonli)])
Pipeline([("t", tayyor), ("m", model)])
QOIDA: turni aniqla · leakage ni tekshir · modelga mosla ·
       CV std bilan solishtir

Belgi xulosasi

Turlar: sonli, kategoriyali (nominal/ordinal), ikkilik, vaqt, matn
Yaxshi belgi: ma'lumotli, mavjud, barqaror, tushunarli, arzon
Model va belgi bog'liq: chiziqli != daraxt
Eng katta xavf: leakage ("juda yaxshi natija" - shubhali)

4. Batafsil misollar

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

Misol 1 — Belgi turlarini aniqlash

python
"""Xom ma'lumotdan belgi turlarini ajratish (real pandas/sklearn)."""

import numpy as np
import pandas as pd


def yarat(seed: int = 7, n: int = 2000) -> pd.DataFrame:
    """Aralash turdagi belgilar."""
    rng = np.random.default_rng(seed)
    sanalar = pd.to_datetime("2025-01-01") + pd.to_timedelta(
        rng.integers(0, 540, n), unit="D")
    return pd.DataFrame({
        "buyurtma_id": np.arange(100000, 100000 + n),
        "sana": sanalar,
        "hudud": rng.choice(["toshkent", "samarqand", "buxoro", "fargona"], n),
        "daraja": rng.choice(["oddiy", "kumush", "oltin", "platina"], n,
                             p=[0.5, 0.3, 0.15, 0.05]),
        "yetkazish": rng.choice(["tezkor", "oddiy"], n, p=[0.3, 0.7]),
        "summa": rng.lognormal(12.5, 0.7, n),
        "mahsulotlar": rng.integers(1, 12, n),
        "izoh": rng.choice(["", "tezroq yetkazing", "sovg'a qog'ozi",
                            "qo'ng'iroq qilmang"], n, p=[0.7, 0.1, 0.1, 0.1]),
        "kechikdi": rng.integers(0, 2, n),
    })


def turlarni_aniqla(df: pd.DataFrame, id_ustunlar=(), maqsad="") -> dict:
    """Ustunlarni turlarga ajratadi."""
    natija = {"id": [], "vaqt": [], "sonli": [], "kategoriyali": [],
              "ikkilik": [], "matn": [], "maqsad": []}
    for ustun in df.columns:
        seriya = df[ustun]
        if ustun == maqsad:
            natija["maqsad"].append(ustun)
        elif ustun in id_ustunlar:
            natija["id"].append(ustun)
        elif pd.api.types.is_datetime64_any_dtype(seriya):
            natija["vaqt"].append(ustun)
        elif pd.api.types.is_numeric_dtype(seriya):
            if seriya.nunique() == 2:
                natija["ikkilik"].append(ustun)
            else:
                natija["sonli"].append(ustun)
        elif seriya.nunique() > 0.3 * len(seriya):
            natija["matn"].append(ustun)
        elif seriya.nunique() == 2:
            natija["ikkilik"].append(ustun)
        else:
            natija["kategoriyali"].append(ustun)
    return natija


def main() -> None:
    df = yarat()

    print("=== 1. Ustunlar va ularning turlari ===")
    print(f"  {'ustun':<14} {'dtype':<16} {'noyob':>7} {'yo_qolgan':>10}")
    for ustun in df.columns:
        print(f"  {ustun:<14} {str(df[ustun].dtype):<16} "
              f"{df[ustun].nunique():>7} {int(df[ustun].isna().sum()):>10}")

    print("\n=== 2. Avtomatik tasniflash ===")
    turlar = turlarni_aniqla(df, id_ustunlar=["buyurtma_id"],
                             maqsad="kechikdi")
    for tur, ustunlar in turlar.items():
        if ustunlar:
            print(f"  {tur:<14}: {ustunlar}")

    print("\n=== 3. Kardinallik tahlili ===")
    kategoriyali = turlar["kategoriyali"] + turlar["ikkilik"]
    print(f"  {'ustun':<14} {'darajalar':>10} {'ulush':>8} {'tavsiya':<22}")
    for ustun in kategoriyali:
        if not pd.api.types.is_numeric_dtype(df[ustun]):
            k = df[ustun].nunique()
            ulush = k / len(df)
            tavsiya = ("OneHot" if k <= 15
                       else "Target/Frequency encoding")
            print(f"  {ustun:<14} {k:>10} {ulush:>7.2%} {tavsiya:<22}")

    print("\n=== 4. Ordinal va nominalni ajratish ===")
    print(f"  daraja qiymatlari: {sorted(df['daraja'].unique())}")
    print(f"  hudud qiymatlari: {sorted(df['hudud'].unique())}")
    print("  daraja  -> ORDINAL (oddiy < kumush < oltin < platina)")
    print("  hudud   -> NOMINAL (tartib yo'q)")
    tartib = ["oddiy", "kumush", "oltin", "platina"]
    kod = {nom: i for i, nom in enumerate(tartib)}
    df["daraja_kod"] = df["daraja"].map(kod)
    print(f"  daraja_kod va summa korrelyatsiyasi: "
          f"{df['daraja_kod'].corr(df['summa']):.4f}")
    # nominal uchun tasodifiy raqam berilsa nima bo'ladi
    tasodifiy_kod = {h: i for i, h in enumerate(sorted(df["hudud"].unique()))}
    df["hudud_kod"] = df["hudud"].map(tasodifiy_kod)
    print(f"  hudud_kod va summa korrelyatsiyasi: "
          f"{df['hudud_kod'].corr(df['summa']):.4f} (ma'nosiz)")
    print("  ⭐ Ordinalga tartib, nominalga one-hot")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
<<NATIJA>>

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — Yaxshi belgi natijani qanday o'zgartiradi

python
"""Bitta belgi sozlashdan ko'ra ko'proq beradi (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler


def yarat(seed: int = 11, n: int = 6000) -> pd.DataFrame:
    """Kredit: risk NISBATLARDA yashiringan, xom qiymatlarda emas."""
    rng = np.random.default_rng(seed)
    daromad = rng.lognormal(14.2, 0.55, n)
    qarz = daromad * rng.lognormal(-0.6, 0.6, n)
    tolovlar = rng.integers(6, 60, n)
    kechikishlar = rng.binomial(tolovlar, rng.beta(1.5, 12, n))
    kredit_summa = daromad * rng.lognormal(0.5, 0.5, n)
    # HAQIQIY risk: nisbatlarga bog'liq
    kuch = (-1.2 + 2.6 * (qarz / daromad) + 4.0 * (kechikishlar / tolovlar)
            + 1.1 * (kredit_summa / daromad) - 0.3)
    y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
    return pd.DataFrame({"daromad": daromad, "qarz": qarz,
                         "tolovlar": tolovlar, "kechikishlar": kechikishlar,
                         "kredit_summa": kredit_summa, "defolt": y})


def main() -> None:
    df = yarat()
    y = df["defolt"].to_numpy()
    cv = StratifiedKFold(5, shuffle=True, random_state=0)

    print("=== 1. Ma'lumot ===")
    print(f"  {len(df)} ariza, defolt ulushi {y.mean():.2%}")

    xom = ["daromad", "qarz", "tolovlar", "kechikishlar", "kredit_summa"]
    print("\n=== 2. Xom belgilar bilan ===")
    modellar = {
        "LogReg": Pipeline([("sc", StandardScaler()),
                            ("m", LogisticRegression(max_iter=2000))]),
        "HistGB": HistGradientBoostingClassifier(learning_rate=0.1,
                                                 max_iter=200,
                                                 random_state=0),
    }
    asos = {}
    print(f"  {'model':<10} {'CV ROC AUC':>12} {'std':>8}")
    for nom, m in modellar.items():
        b = cross_val_score(m, df[xom], y, cv=cv, scoring="roc_auc")
        asos[nom] = (b.mean(), b.std())
        print(f"  {nom:<10} {b.mean():>12.4f} {b.std():>8.4f}")

    print("\n=== 3. Nisbat belgilarini qo'shish ===")
    df["qarz_nisbati"] = df["qarz"] / df["daromad"]
    df["kechikish_ulushi"] = df["kechikishlar"] / df["tolovlar"]
    df["kredit_nisbati"] = df["kredit_summa"] / df["daromad"]
    yangi = xom + ["qarz_nisbati", "kechikish_ulushi", "kredit_nisbati"]
    print(f"  {'model':<10} {'CV ROC AUC':>12} {'std':>8} {'yaxshilanish':>14}")
    for nom, m in modellar.items():
        b = cross_val_score(m, df[yangi], y, cv=cv, scoring="roc_auc")
        print(f"  {nom:<10} {b.mean():>12.4f} {b.std():>8.4f} "
              f"{b.mean() - asos[nom][0]:>+14.4f}")

    print("\n=== 4. Faqat nisbatlar bilan ===")
    faqat = ["qarz_nisbati", "kechikish_ulushi", "kredit_nisbati"]
    print(f"  {'model':<10} {'belgilar':>9} {'CV ROC AUC':>12}")
    for nom, m in modellar.items():
        b = cross_val_score(m, df[faqat], y, cv=cv, scoring="roc_auc")
        print(f"  {nom:<10} {len(faqat):>9} {b.mean():>12.4f}")
    print(f"  (xom belgilar bilan: LogReg {asos['LogReg'][0]:.4f}, "
          f"HistGB {asos['HistGB'][0]:.4f})")
    print("  ⭐ 3 ta to'g'ri belgi 5 ta xom belgidan yaxshiroq")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
<<NATIJA>>

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

Misol 3 — Model va belgi mosligi

python
"""Bir xil belgi turli modelga turlicha ta'sir qiladi (real numpy/sklearn)."""

import numpy as np
from sklearn.ensemble import HistGradientBoostingClassifier, RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler


def yarat(seed: int = 5, n: int = 5000):
    """Maqsad log(x) va o'zaro ta'sirga bog'liq."""
    rng = np.random.default_rng(seed)
    a = rng.lognormal(10, 1.2, n)          # juda qiyshiq
    b = rng.uniform(0, 1, n)
    c = rng.normal(0, 1, n)
    kuch = 0.8 * (np.log(a) - 10) + 2.5 * (b * c) - 0.5
    y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
    return np.column_stack([a, b, c]), y


def main() -> None:
    X, y = yarat()
    cv = StratifiedKFold(5, shuffle=True, random_state=0)

    modellar = {
        "LogReg": lambda: Pipeline([("sc", StandardScaler()),
                                    ("m", LogisticRegression(max_iter=2000))]),
        "RandomForest": lambda: RandomForestClassifier(n_estimators=200,
                                                       random_state=0,
                                                       n_jobs=1),
        "HistGB": lambda: HistGradientBoostingClassifier(learning_rate=0.1,
                                                         max_iter=200,
                                                         random_state=0),
    }

    print("=== 1. Xom belgilar ===")
    asos = {}
    print(f"  {'model':<14} {'CV ROC AUC':>12} {'std':>8}")
    for nom, yaratuvchi in modellar.items():
        b = cross_val_score(yaratuvchi(), X, y, cv=cv, scoring="roc_auc")
        asos[nom] = b.mean()
        print(f"  {nom:<14} {b.mean():>12.4f} {b.std():>8.4f}")

    print("\n=== 2. log transformatsiyasi qo'shilgandan keyin ===")
    Xlog = np.column_stack([X, np.log1p(X[:, 0])])
    print(f"  {'model':<14} {'CV ROC AUC':>12} {'o_zgarish':>12}")
    for nom, yaratuvchi in modellar.items():
        b = cross_val_score(yaratuvchi(), Xlog, y, cv=cv, scoring="roc_auc")
        print(f"  {nom:<14} {b.mean():>12.4f} {b.mean() - asos[nom]:>+12.4f}")
    print("  (chiziqli model foyda ko'radi, daraxtlar - deyarli yo'q)")

    print("\n=== 3. O'zaro ta'sir qo'shilgandan keyin ===")
    Xoz = np.column_stack([X, X[:, 1] * X[:, 2]])
    print(f"  {'model':<14} {'CV ROC AUC':>12} {'o_zgarish':>12}")
    for nom, yaratuvchi in modellar.items():
        b = cross_val_score(yaratuvchi(), Xoz, y, cv=cv, scoring="roc_auc")
        print(f"  {nom:<14} {b.mean():>12.4f} {b.mean() - asos[nom]:>+12.4f}")
    print("  (chiziqli model katta foyda, daraxtlar o'zi topgan edi)")

    print("\n=== 4. Ikkalasi birga ===")
    Xhammasi = np.column_stack([X, np.log1p(X[:, 0]), X[:, 1] * X[:, 2]])
    print(f"  {'model':<14} {'xom':>9} {'to_liq':>9} {'o_zgarish':>12}")
    for nom, yaratuvchi in modellar.items():
        b = cross_val_score(yaratuvchi(), Xhammasi, y, cv=cv,
                            scoring="roc_auc")
        print(f"  {nom:<14} {asos[nom]:>9.4f} {b.mean():>9.4f} "
              f"{b.mean() - asos[nom]:>+12.4f}")
    print("  ⭐ Belgi muhandisligi modeldan ajralmagan")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
<<NATIJA>>

Nima ko'rsatdi: 2.3-bo'lim.

Misol 4 — Leakage ni aniqlash

python
"""Ikki turdagi leakage va ularni topish (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.inspection import permutation_importance
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split


def yarat(seed: int = 3, n: int = 5000) -> pd.DataFrame:
    """Buyurtma bekor qilinishi; ikkita leakage belgisi bor."""
    rng = np.random.default_rng(seed)
    summa = rng.lognormal(12.0, 0.6, n)
    masofa = rng.gamma(3, 50, n)
    mijoz_yoshi = rng.integers(18, 70, n)
    kanal = rng.integers(0, 3, n)
    kuch = -1.5 + 0.4 * (summa > np.quantile(summa, 0.8)) + 0.004 * masofa \
        - 0.01 * mijoz_yoshi + 0.3 * (kanal == 2)
    bekor = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)

    # LEAKAGE 1 (maqsad): qaytarish summasi - faqat bekor qilinganda > 0
    qaytarish = np.where(bekor == 1, summa * rng.uniform(0.9, 1.0, n), 0.0)
    # LEAKAGE 2 (vaqt): yetkazish vaqti - bekor qilinganda yo'q
    yetkazish_vaqti = np.where(bekor == 1, np.nan,
                               masofa / 30 + rng.normal(0, 5, n))
    return pd.DataFrame({"summa": summa, "masofa": masofa,
                         "mijoz_yoshi": mijoz_yoshi, "kanal": kanal,
                         "qaytarish": qaytarish,
                         "yetkazish_vaqti": yetkazish_vaqti,
                         "bekor": bekor})


def main() -> None:
    df = yarat()
    y = df["bekor"].to_numpy()
    cv = StratifiedKFold(5, shuffle=True, random_state=0)

    def model():
        return HistGradientBoostingClassifier(learning_rate=0.1, max_iter=200,
                                              random_state=0)

    print("=== 1. Barcha belgilar bilan ===")
    hammasi = ["summa", "masofa", "mijoz_yoshi", "kanal", "qaytarish",
               "yetkazish_vaqti"]
    b = cross_val_score(model(), df[hammasi], y, cv=cv, scoring="roc_auc")
    print(f"  CV ROC AUC: {b.mean():.4f} (+-{b.std():.4f})")
    print("  SHUBHA: real vazifada 0.99 deyarli bo'lmaydi")

    print("\n=== 2. Har belgining yakka natijasi ===")
    print(f"  {'belgi':<18} {'yakka CV AUC':>14}")
    for belgi in hammasi:
        Xb = df[[belgi]].to_numpy()
        bb = cross_val_score(model(), Xb, y, cv=cv, scoring="roc_auc").mean()
        belgisi = "  <- SHUBHALI" if bb > 0.9 else ""
        print(f"  {belgi:<18} {bb:>14.4f}{belgisi}")

    print("\n=== 3. Leakage belgilarining tabiati ===")
    print(f"  qaytarish > 0 bo'lgan qatorlar: "
          f"{(df['qaytarish'] > 0).mean():.2%}")
    print(f"  ulardan bekor qilinganlari: "
          f"{df.loc[df['qaytarish'] > 0, 'bekor'].mean():.2%}")
    print(f"  yetkazish_vaqti NaN bo'lgan qatorlar: "
          f"{df['yetkazish_vaqti'].isna().mean():.2%}")
    print(f"  ulardan bekor qilinganlari: "
          f"{df.loc[df['yetkazish_vaqti'].isna(), 'bekor'].mean():.2%}")
    print("  (ikkalasi ham maqsaddan KEYIN aniqlanadi)")

    print("\n=== 4. Leakage siz halol natija ===")
    toza = ["summa", "masofa", "mijoz_yoshi", "kanal"]
    bt = cross_val_score(model(), df[toza], y, cv=cv, scoring="roc_auc")
    print(f"  leakage bilan:  {b.mean():.4f}")
    print(f"  leakage siz:    {bt.mean():.4f} (+-{bt.std():.4f})")
    print(f"  farq:           {b.mean() - bt.mean():+.4f}")
    Xtr, Xte, ytr, yte = train_test_split(df[hammasi], y, test_size=0.3,
                                          random_state=0, stratify=y)
    m = model().fit(Xtr, ytr)
    r = permutation_importance(m, Xte, yte, n_repeats=10, scoring="roc_auc",
                               random_state=0, n_jobs=1)
    print(f"  {'belgi':<18} {'permutation muhimligi':>23}")
    for i in np.argsort(-r.importances_mean):
        print(f"  {hammasi[i]:<18} {r.importances_mean[i]:>+23.4f}")
    print("  ⭐ 'Juda yaxshi natija' - leakage ning asosiy belgisi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
<<NATIJA>>

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


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

Noto'g'ri fikr To'g'risi
"Model tanlash eng muhim" Belgilar ko'pincha muhimroq
"Kategoriyaga raqam berish yetarli" Nominal uchun one-hot
"Belgi model turiga bog'liq emas" Juda bog'liq
"Daraxtlarga log foydali" Monoton — ta'sir qilmaydi
"Yuqori AUC — yaxshi model" Leakage bo'lishi mumkin
"Ko'proq belgi — yaxshiroq" Shovqin va overfitting
"Avtomatik usullar yetarli" Domen bilimi hal qiluvchi
"Kichik yaxshilanish ham foyda" CV std bilan solishtiring

6. Keng tarqalgan xatolar va yechimlari

1. Nominal belgiga tartib berish

python
df["hudud"] = df["hudud"].map({"toshkent": 0, "samarqand": 1})    # ⚠️
OneHotEncoder(handle_unknown="ignore")                            # ✅

2. Leakage ni tekshirmaslik

python
# AUC 0.99 - "ajoyib model!"                                      # ⚠️
# har belgining yakka natijasini ko'ring                          # ✅

3. Daraxtga chiziqlilashtirish

python
X["log_summa"] = np.log1p(X["summa"])    # RandomForest uchun     # ⚠️
# daraxtlarga monoton transformatsiya keraksiz                    # ✅

4. Yaxshilanishni std siz baholash

python
# "0.742 -> 0.744, belgi ishlaydi"                                # ⚠️
# std 0.008 bo'lsa - bu shovqin                                   # ✅

5. Ishlab chiqarishda mavjudlikni tekshirmaslik

python
X["oxirgi_30_kun_xarid"] = ...   # bashoratda hisoblanadi?        # ⚠️
# har belgi uchun "qachon ma'lum bo'ladi?" savolini bering        # ✅

6. Belgilarni Pipeline tashqarisida yaratish

python
X["nisbat"] = X["a"] / X["b"]; cross_val_score(m, X, y)           # ⚠️
# statistikaga tayanadigan belgilar Pipeline ichida bo'lsin       # ✅

7. Domen bilimisiz avtomatik generatsiya

python
PolynomialFeatures(degree=3)     # 500 ta ma'nosiz belgi          # ⚠️
# avval domen belgilarini qo'ying, keyin avtomatik                # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 12.9-dars (o'tilgan): Leakage
  • 17.2-dars: Belgi yaratish
  • 17.3-dars: Kategoriyali belgilarni kodlash
  • 17.7-dars: Feature selection
  • 15.11-dars (o'tilgan): Belgi muhimligi

8. Eng yaxshi amaliyotlar

  1. Belgi turlarini aniq ajrating.

  2. Domen bilimidan boshlang.

  3. Leakage ni har doim tekshiring.

  4. Belgini modelga moslang.

  5. CV std bilan solishtiring.

  6. Ishlab chiqarishda mavjudligini tasdiqlang.

  7. Pipeline ichida ishlang.

  8. Keraksizlarini olib tashlang.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # belgi nima?
2.  # nominal va ordinal farqi?
3.  # nominalga qaysi kodlash?
4.  # yaxshi belgining olti xossasi?
5.  # eng ko'p buziladigan shart?
6.  # daraxtlarga log foydalimi?
7.  # chiziqli modelga o'zaro ta'sir?
8.  # eng qimmatli belgi manbai?
9.  # leakage ning ikki turi?
10. # leakage belgisi?
11. # yaxshilanishni nima bilan solishtirish kerak?
12. # belgilar qanday qo'shiladi?
Javoblar
  1. Modelga beriladigan kirish o'zgaruvchisi
  2. Ordinalda tartib bor
  3. OneHotEncoder
  4. Ma'lumotli, mavjud, barqaror, tushunarli, arzon, toza
  5. Bashorat vaqtida mavjudlik
  6. Yo'q (monoton)
  7. Qo'lda qo'shiladi
  8. Domen bilimi
  9. Maqsad va vaqt leakage
  10. Juda yaxshi natija
  11. CV standart og'ishi
  12. Guruh bo'lib

Vazifa 2: Xatolarni tuzating

python
1.  df["hudud"] = df["hudud"].map({"toshkent": 0, "samarqand": 1})

2.  # AUC 0.99 - "ajoyib model!"

3.  X["log_summa"] = np.log1p(X["summa"])   # RandomForest uchun

4.  # "0.742 -> 0.744, belgi ishlaydi"   (std 0.008)

5.  PolynomialFeatures(degree=3)   # domen belgilarisiz
Javoblar
python
1.  OneHotEncoder(handle_unknown="ignore")

2.  # har belgining yakka natijasini tekshiring

3.  # daraxtlarga monoton transformatsiya keraksiz

4.  # bu shovqin, belgi emas

5.  # avval domen belgilarini qo'ying

Vazifa 3: Turlar

Modellang:

  1. Ustunlar
  2. Avtomatik tasniflash
  3. Kardinallik
  4. Ordinal va nominal

Vazifa 4: Nisbatlar

Modellang:

  1. Ma'lumot
  2. Xom belgilar
  3. Nisbatlar
  4. Faqat nisbatlar

Vazifa 5: Moslik

Modellang:

  1. Xom
  2. log
  3. O'zaro ta'sir
  4. Birga

Vazifa 6: Leakage

Modellang:

  1. Barcha belgilar
  2. Yakka natijalar
  3. Tabiati
  4. Halol natija

Vazifa 7: O'ylash

Zamonaviy modellar (gradient boosting, neyron tarmoqlar) belgilarni "o'zi topadi" deyiladi. Feature engineering hali ham kerakmi?

Javob

Qisqa javob: modellar mavjud belgilardan kombinatsiya topa oladi, lekin yo'q bo'lgan ma'lumotni yarata olmaydi. Feature engineering ikki narsani beradi: tashqi bilim va to'g'ri ko'rinish.

1. Model nimani topa oladi

Vazifa Model topa oladimi
a * b o'zaro ta'siri Ha (daraxtlar, NN)
log(a) Ha (daraxtlar — monoton)
a / b nisbati Qiyin (bo'linish daraxtga tabiiy emas)
"bayram kuni" Yo'q (tashqi ma'lumot)
"oxirgi 30 kun o'rtachasi" Yo'q (agregatsiya berilmagan)
"manzil koordinatasi" Yo'q (tashqi manba)

2. Nisbatlar — eng yaxshi misol

Daraxt qarz/daromad > 0.4 shartini to'g'ridan-to'g'ri ifodalay olmaydi: u faqat o'qlarga parallel chegaralar qo'yadi 15.1-bob. Shuning uchun u bu nisbatni zinapoya bilan taqriblaydi va ko'p bo'linish sarflaydi. Nisbatni belgi sifatida berish darhol yordam beradi — Misol 2 da bu ko'rindi.

3. Deep learning nima o'zgartirdi

  • Tasvir, matn, audio: xom ma'lumotdan belgilarni model o'zi o'rganadi — feature engineering deyarli yo'qoldi
  • Jadval ma'lumoti: hali ham qo'lda muhandislik ustun; gradient boosting + yaxshi belgilar neyron tarmoqdan yaxshiroq
  • Sabab: jadval ma'lumotida namunalar kam va tuzilma heterogen

4. Amaliy taqsimot

Jadval ma'lumoti:  feature engineering 60%, model 40%
Tasvir/matn:       feature engineering 10%, arxitektura va ma'lumot 90%
Vaqt qatorlari:    feature engineering 70% (lag, oyna, mavsum)

5. Xulosa

  1. Model kombinatsiya topadi, tashqi bilim topa olmaydi
  2. Nisbatlar va agregatsiyalar hali ham qo'lda kerak
  3. Jadval ma'lumotida feature engineering ustun
  4. Tasvir/matnda uning o'rni kichraygan

Nimani mustahkamlaydi: 2.3, 2.4-bo'limlar.


Xulosa

Bu darsda belgi tushunchasini o'rgandik.

Eng muhim uch fikr:

  1. Belgilar modeldan muhimroq. Uch haftalik giperparametr sozlash 0.007 AUC bergan joyda bitta to'g'ri nisbat belgisi 0.035 beradi. Belgi turlarini aniq ajrating: nominal (one-hot) va ordinal (tartib saqlanadi) ni adashtirish modelga yolg'on tartib o'rgatadi.

  2. Belgi modeldan ajralmagan. Chiziqli modelga masshtablash, log va o'zaro ta'sirlar qo'lda kerak; daraxtlar esa monoton transformatsiyadan foyda ko'rmaydi va o'zaro ta'sirlarni o'zi topadi. Shuning uchun "universal eng yaxshi belgilar to'plami" yo'q — u tanlangan modelga bog'liq.

  3. Leakage — eng katta xavf. Maqsad leakage (belgi maqsaddan hosil bo'lgan) va vaqt leakage (belgi bashorat vaqtida hali ma'lum emas) CV da ajoyib natija beradi va ishlab chiqarishda butunlay ishlamaydi. Asosiy signal — "juda yaxshi natija": har belgining yakka CV natijasini ko'ring va 0.9 dan yuqorisidan shubhalaning.

Keyingi darsda belgi yaratishni o'rganamiz: nisbatlar, agregatsiyalar, binning va polinomial belgilar.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
17.1-dars: Feature nima — IlmHamroh