Scikit-learn 1.8 v praxi: Kompletný sprievodca budovaním ML pipeline v roku 2026

Praktický sprievodca novinkami scikit-learn 1.8 a budovaním ML pipeline v Pythone — od prípravy dát a tréningu modelov cez ladenie hyperparametrov až po nasadenie do produkcie.

Úvod: Scikit-learn v roku 2026 — stále kráľ strojového učenia v Pythone

Povedzme si to rovno — keď sa povie strojové učenie v Pythone, scikit-learn je stále tá prvá knižnica, po ktorej väčšina z nás siahne. Aj po viac ako 15 rokoch vývoja. Aj napriek tomu, že sa každý rok objaví nejaký nový "game-changer" framework.

A verzia 1.8? Tá posunula latku ešte vyššie. Natívna podpora GPU výpočtov cez Array API, podpora free-threaded CPythonu 3.14 (konečne bez GIL!), dramatické zrýchlenie lineárnych modelov a úplne nový algoritmus Classical MDS. Úprimne, keď som si to prvýkrát vyskúšal, bol som milo prekvapený.

V tomto sprievodcovi si prejdeme všetky tieto novinky a — čo je dôležitejšie — ukážeme si, ako ich reálne využiť pri budovaní kompletného ML pipeline. Od načítania dát cez tréning modelov až po nasadenie. Článok nadväzuje na naše porovnanie Pandas 3.0 a Polars, pretože práve tieto knižnice budeme používať na prípravu dát pred vstupom do scikit-learn.

Čo je nové v scikit-learn 1.8 — prehľad kľúčových zmien

Skôr než sa pustíme do budovania pipeline, poďme si zhrnúť najdôležitejšie novinky. Sľubujem, stojí to za to.

Array API — GPU akcelerácia priamo v scikit-learn

Toto je podľa mňa najvýznamnejšia zmena v celej verzii. Scikit-learn 1.8 rozšíril experimentálnu podporu Array API, čo v praxi znamená, že mnoho estimátorov a funkcií teraz dokáže pracovať priamo s PyTorch tenzormi a CuPy poľami. Jednoducho povedané: GPU akcelerácia bez nutnosti prepisovať kód.

Podporované estimátory a funkcie zahŕňajú:

  • Preprocessing: StandardScaler, PolynomialFeatures, LabelBinarizer
  • Lineárne modely: RidgeCV, RidgeClassifier, RidgeClassifierCV
  • Metriky: confusion_matrix, roc_curve, precision_recall_curve, balanced_accuracy_score
  • Klastrovanie: GaussianMixture, calinski_harabasz_score
  • Kalibrácia: CalibratedClassifierCV
import numpy as np
import torch
import sklearn
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import RidgeClassifierCV
from sklearn.calibration import CalibratedClassifierCV
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_validate

# Vytvorenie pipeline s GPU akceleráciou
pipeline_gpu = make_pipeline(
    StandardScaler(),
    CalibratedClassifierCV(
        RidgeClassifierCV(alphas=np.logspace(-3, 3, 10)),
        method="temperature"
    )
)

# Konverzia dát na GPU tenzory
X_gpu = torch.tensor(X_train.astype(np.float32), device="cuda")
y_gpu = torch.tensor(y_train, device="cuda")

# Tréning na GPU — aktivácia Array API dispatch
with sklearn.config_context(array_api_dispatch=True):
    vysledky = cross_validate(pipeline_gpu, X_gpu, y_gpu, cv=5)
    print(f"Priemerná presnosť: {vysledky['test_score'].mean():.4f}")

Zrýchlenie na GPU závisí od veľkosti datasetu a typu estimátora, ale v niektorých prípadoch môžete dosiahnuť až 10-násobné zrýchlenie oproti CPU. Najväčší benefit uvidíte pri veľkých datasetoch a operáciách s maticovým násobením.

Free-threaded CPython 3.14 — koniec GIL éry

Na toto sme čakali roky. Python 3.14 prináša experimentálnu podporu free-threaded režimu — teda behu bez Global Interpreter Lock (GIL). Scikit-learn 1.8 túto zmenu plne využíva a poskytuje predkompilované wheel balíčky pre všetky podporované platformy.

Čo to znamená v praxi? Keď nastavíte n_jobs na viac ako 1 a použijete threading backend, vlákna sa teraz môžu skutočne vykonávať paralelne:

import joblib
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

# Definícia mriežky parametrov
param_grid = {
    "n_estimators": [100, 200, 500],
    "max_depth": [5, 10, 20, None],
    "min_samples_split": [2, 5, 10]
}

clf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(clf, param_grid=param_grid, cv=5, n_jobs=4)

# S free-threaded CPython je threading backend efektívnejší
with joblib.parallel_config(backend="threading"):
    grid_search.fit(X_train, y_train)

print(f"Najlepšie parametre: {grid_search.best_params_}")
print(f"Najlepšie skóre: {grid_search.best_score_:.4f}")

Dramatické zrýchlenie lineárnych modelov

Scikit-learn 1.8 implementoval gap-safe screening rules pre modely s L1 penalizáciou — Lasso, ElasticNet, MultiTaskLasso a MultiTaskElasticNet (vrátane ich CV variant). Tento algoritmus umožňuje koordinátovému zostupu vylúčiť irelevantné feature ešte pred začatím optimalizácie.

Výsledok? Až 10-násobné zrýchlenie na datasetoch s veľkým počtom feature:

from sklearn.linear_model import ElasticNetCV
from sklearn.datasets import make_regression
from time import time

# Dataset s veľkým počtom feature
X, y = make_regression(n_samples=5000, n_features=10000, random_state=42)

model = ElasticNetCV(cv=5, random_state=42)

start = time()
model.fit(X, y)
elapsed = time() - start

print(f"ElasticNetCV tréning: {elapsed:.1f} sekúnd")
print(f"Počet vybraných feature: {(model.coef_ != 0).sum()}")
print(f"Najlepšia alpha: {model.alpha_:.6f}")

DecisionTreeRegressor — z O(n²) na O(n log n)

Toto je jedna z tých zmien, ktoré vás donútia povedať "konečne!" Použitie kritéria absolute_error v rozhodovacom strome bolo predtým extrémne pomalé pri väčších datasetoch. Verzia 1.8 prináša algoritmické zlepšenie z O(n²) na O(n log n) — to je približne 200-násobné zrýchlenie na 100 000 vzorkách.

from sklearn.tree import DecisionTreeRegressor
from sklearn.datasets import make_regression
import time

X, y = make_regression(n_samples=100_000, n_features=10, random_state=42)

strom = DecisionTreeRegressor(criterion="absolute_error", max_depth=10)

start = time.time()
strom.fit(X, y)
cas = time.time() - start

print(f"Tréning s absolute_error: {cas:.2f} sekúnd")
# V scikit-learn 1.7: ~20 sekúnd
# V scikit-learn 1.8: ~0.10 sekúnd

Classical MDS — nový algoritmus na redukciu dimenzionality

Classical MDS (tiež známy ako Principal Coordinates Analysis alebo Torgersenovo škálovanie) je nová metóda v module sklearn.manifold. Na rozdiel od štandardného MDS, ktoré iteratívne minimalizuje stresovú funkciu, Classical MDS má presné analytické riešenie cez vlastný rozklad. To je pekný rozdiel, keď vám záleží na rýchlosti a reprodukovateľnosti.

from sklearn.manifold import ClassicalMDS
from sklearn.datasets import make_s_curve
import matplotlib.pyplot as plt

# Generovanie 3D dát v tvare písmena S
X, farba = make_s_curve(n_samples=1500, random_state=42)

# Redukcia na 2 dimenzie pomocou Classical MDS
cmds = ClassicalMDS(n_components=2)
X_2d = cmds.fit_transform(X)

plt.figure(figsize=(10, 6))
plt.scatter(X_2d[:, 0], X_2d[:, 1], c=farba, cmap="viridis", s=5)
plt.title("Classical MDS — redukcia dimenzionality")
plt.colorbar(label="Pozícia na S-krivke")
plt.xlabel("Komponent 1")
plt.ylabel("Komponent 2")
plt.show()

Temperature Scaling — lepšia kalibrácia pravdepodobností

Nová metóda method="temperature" v CalibratedClassifierCV je obzvlášť vhodná pre multiclass problémy. Temperature Scaling používa jediný parameter na kalibráciu výstupných pravdepodobností — je to elegantnejšie a menej náchylné na overfitting než sigmoidná kalibrácia. Osobne som to testoval na niekoľkých projektoch a rozdiel bol viditeľný.

from sklearn.calibration import CalibratedClassifierCV, calibration_curve
from sklearn.naive_bayes import GaussianNB
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

# Multiclass dataset
X, y = make_classification(
    n_samples=5000, n_classes=4, n_informative=10,
    n_features=20, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# Základný klasifikátor
gnb = GaussianNB()
gnb.fit(X_train, y_train)

# Temperature Scaling kalibrácia
kalibracia = CalibratedClassifierCV(
    gnb, method="temperature", ensemble=False, cv="prefit"
)
kalibracia.fit(X_train, y_train)

# Porovnanie pred a po kalibrácii
print(f"Presnosť pred kalibráciou: {gnb.score(X_test, y_test):.4f}")
print(f"Presnosť po kalibrácii:    {kalibracia.score(X_test, y_test):.4f}")

Budovanie kompletného ML pipeline — krok za krokom

Tak, dosť bolo teórie. Poďme si ukázať, ako postaviť kompletný ML pipeline od začiatku do konca. Použijeme realistický scenár: predikcia odchodu zákazníkov (customer churn prediction) pre telekomunikačnú spoločnosť. Je to klasický problém, na ktorom sa dobre ukazujú všetky dôležité koncepty.

Krok 1: Načítanie a prieskum dát

Začneme načítaním a základným prieskumom dát. Tu využijeme Pandas 3.0 s jeho natívnymi PyArrow typmi:

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split

# Generovanie realistického datasetu
np.random.seed(42)
n = 5000

data = pd.DataFrame({
    "vek": np.random.randint(18, 70, n),
    "mesacny_poplatok": np.round(np.random.uniform(20, 120, n), 2),
    "pocet_mesiacov": np.random.randint(1, 72, n),
    "pocet_sluzieb": np.random.randint(1, 7, n),
    "celkovy_utrata": np.round(np.random.uniform(100, 8000, n), 2),
    "typ_zmluvy": np.random.choice(["mesacna", "rocna", "dvojrocna"], n),
    "sposob_platby": np.random.choice(
        ["karta", "prevod", "elektronicky", "postova_poukazka"], n
    ),
    "internet_sluzba": np.random.choice(["DSL", "optika", "ziadna"], n),
    "technicka_podpora": np.random.choice(["ano", "nie"], n),
    "partner": np.random.choice(["ano", "nie"], n),
    "odchod": np.random.choice([0, 1], n, p=[0.73, 0.27])
})

# Umelo pridáme chýbajúce hodnoty
for col in ["mesacny_poplatok", "celkovy_utrata", "technicka_podpora"]:
    mask = np.random.random(n) < 0.05
    data.loc[mask, col] = np.nan

print(f"Rozmer datasetu: {data.shape}")
print(f"\nChýbajúce hodnoty:")
print(data.isnull().sum())
print(f"\nRozdelenie cieľovej premennej:")
print(data["odchod"].value_counts(normalize=True).round(3))

Krok 2: Rozdelenie dát

Dáta rozdelíme ešte pred akýmkoľvek preprocessingom. Toto je naozaj kritický bod — a chyba, ktorú vidím stále dokola. Ak by sme škálovali alebo imputovali chýbajúce hodnoty pred rozdelením, unikli by informácie z testovacích dát do tréningového procesu (tzv. data leakage). A to nechcete.

# Oddelenie feature od cieľovej premennej
X = data.drop("odchod", axis=1)
y = data["odchod"]

# Rozdelenie na tréningové a testovacie dáta
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

print(f"Tréningová množina: {X_train.shape}")
print(f"Testovacia množina: {X_test.shape}")
print(f"\nRozdelenie v tréningovej množine:")
print(y_train.value_counts(normalize=True).round(3))

Krok 3: Definícia preprocessing pipeline

V scikit-learn budujeme preprocessing ako modulárny pipeline pomocou ColumnTransformer. Numerické a kategorické stĺpce sa spracúvajú rozdielne, ale v rámci jedného konzistentného procesu. Je to elegantné riešenie, ktoré vám ušetrí kopec problémov neskôr.

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

# Identifikácia typov stĺpcov
numericke_stlpce = ["vek", "mesacny_poplatok", "pocet_mesiacov",
                     "pocet_sluzieb", "celkovy_utrata"]
kategoricke_stlpce = ["typ_zmluvy", "sposob_platby", "internet_sluzba",
                       "technicka_podpora", "partner"]

# Pipeline pre numerické stĺpce
numericka_transformacia = Pipeline(steps=[
    ("imputacia", SimpleImputer(strategy="median")),
    ("skalovanie", StandardScaler())
])

# Pipeline pre kategorické stĺpce
kategoricka_transformacia = Pipeline(steps=[
    ("imputacia", SimpleImputer(strategy="most_frequent")),
    ("encoding", OneHotEncoder(drop="first", sparse_output=False,
                                handle_unknown="infrequent_if_exist"))
])

# Kombinovaný preprocessing
preprocessor = ColumnTransformer(transformers=[
    ("num", numericka_transformacia, numericke_stlpce),
    ("cat", kategoricka_transformacia, kategoricke_stlpce)
])

print("Preprocessing pipeline pripravený:")
print(preprocessor)

Všimnite si niekoľko dôležitých detailov:

  • SimpleImputer nahrádza chýbajúce numerické hodnoty mediánom (robustnejší voči outlierom než priemer) a kategorické najčastejšou hodnotou
  • StandardScaler normalizuje numerické feature na nulový priemer a jednotkovú varianciu
  • OneHotEncoder s drop="first" zabráni problému multikolinearity a parameter handle_unknown="infrequent_if_exist" ošetrí neznáme kategórie pri predikcii

Krok 4: Kompletný ML pipeline s modelom

A teraz prichádza tá najlepšia časť — spojíme preprocessing s klasifikátorom do jedného pipeline:

from sklearn.ensemble import GradientBoostingClassifier

# Kompletný pipeline: preprocessing + model
pipeline = Pipeline(steps=[
    ("preprocessing", preprocessor),
    ("klasifikator", GradientBoostingClassifier(
        n_estimators=200,
        learning_rate=0.1,
        max_depth=5,
        subsample=0.8,
        random_state=42
    ))
])

# Tréning celého pipeline
pipeline.fit(X_train, y_train)

# Vyhodnotenie na testovacích dátach
presnost = pipeline.score(X_test, y_test)
print(f"Presnosť na testovacích dátach: {presnost:.4f}")

Krása pipeline prístupu je v tom, že celý proces — od imputácie chýbajúcich hodnôt cez škálovanie až po predikciu — je zapuzdrený v jednom objekte. To zjednodušuje nielen tréning, ale aj nasadenie modelu v produkcii. Žiadne "zabudol som zavolať scaler.transform()" v produkcii o tretej ráno.

Krok 5: Ladenie hyperparametrov

Na ladenie hyperparametrov použijeme GridSearchCV. Vďaka pipeline syntaxi môžeme ladiť parametre akejkoľvek časti pipeline — aj preprocessingu, aj modelu:

from sklearn.model_selection import GridSearchCV

# Mriežka parametrov — prefix udáva krok v pipeline
param_grid = {
    "preprocessing__num__skalovanie__with_mean": [True, False],
    "klasifikator__n_estimators": [100, 200, 300],
    "klasifikator__max_depth": [3, 5, 7],
    "klasifikator__learning_rate": [0.05, 0.1, 0.2],
    "klasifikator__subsample": [0.8, 1.0]
}

# Grid Search s cross-validáciou
grid_search = GridSearchCV(
    pipeline,
    param_grid=param_grid,
    cv=5,
    scoring="f1",
    n_jobs=-1,
    verbose=1
)

grid_search.fit(X_train, y_train)

print(f"\nNajlepšie parametre:")
for param, value in grid_search.best_params_.items():
    print(f"  {param}: {value}")
print(f"\nNajlepšie F1 skóre (CV): {grid_search.best_score_:.4f}")
print(f"F1 skóre na teste: {grid_search.score(X_test, y_test):.4f}")

Krok 6: Komplexné vyhodnotenie modelu

Samotná presnosť nestačí — obzvlášť pri nevyvážených datasetoch. A keďže predikcia churn-u je typicky nevyvážený problém, musíme ísť hlbšie:

from sklearn.metrics import (
    classification_report, confusion_matrix,
    roc_auc_score, roc_curve, precision_recall_curve,
    average_precision_score
)
import matplotlib.pyplot as plt

# Predikcie
y_pred = grid_search.predict(X_test)
y_proba = grid_search.predict_proba(X_test)[:, 1]

# Klasifikačný report
print("Klasifikačný report:")
print(classification_report(y_test, y_pred,
                            target_names=["Zostáva", "Odchádza"]))

# Matica zámen
cm = confusion_matrix(y_test, y_pred)
print(f"\nMatica zámen:")
print(cm)

# ROC AUC
roc_auc = roc_auc_score(y_test, y_proba)
print(f"\nROC AUC: {roc_auc:.4f}")

# Average Precision
ap = average_precision_score(y_test, y_proba)
print(f"Average Precision: {ap:.4f}")

# Vizualizácia ROC krivky
fpr, tpr, _ = roc_curve(y_test, y_proba)
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.plot(fpr, tpr, color="darkorange", lw=2,
         label=f"ROC krivka (AUC = {roc_auc:.3f})")
plt.plot([0, 1], [0, 1], color="navy", lw=1, linestyle="--")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC krivka")
plt.legend()

# Precision-Recall krivka
precision, recall, _ = precision_recall_curve(y_test, y_proba)
plt.subplot(1, 2, 2)
plt.plot(recall, precision, color="green", lw=2,
         label=f"PR krivka (AP = {ap:.3f})")
plt.xlabel("Recall")
plt.ylabel("Precision")
plt.title("Precision-Recall krivka")
plt.legend()

plt.tight_layout()
plt.show()

Pokročilé techniky: Feature Engineering v pipeline

Jednou z najsilnejších stránok scikit-learn pipeline je možnosť integrovať vlastné transformácie. A tu sa to začína naozaj zaujímavé. Ukážeme si, ako vytvoriť custom transformátor pomocou FunctionTransformer a vlastnej triedy.

FunctionTransformer pre jednoduché transformácie

from sklearn.preprocessing import FunctionTransformer

# Logaritmická transformácia pre zošikmené distribúcie
log_transformer = FunctionTransformer(
    func=np.log1p,
    inverse_func=np.expm1
)

# Binning vekovej skupiny
def vytvor_vekove_skupiny(X):
    """Rozdelí vek do skupín."""
    X = X.copy()
    X["vekova_skupina"] = pd.cut(
        X["vek"],
        bins=[0, 25, 35, 50, 100],
        labels=["mlady", "stredny", "starsi", "senior"]
    )
    return X

Vlastný transformátor — BaseEstimator a TransformerMixin

Pre komplexnejšie transformácie si vytvoríme vlastnú triedu. Je to o niečo viac kódu, ale výsledok stojí za to:

from sklearn.base import BaseEstimator, TransformerMixin

class PomeroveFeature(BaseEstimator, TransformerMixin):
    """Vytvorí pomerové feature z existujúcich stĺpcov."""

    def __init__(self, citatel, menovatel, nazov_vystupu):
        self.citatel = citatel
        self.menovatel = menovatel
        self.nazov_vystupu = nazov_vystupu

    def fit(self, X, y=None):
        return self

    def transform(self, X):
        X = X.copy()
        X[self.nazov_vystupu] = X[self.citatel] / (X[self.menovatel] + 1e-8)
        return X

class InterakcneFeature(BaseEstimator, TransformerMixin):
    """Vytvorí interakčné feature medzi numerickými a kategorickými stĺpcami."""

    def __init__(self, num_col, cat_col):
        self.num_col = num_col
        self.cat_col = cat_col

    def fit(self, X, y=None):
        self.priemery_ = X.groupby(self.cat_col)[self.num_col].mean()
        return self

    def transform(self, X):
        X = X.copy()
        nazov = f"{self.num_col}_vs_{self.cat_col}_priemer"
        priemer_map = X[self.cat_col].map(self.priemery_)
        X[nazov] = X[self.num_col] - priemer_map.fillna(0)
        return X

# Použitie v pipeline
feature_pipeline = Pipeline(steps=[
    ("pomer", PomeroveFeature("celkovy_utrata", "pocet_mesiacov",
                               "priemerny_mesacny_utrata")),
    ("interakcia", InterakcneFeature("mesacny_poplatok", "typ_zmluvy"))
])

Porovnanie modelov — aký algoritmus zvoliť?

V praxi by ste nikdy nemali stavať len na jednom modeli. Poďme porovnať niekoľko populárnych algoritmov na našom datasete a nechajme čísla hovoriť:

from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import (
    RandomForestClassifier, GradientBoostingClassifier,
    HistGradientBoostingClassifier
)
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score

# Slovník modelov
modely = {
    "Logistická regresia": LogisticRegression(max_iter=1000, random_state=42),
    "Random Forest": RandomForestClassifier(n_estimators=200, random_state=42),
    "Gradient Boosting": GradientBoostingClassifier(
        n_estimators=200, random_state=42
    ),
    "HistGradient Boosting": HistGradientBoostingClassifier(
        max_iter=200, random_state=42
    ),
    "SVM": SVC(kernel="rbf", probability=True, random_state=42),
    "KNN": KNeighborsClassifier(n_neighbors=5)
}

vysledky = {}

for nazov, model in modely.items():
    # Vytvorenie pipeline pre každý model
    pipe = Pipeline(steps=[
        ("preprocessing", preprocessor),
        ("model", model)
    ])

    # Cross-validácia
    skore = cross_val_score(pipe, X_train, y_train, cv=5, scoring="f1")
    vysledky[nazov] = skore
    print(f"{nazov:25s} | F1: {skore.mean():.4f} (+/- {skore.std():.4f})")

# Vizualizácia porovnania
fig, ax = plt.subplots(figsize=(10, 6))
box_data = [vysledky[m] for m in vysledky]
bp = ax.boxplot(box_data, labels=list(vysledky.keys()), patch_artist=True)

for patch, color in zip(bp["boxes"],
    ["#3498db", "#2ecc71", "#e74c3c", "#f39c12", "#9b59b6", "#1abc9c"]):
    patch.set_facecolor(color)
    patch.set_alpha(0.7)

ax.set_ylabel("F1 skóre")
ax.set_title("Porovnanie modelov — cross-validácia")
plt.xticks(rotation=45, ha="right")
plt.tight_layout()
plt.show()

HistGradientBoosting — rýchlosť bez kompromisov

Špeciálnu pozornosť si zaslúži HistGradientBoostingClassifier. Na rozdiel od klasického Gradient Boosting, používa histogramový prístup inšpirovaný LightGBM. Je výrazne rýchlejší na veľkých datasetoch a (čo je super) natívne podporuje chýbajúce hodnoty — nepotrebujete ani imputáciu:

from sklearn.ensemble import HistGradientBoostingClassifier

# HistGradientBoosting zvláda chýbajúce hodnoty priamo
hgb = HistGradientBoostingClassifier(
    max_iter=300,
    learning_rate=0.1,
    max_depth=6,
    min_samples_leaf=20,
    l2_regularization=0.1,
    random_state=42,
    early_stopping=True,
    validation_fraction=0.1,
    n_iter_no_change=10,
    scoring="loss"
)

# Zjednodušený pipeline — len kategorický encoding
zjednoduseny_preprocessor = ColumnTransformer(transformers=[
    ("num", "passthrough", numericke_stlpce),
    ("cat", OneHotEncoder(drop="first", sparse_output=False,
                           handle_unknown="infrequent_if_exist"),
     kategoricke_stlpce)
])

pipe_hgb = Pipeline(steps=[
    ("preprocessing", zjednoduseny_preprocessor),
    ("model", hgb)
])

pipe_hgb.fit(X_train, y_train)

# Dôležitosť feature
nazvy_feature = (
    numericke_stlpce +
    pipe_hgb.named_steps["preprocessing"]
    .transformers_[1][1].get_feature_names_out(kategoricke_stlpce).tolist()
)

dolezitosti = pipe_hgb.named_steps["model"].feature_importances_
zoradene = sorted(zip(nazvy_feature, dolezitosti),
                  key=lambda x: x[1], reverse=True)

print("Top 10 najdôležitejších feature:")
for nazov, dolezitost in zoradene[:10]:
    print(f"  {nazov:35s} {dolezitost:.4f}")

SMOTE a práca s nevyváženými datasetmi

Pri predikcii zákazníckeho odchodu máme typicky nevyvážený dataset — odchádza len malé percento zákazníkov. A to je problém, pretože väčšina modelov má tendenciu jednoducho "tipovať" väčšinovú triedu. Scikit-learn v kombinácii s knižnicou imbalanced-learn na to má riešenie:

# pip install imbalanced-learn
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline

# Pipeline s SMOTE — používame imblearn Pipeline
pipeline_smote = ImbPipeline(steps=[
    ("preprocessing", preprocessor),
    ("smote", SMOTE(random_state=42, sampling_strategy=0.5)),
    ("klasifikator", GradientBoostingClassifier(
        n_estimators=200, learning_rate=0.1, max_depth=5, random_state=42
    ))
])

# Cross-validácia so SMOTE
from sklearn.model_selection import StratifiedKFold

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
skore_smote = cross_val_score(pipeline_smote, X_train, y_train,
                               cv=cv, scoring="f1")

print(f"F1 skóre so SMOTE: {skore_smote.mean():.4f} (+/- {skore_smote.std():.4f})")

Dôležitá poznámka: SMOTE sa musí aplikovať len na tréningové dáta v rámci cross-validácie, nikdy na celý dataset. Práve preto používame imblearn.pipeline.Pipeline, ktorý to zabezpečí automaticky. Toto je jedna z najčastejších chýb, ktoré v praxi vidím.

Uloženie a nasadenie modelu

Keď máme natrénovaný pipeline, chceme ho samozrejme uložiť na neskoršie použitie. Scikit-learn podporuje serializáciu cez joblib a je to naozaj jednoduché:

import joblib
from datetime import datetime

# Uloženie celého pipeline (vrátane preprocessingu)
nazov_suboru = f"churn_model_{datetime.now().strftime('%Y%m%d_%H%M%S')}.joblib"
joblib.dump(grid_search.best_estimator_, nazov_suboru)
print(f"Model uložený do: {nazov_suboru}")

# Načítanie modelu
nacitany_model = joblib.load(nazov_suboru)

# Predikcia na nových dátach
nove_data = pd.DataFrame({
    "vek": [35],
    "mesacny_poplatok": [85.50],
    "pocet_mesiacov": [24],
    "pocet_sluzieb": [4],
    "celkovy_utrata": [2052.00],
    "typ_zmluvy": ["mesacna"],
    "sposob_platby": ["elektronicky"],
    "internet_sluzba": ["optika"],
    "technicka_podpora": ["nie"],
    "partner": ["ano"]
})

predikcia = nacitany_model.predict(nove_data)
pravdepodobnost = nacitany_model.predict_proba(nove_data)

print(f"\nPredikcia: {'Odchod' if predikcia[0] == 1 else 'Zostáva'}")
print(f"Pravdepodobnosť odchodu: {pravdepodobnost[0][1]:.2%}")

Monitoring a sledovanie experimentov

V produkčnom prostredí nestačí model len nasadiť — treba ho aj sledovať. Tu je jednoduchý príklad sledovača experimentov, ktorý si môžete prispôsobiť:

import json
from datetime import datetime

class ExperimentTracker:
    """Jednoduchý sledovač ML experimentov."""

    def __init__(self, subor="experimenty.json"):
        self.subor = subor
        self.experimenty = []

    def zaznamenaj(self, nazov, parametre, metriky, poznamky=""):
        experiment = {
            "nazov": nazov,
            "cas": datetime.now().isoformat(),
            "parametre": parametre,
            "metriky": metriky,
            "poznamky": poznamky
        }
        self.experimenty.append(experiment)

        with open(self.subor, "w") as f:
            json.dump(self.experimenty, f, indent=2, ensure_ascii=False)

        print(f"Experiment '{nazov}' zaznamenaný")

    def najlepsi(self, metrika="f1"):
        return max(self.experimenty,
                   key=lambda e: e["metriky"].get(metrika, 0))

# Použitie
tracker = ExperimentTracker()

tracker.zaznamenaj(
    nazov="GradientBoosting v1",
    parametre=grid_search.best_params_,
    metriky={
        "f1": float(grid_search.best_score_),
        "roc_auc": float(roc_auc),
        "presnost": float(presnost)
    },
    poznamky="Základný model s GridSearchCV"
)

najlepsi = tracker.najlepsi("f1")
print(f"\nNajlepší experiment: {najlepsi['nazov']}")
print(f"F1 skóre: {najlepsi['metriky']['f1']:.4f}")

Praktické tipy a osvedčené postupy

Na záver si zhrnieme najdôležitejšie veci, ktoré som sa za roky práce so scikit-learn naučil. Niektoré sa zdajú samozrejmé, ale verte mi — v praxi sa na ne zabúda prekvapivo často.

1. Vždy používajte pipeline

Nikdy nerobte preprocessing a tréning modelu oddelene. Pipeline zabezpečuje:

  • Konzistenciu medzi tréningom a predikciou
  • Prevenciu data leakage
  • Jednoduché ukladanie a nasadzovanie
  • Čistý a reprodukovateľný kód

2. Správne rozdeľujte dáta

Poradie operácií je kľúčové:

  1. Rozdelenie na tréningové a testovacie dáta
  2. Preprocessing (fit len na tréningových dátach)
  3. Feature engineering
  4. Tréning modelu
  5. Vyhodnotenie na testovacích dátach

3. Voľte správnu metriku

Nie každý problém vyžaduje optimalizáciu presnosti (accuracy):

  • Accuracy — pre vyvážené datasety
  • F1 skóre — pre nevyvážené datasety (harmonický priemer precision a recall)
  • ROC AUC — pre binárnu klasifikáciu, keď nás zaujíma celková schopnosť diskriminácie
  • Precision — keď sú falošné pozitíva drahé (napr. spam filter)
  • Recall — keď sú falošné negatíva nebezpečné (napr. detekcia chorôb)

4. Využívajte novinky verzie 1.8

  • Na veľkých datasetoch aktivujte Array API dispatch pre GPU akceleráciu
  • Pre L1-regularizované modely automaticky profitujete z gap-safe screening
  • Pri multiclass problémoch vyskúšajte Temperature Scaling pre lepšiu kalibráciu
  • Na redukciu dimenzionality zvážte Classical MDS ako alternatívu k PCA

5. Monitorujte produkčné modely

Natrénovaný model nie je koniec cesty. Je to skôr začiatok novej fázy. V produkcii sledujte:

  • Data drift — zmenu distribúcie vstupných dát
  • Concept drift — zmenu vzťahu medzi feature a cieľovou premennou
  • Výkonnostné metriky — priebežné porovnanie predikcií s realitou
  • Latenciu — čas odozvy modelu pri inferencii

Záver

Scikit-learn 1.8 je podľa mňa najlepšia verzia tejto knižnice, akú sme kedy mali. S podporou GPU výpočtov, free-threaded CPythonu a dramatickým zrýchlením kľúčových algoritmov sa stáva ešte relevantnejším nástrojom pre moderné strojové učenie.

V tomto článku sme si prešli kompletný životný cyklus ML projektu — od prieskumu dát, cez budovanie preprocessing pipeline, tréning a porovnanie modelov, ladenie hyperparametrov, prácu s nevyváženými datasetmi až po uloženie a monitoring modelu v produkcii. Nie je to málo.

Kľúčové poznatky na záver:

  • Scikit-learn 1.8 prináša Array API pre GPU akceleráciu — bez zmeny API
  • Pipeline je základ každého ML projektu — používajte ho vždy
  • HistGradientBoosting je často najlepšou voľbou pre tabulárne dáta
  • Vždy rozdeľujte dáta pred preprocessingom — nie po ňom
  • Vyberte si správnu metriku podľa povahy problému
  • Sledujte drift v produkcii — model bez údržby degraduje

V ďalšom článku sa pozrieme na pokročilé techniky feature engineeringu a automatizovaného strojového učenia (AutoML) s využitím scikit-learn a moderných nástrojov ako FLAML a Auto-sklearn.

O Autorovi Editorial Team

Our team of expert writers and editors.