Scikit-Learn Pipeline w 2026: Inżynieria Cech i Pytania Rekrutacyjne

Kompleksowy przewodnik po Scikit-learn Pipeline i ColumnTransformer. Obejmuje inżynierię cech, zapobieganie wyciekowi danych oraz pytania zadawane na rozmowach kwalifikacyjnych z machine learning.

Scikit-learn Pipeline i inżynieria cech w Pythonie

Potoki scikit-learn przekształcają chaotyczny kod przetwarzania wstępnego w reprodukowalne, gotowe do produkcji przepływy pracy. Wraz z wersją 1.9 wydaną w czerwcu 2026, Pipeline i ColumnTransformer pozostają fundamentem każdego poważnego projektu uczenia maszynowego, teraz z monitorowaniem przez callbacki, ulepszoną wizualizacją HTML i wsparciem dla Array API.

Wskazówka rekrutacyjna

Rekruterzy często pytają: "Jak zapobiegać wyciekowi danych podczas walidacji krzyżowej?" Odpowiedzią są potoki. Dopasowywanie kroków przetwarzania wstępnego wewnątrz pętli CV zapewnia, że dane testowe nigdy nie wpływają na decyzje o skalowaniu czy kodowaniu.

Dlaczego Potoki Eliminują Wyciek Danych

Wyciek danych występuje, gdy informacje ze zbioru testowego wpływają na przetwarzanie wstępne. Częsty błąd: dopasowanie StandardScaler na całym zbiorze danych przed podziałem. Skaler uczy się średniej i wariancji z próbek testowych, sztucznie zawyżając wyniki walidacji krzyżowej.

Potoki rozwiązują ten problem, łącząc transformery i estymatory w pojedynczy obiekt, który dopasowuje wszystkie kroki razem:

python
# pipeline_basics.py
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score

# Create a pipeline that scales features and trains a classifier
pipeline = Pipeline([
    ('scaler', StandardScaler()),  # Step 1: Normalize features
    ('classifier', LogisticRegression(max_iter=1000))  # Step 2: Train model
])

# cross_val_score fits the scaler separately for each fold
# No data leakage: test fold is never seen during scaler.fit()
scores = cross_val_score(pipeline, X, y, cv=5, scoring='accuracy')
print(f"Mean accuracy: {scores.mean():.3f} (+/- {scores.std():.3f})")

Obiekt Pipeline implementuje metody fit, predict i score, co czyni go wymiennym z każdym estymatorem scikit-learn. Oznacza to, że GridSearchCV, RandomizedSearchCV i wszystkie narzędzia walidacji krzyżowej działają bez modyfikacji.

ColumnTransformer dla Mieszanych Typów Danych

Rzeczywiste zbiory danych zawierają kolumny numeryczne (wiek, wynagrodzenie) i kategoryczne (kraj, typ_produktu). ColumnTransformer stosuje różne przetwarzanie wstępne do różnych podzbiorów kolumn, a następnie łączy wyniki:

python
# column_transformer_example.py
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
import pandas as pd

# Sample dataset with mixed types
df = pd.DataFrame({
    'age': [25, 30, None, 45],
    'salary': [50000, 60000, 75000, None],
    'country': ['US', 'UK', 'DE', 'US'],
    'education': ['Bachelor', 'Master', 'PhD', 'Bachelor']
})

# Define numeric preprocessing: impute missing values, then scale
numeric_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# Define categorical preprocessing: impute with mode, then one-hot encode
categorical_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('encoder', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
])

# Combine transformers with ColumnTransformer
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, ['age', 'salary']),
        ('cat', categorical_transformer, ['country', 'education'])
    ],
    verbose_feature_names_out=True  # Prefix output names with transformer name
)

# Fit and inspect output shape
X_transformed = preprocessor.fit_transform(df)
print(f"Transformed shape: {X_transformed.shape}")
print(f"Feature names: {preprocessor.get_feature_names_out()}")

Parametr verbose_feature_names_out (rozszerzony w wersji 1.6 o obsługę stringów i funkcji callable) kontroluje nazewnictwo wyjściowych cech. Ustawienie go na True dodaje prefiks z nazwą transformera do każdej cechy, zapobiegając kolizjom nazw, gdy wiele transformerów generuje podobne nazwy kolumn.

Automatyczny Wybór Kolumn z make_column_selector

Zakodowane na sztywno nazwy kolumn psują się, gdy zbiory danych się zmieniają. make_column_selector automatycznie wybiera kolumny według typu danych:

python
# automatic_column_selection.py
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.preprocessing import StandardScaler, OneHotEncoder

# Select columns by dtype: no hardcoded column names
preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), make_column_selector(dtype_include='number')),
        ('cat', OneHotEncoder(handle_unknown='ignore'),
         make_column_selector(dtype_include='object'))
    ],
    remainder='passthrough'  # Keep unprocessed columns as-is
)

# Works with any DataFrame that has numeric and object columns
X_processed = preprocessor.fit_transform(df)

Parametr remainder kontroluje, co dzieje się z kolumnami, które nie zostały dopasowane przez żaden transformer. Opcje obejmują 'drop' (domyślnie), 'passthrough' (zachowaj bez zmian) lub transformer do zastosowania na pozostałych kolumnach.

Gotowy na rozmowy o Data Science & ML?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Inżynieria Cech Wewnątrz Potoków

Potoki wykraczają poza przetwarzanie wstępne i obejmują kroki inżynierii cech. Niestandardowe transformery dziedziczą po BaseEstimator i TransformerMixin:

python
# custom_transformer.py
from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np

class DateFeatureExtractor(BaseEstimator, TransformerMixin):
    """Extract year, month, day, and weekday from datetime columns."""
    
    def __init__(self, date_column='date'):
        self.date_column = date_column
    
    def fit(self, X, y=None):
        # No fitting required for date extraction
        return self
    
    def transform(self, X):
        X = X.copy()
        dates = pd.to_datetime(X[self.date_column])
        
        # Extract temporal features
        X['year'] = dates.dt.year
        X['month'] = dates.dt.month
        X['day'] = dates.dt.day
        X['weekday'] = dates.dt.weekday
        X['is_weekend'] = (dates.dt.weekday >= 5).astype(int)
        
        # Drop original date column
        return X.drop(columns=[self.date_column])
    
    def get_feature_names_out(self, input_features=None):
        # Required for Pipeline.get_feature_names_out() to work
        return ['year', 'month', 'day', 'weekday', 'is_weekend']

# Integrate into a full pipeline
full_pipeline = Pipeline([
    ('date_features', DateFeatureExtractor(date_column='signup_date')),
    ('preprocessor', preprocessor),
    ('model', LogisticRegression())
])

Implementacja get_feature_names_out umożliwia nowej reprezentacji HTML w scikit-learn 1.9 wyświetlanie nazw wyjściowych cech, co ułatwia debugowanie i dokumentację.

Dostrajanie Hiperparametrów Między Krokami Potoku

GridSearchCV dostraja parametry we wszystkich krokach potoku, używając konwencji nazewnictwa krok__parametr:

python
# pipeline_gridsearch.py
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

# Build pipeline with tunable components
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier(random_state=42))
])

# Define parameter grid: note the double underscore syntax
param_grid = {
    'preprocessor__num__imputer__strategy': ['mean', 'median'],
    'classifier__n_estimators': [100, 200],
    'classifier__max_depth': [10, 20, None]
}

# GridSearchCV handles all combinations automatically
grid_search = GridSearchCV(
    pipeline,
    param_grid,
    cv=5,
    scoring='f1_weighted',
    n_jobs=-1
)

grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_:.3f}")

Podwójne podkreślenie (__) umożliwia dostęp do zagnieżdżonych parametrów. Dla ColumnTransformer ścieżka zawiera nazwę transformera: preprocessor__num__imputer__strategy dociera do parametru strategy imputera wewnątrz transformera numerycznego.

Buforowanie Transformerów z Parametrem memory

Złożone przetwarzanie wstępne na dużych zbiorach danych może być powolne. Parametr memory buforuje dopasowane transformery na dysku:

python
# cached_pipeline.py
from sklearn.pipeline import Pipeline
from tempfile import mkdtemp
import joblib

# Create a cache directory
cachedir = mkdtemp()

# Pipeline caches intermediate steps
cached_pipeline = Pipeline(
    [
        ('preprocessor', preprocessor),  # Cached after first fit
        ('classifier', RandomForestClassifier())
    ],
    memory=cachedir  # Or use joblib.Memory for more control
)

# First fit: slow (computes all transformations)
cached_pipeline.fit(X_train, y_train)

# Second fit with same X_train: fast (loads cached transformers)
cached_pipeline.fit(X_train, y_train)  # Skips preprocessor fitting

Buforowanie jest szczególnie przydatne podczas wyszukiwania hiperparametrów, gdzie to samo przetwarzanie wstępne jest stosowane do wielu konfiguracji modelu. Końcowy estymator (klasyfikator lub regresor) nigdy nie jest buforowany, tylko pośrednie transformery.

Monitorowanie Treningu za Pomocą Callbacków (scikit-learn 1.9)

API callbacków w wersji 1.9 dodaje monitorowanie postępu do potoków:

python
# callback_monitoring.py
from sklearn.callback import ProgressBar, ScoringMonitor
from sklearn.linear_model import LogisticRegression

# Display progress during GridSearchCV
with ProgressBar():
    grid_search = GridSearchCV(
        pipeline,
        param_grid,
        cv=5,
        n_jobs=1  # Progress bars require single-threaded execution
    )
    grid_search.fit(X_train, y_train)

# Monitor scoring metrics per iteration
with ScoringMonitor(X_val, y_val, scoring='accuracy') as monitor:
    logreg = LogisticRegression(solver='lbfgs', max_iter=500)
    logreg.fit(X_train, y_train)
    print(f"Scores per iteration: {monitor.scores_}")

Callbacki działają z Pipeline, StandardScaler, LogisticRegression (solver LBFGS) i wszystkimi klasami search CV. Ta eksperymentalna funkcja zapewnia wgląd w długotrwałe dopasowania bez niestandardowego kodu logowania.

FeatureUnion dla Równoległej Ekstrakcji Cech

FeatureUnion uruchamia wiele transformerów równolegle i łączy ich wyniki poziomo:

python
# feature_union_example.py
from sklearn.pipeline import FeatureUnion, Pipeline
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest, f_classif

# Combine PCA and univariate selection
feature_union = FeatureUnion([
    ('pca', PCA(n_components=5)),
    ('select', SelectKBest(f_classif, k=10))
])

# Pipeline: preprocess, combine features, classify
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('features', feature_union),
    ('classifier', LogisticRegression())
])

# Output contains 5 PCA components + 10 selected features = 15 features
pipeline.fit(X_train, y_train)

Wersja 1.7.2 dodała walidację zapewniającą, że wszystkie transformery zwracają wyjścia 2D. Pozwala to wykryć błędy wcześnie, gdy niestandardowy transformer przypadkowo zwraca tablicę 1D.

Serializacja i Wdrożenie

Potoki serializują się za pomocą joblib, zachowując wszystkie dopasowane parametry:

python
# model_deployment.py
import joblib

# Save the entire pipeline (preprocessing + model)
joblib.dump(pipeline, 'churn_predictor.pkl')

# Load in production
loaded_pipeline = joblib.load('churn_predictor.pkl')

# Predict on new data (same preprocessing automatically applied)
new_data = pd.DataFrame({'age': [32], 'salary': [65000], ...})
predictions = loaded_pipeline.predict(new_data)

Serializowany plik zawiera wszystko: logikę selektora kolumn, dopasowane średnie i wariancje skalera, kategorie enkodera oraz wagi modelu. Wdrożenie wymaga tylko scikit-learn i joblib, bez niestandardowego kodu przetwarzania wstępnego.

Kompatybilność Wersji

Potoki serializowane w scikit-learn 1.8 mogą nie ładować się poprawnie w wersji 1.9, jeśli używają przestarzałych parametrów. Zawsze należy przypinać wersję scikit-learn w produkcji i testować aktualizacje przed wdrożeniem.

Pytania Rekrutacyjne o Scikit-Learn Pipeline

P: Czym jest wyciek danych i jak potoki mu zapobiegają?

Wyciek danych występuje, gdy informacje ze zbioru walidacyjnego lub testowego wpływają na trening modelu. Klasyczny przykład: dopasowanie StandardScaler na całym zbiorze danych przed podziałem. Średnia i odchylenie standardowe skalera zawierają próbki testowe, sztucznie zawyżając wyniki walidacji krzyżowej.

Potoki zapobiegają temu, enkapsulując przetwarzanie wstępne wewnątrz pętli walidacji krzyżowej. Gdy cross_val_score wywołuje pipeline.fit(), każdy fold dopasowuje skaler tylko na danych treningowych. Statystyki foldu testowego nigdy nie wyciekają do przetwarzania wstępnego.

P: Jak uzyskać dostęp i modyfikować parametry zagnieżdżonych kroków potoku?

Należy użyć składni z podwójnym podkreśleniem: pipeline.set_params(classifier__n_estimators=200). Dla ColumnTransformer należy uwzględnić nazwę transformera: pipeline.set_params(preprocessor__num__scaler__with_mean=False). Metoda get_params() zwraca wszystkie zagnieżdżone parametry jako płaski słownik, przydatny do inspekcji i logowania.

P: Kiedy używać FeatureUnion, a kiedy ColumnTransformer?

ColumnTransformer stosuje różne transformery do różnych kolumn tego samego wejścia. FeatureUnion stosuje różne transformery do całego wejścia i łączy je poziomo. ColumnTransformer należy używać do heterogenicznego przetwarzania wstępnego kolumn (numeryczne vs kategoryczne). FeatureUnion służy do augmentacji cech, gdzie wiele metod ekstrakcji (PCA, cechy wielomianowe, ekstraktory domenowe) produkuje komplementarne cechy z tych samych danych.

P: Jak debugować niedziałający potok?

Należy ustawić verbose=True na potoku, aby zobaczyć czasy kroków. Można uzyskać dostęp do wyników pośrednich za pomocą pipeline.named_steps['nazwa_kroku'].transform(X) dla dopasowanego potoku. W scikit-learn 1.9 reprezentacja HTML wyświetla dopasowane atrybuty i nazwy wyjściowych cech, upraszczając debugowanie w notatnikach Jupyter. Dla głębszej inspekcji można użyć pipeline[:-1].fit_transform(X, y), aby uzyskać wynik tuż przed końcowym estymatorem.

Lista Kontrolna Produkcyjna dla Potoków ML

  • Przypinanie wersji scikit-learn w requirements, aby uniknąć problemów z serializacją
  • Używanie make_column_selector zamiast zakodowanych nazw kolumn, gdy zbiory danych mogą się zmieniać
  • Ustawianie handle_unknown='ignore' na OneHotEncoder, aby obsługiwać nowe kategorie w produkcji
  • Buforowanie kosztownego przetwarzania wstępnego parametrem memory podczas rozwoju
  • Walidowanie kształtu wyjściowego potoku i nazw cech w testach przed wdrożeniem
  • Przechowywanie get_feature_names_out() wraz z modelem do analizy ważności cech
  • Używanie Pipeline wewnątrz GridSearchCV, nigdy odwrotnie, aby zapewnić prawidłowe zachowanie CV

Zacznij ćwiczyć!

Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.

Wyzwanie dnia

Znajdziesz błąd w Data Science & ML?

Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Anthony Fillion-Maillet

Autor:

Anthony Fillion-Maillet

Założyciel SharpSkill

Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.

Zaktualizowano 2 września 2026

Tagi

#scikit-learn
#machine-learning
#python
#pipeline
#feature-engineering

Udostępnij

Powiązane artykuły