Scikit-learn Pipeline'ları
2025 · 20 dakika okuma · scikit-learn 1.4+
Çoğu ML kodu şöyle başlar: veriyi yükle, eksik değerleri doldur, ölçeklendir, encode et, modeli eğit. Çalışır. Ama tekrar kullanılamaz, test verisine yanlışlıkla uygulanır ve GridSearchCV'ye giremiyor.
Pipeline, bu adımları tek bir nesne altında birleştirir. Bir fitçağrısıyla hepsi sırayla çalışır. Veri sızıntısını önler, kodu temizler, production'a taşımayı kolaylaştırır.
Tipik bir pipeline akışı:
Pipeline olmadan ne olur?
Klasik hata: StandardScaler'ı tüm veri üzerinde fit edip sonra train/test split yapmak. Test verisindeki istatistikler modele sızdı — sonuçlar gerçekten iyi görünür, production'da berbat çalışır.
# ❌ Yanlış — veri sızıntısı var
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # test verisi de burada!
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y)
model = LogisticRegression()
model.fit(X_train, y_train)
# Test istatistikleri scale işlemine karıştı → iyimser sonuç
# ✅ Doğru — Pipeline ile
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('model', LogisticRegression()),
])
X_train, X_test, y_train, y_test = train_test_split(X, y)
pipeline.fit(X_train, y_train) # scaler sadece X_train'e fit olur
pipeline.score(X_test, y_test) # X_test transform edilir, hiç fit olmazTemel yapı
Pipeline bir adımlar listesi alır. Her adım bir(isim, transformer) demetidir. Son adım model (estimator), öncekiler transformer olmak zorundadır.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
# Her adım: ('isim', nesne)
pipeline = Pipeline([
('scaler', StandardScaler()),
('model', LogisticRegression(max_iter=1000)),
])
# Tüm sklearn API'si geçerli
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
y_prob = pipeline.predict_proba(X_test)
score = pipeline.score(X_test, y_test)
# Ara adıma erişim
fitted_scaler = pipeline.named_steps['scaler']
print(fitted_scaler.mean_) # fit sonrası öğrenilen meanmake_pipeline — kısa yol
İsim vermek istemiyorsan make_pipeline class adını küçük harfe çevirerek otomatik atar.
from sklearn.pipeline import make_pipeline
# Uzun hali
Pipeline([('standardscaler', StandardScaler()), ('logisticregression', LogisticRegression())])
# make_pipeline kısa yolu — aynı sonuç
pipe = make_pipeline(StandardScaler(), LogisticRegression())
# Adım isimlerine erişim
pipe.named_steps.keys()
# dict_keys(['standardscaler', 'logisticregression'])ColumnTransformer — karışık veri tipleri
Gerçek veriler karışık gelir: bazı sütunlar numerik, bazıları kategorik.ColumnTransformer, farklı sütun gruplarına farklı transformer uygulamanı sağlar.
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
# Sütun grupları
numerik = ['yas', 'gelir', 'toplam_siparis']
kategorik = ['sehir', 'meslek', 'urun_kategorisi']
# Numerik pipeline
num_pipe = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler()),
])
# Kategorik pipeline
cat_pipe = Pipeline([
('imputer', SimpleImputer(strategy='most_frequent')),
('encoder', OneHotEncoder(handle_unknown='ignore', sparse_output=False)),
])
# ColumnTransformer ile birleştir
preprocessor = ColumnTransformer([
('numerik', num_pipe, numerik),
('kategorik', cat_pipe, kategorik),
])
# Tam pipeline
pipeline = Pipeline([
('preprocessor', preprocessor),
('model', LogisticRegression(max_iter=1000)),
])
handle_unknown='ignore'olmadan, test verisinde eğitimde görmediğin bir kategori değeri gelirse OneHotEncoder hata fırlatır. Production'da bu mutlaka olur — parametreyi unutma.
Pipeline oluşturucu
Hangi adımları kullanacağını seç, kod otomatik oluşsun. Kopyalayıp projena yapıştırabilirsin.
GridSearchCV entegrasyonu
Pipeline'ın en büyük gücü burada. Hyperparameter search'te hem model hem de ön-işlem adımlarının parametrelerini aynı anda arayabilirsin. Parametre isimleri adim__parametre şeklinde belirtilir.
from sklearn.model_selection import GridSearchCV
pipeline = Pipeline([
('imputer', SimpleImputer()),
('scaler', StandardScaler()),
('model', RandomForestClassifier()),
])
# 'adim__parametre' formatı
param_grid = {
'imputer__strategy': ['mean', 'median'],
'model__n_estimators': [50, 100, 200],
'model__max_depth': [None, 5, 10],
'model__min_samples_split': [2, 5],
}
grid = GridSearchCV(
pipeline,
param_grid,
cv=5,
scoring='roc_auc',
n_jobs=-1,
)
grid.fit(X_train, y_train)
print(grid.best_params_)
print(f"En iyi CV skoru: {grid.best_score_:.4f}")
print(f"Test skoru: {grid.score(X_test, y_test):.4f}")Custom Transformer yazmak
Sklearn'in sunmadığı bir dönüşüme ihtiyacın varsa — log transform, domain-specific feature engineering, özel imputation — kendi transformer'ını yazabilirsin. İki şartı sağlarsan Pipeline'a doğrudan girebilir.
from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np
class LogTransformer(BaseEstimator, TransformerMixin):
"""Pozitif sayısal sütunlara log1p uygular (0 değerlerine güvenli)."""
def __init__(self, epsilon=1e-8):
self.epsilon = epsilon
def fit(self, X, y=None):
return self # öğrenecek bir şey yok
def transform(self, X):
return np.log1p(np.maximum(X, self.epsilon))
def inverse_transform(self, X):
return np.expm1(X)
# Pipeline'a tak
pipe = Pipeline([
('log', LogTransformer()),
('scaler', StandardScaler()),
('model', Ridge()),
])
pipe.fit(X_train, y_train)# Daha karmaşık örnek: özel feature engineering
class GelirYasOran(BaseEstimator, TransformerMixin):
"""Gelir/Yaş oranı özelliği ekler."""
def __init__(self, gelir_col=0, yas_col=1):
self.gelir_col = gelir_col
self.yas_col = yas_col
def fit(self, X, y=None):
return self
def transform(self, X):
X = X.copy()
oran = X[:, self.gelir_col] / (X[:, self.yas_col] + 1)
return np.hstack([X, oran.reshape(-1, 1)])
def get_feature_names_out(self, input_features=None):
base = list(input_features or [f'x{i}' for i in range(self.n_features_in_)])
return base + ['gelir_yas_orani']Pipeline'ı kaydetmek ve yüklemek
Fit edilmiş pipeline tek bir dosyaya kaydedilebilir — scaler'ın öğrendiği istatistikler, encoder'ın kategorileri, modelin ağırlıkları hepsi birlikte.
import joblib
# Kaydet
joblib.dump(pipeline, 'model_pipeline.pkl')
# Yükle ve tahmin et — tek satır
pipeline = joblib.load('model_pipeline.pkl')
y_pred = pipeline.predict(yeni_veri) # scaler/encoder otomatik uygulanır
# Versiyon notı: joblib.load, modeli eğittiğin sklearn
# sürümüyle aynı veya uyumlu sürümde çalışmalı.
# Production'da sklearn versiyonunu requirements.txt'e yaz.set_output API — DataFrame çıktısı
Sklearn 1.2'den itibaren Pipeline çıktısını otomatik DataFrame'e dönüştürebilirsin. Debug ve feature importance için çok kullanışlı.
from sklearn import set_config
set_config(transform_output='pandas') # global ayar
pipeline = Pipeline([
('preprocessor', preprocessor),
('model', RandomForestClassifier()),
])
# Sadece transform adımlarının çıktısı
X_transformed = pipeline[:-1].transform(X_train)
print(type(X_transformed)) # <class 'pandas.core.frame.DataFrame'>
print(X_transformed.columns) # sütun isimli çıktı
# Feature importance ile birleştir
importances = pipeline['model'].feature_importances_
feat_imp = pd.Series(importances, index=X_transformed.columns)
feat_imp.sort_values(ascending=False).head(10)Sık yapılan hatalar
- Pipeline dışında fit etmek.Encoder'ı veya scaler'ı Pipeline'dan önce ayrıca fit edip pipeline'a fitted nesne vermek veri sızıntısını önlemez — fit her zaman pipeline.fit() ile olmalı.
- Test verisinde transform çağırmak.
pipeline.transform(X_test)değil,pipeline.predict(X_test)çağır. predict zaten içinde transform çalıştırır. - Sütun sırasına dikkat.ColumnTransformer sütunları eğitimde gördüğü sırayla bekler. Inference'ta DataFrame sütunları farklı sıradaysa hata alırsın.
X[numerik + kategorik]ile sıralamayı sabit tut. - sparse_output.sklearn 1.2'den itibaren OneHotEncoder'da varsayılan
sparse_output=True. StandardScaler sonrası gelen OHE içinsparse_output=Falseveyaset_output="pandas"kullan.
Pipeline, prototipten production'a en temiz geçişi sağlar. Bir sonraki adım: Pipeline'ı MLflow veya BentoMLile deploy etmek — aynı Pipeline nesnesi REST endpoint'e dönüşür.