Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Pipeline asoslari
- 2.2. ColumnTransformer
- 2.3. Maxsus transformer
- 2.4. FunctionTransformer
- 2.5. Giperparametr sozlash
- 2.6. Nested CV va saqlash
- 2.7. Tuzoqlar
- 2.8. Tuzilmaviy himoya
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Pipeline va ColumnTransformer
- Misol 2 — Maxsus transformer
- Misol 3 — Giperparametr sozlash va passthrough
- Misol 4 — Nested CV va saqlash
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
17.9-dars: Pipeline
17-QISM — FEATURE ENGINEERING · 9-dars
1. Kirish va motivatsiya
Oldingi sakkiz darsda bir xil maslahat qaytarildi: Pipeline ichida ishlang. Endi uni batafsil ko'ramiz — chunki Pipeline shunchaki qulaylik emas, balki leakage ga qarshi tuzilmaviy himoya.
Pipeline uchta muammoni bir vaqtda hal qiladi: leakage (har fit faqat o'quv qismida bajariladi), takrorlanuvchanlik (butun tayyorlash bitta obyektda) va ishlab chiqarish (model va tayyorlash birga versiyalanadi va saqlanadi).
Bundan tashqari, Pipeline giperparametr sozlashni kengaytiradi: GridSearchCV faqat model parametrlarini emas, tayyorlash parametrlarini ham (n_components, min_df, k) sozlay oladi.
Bu darsda: Pipeline asoslari, ColumnTransformer, maxsus transformer yozish, FunctionTransformer, Pipeline da giperparametr sozlash, nested CV va tayyor quvurni saqlash.
Real vaziyat. Jamoada uch odam bir xil ma'lumot bilan ishlardi va har biri o'z tayyorlash kodini yozgandi. Modellar solishtirilganda farq 0.06 AUC edi — lekin sabab model emas, tayyorlashdagi farq bo'lib chiqdi. Bitta umumiy Pipeline ga o'tilgach, taqqoslash ma'noli bo'ldi.
Bu darsda Pipeline ni o'rganamiz.
Bu darsda:
- Pipeline asoslari
- ColumnTransformer
- Maxsus transformer
- FunctionTransformer
- Giperparametr sozlash
- Nested CV va saqlash
- Tuzoqlar
- Amaliy: to'liq quvur
ℹ Misollar real numpy/pandas/sklearn bilan (Python 3.14).
2. Nazariya — chuqur tushuntirish
2.1. Pipeline asoslari
from sklearn.pipeline import Pipeline, make_pipeline
quvur = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
("model", LogisticRegression(max_iter=2000)),
])
quvur.fit(Xtr, ytr)
quvur.predict(Xte)
make_pipeline(SimpleImputer(), StandardScaler(), LogisticRegression())QANDAY ISHLAYDI:
fit: har bosqich fit_transform, oxirgisi fit
predict: har bosqich transform, oxirgisi predict
KIRISH:
quvur.named_steps["scaler"] - bosqichga murojaat
quvur[:-1] - modelsiz qism
quvur[-1] - faqat model
quvur.get_params() - barcha parametrlar
OXIRGI bosqich model bo'lishi SHART EMAS
(faqat transformerlardan iborat quvur ham bo'ladi) quvur[:-1] — modelsiz qism: u tayyorlangan ma'lumotni ko'rish, markazlarni teskari aylantirish va oraliq natijalarni tekshirish uchun juda qulay.
2.2. ColumnTransformer
from sklearn.compose import ColumnTransformer, make_column_selector
tayyor = ColumnTransformer([
("sonli", Pipeline([("imp", SimpleImputer(strategy="median")),
("sc", StandardScaler())]), sonli_ustunlar),
("kategoriya", OneHotEncoder(handle_unknown="ignore"), kat_ustunlar),
("matn", TfidfVectorizer(min_df=5), "izoh"), # ustun NOMI
], remainder="drop", verbose_feature_names_out=False)remainder:
"drop" - qolgan ustunlar tashlanadi (standart)
"passthrough" - o'zgarishsiz qo'shiladi
transformer - qolganlarga qo'llaniladi
make_column_selector - ustunlarni AVTOMATIK tanlash:
make_column_selector(dtype_include="number")
make_column_selector(dtype_include=["object", "category"])
get_feature_names_out() - natija ustunlari nomlari make_column_selector ustunlarni dtype bo'yicha avtomatik tanlaydi — bu yangi ustun qo'shilganda kodni o'zgartirmaslikka imkon beradi.
2.3. Maxsus transformer
from sklearn.base import BaseEstimator, TransformerMixin
class NisbatBelgilari(BaseEstimator, TransformerMixin):
def __init__(self, juftliklar=None):
self.juftliklar = juftliklar # __init__ da FAQAT saqlash
def fit(self, X, y=None):
self.n_features_in_ = X.shape[1] # o'rgangan narsalar _ bilan
return self
def transform(self, X):
natija = [X]
for a, b in self.juftliklar:
natija.append((X[:, a] / np.where(X[:, b] == 0, np.nan,
X[:, b])).reshape(-1, 1))
return np.hstack(natija)QOIDALAR (sklearn API):
1. __init__ da faqat parametrlarni SAQLANG (tekshirmang, o'zgartirmang)
2. O'rganilgan narsalar oxirida _ bilan (self.ortacha_)
3. fit(X, y=None) -> self qaytaradi
4. transform(X) -> massiv yoki DataFrame
5. get_feature_names_out() - ixtiyoriy, lekin foydali
TEKSHIRUV: sklearn.utils.estimator_checks.check_estimator __init__ da parametrlarni o'zgartirmang — bu get_params/set_params ni buzadi va GridSearchCV ishlamay qoladi. Barcha tekshiruv va hisoblash fit da bo'lishi kerak.
2.4. FunctionTransformer
from sklearn.preprocessing import FunctionTransformer
FunctionTransformer(np.log1p, inverse_func=np.expm1, validate=True)
FunctionTransformer(lambda X: X[:, :5]) # ustun tanlash
FunctionTransformer(func, kw_args={"parametr": 3})QACHON:
+ oddiy, holatsiz (stateless) transformatsiyalar
+ log, sqrt, ustun tanlash, qayta shakllantirish
- o'rganish kerak bo'lsa (o'rtacha, min/max) -> maxsus transformer
feature_names_out="one-to-one" - nomlarni saqlash
validate=True - kirishni massivga aylantiradi (DataFrame yo'qoladi) FunctionTransformer holatsiz transformatsiyalar uchun: agar sizga o'quvdan biror narsa o'rganish kerak bo'lsa (o'rtacha, kvantil, lug'at), maxsus transformer yozing.
2.5. Giperparametr sozlash
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
setka = {
"tayyor__matn__min_df": [2, 5, 10],
"tayyor__matn__ngram_range": [(1, 1), (1, 2)],
"tanlov__k": [20, 50, 100],
"model__C": [0.1, 1.0, 10.0],
"model": [LogisticRegression(max_iter=2000), LinearSVC()], # MODEL ham
}
q = GridSearchCV(quvur, setka, cv=5, scoring="roc_auc").fit(X, y)NOM BERISH: bosqich__parametr, ichma-ich: bosqich__ichki__parametr
"tayyor__matn__min_df" = tayyor (ColumnTransformer)
-> matn (TfidfVectorizer) -> min_df
MODELNI HAM SOZLASH mumkin: {"model": [A(), B()]}
BOSQICHNI O'CHIRISH: {"tanlov": ["passthrough"]}
memory="cache_papka" - takroriy tayyorlashni keshlaydi (tezlashtiradi) Bosqichni "passthrough" bilan o'chirish — juda foydali naqsh: {"pca": ["passthrough", PCA(10), PCA(30)]} "PCA kerakmi?" degan savolni CV bilan hal qiladi.
2.6. Nested CV va saqlash
from sklearn.model_selection import cross_val_score, GridSearchCV
# ichki CV - giperparametr, tashqi CV - halol baho
ichki = GridSearchCV(quvur, setka, cv=5, scoring="roc_auc")
ballar = cross_val_score(ichki, X, y, cv=5, scoring="roc_auc")
# saqlash (15.13)
import joblib
joblib.dump({"quvur": q.best_estimator_, "belgilar": list(X.columns),
"versiyalar": {...}}, "model.joblib", compress=3)NESTED CV NEGA:
oddiy GridSearchCV.best_score_ OPTIMISTIK
(ko'p konfiguratsiya sinalgan -> eng yaxshisi tasodifan yaxshi)
nested CV halol baho beradi, lekin qimmat (k_tashqi * k_ichki * n_iter)
AMALDA: alohida test to'plami ko'pincha yetarli va arzonroq GridSearchCV.best_score_ optimistik: u 100 konfiguratsiya orasidan eng yaxshisi, shuning uchun u tasodifan yuqori bo'ladi. Halol baho uchun alohida test yoki nested CV.
2.7. Tuzoqlar
Asosiy tuzoqlar: tayyorlashni Pipeline tashqarisida qilish; maxsus transformer __init__ ida parametrni o'zgartirish; ColumnTransformer da matn ustunini ro'yxat sifatida berish; remainder ni e'tiborsiz qoldirish (ustunlar jim tashlanadi); GridSearchCV.best_score_ ni halol baho deb qabul qilish; FunctionTransformer ni o'rganish kerak bo'lgan joyda ishlatish; bosqich nomlarida __ ishlatish; memory ni katta setkada unutish.
2.8. Tuzilmaviy himoya
Pipeline leakage ni tuzilma darajasida oldini oladi: har fit faqat o'quv qismida bajariladi. ColumnTransformer turli ustun turlariga turli tayyorlashni qo'llaydi (matn ustuni ro'yxatsiz). Maxsus transformer da __init__ faqat saqlaydi, o'rganilgan narsalar _ bilan tugaydi. Pipeline giperparametr sozlashni kengaytiradi (bosqich__parametr, "passthrough") va butun tayyorlashni bitta obyektda saqlashga imkon beradi. Keyingi dars — amaliyot.
3. Tez ma'lumotnoma
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline, make_pipeline
tayyor = ColumnTransformer([
("s", Pipeline([("imp", SimpleImputer()), ("sc", StandardScaler())]),
make_column_selector(dtype_include="number")),
("k", OneHotEncoder(handle_unknown="ignore"),
make_column_selector(dtype_include=["object", "category"]))],
remainder="drop", verbose_feature_names_out=False)
quvur = Pipeline([("t", tayyor), ("m", model)], memory="cache")
quvur[:-1].transform(X) # modelsiz qism
tayyor.get_feature_names_out() # natija ustunlari
GridSearchCV(quvur, {"t__s__imp__strategy": ["mean", "median"],
"m__C": [0.1, 1.0]}, cv=5)
QOIDA: hamma tayyorlash ichida · matn ustuni ro'yxatsiz ·
__init__ da faqat saqla · best_score_ optimistikPipeline xulosasi
Pipeline: fit faqat o'quvda -> leakage ga tuzilmaviy himoya
ColumnTransformer: ustun turlariga turli tayyorlash
Maxsus transformer: __init__ saqlaydi, o'rganilgan _ bilan
Sozlash: bosqich__parametr; "passthrough" bilan bosqichni o'chirish4. Batafsil misollar
Misollar real numpy/pandas/sklearn bilan (Python 3.14).
Misol 1 — Pipeline va ColumnTransformer
"""Aralash turdagi ma'lumot uchun to'liq tayyorlash (real pandas/sklearn)."""
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
def yarat(seed: int = 7, n: int = 5000) -> pd.DataFrame:
rng = np.random.default_rng(seed)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona"], n)
kanal = rng.choice(["ilova", "sayt", "qongiroq"], n, p=[0.5, 0.35, 0.15])
summa = rng.lognormal(12.0, 0.7, n)
masofa = rng.gamma(3, 50, n)
yosh = rng.integers(18, 70, n).astype(float)
hq = pd.Series(hudud).map({"toshkent": 0.0, "samarqand": 0.4,
"buxoro": 0.85, "fargona": 0.3}).to_numpy()
kuch = -1.8 + hq + 0.004 * masofa + 0.35 * (np.log(summa) - 12.0) \
- 0.012 * (yosh - 40) + 0.4 * (kanal == "qongiroq")
y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
df = pd.DataFrame({"hudud": hudud, "kanal": kanal, "summa": summa,
"masofa": masofa, "yosh": yosh, "kechikdi": y})
df.loc[rng.random(n) < 0.06, "yosh"] = np.nan
df.loc[rng.random(n) < 0.03, "masofa"] = np.nan
return df
def main() -> None:
df = yarat()
X = df.drop(columns="kechikdi")
y = df["kechikdi"].to_numpy()
cv = StratifiedKFold(5, shuffle=True, random_state=0)
print("=== 1. Ma'lumot ===")
print(f" {len(df)} qator, {X.shape[1]} belgi")
print(f" {'ustun':<10} {'dtype':<10} {'yo_qolgan':>10}")
for ustun in X.columns:
print(f" {ustun:<10} {str(X[ustun].dtype):<10} "
f"{int(X[ustun].isna().sum()):>10}")
print("\n=== 2. ColumnTransformer qo'lda ===")
sonli = ["summa", "masofa", "yosh"]
kategoriya = ["hudud", "kanal"]
tayyor = ColumnTransformer([
("s", Pipeline([("imp", SimpleImputer(strategy="median")),
("sc", StandardScaler())]), sonli),
("k", OneHotEncoder(handle_unknown="ignore", sparse_output=False),
kategoriya)], verbose_feature_names_out=False)
Z = tayyor.fit_transform(X, y)
print(f" natija o'lchami: {Z.shape}")
print(f" ustun nomlari: {list(tayyor.get_feature_names_out())}")
print("\n=== 3. make_column_selector bilan avtomatik ===")
avto = ColumnTransformer([
("s", Pipeline([("imp", SimpleImputer(strategy="median")),
("sc", StandardScaler())]),
make_column_selector(dtype_include="number")),
("k", OneHotEncoder(handle_unknown="ignore", sparse_output=False),
make_column_selector(dtype_include=["object", "category"]))],
verbose_feature_names_out=False)
Z2 = avto.fit_transform(X, y)
print(f" natija o'lchami: {Z2.shape} (qo'lda bilan bir xil: "
f"{Z.shape == Z2.shape})")
# yangi ustun qo'shilganda avtomatik tanlov uni oladi
X2 = X.assign(yangi_belgi=np.arange(len(X), dtype=float))
print(f" yangi ustun qo'shilgandan keyin: "
f"{avto.fit_transform(X2, y).shape}")
print("\n=== 4. To'liq quvur va CV ===")
modellar = {
"LogReg": LogisticRegression(max_iter=2000),
"HistGB": HistGradientBoostingClassifier(learning_rate=0.1,
max_iter=200,
random_state=0),
}
print(f" {'model':<10} {'CV ROC AUC':>12} {'std':>8}")
for nom, m in modellar.items():
quvur = Pipeline([("t", avto), ("m", m)])
b = cross_val_score(quvur, X, y, cv=cv, scoring="roc_auc")
print(f" {nom:<10} {b.mean():>12.4f} {b.std():>8.4f}")
quvur = Pipeline([("t", avto), ("m", modellar["LogReg"])]).fit(X, y)
print(f" bosqichlar: {list(quvur.named_steps)}")
print(f" modelsiz qism o'lchami: {quvur[:-1].transform(X[:3]).shape}")
print(" ⭐ ColumnTransformer ustun turlarini ajratadi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot ===
5000 qator, 5 belgi
ustun dtype yo_qolgan
hudud str 0
kanal str 0
summa float64 0
masofa float64 158
yosh float64 298
=== 2. ColumnTransformer qo'lda ===
natija o'lchami: (5000, 10)
ustun nomlari: ['summa', 'masofa', 'yosh', 'hudud_buxoro', 'hudud_fargona', 'hudud_samarqand', 'hudud_toshkent', 'kanal_ilova', 'kanal_qongiroq', 'kanal_sayt']
=== 3. make_column_selector bilan avtomatik ===
natija o'lchami: (5000, 10) (qo'lda bilan bir xil: True)
yangi ustun qo'shilgandan keyin: (5000, 11)
=== 4. To'liq quvur va CV ===
model CV ROC AUC std
LogReg 0.6408 0.0141
HistGB 0.5833 0.0130
bosqichlar: ['t', 'm']
modelsiz qism o'lchami: (3, 10)
⭐ ColumnTransformer ustun turlarini ajratadiNima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — Maxsus transformer
"""sklearn API ga mos transformer yozish (real numpy/pandas/sklearn)."""
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.model_selection import GridSearchCV, StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
class NisbatQoshuvchi(BaseEstimator, TransformerMixin):
"""Berilgan juftliklar uchun nisbat belgilarini qo'shadi."""
def __init__(self, juftliklar=None):
self.juftliklar = juftliklar # FAQAT saqlash
def fit(self, X, y=None):
X = np.asarray(X, dtype=float)
self.n_features_in_ = X.shape[1]
self.juftliklar_ = list(self.juftliklar or [])
return self
def transform(self, X):
X = np.asarray(X, dtype=float)
ustunlar = [X]
for a, b in self.juftliklar_:
bolvuchi = np.where(np.abs(X[:, b]) < 1e-12, np.nan, X[:, b])
ustunlar.append((X[:, a] / bolvuchi).reshape(-1, 1))
return np.hstack(ustunlar)
def get_feature_names_out(self, input_features=None):
asl = (list(input_features) if input_features is not None
else [f"x{i}" for i in range(self.n_features_in_)])
return np.array(asl + [f"{asl[a]}_bo_{asl[b]}"
for a, b in self.juftliklar_])
class GuruhOrtachasi(BaseEstimator, TransformerMixin):
"""Guruh bo'yicha o'rtachani O'QUVDAN o'rganadi (leakage ga qarshi)."""
def __init__(self, guruh_ustuni: int = 0, qiymat_ustuni: int = 1):
self.guruh_ustuni = guruh_ustuni
self.qiymat_ustuni = qiymat_ustuni
def fit(self, X, y=None):
X = np.asarray(X)
guruhlar = X[:, self.guruh_ustuni]
qiymatlar = X[:, self.qiymat_ustuni].astype(float)
self.ortachalar_ = {}
for g in np.unique(guruhlar):
self.ortachalar_[g] = float(qiymatlar[guruhlar == g].mean())
self.umumiy_ = float(qiymatlar.mean())
return self
def transform(self, X):
X = np.asarray(X)
guruhlar = X[:, self.guruh_ustuni]
qiymatlar = X[:, self.qiymat_ustuni].astype(float)
ort = np.array([self.ortachalar_.get(g, self.umumiy_) for g in guruhlar])
return np.column_stack([qiymatlar, ort, qiymatlar / np.where(
np.abs(ort) < 1e-12, np.nan, ort)])
def yarat(seed: int = 11, n: int = 5000):
rng = np.random.default_rng(seed)
daromad = rng.lognormal(14.0, 0.5, n)
qarz = daromad * rng.lognormal(-0.5, 0.6, n)
tolov = rng.integers(6, 60, n).astype(float)
kechikish = rng.binomial(tolov.astype(int), rng.beta(1.5, 12, n)).astype(float)
kuch = -1.0 + 2.5 * (qarz / daromad) + 4.0 * (kechikish / tolov)
y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
return np.column_stack([daromad, qarz, tolov, kechikish]), y
def main() -> None:
X, y = yarat()
nomlar = ["daromad", "qarz", "tolov", "kechikish"]
cv = StratifiedKFold(5, shuffle=True, random_state=0)
def model():
return HistGradientBoostingClassifier(learning_rate=0.1, max_iter=200,
random_state=0)
print("=== 1. Bazaviy ===")
b0 = cross_val_score(model(), X, y, cv=cv, scoring="roc_auc")
print(f" CV ROC AUC: {b0.mean():.4f} (+-{b0.std():.4f})")
print("\n=== 2. Maxsus transformer bilan ===")
nq = NisbatQoshuvchi(juftliklar=[(1, 0), (3, 2)])
quvur = Pipeline([("n", nq), ("m", model())])
b1 = cross_val_score(quvur, X, y, cv=cv, scoring="roc_auc")
print(f" CV ROC AUC: {b1.mean():.4f} (+-{b1.std():.4f})")
print(f" yaxshilanish: {b1.mean() - b0.mean():+.4f}")
nq.fit(X)
print(f" belgilar: {list(nq.get_feature_names_out(nomlar))}")
print("\n=== 3. sklearn API talablari ===")
print(f" get_params(): {sorted(nq.get_params())}")
nq2 = NisbatQoshuvchi().set_params(juftliklar=[(1, 0)])
print(f" set_params ishladi: {nq2.juftliklar}")
print(f" o'rganilgan atributlar (_ bilan): "
f"{[a for a in vars(nq) if a.endswith('_')]}")
from sklearn.base import clone
nusxa = clone(nq)
print(f" clone() ishladi: {nusxa.juftliklar == nq.juftliklar}")
print("\n=== 4. GridSearchCV bilan sozlash ===")
quvur = Pipeline([("n", NisbatQoshuvchi()), ("m", model())])
setka = {"n__juftliklar": [[], [(1, 0)], [(3, 2)], [(1, 0), (3, 2)]],
"m__max_iter": [100, 200]}
q = GridSearchCV(quvur, setka, cv=cv, scoring="roc_auc", n_jobs=1).fit(X, y)
print(f" konfiguratsiyalar: {len(q.cv_results_['params'])}")
print(f" eng yaxshi juftliklar: {q.best_params_['n__juftliklar']}")
print(f" eng yaxshi CV: {q.best_score_:.4f}")
print(f" {'juftliklar':<22} {'CV AUC':>9}")
for p, s in zip(q.cv_results_["params"], q.cv_results_["mean_test_score"]):
if p["m__max_iter"] == 200:
print(f" {str(p['n__juftliklar']):<22} {s:>9.4f}")
print(" ⭐ __init__ faqat saqlaydi, o'rganilgan atributlar _ bilan")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bazaviy ===
CV ROC AUC: 0.6849 (+-0.0057)
=== 2. Maxsus transformer bilan ===
CV ROC AUC: 0.6874 (+-0.0063)
yaxshilanish: +0.0025
belgilar: [np.str_('daromad'), np.str_('qarz'), np.str_('tolov'), np.str_('kechikish'), np.str_('qarz_bo_daromad'), np.str_('kechikish_bo_tolov')]
=== 3. sklearn API talablari ===
get_params(): ['juftliklar']
set_params ishladi: [(1, 0)]
o'rganilgan atributlar (_ bilan): ['n_features_in_', 'juftliklar_']
clone() ishladi: True
=== 4. GridSearchCV bilan sozlash ===
konfiguratsiyalar: 8
eng yaxshi juftliklar: [(1, 0)]
eng yaxshi CV: 0.6983
juftliklar CV AUC
[] 0.6849
[(1, 0)] 0.6867
[(3, 2)] 0.6833
[(1, 0), (3, 2)] 0.6874
⭐ __init__ faqat saqlaydi, o'rganilgan atributlar _ bilanNima ko'rsatdi: 2.3, 2.4-bo'limlar.
Misol 3 — Giperparametr sozlash va passthrough
"""Tayyorlash parametrlarini ham sozlash (real numpy/sklearn)."""
import numpy as np
from sklearn.decomposition import PCA
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import (GridSearchCV, StratifiedKFold,
cross_val_score, train_test_split)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import (PowerTransformer, QuantileTransformer,
StandardScaler)
def yarat(seed: int = 5, n: int = 3000, p: int = 40):
rng = np.random.default_rng(seed)
X = rng.lognormal(0, 1, (n, p)) # qiyshiq taqsimot
kuch = np.log(X[:, :6]) @ rng.normal(0, 1.0, 6)
y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
return X, y
def main() -> None:
X, y = yarat()
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=0,
stratify=y)
cv = StratifiedKFold(4, shuffle=True, random_state=0)
print("=== 1. Bazaviy quvur ===")
quvur = Pipeline([
("trans", StandardScaler()),
("tanlov", "passthrough"),
("model", LogisticRegression(max_iter=3000)),
])
b = cross_val_score(quvur, Xtr, ytr, cv=cv, scoring="roc_auc")
print(f" CV ROC AUC: {b.mean():.4f} (+-{b.std():.4f})")
print("\n=== 2. Transformatsiyani sozlash ===")
setka = {"trans": [StandardScaler(), PowerTransformer(),
QuantileTransformer(n_quantiles=200, random_state=0)]}
q = GridSearchCV(quvur, setka, cv=cv, scoring="roc_auc", n_jobs=1).fit(Xtr, ytr)
print(f" {'transformatsiya':<24} {'CV AUC':>9}")
for p, s in zip(q.cv_results_["params"], q.cv_results_["mean_test_score"]):
print(f" {type(p['trans']).__name__:<24} {s:>9.4f}")
print("\n=== 3. Bosqichni 'passthrough' bilan o'chirish ===")
setka2 = {"tanlov": ["passthrough", SelectKBest(f_classif, k=10),
SelectKBest(f_classif, k=20), PCA(n_components=10,
random_state=0)]}
q2 = GridSearchCV(quvur, setka2, cv=cv, scoring="roc_auc",
n_jobs=1).fit(Xtr, ytr)
print(f" {'tanlov bosqichi':<28} {'CV AUC':>9}")
for p, s in zip(q2.cv_results_["params"], q2.cv_results_["mean_test_score"]):
nom = ("passthrough" if p["tanlov"] == "passthrough"
else f"{type(p['tanlov']).__name__}"
f"({getattr(p['tanlov'], 'k', getattr(p['tanlov'], 'n_components', ''))})")
print(f" {nom:<28} {s:>9.4f}")
print("\n=== 4. To'liq setka va halol baho ===")
toliq = {
"trans": [StandardScaler(), PowerTransformer()],
"tanlov": ["passthrough", SelectKBest(f_classif, k=15)],
"model": [LogisticRegression(max_iter=3000),
HistGradientBoostingClassifier(learning_rate=0.1,
max_iter=150,
random_state=0)],
}
q3 = GridSearchCV(quvur, toliq, cv=cv, scoring="roc_auc",
n_jobs=1).fit(Xtr, ytr)
print(f" konfiguratsiyalar: {len(q3.cv_results_['params'])}")
eng = {k: (type(v).__name__ if not isinstance(v, str) else v)
for k, v in q3.best_params_.items()}
print(f" eng yaxshi: {eng}")
from sklearn.metrics import roc_auc_score
test = roc_auc_score(yte, q3.predict_proba(Xte)[:, 1])
print(f" GridSearchCV.best_score_: {q3.best_score_:.4f} (OPTIMISTIK)")
print(f" alohida test to'plamida: {test:.4f}")
print(f" optimizm: {q3.best_score_ - test:+.4f}")
print(" ⭐ best_score_ - eng yaxshisi, shuning uchun optimistik")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bazaviy quvur ===
CV ROC AUC: 0.8431 (+-0.0219)
=== 2. Transformatsiyani sozlash ===
transformatsiya CV AUC
StandardScaler 0.8431
PowerTransformer 0.8867
QuantileTransformer 0.8818
=== 3. Bosqichni 'passthrough' bilan o'chirish ===
tanlov bosqichi CV AUC
passthrough 0.8431
SelectKBest(10) 0.8488
SelectKBest(20) 0.8467
PCA(10) 0.6775
=== 4. To'liq setka va halol baho ===
konfiguratsiyalar: 8
eng yaxshi: {'model': 'LogisticRegression', 'tanlov': 'SelectKBest', 'trans': 'PowerTransformer'}
GridSearchCV.best_score_: 0.8920 (OPTIMISTIK)
alohida test to'plamida: 0.8760
optimizm: +0.0161
⭐ best_score_ - eng yaxshisi, shuning uchun optimistikNima ko'rsatdi: 2.5, 2.6-bo'limlar.
Misol 4 — Nested CV va saqlash
"""Halol baho va tayyor quvurni saqlash (real numpy/sklearn/joblib)."""
import io
import pickle
import numpy as np
import sklearn
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import (GridSearchCV, StratifiedKFold,
cross_val_score, train_test_split)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
def yarat(seed: int = 3, n: int = 1500, p: int = 60, signal: int = 6):
rng = np.random.default_rng(seed)
X = rng.normal(0, 1, (n, p))
kuch = X[:, :signal] @ rng.normal(0, 1.0, signal)
y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
return X, y
def hajm_kb(obyekt) -> float:
b = io.BytesIO()
pickle.dump(obyekt, b, protocol=pickle.HIGHEST_PROTOCOL)
return b.tell() / 1024
def main() -> None:
X, y = yarat()
ichki_cv = StratifiedKFold(3, shuffle=True, random_state=0)
tashqi_cv = StratifiedKFold(4, shuffle=True, random_state=1)
quvur = Pipeline([("sc", StandardScaler()),
("s", SelectKBest(f_classif, k=20)),
("m", LogisticRegression(max_iter=3000))])
setka = {"s__k": [10, 20, 40], "m__C": [0.1, 1.0, 10.0]}
print("=== 1. Oddiy GridSearchCV ===")
q = GridSearchCV(quvur, setka, cv=ichki_cv, scoring="roc_auc",
n_jobs=1).fit(X, y)
print(f" konfiguratsiyalar: {len(q.cv_results_['params'])}")
print(f" eng yaxshi: {q.best_params_}")
print(f" best_score_: {q.best_score_:.4f}")
print("\n=== 2. Nested CV (halol baho) ===")
ichki = GridSearchCV(quvur, setka, cv=ichki_cv, scoring="roc_auc",
n_jobs=1)
nested = cross_val_score(ichki, X, y, cv=tashqi_cv, scoring="roc_auc")
print(f" nested CV: {nested.mean():.4f} (+-{nested.std():.4f})")
print(f" best_score_ dan farq: {q.best_score_ - nested.mean():+.4f}")
print(f" (best_score_ ko'p konfiguratsiya orasidan eng yaxshisi)")
print("\n=== 3. Alohida test to'plami (arzonroq muqobil) ===")
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0,
stratify=y)
q2 = GridSearchCV(quvur, setka, cv=ichki_cv, scoring="roc_auc",
n_jobs=1).fit(Xtr, ytr)
from sklearn.metrics import roc_auc_score
test = roc_auc_score(yte, q2.predict_proba(Xte)[:, 1])
print(f" ichki best_score_: {q2.best_score_:.4f}")
print(f" alohida test: {test:.4f}")
print(f" nested CV: {nested.mean():.4f}")
print(f" (uchalasi ham bir xil savolga javob beradi)")
print("\n=== 4. Tayyor quvurni saqlash ===")
yakuniy = q.best_estimator_
paket = {
"quvur": yakuniy,
"belgilar_soni": X.shape[1],
"eng_yaxshi_parametrlar": q.best_params_,
"nested_cv": round(float(nested.mean()), 4),
"versiyalar": {"sklearn": sklearn.__version__,
"numpy": np.__version__},
"sana": "2026-09-21",
}
print(f" kalitlar: {sorted(paket)}")
print(f" paket hajmi: {hajm_kb(paket):.1f} KB")
print(f" bosqichlar: {list(yakuniy.named_steps)}")
tanlangan = yakuniy.named_steps["s"].get_support().sum()
print(f" tanlangan belgilar: {tanlangan} / {X.shape[1]}")
yangi = np.random.default_rng(99).normal(0, 1, (3, X.shape[1]))
print(f" yangi qatorlar uchun bashorat: "
f"{np.round(yakuniy.predict_proba(yangi)[:, 1], 4).tolist()}")
print(" ⭐ Butun tayyorlash va model bitta obyektda saqlanadi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Oddiy GridSearchCV ===
konfiguratsiyalar: 9
eng yaxshi: {'m__C': 10.0, 's__k': 10}
best_score_: 0.9470
=== 2. Nested CV (halol baho) ===
nested CV: 0.9448 (+-0.0129)
best_score_ dan farq: +0.0022
(best_score_ ko'p konfiguratsiya orasidan eng yaxshisi)
=== 3. Alohida test to'plami (arzonroq muqobil) ===
ichki best_score_: 0.9516
alohida test: 0.9290
nested CV: 0.9448
(uchalasi ham bir xil savolga javob beradi)
=== 4. Tayyor quvurni saqlash ===
kalitlar: ['belgilar_soni', 'eng_yaxshi_parametrlar', 'nested_cv', 'quvur', 'sana', 'versiyalar']
paket hajmi: 3.8 KB
bosqichlar: ['sc', 's', 'm']
tanlangan belgilar: 10 / 60
yangi qatorlar uchun bashorat: [0.0136, 0.6868, 0.9445]
⭐ Butun tayyorlash va model bitta obyektda saqlanadiNima ko'rsatdi: 2.6-bo'lim.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Pipeline — faqat qulaylik" | Leakage ga tuzilmaviy himoya |
| "init da tekshirish mumkin" | Faqat saqlash |
| "ColumnTransformer da ['matn']" | Ustun nomi ro'yxatsiz |
| "remainder ahamiyatsiz" | Standartda ustunlar tashlanadi |
| "best_score_ — halol baho" | Optimistik |
| "FunctionTransformer universal" | Holatsiz transformatsiyalar uchun |
| "Bosqichni o'chirib bo'lmaydi" | "passthrough" |
| "Nested CV shart" | Alohida test ko'pincha yetarli |
6. Keng tarqalgan xatolar va yechimlari
1. Tayyorlash Pipeline tashqarisida
Xs = scaler.fit_transform(X); GridSearchCV(model, setka).fit(Xs, y) # ⚠️
GridSearchCV(Pipeline([("sc", scaler), ("m", model)]), setka).fit(X, y) # ✅2. init da o'zgartirish
def __init__(self, k): self.k = int(k) if k else 10 # ⚠️
def __init__(self, k=10): self.k = k # tekshiruv fit da # ✅3. Matn ustunini ro'yxat sifatida
ColumnTransformer([("t", TfidfVectorizer(), ["izoh"])]) # ⚠️
ColumnTransformer([("t", TfidfVectorizer(), "izoh")]) # ✅4. remainder ni unutish
ColumnTransformer([("s", sc, sonli)]) # kategoriyalar TASHLANDI # ⚠️
ColumnTransformer([...], remainder="passthrough") # ✅5. best_score_ ni halol baho deb olish
print(f"Model natijasi: {q.best_score_}") # ⚠️
print(f"Test natijasi: {roc_auc_score(yte, q.predict_proba(Xte)[:, 1])}") # ✅6. O'rganish kerak bo'lgan joyda FunctionTransformer
FunctionTransformer(lambda X: X - X.mean(axis=0)) # test o'z o'rtachasi # ⚠️
StandardScaler(with_std=False) # o'quv o'rtachasini eslab qoladi # ✅7. Katta setkada memory ni unutish
GridSearchCV(quvur, katta_setka) # tayyorlash qayta-qayta # ⚠️
Pipeline([...], memory="cache_papka") # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 17.8-dars (o'tilgan): Leakage
- 12.9-dars (o'tilgan): Leakage asoslari
- 15.13-dars (o'tilgan): Ishlab chiqarish
- 19-qism: scikit-learn to'liq
- 17.10-dars: Amaliyot
8. Eng yaxshi amaliyotlar
Hamma tayyorlashni Pipeline ichida.
ColumnTransformer bilan ustun turlarini ajrating.
make_column_selector dan foydalaning.
remainder ni aniq belgilang.
Maxsus transformer da API qoidalariga rioya qiling.
Tayyorlash parametrlarini ham sozlang.
best_score_ ni halol baho deb olmang.
Butun quvurni saqlang.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # Pipeline fit da nima qiladi?
2. # predict da?
3. # quvur[:-1] nima?
4. # ColumnTransformer nima uchun?
5. # matn ustuni qanday beriladi?
6. # remainder standart qiymati?
7. # make_column_selector nima qiladi?
8. # __init__ da nima qilish mumkin?
9. # o'rganilgan atributlar qanday nomlanadi?
10. # setkada nom berish?
11. # bosqichni qanday o'chirish mumkin?
12. # best_score_ halolmi?Javoblar
- Har bosqich fit_transform, oxirgisi fit
- Har bosqich transform, oxirgisi predict
- Modelsiz qism
- Turli ustunlarga turli tayyorlash
- Ustun nomi, ro'yxatsiz
- "drop"
- dtype bo'yicha ustun tanlaydi
- Faqat parametrlarni saqlash
- Oxirida _ bilan
- bosqich__parametr
- "passthrough"
- Yo'q, optimistik
Vazifa 2: Xatolarni tuzating
1. Xs = scaler.fit_transform(X); GridSearchCV(model, setka).fit(Xs, y)
2. def __init__(self, k): self.k = int(k) if k else 10
3. ColumnTransformer([("t", TfidfVectorizer(), ["izoh"])])
4. ColumnTransformer([("s", sc, sonli)]) # kategoriyalar ham bor
5. print(f"Model natijasi: {q.best_score_}")Javoblar
1. GridSearchCV(Pipeline([("sc", scaler), ("m", model)]), setka).fit(X, y)
2. def __init__(self, k=10): self.k = k
3. ColumnTransformer([("t", TfidfVectorizer(), "izoh")])
4. ColumnTransformer([...], remainder="passthrough")
5. print(f"Test natijasi: {roc_auc_score(yte, ...)}")Vazifa 3: ColumnTransformer
Modellang:
- Ma'lumot
- Qo'lda
- Avtomatik
- To'liq quvur
Vazifa 4: Maxsus transformer
Modellang:
- Bazaviy
- Transformer
- API talablari
- GridSearchCV
Vazifa 5: Sozlash
Modellang:
- Bazaviy
- Transformatsiya
- passthrough
- To'liq setka
Vazifa 6: Nested CV
Modellang:
- GridSearchCV
- Nested CV
- Alohida test
- Saqlash
Vazifa 7: O'ylash
Pipeline kodni murakkablashtiradi: oddiy scaler.fit_transform(X) o'rniga uch qator yozish kerak. Bu narx nima uchun oqlanadi?
Javob
Qisqa javob: Pipeline kodni murakkablashtirmaydi, balki murakkablikni ko'rinadigan joyga ko'chiradi. scaler.fit_transform(X) sodda ko'rinadi, lekin u yashirin qaror qabul qiladi: "qaysi ma'lumotda fit qilamiz?" — va bu qaror ko'pincha noto'g'ri bo'ladi.
1. Haqiqiy narx taqqoslashi
| Yondashuv | Kod uzunligi | Leakage xavfi | Ishlab chiqarish |
|---|---|---|---|
| Qo'lda transformatsiyalar | Qisqaroq | Yuqori | Qo'lda takrorlash kerak |
Pipeline |
2-3 qator uzunroq | Nolga yaqin | joblib.dump yetarli |
Ikki qator uchun leakage xavfini yo'qotish — juda arzon savdo.
2. Yashirin narxlar (Pipeline siz)
- Takrorlash: tayyorlashni o'quv, test va ishlab chiqarish uchun uch marta yozish
- Nomuvofiqlik: uch joyda uch xil versiya paydo bo'ladi
- Sozlash cheklovi: tayyorlash parametrlarini
GridSearchCVbilan sozlab bo'lmaydi - Saqlash: model va tayyorlashni alohida saqlash va versiyalash
- Jamoaviy ish: har kim o'z tayyorlashini yozadi (dars kirishidagi holat)
3. Qo'shimcha imkoniyatlar
# tayyorlash parametrlarini sozlash
GridSearchCV(quvur, {"t__matn__min_df": [2, 5, 10]})
# bosqichni o'chirish savolini CV bilan hal qilish
{"pca": ["passthrough", PCA(10), PCA(30)]}
# keshlash bilan tezlashtirish
Pipeline([...], memory="cache")
# bitta obyekt sifatida saqlash
joblib.dump(quvur, "model.joblib")Bularning hech biri qo'lda yondashuvda mumkin emas.
4. Qachon Pipeline shart emas
- Tez tadqiqot, natija hech qayerga chiqmaydi
- Transformatsiya butunlay holatsiz (
log, ustun tanlash) - Bir martalik tahlil, model yo'q
Lekin bu hollarda ham odat sifatida ishlatish zarar qilmaydi.
5. Xulosa
- Narx — 2-3 qator kod
- Foyda — leakage himoyasi, sozlash, saqlash
- Yashirin narxlar Pipeline siz ancha katta
- Odat sifatida har doim ishlating
Nimani mustahkamlaydi: 2.1, 2.5-bo'limlar.
Xulosa
Bu darsda Pipeline ni o'rgandik.
Eng muhim uch fikr:
Pipeline— leakage ga tuzilmaviy himoya.fitda har bosqich faqat o'quv qismida o'qitiladi,predictda esatransformqilinadi. Bu leakage ni eslab qolish masalasidan tuzilma masalasiga aylantiradi.ColumnTransformerturli ustun turlariga turli tayyorlashni qo'llaydi; matn ustuni ro'yxatsiz beriladi varemainderni aniq belgilang (standartda qolgan ustunlar tashlanadi).Maxsus transformer — uchta qoida.
__init__da faqat parametrlarni saqlang (tekshirmang, o'zgartirmang — aks holdaget_params/clonebuziladi), o'rganilgan narsalarni_bilan tugaydigan atributlarda saqlang,fitesaselfqaytarsin. Holatsiz transformatsiyalar uchun esaFunctionTransformeryetarli.best_score_optimistik.GridSearchCV100 konfiguratsiya orasidan eng yaxshisini tanlaydi, shuning uchun uning bali tasodifan yuqori bo'ladi. Halol baho uchun alohida test to'plami (arzon) yoki nested CV (qimmat, lekin to'liqroq).Pipelineshuningdek tayyorlash parametrlarini sozlashga imkon beradi (bosqich__parametr) va bosqichni"passthrough"bilan o'chirib, "bu qadam kerakmi?" savolini CV bilan hal qiladi.
Keyingi darsda amaliyot: 17-qism bo'yicha to'liq loyiha.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!