Manfaat Pipeline yang paling sering diremehkan bukan keringkasan kodenya, melainkan penjagaan disiplin fit terhadap data latih. Ketika evaluasi menggunakan cross-validation, yaitu skema yang memecah data menjadi beberapa lipatan dan bergantian menjadikan satu lipatan sebagai data uji, praproses yang dipasang di luar lipatan dapat menyerap informasi dari data uji tanpa disadari. Topik ini membuktikan perbedaannya dengan angka nyata, menjelaskan mekanisme kebocorannya, dan menunjukkan bahwa Pipeline sekaligus menyatukan jalur kode inferensi.
Mengapa scaler di luar lipatan menjadi bocor#
StandardScaler mempelajari dua parameter dari data yang diberikan, yaitu rata-rata dan simpangan baku per fitur. Apabila scaler di-fit pada seluruh data sebelum data dipecah menjadi lipatan, kedua parameter itu dihitung dari gabungan baris data latih dan data uji pada setiap lipatan. Baris data uji yang seharusnya tidak disentuh sampai penilaian ternyata sudah ikut menentukan bentuk transformasinya. Kondisi inilah yang disebut data leakage, yaitu masuknya informasi dari data uji ke dalam proses yang seharusnya hanya menggunakan data latih.
Akibatnya, transformasi pada setiap lipatan menjadi lebih menguntungkan daripada kenyataan. Fitur data latih telah disesuaikan menggunakan statistik yang dihitung dari data yang mencakup baris uji tersebut, sehingga model menerima masukan yang sedikit lebih bersih daripada yang akan diterimanya pada data yang benar-benar baru. Skor cross-validation pun memperoleh bias, yaitu kecondongan hasil yang sistematis, ke arah optimistis. Kesalahan ini tidak terlihat pada tampilan angkanya karena angkanya tetap tampak wajar.
Kasus StandardScaler tergolong kebocoran yang halus. Perbandingan antarmodel, pemilihan fitur berbasis seluruh data, dan penalaan hyperparameter di luar lipatan memunculkan kebocoran yang jauh lebih besar. Semuanya satu akar persoalan, yaitu komputasi yang menyentuh data uji sebelum waktunya, dan satu penawarnya, yaitu menaruh seluruh proses itu di dalam Pipeline.
Tiga jalur pada dataset sintetis#
Contoh berikut membandingkan tiga jalur pada dataset klasifikasi sintetis yang skalanya tidak seragam. Jalur a memfit scaler pada seluruh data sebelum cross-validation, jalur b menaruh scaler di dalam Pipeline, dan jalur c membiarkan model bekerja tanpa scaling sebagai pembanding. Ketiganya dievaluasi dengan lima lipatan yang sama, sehingga selisihnya murni berasal dari cara praprosenya diperlakukan.
import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
# dataset klasifikasi sintetis dengan skala antarfitur yang tidak seragam
X, y = make_classification(
n_samples=500, n_features=20, n_informative=10,
weights=[0.6, 0.4], random_state=42,
scale=np.linspace(0.1, 50.0, 20),
)
# jalur a: scaler di-fit pada SELURUH data sebelum cross-validation
X_leaky = StandardScaler().fit_transform(X)
skor_leaky = cross_val_score(
LogisticRegression(max_iter=1000), X_leaky, y, cv=5
)
# jalur b: scaler ditempatkan di dalam pipeline, di-fit ulang tiap lipatan
pipeline = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
skor_jujur = cross_val_score(pipeline, X, y, cv=5)
# jalur c: model tanpa scaling sebagai pembanding
skor_tanpa = cross_val_score(
LogisticRegression(max_iter=1000), X, y, cv=5
)
print("Skor per lipatan (5 lipatan):")
print(f" a. scaling bocor : {skor_leaky.round(3)}")
print(f" b. pipeline : {skor_jujur.round(3)}")
print(f" c. tanpa scaling : {skor_tanpa.round(3)}")
print(f"Rata-rata a: {skor_leaky.mean():.4f}")
print(f"Rata-rata b: {skor_jujur.mean():.4f}")
print(f"Rata-rata c: {skor_tanpa.mean():.4f}")
print(f"Selisih a - b: {skor_leaky.mean() - skor_jujur.mean():+.4f}")Skor per lipatan (5 lipatan):
a. scaling bocor : [0.88 0.82 0.82 0.79 0.82]
b. pipeline : [0.88 0.81 0.82 0.79 0.82]
c. tanpa scaling : [0.88 0.81 0.82 0.79 0.82]
Rata-rata a: 0.8260
Rata-rata b: 0.8240
Rata-rata c: 0.8240
Selisih a - b: +0.0020Angka pada keluaran sengaja ditampilkan apa adanya, termasuk selisihnya yang kecil. Jalur a unggul 0.0020 atas jalur b. Pada granularitas akurasi, selisih itu berarti satu baris uji pada lipatan kedua berubah tebakannya karena statistik lipatan ikut masuk ke scaler. Selisih sebesar ini wajar untuk dataset 500 baris dengan metrik diskrit, karena statistik lima lipatannya memang dekat dengan statistik gabungan. Yang penting adalah arah pergeserannya, yaitu kebocoran hanya bisa memindahkan skor ke arah yang menguntungkan, tidak pernah ke arah yang lebih buruk. Pada dataset yang lebih kecil, metrik kontinu seperti ROC AUC, atau praproses yang bergantung pada label seperti seleksi fitur, selisih yang sama bisa tumbuh berkali lipat.
Jalur c yang sama dengan jalur b juga membawa pesan. Scaling tidak selalu mengubah akurasi LogisticRegression pada dataset tertentu, tetapi jalur a tetap bergeser karena transformasi bocornya, bukan karena scaling itu sendiri. Perbandingan antarjalur dengan demikian memisahkan dua pertanyaan yang sering tercampur, yaitu apakah scaling membantu dan apakah skornya boleh dipercaya.
Pipeline menjaga aturan fit di data latih#
cross_val_score tidak meminjam objek pipeline yang sudah dilatih, melainkan menggandakannya (clone) untuk setiap lipatan. Pada tiap lipatan, salinan itu menjalankan fit terhadap bagian latihnya saja, termasuk fit pada scaler yang berada di dalamnya, kemudian mentransformasi bagian ujinya menggunakan parameter bagian latih tersebut. Statistik data uji tidak pernah menyentuh transformasi di lipatannya masing-masing, dan aturan fit di data latih kini dipatuhi secara struktural.
Kata kuncinya adalah struktural. Penjagaan manual memungkinkan, yaitu memfit scaler pada bagian latih setiap lipatan dengan tangan, tetapi rutinitas itu harus ditulis dan dijaga pada setiap eksperimen. Dengan Pipeline, penjagaan itu menjadi sifat bawaan objeknya, sehingga kesalahan paling mahal, yaitu lupa memfit ulang praproses pada bagian latih, tidak lagi bergantung pada kewaspadaan penulis kodenya.
Satu jalur kode untuk latih dan inferensi#
Manfaat kedua muncul pada saat model digunakan. Karena transformasi tinggal di dalam objeknya, data baru dapat langsung diberikan kepada Pipeline dalam bentuk mentahnya, dan scaler menjalankan transform secara otomatis sebelum estimator menebak. Tidak ada langkah praproses ganda yang harus diulang di tempat lain.
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y
)
pipeline = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
pipeline.fit(X_train, y_train)
# predict menjalankan transform scaler secara otomatis
baris_baru = [[5.0, 3.5, 1.5, 0.3]]
print("Prediksi kelas:", pipeline.predict(baris_baru))
print(f"Akurasi test: {pipeline.score(X_test, y_test):.3f}")Prediksi kelas: [0]
Akurasi test: 0.921Satu objek melayani kedua situasi, yaitu evaluasi pada data berlabel dan prediksi pada data mentah. Kesatuan ini mencegah jenis kegagalan lain, yaitu praproses yang ditulis ulang secara terpisah untuk inferensi dan lama-kelamaan menyimpang dari versi latihannya. Statistik scaler yang tersimpan di dalam objek adalah satu-satunya statistik yang digunakan, sehingga apa yang diukur saat pelatihan sama persis dengan yang dijalankan saat digunakan.
Kesalahan umum#
- Memfit praproses sebelum membagi data. Variabel seperti
X_scaled = scaler.fit_transform(X)yang dibuat sebelum split atau cross-validation membawa statistik data uji ke dalam pelatihan. Perbaikannya dengan memindahkan seluruh praproses ke dalamPipelinedan membiarkancross_val_scoremengurus pemecahannya. - Menilai pipeline sebagai penambah skor.
Pipelinetidak mengubah model di ujungnya, melainkan mengubah kejujuran proses estimasinya. Perbaikannya dengan membaca skor pipeline sebagai angka yang lebih dapat dipercaya, bukan sebagai angka yang lebih tinggi. - Membuat praproses inferensi secara terpisah. Fungsi transformasi ganda yang ditulis ulang untuk melayani prediksi rentan berbeda dari versi latihannya. Perbaikannya dengan menyimpan dan memuat objek
Pipelineutuh, bukan modelnya saja.
Ringkasan#
- Praproses yang di-fit pada seluruh data sebelum cross-validation menyerap statistik data uji tiap lipatan dan membuat skor condong ke arah optimistis.
cross_val_scoremenggandakanPipelineper lipatan, sehingga scaler di-fit ulang hanya pada bagian latih dan data uji ditransformasi dengan parameter bagian latih tersebut.- Selisih yang muncul pada dataset kecil bisa tipis, tetapi arah kebocorannya selalu menguntungkan dan pengaruhnya membesar pada data kecil, metrik kontinu, atau praproses yang bergantung pada label.
- Objek
Pipelineyang sama melayani evaluasi dan inferensi, sehingga praproses saat prediksi tidak mungkin menyimpang dari praproses saat latih.
Referensi#
- Common pitfalls and recommended practices — scikit-learn — Panduan resmi tentang data leakage, pipeline sebagai perbaikannya, dan pembandingan model yang jujur.
- Cross-validation: evaluating estimator performance — scikit-learn — Mekanisme lipatan pada cross-validation yang menjadi dasar pembahasan topik ini.
- sklearn.model_selection.cross_val_score — scikit-learn — Dokumentasi perilaku penggandaan estimator per lipatan dan parameter cv.
- Pipelines and composite estimators — scikit-learn — Panduan resmi yang menyarankan Pipeline untuk mencegah kebocoran data pada cross-validation.