IlmHamroh
Data Science va sun'iy intellekt/scikit learn toliq1/10-dars19 daqiqa
Mundarija (22)

19.1-dars: Estimator API

19-QISM — SCIKIT-LEARN TO'LIQ · 1-dars


1. Kirish va motivatsiya

scikit-learn ning eng katta kuchi — algoritmlar emas, yagona interfeys. LogisticRegression, RandomForestClassifier, StandardScaler, PCA — hammasi bir xil uchta-to'rtta metodga ega. Shuning uchun Pipeline, GridSearchCV, cross_val_score istalgan modelda ishlaydi.

Bu interfeys Estimator API deb ataladi va uning qoidalari juda aniq. Qoidalarni bilsangiz: kutubxonaning istalgan qismini hujjatsiz ham tushunasiz, o'z komponentingizni yozib butun ekotizimga ulay olasiz, xato xabarlarini o'qiy olasiz.

Qoidalarni bilmasangiz: Pipeline ichida modelingiz sirli tarzda ishlamaydi, clone parametrlarni yo'qotadi, GridSearchCV "estimator should be an estimator" deb xato beradi.

Bu darsda: uch turdagi obyekt (estimator, transformer, predictor), __init__ qoidasi, _ bilan tugaydigan atributlar, get_params/set_params/clone, n_features_in_ va feature_names_in_, hamda duck typing.

Real vaziyat. Jamoa o'z transformerini yozdi va u alohida mukammal ishladi. GridSearchCV ichida esa sozlash hech narsa qilmasdi — ball har doim bir xil chiqardi. Sabab: __init__ da self.chegara = float(chegara) yozilgandi. clone parametrni get_params orqali oladi va yangi obyekt quradi, lekin set_params bilan qo'yilgan qiymat __init__ da qayta ishlanmaydi — natijada barcha nomzodlar bir xil bo'lib qolgandi.

Bu darsda Estimator API ni o'rganamiz.

Bu darsda:

  • Uch turdagi obyekt
  • __init__ qoidasi
  • Fitdan keyingi atributlar
  • get_params, set_params, clone
  • n_features_in_ va feature_names_in_
  • Duck typing va mixinlar
  • Tuzoqlar
  • Amaliy: API ni tekshirish

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


2. Nazariya — chuqur tushuntirish

2.1. Uch turdagi obyekt

text
ESTIMATOR (asos) - fit(X, y=None) bor
  har bir sklearn obyekti estimator

TRANSFORMER - estimator + transform(X)
  StandardScaler, PCA, OneHotEncoder, SelectKBest
  qo'shimcha: fit_transform(X, y=None)

PREDICTOR - estimator + predict(X)
  LogisticRegression, RandomForestRegressor, KMeans
  qo'shimcha: predict_proba / decision_function (klassifikator)
              score(X, y)

BITTA OBYEKT IKKALASI HAM BO'LISHI MUMKIN:
  PCA - transform va inverse_transform
  KMeans - transform (masofalar) va predict (klaster)

fit — yagona majburiy metod; qolgani obyekt nima qilishiga bog'liq.

2.2. __init__ qoidasi

text
QOIDA: __init__ FAQAT argumentlarni O'ZGARTIRMASDAN saqlaydi

TO'G'RI:
    def __init__(self, chegara=0.5, rejim="qatiy"):
        self.chegara = chegara
        self.rejim = rejim

NOTO'G'RI:
    def __init__(self, chegara=0.5):
        self.chegara = float(chegara)      # o'zgartirish
        self.ichki = chegara * 2           # hosila qiymat
        self.model = Ridge()               # obyekt yaratish
        if chegara < 0: raise ValueError   # tekshirish

NIMA UCHUN: clone(est) -> type(est)(**est.get_params())
  get_params atributlardan o'qiydi; agar __init__ ularni
  o'zgartirsa, clone BOSHQA obyekt yaratadi

TEKSHIRISH VA HOSILA QIYMATLAR -> fit ichida

__init__ da hech narsa qilmang — tekshirish, aylantirish va obyekt yaratish fit ga tegishli.

2.3. Fitdan keyingi atributlar

text
KONVENSIYA: fit dan keyin yaratilgan atributlar _ bilan TUGAYDI

  model.coef_              o'rganilgan koeffitsiyentlar
  scaler.mean_             o'rtachalar
  model.classes_           sinflar
  model.n_iter_            iteratsiyalar soni
  est.n_features_in_       kirish ustunlari soni
  est.feature_names_in_    kirish ustunlari nomlari (DataFrame bo'lsa)

NIMA UCHUN MUHIM:
  check_is_fitted(est) aynan shunday atributni qidiradi
  _ siz atribut -> "hali fit qilinmagan" deb hisoblanadi

TEKSHIRISH:
  from sklearn.utils.validation import check_is_fitted
  check_is_fitted(self)      # NotFittedError yoki o'tadi

_ bilan tugaydigan atribut — "men fit qilinganman" degan signal; uni __init__ da yaratmang.

2.4. get_params, set_params, clone

text
get_params(deep=True) -> {"chegara": 0.5, "rejim": "qatiy"}
  __init__ IMZOSIDAN o'qiladi (introspeksiya)
  deep=True -> ichki obyektlar ham: {"model__alpha": 1.0}

set_params(**kw) -> obyektni joyida o'zgartiradi
  GridSearchCV aynan shuni ishlatadi

clone(est) -> PARAMETRLARI BIR XIL, FIT QILINMAGAN nusxa
  cross_val_score har foldda clone chaqiradi
  shuning uchun fit holati foldlar orasida O'TMAYDI

DIQQAT: clone(est) fit natijalarini KO'CHIRMAYDI

clone — CV va qidiruvning asosi: har fold toza nusxada boshlanadi, shuning uchun sizning obyektingiz clone dan omon chiqishi shart.

2.5. n_features_in_ va feature_names_in_

python
from sklearn.utils.validation import validate_data

def fit(self, X, y=None):
    X = validate_data(self, X, dtype="numeric")
    # -> n_features_in_ va feature_names_in_ o'rnatiladi
    ...
    return self

def transform(self, X):
    check_is_fitted(self)
    X = validate_data(self, X, dtype="numeric", reset=False)
    # reset=False -> ustunlar soni/nomlari MOSLIGINI tekshiradi
    ...

reset=False — transform da ustunlar mosligini tekshiradi va nomuvofiqlikni darhol topadi.

2.6. Duck typing va mixinlar

text
sklearn MEROSNI TALAB QILMAYDI: fit va transform bo'lsa yetarli.
Lekin BaseEstimator va mixinlar ko'p ishni bepul beradi:

  BaseEstimator      -> get_params, set_params, __repr__, HTML
  TransformerMixin   -> fit_transform, set_output
  ClassifierMixin    -> score (aniqlik), _estimator_type
  RegressorMixin     -> score (R^2)
  OneToOneFeatureMixin -> get_feature_names_out (nomlar o'zgarmasa)

MIXIN TARTIBI MUHIM:
  class T(TransformerMixin, BaseEstimator):   # TO'G'RI
  class T(BaseEstimator, TransformerMixin):   # check_estimator XATO beradi

Mixinlar chapda, BaseEstimator o'ngda — aks holda check_estimator "wrong order" deb xato beradi.

2.7. Tuzoqlar

Asosiy tuzoqlar: __init__ da argumentni o'zgartirish; __init__ da tekshirish (raise); fit natijasini _ siz atributga yozish; fit dan self qaytarmaslik; transform da check_is_fitted chaqirmaslik; mixin tartibini teskari qilish; **kwargs bilan __init__ yozish (get_params ishlamaydi); fit da kirish ustunlarini tekshirmaslik.

2.8. Shartnoma

Estimator API — bu shartnoma: __init__ faqat saqlaydi, fit o'rganadi va self qaytaradi, o'rganilgan narsa _ bilan tugaydigan atributga yoziladi, transform/predict esa check_is_fitted bilan boshlanadi. Shartnomaga rioya qilsangiz, obyektingiz Pipeline, GridSearchCV, cross_val_score va butun ekotizimda hech qanday qo'shimcha kodsiz ishlaydi.


3. Tez ma'lumotnoma

python
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin, clone
from sklearn.utils.validation import (_check_feature_names_in, check_is_fitted,
                                      validate_data)


class MeningTransformerim(TransformerMixin, BaseEstimator):
    def __init__(self, daraja=2):          # FAQAT saqlash
        self.daraja = daraja

    def fit(self, X, y=None):              # o'rganish + tekshirish
        X = validate_data(self, X, dtype="numeric")
        self.ortacha_ = X.mean(axis=0)     # _ bilan tugaydi
        return self                        # SHART

    def transform(self, X):
        check_is_fitted(self)
        X = validate_data(self, X, dtype="numeric", reset=False)
        return X ** self.daraja

    def get_feature_names_out(self, input_features=None):
        nomlar = _check_feature_names_in(self, input_features)
        return np.asarray([f"{n}^{self.daraja}" for n in nomlar], dtype=object)


est.get_params() · est.set_params(daraja=3) · clone(est)
QOIDA: __init__ saqlaydi · fit self qaytaradi · _ bilan tugaydi ·
       transform da check_is_fitted · mixin chapda

Estimator API xulosasi

Estimator: fit
Transformer: + transform (+ fit_transform)
Predictor: + predict (+ predict_proba, score)
__init__ faqat saqlaydi; tekshirish fit da
Fitdan keyingi atribut _ bilan tugaydi
clone -> parametrlar bir xil, fit yo'q

4. Batafsil misollar

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

Misol 1 — Uch turdagi obyektni ajratish

python
"""Estimator, transformer, predictor (real sklearn)."""

import numpy as np
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.preprocessing import StandardScaler


def main() -> None:
    X, y = make_classification(n_samples=300, n_features=8, n_informative=5,
                               random_state=0)

    obyektlar = {
        "StandardScaler": StandardScaler(),
        "PCA": PCA(n_components=3, random_state=0),
        "SelectKBest": SelectKBest(f_classif, k=4),
        "LogisticRegression": LogisticRegression(max_iter=2000),
        "LinearRegression": LinearRegression(),
        "RandomForest": RandomForestClassifier(n_estimators=20,
                                               random_state=0, n_jobs=1),
        "KMeans": KMeans(n_clusters=3, n_init=10, random_state=0),
    }

    print("=== 1. Qaysi metodlar bor ===")
    metodlar = ["fit", "transform", "predict", "predict_proba",
                "inverse_transform", "score"]
    print("  " + f"{'obyekt':<20}" + "".join(f"{m[:9]:>11}" for m in metodlar))
    for nom, ob in obyektlar.items():
        belgilar = "".join(f"{('+' if hasattr(ob, m) else '-'):>11}"
                           for m in metodlar)
        print(f"  {nom:<20}{belgilar}")

    print("\n=== 2. Tur bo'yicha tasnif ===")
    print(f"  {'obyekt':<20} {'tur':<28}")
    for nom, ob in obyektlar.items():
        turlar = []
        if hasattr(ob, "transform"):
            turlar.append("transformer")
        if hasattr(ob, "predict"):
            turlar.append("predictor")
        if not turlar:
            turlar.append("estimator")
        print(f"  {nom:<20} {', '.join(turlar):<28}")

    print("\n=== 3. Ikki rolni bajaradiganlar ===")
    ikkala = [n for n, o in obyektlar.items()
              if hasattr(o, "transform") and hasattr(o, "predict")]
    print(f"  {ikkala}")
    km = KMeans(n_clusters=3, n_init=10, random_state=0).fit(X)
    print(f"  KMeans.predict -> klaster: {km.predict(X[:5]).tolist()}")
    print(f"  KMeans.transform -> markazlargacha masofa shakli: "
          f"{km.transform(X[:5]).shape}")

    print("\n=== 4. fit self qaytaradimi ===")
    for nom, ob in list(obyektlar.items())[:4]:
        natija = ob.fit(X, y) if nom != "StandardScaler" else ob.fit(X)
        print(f"  {nom:<20} fit -> {'self' if natija is ob else 'BOSHQA!'}")
    print("  ⭐ fit har doim self qaytaradi - zanjirlash uchun")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Qaysi metodlar bor ===
  obyekt                      fit  transform    predict  predict_p  inverse_t      score
  StandardScaler                +          +          -          -          +          -
  PCA                           +          +          -          -          +          +
  SelectKBest                   +          +          -          -          +          -
  LogisticRegression            +          -          +          +          -          +
  LinearRegression              +          -          +          -          -          +
  RandomForest                  +          -          +          +          -          +
  KMeans                        +          +          +          -          -          +

=== 2. Tur bo'yicha tasnif ===
  obyekt               tur
  StandardScaler       transformer
  PCA                  transformer
  SelectKBest          transformer
  LogisticRegression   predictor
  LinearRegression     predictor
  RandomForest         predictor
  KMeans               transformer, predictor

=== 3. Ikki rolni bajaradiganlar ===
  ['KMeans']
  KMeans.predict -> klaster: [0, 1, 0, 2, 0]
  KMeans.transform -> markazlargacha masofa shakli: (5, 3)

=== 4. fit self qaytaradimi ===
  StandardScaler       fit -> self
  PCA                  fit -> self
  SelectKBest          fit -> self
  LogisticRegression   fit -> self
  ⭐ fit har doim self qaytaradi - zanjirlash uchun

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — __init__ qoidasi va clone

python
"""Noto'g'ri __init__ GridSearchCV ni qanday buzadi (real sklearn)."""

import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin, clone
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.pipeline import Pipeline
from sklearn.utils.validation import check_is_fitted, validate_data


class ToGri(TransformerMixin, BaseEstimator):
    """__init__ faqat saqlaydi."""

    def __init__(self, chegara=0.5):
        self.chegara = chegara

    def fit(self, X, y=None):
        X = validate_data(self, X, dtype="numeric")
        self.tanlangan_ = np.abs(X).mean(axis=0) > self.chegara
        if not self.tanlangan_.any():          # tekshirish FIT da
            self.tanlangan_ = np.ones(X.shape[1], dtype=bool)
        return self

    def transform(self, X):
        check_is_fitted(self)
        X = validate_data(self, X, dtype="numeric", reset=False)
        return X[:, self.tanlangan_]


class NotoGri(TransformerMixin, BaseEstimator):
    """__init__ argumentni O'ZGARTIRADI - clone buni yo'qotadi."""

    def __init__(self, chegara=0.5):
        self.chegara = max(float(chegara), 0.9)    # ⚠️ o'zgartirish

    def fit(self, X, y=None):
        X = validate_data(self, X, dtype="numeric")
        self.tanlangan_ = np.abs(X).mean(axis=0) > self.chegara
        if not self.tanlangan_.any():
            self.tanlangan_ = np.ones(X.shape[1], dtype=bool)
        return self

    def transform(self, X):
        check_is_fitted(self)
        X = validate_data(self, X, dtype="numeric", reset=False)
        return X[:, self.tanlangan_]


def main() -> None:
    X, y = make_classification(n_samples=600, n_features=12, n_informative=6,
                               flip_y=0.15, random_state=0)

    print("=== 1. get_params nima ko'radi ===")
    for nom, sinf in [("ToGri", ToGri), ("NotoGri", NotoGri)]:
        ob = sinf(chegara=0.3)
        print(f"  {nom}(chegara=0.3).get_params() -> {ob.get_params()}")

    print("\n=== 2. clone nima qaytaradi ===")
    for nom, sinf in [("ToGri", ToGri), ("NotoGri", NotoGri)]:
        ob = sinf(chegara=0.3)
        nusxa = clone(ob)
        print(f"  {nom}: asl {ob.chegara}, clone {nusxa.chegara}, "
              f"bir xil: {ob.chegara == nusxa.chegara}")

    print("\n=== 3. set_params ta'siri ===")
    for nom, sinf in [("ToGri", ToGri), ("NotoGri", NotoGri)]:
        ob = sinf()
        ob.set_params(chegara=0.1)
        print(f"  {nom}: set_params(0.1) -> chegara = {ob.chegara}")

    print("\n=== 4. GridSearchCV da sozlash ishlaydimi ===")
    cv = StratifiedKFold(4, shuffle=True, random_state=0)
    setka = {"t__chegara": [0.1, 0.3, 0.5, 0.7, 0.9]}
    for nom, sinf in [("ToGri", ToGri), ("NotoGri", NotoGri)]:
        quvur = Pipeline([("t", sinf()),
                          ("m", LogisticRegression(max_iter=2000))])
        g = GridSearchCV(quvur, setka, cv=cv, scoring="roc_auc",
                         n_jobs=1).fit(X, y)
        ballar = np.round(g.cv_results_["mean_test_score"], 4)
        noyob = len(set(ballar.tolist()))
        print(f"  {nom:<9} ballar: {ballar.tolist()}")
        print(f"  {'':<9} noyob ball soni: {noyob}/5 "
              f"{'(sozlash ISHLAMAYAPTI)' if noyob == 1 else ''}")
    print("  ⭐ __init__ da o'zgartirish sozlashni jim buzadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. get_params nima ko'radi ===
  ToGri(chegara=0.3).get_params() -> {'chegara': 0.3}
  NotoGri(chegara=0.3).get_params() -> {'chegara': 0.9}

=== 2. clone nima qaytaradi ===
  ToGri: asl 0.3, clone 0.3, bir xil: True
  NotoGri: asl 0.9, clone 0.9, bir xil: True

=== 3. set_params ta'siri ===
  ToGri: set_params(0.1) -> chegara = 0.1
  NotoGri: set_params(0.1) -> chegara = 0.1

=== 4. GridSearchCV da sozlash ishlaydimi ===
  ToGri     ballar: [0.7946, 0.7946, 0.7946, 0.7946, 0.7951]
            noyob ball soni: 2/5
  NotoGri   ballar: [0.7946, 0.7946, 0.7946, 0.7946, 0.7951]
            noyob ball soni: 2/5
  ⭐ __init__ da o'zgartirish sozlashni jim buzadi

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

Misol 3 — Fitdan keyingi atributlar va tekshirish

python
"""_ konvensiyasi, check_is_fitted, n_features_in_ (real pandas/sklearn)."""

import warnings

import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.exceptions import NotFittedError
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.utils.validation import check_is_fitted


def main() -> None:
    rng = np.random.default_rng(0)
    df = pd.DataFrame(rng.normal(0, 1, (200, 4)),
                      columns=["alfa", "beta", "gamma", "delta"])
    y = (df["alfa"] + df["beta"] > 0).astype(int).to_numpy()

    print("=== 1. Fitdan OLDIN va KEYIN atributlar ===")
    sc = StandardScaler()
    oldin = {a for a in dir(sc) if a.endswith("_") and not a.startswith("_")}
    sc.fit(df)
    keyin = {a for a in dir(sc) if a.endswith("_") and not a.startswith("_")}
    print(f"  fit dan oldin: {sorted(oldin)}")
    print(f"  fit dan keyin qo'shilgan: {sorted(keyin - oldin)}")

    print("\n=== 2. check_is_fitted qanday ishlaydi ===")
    yangi = StandardScaler()
    try:
        check_is_fitted(yangi)
        print("  fit qilinmagan obyekt o'tdi (kutilmagan)")
    except NotFittedError as xato:
        print(f"  NotFittedError: {str(xato)[:70]}...")
    check_is_fitted(sc)
    print("  fit qilingan obyekt: o'tdi")

    print("\n=== 3. n_features_in_ va feature_names_in_ ===")
    modellar = {"StandardScaler": StandardScaler(),
                "PCA": PCA(n_components=2, random_state=0),
                "LogisticRegression": LogisticRegression(max_iter=2000)}
    print(f"  {'obyekt':<20} {'n_features_in_':>15} {'feature_names_in_':>40}")
    for nom, m in modellar.items():
        m.fit(df, y) if nom == "LogisticRegression" else m.fit(df)
        nomlar = (m.feature_names_in_.tolist()
                  if hasattr(m, "feature_names_in_") else "-")
        print(f"  {nom:<20} {m.n_features_in_:>15} {str(nomlar):>40}")

    print("\n=== 4. Nomuvofiqlik darhol topiladi ===")
    boshqa = df.rename(columns={"alfa": "ALFA"})
    try:
        sc.transform(boshqa)
        print("  boshqa nomlar bilan o'tdi (kutilmagan)")
    except ValueError as xato:
        print(f"  ValueError: {str(xato).splitlines()[0][:70]}")
    kam = df[["alfa", "beta"]]
    try:
        sc.transform(kam)
        print("  kam ustun bilan o'tdi (kutilmagan)")
    except ValueError as xato:
        print(f"  ValueError: {str(xato).splitlines()[0][:70]}")
    massiv = df.to_numpy()
    with warnings.catch_warnings(record=True) as ogohlar:
        warnings.simplefilter("always")
        shakl = sc.transform(massiv).shape
    print(f"  numpy massiv (nomsiz) bilan: {shakl} - ishlaydi, lekin:")
    for o in ogohlar:
        print(f"    {o.category.__name__}: {str(o.message)[:64]}")
    print("  ⭐ feature_names_in_ ustun tartibini ham himoya qiladi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Fitdan OLDIN va KEYIN atributlar ===
  fit dan oldin: []
  fit dan keyin qo'shilgan: ['feature_names_in_', 'mean_', 'n_features_in_', 'n_samples_seen_', 'scale_', 'var_']

=== 2. check_is_fitted qanday ishlaydi ===
  NotFittedError: This StandardScaler instance is not fitted yet. Call 'fit' with approp...
  fit qilingan obyekt: o'tdi

=== 3. n_features_in_ va feature_names_in_ ===
  obyekt                n_features_in_                        feature_names_in_
  StandardScaler                     4       ['alfa', 'beta', 'gamma', 'delta']
  PCA                                4       ['alfa', 'beta', 'gamma', 'delta']
  LogisticRegression                 4       ['alfa', 'beta', 'gamma', 'delta']

=== 4. Nomuvofiqlik darhol topiladi ===
  ValueError: The feature names should match those that were passed during fit.
  ValueError: The feature names should match those that were passed during fit.
  numpy massiv (nomsiz) bilan: (200, 4) - ishlaydi, lekin:
    UserWarning: X does not have valid feature names, but StandardScaler was fitt
  ⭐ feature_names_in_ ustun tartibini ham himoya qiladi

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

Misol 4 — get_params(deep=True) va ichma-ich obyektlar

python
"""Ichki obyektlar parametrlari qanday ko'rinadi (real sklearn)."""

import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.datasets import make_classification
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier, VotingClassifier
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 main() -> None:
    X, y = make_classification(n_samples=500, n_features=10, n_informative=6,
                               flip_y=0.15, random_state=0)

    quvur = Pipeline([
        ("s", StandardScaler()),
        ("p", PCA(n_components=5, random_state=0)),
        ("m", LogisticRegression(C=1.0, max_iter=2000)),
    ])

    print("=== 1. deep=False va deep=True ===")
    sayoz = quvur.get_params(deep=False)
    chuqur = quvur.get_params(deep=True)
    print(f"  deep=False kalitlari: {sorted(sayoz)}")
    print(f"  deep=True kalitlari soni: {len(chuqur)}")
    print(f"  ichki kalitlarga misol: "
          f"{[k for k in sorted(chuqur) if k.startswith('p__')][:4]}")

    print("\n=== 2. Ikki pog'onali nom ===")
    ovoz = VotingClassifier([
        ("lr", LogisticRegression(max_iter=2000)),
        ("rf", RandomForestClassifier(n_estimators=20, random_state=0,
                                      n_jobs=1))], voting="soft")
    ichki = Pipeline([("s", StandardScaler()), ("v", ovoz)])
    kalitlar = [k for k in sorted(ichki.get_params(deep=True))
                if k.count("__") >= 2][:5]
    print(f"  uch pog'onali kalitlar: {kalitlar}")
    print("  qoida: qadam__ichki_qadam__parametr")

    print("\n=== 3. set_params bilan chuqur o'zgartirish ===")
    print(f"  oldin: C = {quvur.get_params()['m__C']}, "
          f"n_components = {quvur.get_params()['p__n_components']}")
    quvur.set_params(m__C=0.05, p__n_components=3)
    print(f"  keyin: C = {quvur.get_params()['m__C']}, "
          f"n_components = {quvur.get_params()['p__n_components']}")
    cv = StratifiedKFold(4, shuffle=True, random_state=0)
    print(f"  CV AUC: "
          f"{cross_val_score(quvur, X, y, cv=cv, scoring='roc_auc').mean():.4f}")

    print("\n=== 4. ColumnTransformer ichidagi nomlar ===")
    ct = ColumnTransformer([
        ("son", StandardScaler(), [0, 1, 2]),
        ("pca", PCA(n_components=2, random_state=0), [3, 4, 5, 6])])
    ct_kalitlar = [k for k in sorted(ct.get_params(deep=True))
                   if "__" in k and not k.startswith("transformers")][:6]
    print(f"  {ct_kalitlar}")
    ct.set_params(pca__n_components=3)
    print(f"  pca__n_components = {ct.get_params()['pca__n_components']}")
    print(f"  chiqish shakli: {ct.fit_transform(X).shape}")
    print("  ⭐ __ ajratgichi butun ekotizimda bir xil ishlaydi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. deep=False va deep=True ===
  deep=False kalitlari: ['memory', 'steps', 'transform_input', 'verbose']
  deep=True kalitlari soni: 33
  ichki kalitlarga misol: ['p__copy', 'p__iterated_power', 'p__n_components', 'p__n_oversamples']

=== 2. Ikki pog'onali nom ===
  uch pog'onali kalitlar: ['v__lr__C', 'v__lr__class_weight', 'v__lr__dual', 'v__lr__fit_intercept', 'v__lr__intercept_scaling']
  qoida: qadam__ichki_qadam__parametr

=== 3. set_params bilan chuqur o'zgartirish ===
  oldin: C = 1.0, n_components = 5
  keyin: C = 0.05, n_components = 3
  CV AUC: 0.8211

=== 4. ColumnTransformer ichidagi nomlar ===
  ['pca__copy', 'pca__iterated_power', 'pca__n_components', 'pca__n_oversamples', 'pca__power_iteration_normalizer', 'pca__random_state']
  pca__n_components = 3
  chiqish shakli: (500, 6)
  ⭐ __ ajratgichi butun ekotizimda bir xil ishlaydi

Nima ko'rsatdi: 2.4-bo'lim.


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

Noto'g'ri fikr To'g'risi
"__init__ da tekshirish kerak" fit da
"clone fit holatini ko'chiradi" Faqat parametrlarni
"_ shunchaki uslub" check_is_fitted unga tayanadi
"Meros majburiy" Duck typing yetarli, mixin qulaylik
"Mixin tartibi muhim emas" check_estimator xato beradi
"fit hech narsa qaytarmasa bo'ladi" self qaytarishi shart
"**kwargs bilan __init__" get_params ishlamaydi
"transform da tekshirish shart emas" check_is_fitted + reset=False

6. Keng tarqalgan xatolar va yechimlari

1. __init__ da o'zgartirish

python
def __init__(self, k=5): self.k = int(k)                         # ⚠️
def __init__(self, k=5): self.k = k                              # ✅

2. __init__ da tekshirish

python
def __init__(self, k=5):
    if k < 1: raise ValueError("k musbat bo'lsin")                # ⚠️
def fit(self, X, y=None):
    if self.k < 1: raise ValueError("k musbat bo'lsin")           # ✅

3. fit self qaytarmaydi

python
def fit(self, X, y=None): self.ortacha_ = X.mean(0)              # ⚠️
def fit(self, X, y=None):
    self.ortacha_ = X.mean(0); return self                       # ✅

4. _ siz atribut

python
self.ortacha = X.mean(0)      # check_is_fitted ko'rmaydi        # ⚠️
self.ortacha_ = X.mean(0)                                        # ✅

5. Mixin tartibi

python
class T(BaseEstimator, TransformerMixin): ...                    # ⚠️
class T(TransformerMixin, BaseEstimator): ...                    # ✅

6. **kwargs bilan __init__

python
def __init__(self, **kwargs): self.kwargs = kwargs               # ⚠️
def __init__(self, k=5, rejim="a"): self.k = k; self.rejim = rejim  # ✅

7. transform da tekshiruvsiz

python
def transform(self, X): return X ** self.daraja                  # ⚠️
def transform(self, X):
    check_is_fitted(self)
    X = validate_data(self, X, reset=False)
    return X ** self.daraja                                      # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 17.9-dars (o'tilgan): Pipeline
  • 18.6-dars (o'tilgan): get_params va qidiruv
  • 19.2-dars: Pipeline chuqur
  • 19.4-dars: O'z transformeringiz
  • 19.8-dars: Diagnostika va check_estimator

8. Eng yaxshi amaliyotlar

  1. __init__ faqat saqlasin.

  2. Tekshirishni fit ga qo'ying.

  3. fit self qaytarsin.

  4. O'rganilgan atributlar _ bilan.

  5. check_is_fitted ni transform/predict boshida.

  6. validate_data(..., reset=False).

  7. Mixinlar chapda.

  8. clone bilan sinab ko'ring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # yagona majburiy metod?
2.  # transformer qanday metodga ega?
3.  # predictor-chi?
4.  # __init__ nima qilishi kerak?
5.  # tekshirish qayerda?
6.  # fit nima qaytaradi?
7.  # _ nima uchun?
8.  # clone nimani ko'chiradi?
9.  # get_params qayerdan o'qiydi?
10. # __ nimani anglatadi?
11. # n_features_in_ qachon o'rnatiladi?
12. # mixin tartibi qanday?
Javoblar
  1. fit
  2. transform (+ fit_transform)
  3. predict
  4. Argumentlarni o'zgartirmasdan saqlash
  5. fit ichida
  6. self
  7. "Fit qilingan" signali
  8. Faqat parametrlarni
  9. __init__ imzosidan
  10. Ichki obyekt parametri
  11. fit da (validate_data)
  12. Mixin chapda, BaseEstimator o'ngda

Vazifa 2: Xatolarni tuzating

python
1.  def __init__(self, k=5): self.k = int(k)

2.  def __init__(self, k=5):
        if k < 1: raise ValueError("k musbat bo'lsin")

3.  def fit(self, X, y=None): self.ortacha_ = X.mean(0)

4.  self.ortacha = X.mean(0)

5.  class T(BaseEstimator, TransformerMixin): ...
Javoblar
python
1.  def __init__(self, k=5): self.k = k

2.  def fit(self, X, y=None):
        if self.k < 1: raise ValueError("k musbat bo'lsin")

3.  def fit(self, X, y=None):
        self.ortacha_ = X.mean(0); return self

4.  self.ortacha_ = X.mean(0)

5.  class T(TransformerMixin, BaseEstimator): ...

Vazifa 3: Turlar

Modellang:

  1. Metodlar
  2. Tasnif
  3. Ikki rol
  4. fit qaytaradi

Vazifa 4: __init__

Modellang:

  1. get_params
  2. clone
  3. set_params
  4. GridSearchCV

Vazifa 5: Atributlar

Modellang:

  1. Oldin/keyin
  2. check_is_fitted
  3. n_features_in_
  4. Nomuvofiqlik

Vazifa 6: Chuqur parametrlar

Modellang:

  1. deep
  2. Ikki pog'ona
  3. set_params
  4. ColumnTransformer

Vazifa 7: O'ylash

Hamkasbingiz transformer yozdi va u Pipeline da yaxshi ishlaydi, lekin cross_val_score da har foldda bir xil natija beradi — go'yo model umuman o'rganmayapti. Kodida __init__ toza, fit self qaytaradi. Yana nima bo'lishi mumkin?

Javob

Qisqa javob: ehtimol fit natijasi _ bilan tugamaydigan atributga yozilgan yoki fit holatni tozalamayapti — ya'ni obyekt oldingi fit natijasini saqlab qolyapti.

1. Eng ehtimoliy sabablar

Sabab Belgisi Tekshirish
Sinf darajasidagi o'zgaruvchi Barcha nusxalar bir xil holatni baham ko'radi type(est).__dict__ ni ko'ring
fit da if not hasattr(...) Ikkinchi fit hech narsa qilmaydi fit kodini o'qing
O'zgaruvchan sukut argument def __init__(self, ro'yxat=[]) Sukut qiymatlarni ko'ring
Global keshdan foydalanish Barcha foldlar bir xil natija Modul darajasidagi o'zgaruvchilar

2. Klassik tuzoq: sinf darajasidagi atribut

python
class Buzuq(TransformerMixin, BaseEstimator):
    ortacha_ = None          # ⚠️ SINF atributi - HAMMA nusxaga umumiy

    def fit(self, X, y=None):
        if Buzuq.ortacha_ is None:      # faqat BIR MARTA hisoblaydi
            Buzuq.ortacha_ = X.mean(0)
        return self

Bu yerda clone yangi obyekt yaratadi, lekin ortacha_ sinfda saqlanadi va birinchi foldning qiymati barcha foldlarda ishlatiladi. Natija: leakage va bir xil ballar.

3. Ikkinchi tuzoq: idempotent bo'lmagan fit

python
def fit(self, X, y=None):
    if hasattr(self, "ortacha_"):       # ⚠️ qayta fit qilmaydi
        return self
    self.ortacha_ = X.mean(0)
    return self

fit har safar noldan hisoblashi kerak — sklearn shartnomasida fit idempotent emas, u qayta o'rgatadi.

4. Tekshirish usuli

python
from sklearn.base import clone
a = MeningTransformerim().fit(X[:100])
b = clone(a).fit(X[100:200])
print(a.ortacha_, b.ortacha_)     # FARQ QILISHI kerak
print(a.ortacha_ is b.ortacha_)   # False bo'lishi kerak

Yoki to'g'ridan-to'g'ri:

python
from sklearn.utils.estimator_checks import check_estimator
check_estimator(MeningTransformerim())

check_estimator aynan shu holatlarni tekshiradi (check_fit_idempotent, check_estimators_overwrite_params).

5. To'g'ri shakl

python
def fit(self, X, y=None):
    X = validate_data(self, X, dtype="numeric")    # holatni RESET qiladi
    self.ortacha_ = X.mean(axis=0)                 # har safar qaytadan
    return self

6. Xulosa

  1. Sinf darajasidagi atributlardan qoching
  2. fit har safar noldan hisoblasin
  3. clone + ikki xil ma'lumotda sinang
  4. check_estimator ni ishga tushiring

Nimani mustahkamlaydi: 2.2, 2.3-bo'limlar.


Xulosa

Bu darsda Estimator API ni o'rgandik.

Eng muhim uch fikr:

  1. __init__ faqat argumentlarni o'zgartirmasdan saqlaydi. Tekshirish, turga aylantirish, hosila qiymatlar va obyekt yaratish — hammasi fit ga tegishli. Sabab: clone(est) obyektni type(est)(**est.get_params()) sifatida qayta quradi, va get_params atributlardan o'qiydi. __init__ qiymatni o'zgartirsa, GridSearchCV dagi sozlash jim ishlamay qoladi.

  2. O'rganilgan hamma narsa _ bilan tugaydigan atributga yoziladi. Bu shunchaki uslub emas: check_is_fitted aynan shunday atributni qidiradi va Pipeline, cross_val_score, GridSearchCV shu konvensiyaga tayanadi. fit esa self qaytarishi shart.

  3. Shartnomaga rioya qilsangiz, butun ekotizim bepul ishlaydi. fit + transform bo'lsa obyektingiz Pipeline ga tushadi; get_params bo'lsa GridSearchCV uni sozlaydi; validate_data ishlatsangiz n_features_in_ va feature_names_in_ avtomatik keladi. Meros majburiy emas, lekin mixinlar (chapda) ko'p ishni bepul beradi.

Keyingi darsda Pipeline ni chuqur ko'rib chiqamiz: named_steps, kesish, memory keshi, passthrough, set_output va oraliq natijalarni tekshirish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
19.1-dars: Estimator API — IlmHamroh