Grid search yang menala dan menilai pada himpunan data yang sama menghasilkan angka kinerja yang condong terlalu tinggi, karena kandidat yang menang memang kandidat yang paling cocok dengan data itu. Nested cross-validation menutup celah tersebut dengan meletakkan penalaan di dalam loop evaluasi yang terpisah. Skema penalaan tanpa lapisan evaluasi terpisah itu disebut penalaan naif. Topik ini membedah sumber biasnya, mekanisme kedua loop beserta biaya pelatihannya, dan membandingkan estimasi nested dengan estimasi naif pada penalaan C regresi logistik di atas dataset breast cancer.
Sumber bias pada penalaan naif#
Atribut best_score_ pada GridSearchCV adalah skor cross-validation dari kandidat yang menang. Angka itu sah sebagai dasar pemilihan model, tetapi melenceng bila dilaporkan sebagai perkiraan kinerja pada data baru. Setiap kandidat diuji berkali-kali pada fold yang sama, dan yang dilaporkan hanya satu kandidat, yaitu yang paling beruntung menyesuaikan diri dengan keanehan fold tersebut. Kondisi ini dikenal sebagai selection bias, yaitu kecondongan hasil karena pemenang dipilih oleh data yang sama dengan data penilainya.
Besarnya kecondongan bergantung pada dua hal. Makin banyak kandidat yang dicoba, makin besar peluang satu kandidat unggul semata karena kebetulan, sehingga bias optimisme naik bersama ukuran grid. Model yang tidak stabil dan data yang sedikit juga memperbesar pengaruh kebetulan. Sebaliknya, pada grid yang kecil dan model yang stabil, estimasi naif dan estimasi nested dapat bertemu hingga nyaris tidak terbedakan, seperti yang ditemui pada praktik di bawah.
Dua loop nested cross-validation#
Mekanismenya terdiri atas dua loop yang tugasnya terpisah. Loop luar membagi data menjadi fold, dan setiap fold bergiliran menjadi data uji luar tepat satu kali. Di dalam data latih luar yang tersisa, penalaan berjalan utuh sebagai loop dalam, yaitu grid kandidat dinilai dengan cross-validation tingkat kedua. Kandidat terbaik dari loop dalam kemudian dilatih ulang pada data latih luar dan dinilai satu kali pada fold uji luar. Skor akhirnya adalah rata-rata seluruh skor fold luar, dan setiap skor itu berasal dari model yang tidak pernah menyentuh data ujinya.
Diagram berikut memetakan kedua loop tersebut beserta arah datanya.
Biaya pelatihannya berlipat karena setiap fold luar menjalankan penalaan penuh. Dengan fold luar, fold dalam, dan grid berukuran kandidat, jumlah pelatihan kira-kira . Praktik di bawah menggunakan pelatihan untuk grid berisi empat kandidat, jauh lebih banyak daripada cross-validation tunggal yang hanya memerlukan lima pelatihan. Angka itu masih ringan, tetapi melonjak cepat begitu grid diperluas, karena setiap kandidat tambahan diuji pada seluruh fold dalam di setiap fold luar.
Praktik: nested versus naif#
Skrip nested-cv.py menala C regresi logistik di atas pipeline penskalaan StandardScaler, dengan grid berisi empat nilai, yaitu 0.01, 0.1, 1, dan 10. Loop luar menggunakan StratifiedKFold lima fold dan loop dalam empat fold, keduanya dengan shuffle=True dan random_state=42. Loop luar ditulis manual agar C yang terpilih pada setiap fold ikut tercetak, lalu skrip menghitung best_score_ versi naif pada seluruh data beserta selisihnya. Skrip dijalankan dari direktori topiknya dengan python3 -B nested-cv.py.
"""Membandingkan estimasi nested cross-validation dengan estimasi penalaan naif."""
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_breast_cancer(return_X_y=True)
pipeline = make_pipeline(StandardScaler(), LogisticRegression(max_iter=5000))
param_grid = {"logisticregression__C": [0.01, 0.1, 1, 10]}
cv_luar = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cv_dalam = StratifiedKFold(n_splits=4, shuffle=True, random_state=42)
# nested CV: penalaan hanya menyentuh data latih setiap fold luar
print("Skor uji setiap fold luar beserta C yang dipilih penalaan dalam:")
skor_luar = []
for i, (idx_latih, idx_uji) in enumerate(cv_luar.split(X, y), start=1):
penala = GridSearchCV(pipeline, param_grid, cv=cv_dalam)
penala.fit(X[idx_latih], y[idx_latih])
skor = penala.score(X[idx_uji], y[idx_uji])
skor_luar.append(skor)
c_terpilih = penala.best_params_["logisticregression__C"]
print(f" fold {i}: C terpilih = {c_terpilih:<6} skor = {skor:.4f}")
rata_nested = sum(skor_luar) / len(skor_luar)
print(f"Rata-rata nested CV : {rata_nested:.4f}")
# cara naif: menala dan menilai pada data yang sama
penala_penuh = GridSearchCV(pipeline, param_grid, cv=cv_dalam)
penala_penuh.fit(X, y)
selisih = penala_penuh.best_score_ - rata_nested
print(f"best_score_ tanpa nesting: {penala_penuh.best_score_:.4f}")
print(f"Selisih (naif - nested) : {selisih:+.5f}")Skor uji setiap fold luar beserta C yang dipilih penalaan dalam:
fold 1: C terpilih = 0.1 skor = 0.9737
fold 2: C terpilih = 1 skor = 0.9474
fold 3: C terpilih = 1 skor = 0.9649
fold 4: C terpilih = 1 skor = 0.9912
fold 5: C terpilih = 0.1 skor = 0.9912
Rata-rata nested CV : 0.9737
best_score_ tanpa nesting: 0.9737
Selisih (naif - nested) : -0.00002Pembacaan pertama terletak pada kolom C terpilih. Penalaan dalam memilih 0.1 pada dua fold dan 1 pada tiga fold lainnya. Artinya, kombinasi hyperparameter terbaik bergantung pada data latihnya masing-masing, dan tidak ada satu nilai yang berlaku untuk semua pemisahan. Skor fold luarnya menyebar dari 0.9474 hingga 0.9912. Rata-ratanya sebesar 0.9737 menjadi estimasi kinerja penalaan yang jujur, karena setiap fold dinilai oleh model yang tidak pernah melihatnya.
Pembacaan kedua memperhatikan bertemunya kedua angka. Estimasi naif mencetak 0.9737 dengan selisih -0.00002 terhadap nested, praktis nol. Hasil ini wajar karena gridnya hanya empat kandidat dan regresi logistik pada fitur terskalakan tergolong stabil, sehingga keberuntungan pemilihan nyaris tidak menyisakan ruang bagi bias. Selisih yang terlihat tegas baru muncul ketika gridnya luas, modelnya goyah, atau datanya kecil, dan pada keadaan itulah kejujuran nested cross-validation paling berharga.
Kesalahan umum#
- Melaporkan
best_score_penalaan sebagai kinerja akhir tanpa nesting. Angka itu condong terlalu tinggi karena dipilih dan dinilai oleh data yang sama. Perbaikannya adalah menghitung skor nested atau menilai sekali pada data uji yang disisihkan. - Mengizinkan penalaan melihat fold uji luar, misalnya menskalakan seluruh data sebelum loop luar dijalankan. Informasi data uji ikut masuk ke transformasi dan menimbulkan kebocoran data. Seluruh preprocessing diletakkan di dalam pipeline yang ikut ditela pada loop dalam.
- Melaporkan rata-rata
best_score_loop dalam sebagai estimasi kinerja. Angka itu berasal dari data latih luar, bukan dari data uji luar, sehingga kecondongannya terhadap kandidat beruntung ikut terbawa. Estimasi kinerja diambil dari skor uji fold luar pada model terbaik loop dalam. - Menetapkan kombinasi hyperparameter final dari satu fold luar. Setiap fold luar dapat memilih kombinasi yang berbeda, sehingga pemenang satu fold belum tentu pemenang keseluruhan. Kombinasi final diperoleh dari penalaan ulang pada seluruh data latih setelah skor nested dihitung.
- Menela grid besar dengan nested cross-validation tanpa menghitung biayanya. Jumlah pelatihan melipatgandakan ukuran grid dengan kedua banyaknya fold, sehingga waktu eksekusi membengkak. Grid diperkecil, model diganti dengan yang lebih cepat, atau fold dalam diturunkan menjadi tiga hingga empat.
Ringkasan#
best_score_dari penalaan yang dinilai pada data yang sama dengan pemilihannya condong terlalu tinggi akibat selection bias.- Nested cross-validation menjalankan penalaan hanya di dalam data latih setiap fold luar, sehingga setiap skor fold berasal dari model yang tidak pernah melihat data ujinya.
- Biayanya kira-kira pelatihan dan dikelola dengan grid kecil serta model yang cepat.
- Pada praktik, estimasi nested 0.9737 bertemu dengan angka naif karena gridnya kecil dan modelnya stabil, sedangkan
Cterpilih berubah antar fold.
Referensi#
- Cross-validation: evaluating estimator performance — scikit-learn — Panduan resmi cross-validation yang memuat bagian nested cross-validation dan penjelasan biasnya.
- Nested versus non-nested cross-validation — scikit-learn
— Contoh resmi perbandingan estimasi nested dan non-nested menggunakan
GridSearchCV. - sklearn.model_selection.GridSearchCV — scikit-learn
— Dokumentasi penalaan yang dipakai sebagai loop dalam beserta atribut
best_score_.