Satu angka akurasi dari satu pemisahan data latih dan data uji adalah estimasi yang bergantung pada keberuntungan pemisahan itu. Cross-validation memperkecil ketergantungan tersebut dengan menilai model pada beberapa pemisahan sekaligus lalu merata-ratakannya. Topik ini membahas mekanisme k-fold, varian stratified yang menjaga proporsi kelas, alasan satu pemisahan tunggal dapat menipu, serta praktik lengkapnya dengan cross_val_score pada dataset breast cancer.
Mekanisme k-fold#
Cross-validation tipe k-fold membagi data menjadi bagian berukuran serupa yang disebut fold, lalu pelatihan dan pengujian diulang sebanyak kali. Pada putaran pertama, fold pertama menjadi data validasi dan sisanya data latih. Pada putaran kedua, fold kedua menjadi data validasi, dan seterusnya sampai setiap fold pernah menjadi data validasi tepat satu kali. Skor akhirnya adalah rata-rata skor seluruh putaran, , beserta sebaran skor antar fold. Setiap sampel dengan demikian dinilai tepat satu kali, dan tak satu pun sampel yang sia-sia hanya karena jatuh di sisi pemisahan yang salah.
Untuk masalah klasifikasi dengan kelas tak seimbang, pembagian polos dapat menghasilkan fold yang hampir tidak memuat kelas minoritas, sehingga skornya tidak mewakili kemampuan model pada kelas itu. StratifiedKFold mengatasi hal ini dengan menjaga proporsi kelas pada setiap fold tetap sama dengan data aslinya. Bila parameter cv pada cross_val_score diisi bilangan bulat untuk masalah klasifikasi, scikit-learn sebenarnya sudah menggunakan StratifiedKFold tanpa pengacakan. Penulisan eksplisit digunakan pada praktik di bawah agar pengacakan beserta seed-nya terkendali. Seed adalah angka yang menentukan hasil pengacakan pemisahan, sehingga nilai yang sama selalu menghasilkan pemisahan yang sama dan hasil skrip dapat direproduksi.
Nilai yang lazim adalah lima atau sepuluh. Nilai yang besar memberi data latih lebih banyak pada setiap putaran, tetapi biaya pelatihannya naik sebanyak kali lipat. Skor antar fold juga saling bergantung karena data latih antar putaran banyak bertumpuk. Nilai yang kecil lebih murah, tetapi setiap putaran melatih model pada data yang jauh lebih sedikit sehingga skornya cenderung lebih pesimistis. Pilihan lima fold menjadi titik tengah yang paling sering digunakan pada praktik.
Alasan satu pemisahan bisa menipu#
Skor dari satu pemisahan bergantung pada dua hal sekaligus, yaitu kualitas model dan komposisi data uji yang tersisa. Komposisi itu sebagian merupakan hasil kebetulan pemisahan. Pemisahan lain menghasilkan komposisi lain, dan selisih skornya bisa cukup besar untuk mengubah keputusan. Masalahnya makin serius karena perbandingan model sering berjarak tipis. Selisih akurasi 0.9649 melawan 0.9737, misalnya, dapat berasal semata dari keberuntungan pemisahan.
Keberuntungan pemisahan bekerja dalam dua arah. Pemisahan yang sial menurunkan skor model yang baik, sedangkan pemisahan yang beruntung menaikkan skor model yang sebenarnya biasa. Satu angka juga tidak membawa ukuran sebaran, sehingga pembaca tidak mendapat gambaran seberapa jauh angka itu dapat bergeser pada pemisahan berikutnya. Dua model dengan skor tunggal yang sama dapat berperilaku berbeda pada pemisahan lain, dan perbedaan itu baru terlihat setelah beberapa pemisahan dicoba.
Cross-validation menjawab kebutuhan ini dengan memberikan rata-rata dan sebaran sekaligus. Rata-rata antar fold menstabilkan penilaian karena keberuntungan satu fold dineutralkan oleh fold lainnya, sedangkan deviasi standar menyatakan lebar fluktuasinya. Dengan kedua angka itu, keunggulan satu model dapat dinyatakan sebagai keunggulan yang konsisten antar fold, bukan hasil satu pemisahan yang kebetulan berpihak padanya.
Mempraktikkan cross-validation#
Skrip berikut menjalankan cross-validation lima fold stratified dengan shuffle=True dan random_state=42, lalu mencetak kelima skor beserta rata-rata dan deviasi standarnya. Hasilnya kemudian dibandingkan dengan satu pemisahan train_test_split tunggal. Akurasi untuk beberapa seed lain juga dicetak agar lebar sebaran antar pemisahan terlihat. Skrip dijalankan pada seluruh dataset agar perbandingannya berlaku pada data yang sama, sedangkan disiplin yang benar pada proyek sungguhan dibahas setelah keluaran.
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_breast_cancer(return_X_y=True)
def model_baru():
return make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
# cross-validation 5-fold stratified pada seluruh dataset
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
skor_cv = cross_val_score(model_baru(), X, y, cv=cv)
print("Skor lima fold cross-validation:")
for i, skor in enumerate(skor_cv, start=1):
print(f" fold {i} = {skor:.4f}")
print(f"rata-rata = {skor_cv.mean():.4f}")
print(f"deviasi standar = {skor_cv.std():.4f}")
# satu pemisahan tunggal untuk dibandingkan
X_latih, X_uji, y_latih, y_uji = train_test_split(
X, y, test_size=0.2, random_state=42
)
model = model_baru().fit(X_latih, y_latih)
print(f"akurasi satu pemisahan (random_state=42) = {model.score(X_uji, y_uji):.4f}")
print("Akurasi satu pemisahan untuk beberapa seed lain:")
for seed in (0, 7, 21, 99, 123):
Xa, Xb, ya, yb = train_test_split(X, y, test_size=0.2, random_state=seed)
akurasi = model_baru().fit(Xa, ya).score(Xb, yb)
print(f" random_state={seed:>3} = {akurasi:.4f}")Skor lima fold cross-validation:
fold 1 = 0.9737
fold 2 = 0.9474
fold 3 = 0.9649
fold 4 = 0.9912
fold 5 = 0.9912
rata-rata = 0.9737
deviasi standar = 0.0166
akurasi satu pemisahan (random_state=42) = 0.9737
Akurasi satu pemisahan untuk beberapa seed lain:
random_state= 0 = 0.9649
random_state= 7 = 0.9825
random_state= 21 = 0.9912
random_state= 99 = 0.9737
random_state=123 = 0.9912Kelima fold memberi skor 0.9474 hingga 0.9912, dengan rata-rata 0.9737 dan deviasi standar 0.0166. Fold kedua menarik karena skornya 0.9474, lebih rendah daripada hasil seluruh seed pemisahan tunggal, padahal ukuran data latih dan data ujinya sama. Kejadian seperti ini memperlihatkan bahaya satu angka, yaitu pemisahan yang sial dapat menurunkan penilaian tanpa perubahan apa pun pada model. Sebaliknya, akurasi satu pemisahan dengan random_state=42 adalah 0.9737 yang kebetulan sama dengan rata-rata k-fold, sedangkan seed lain berkisar dari 0.9649 sampai 0.9912. Rentang antar seed mencapai sekitar 2.6 poin persentase. Angka tunggal tidak memberi peringatan apa pun tentang rentang itu, sedangkan k-fold menyatakan sebarannya sebagai deviasi standar.
Varian lain bernama cross_validate berfungsi seperti cross_val_score, tetapi mengembalikan beberapa metrik sekaligus beserta waktu pelatihan dan pengujian setiap fold. Fungsi ini berguna bila evaluasi memerlukan lebih dari satu metrik, misalnya akurasi yang disertai F1. Keluarannya berupa kamus, sehingga isinya biasanya diolah lebih lanjut sebelum dilaporkan.
Tiga angka pada prosedur itu memiliki peran berbeda. Skor fold digunakan untuk memilih model dan hyperparameter, rata-rata k-fold menjadi estimasi kinerja model terpilih, dan data uji yang belum tersentuh memberikan pengukuran terakhir yang paling jujur. Ketiga peran itu saling melengkapi dan tidak dipertukarkan.
Ringkasan#
- k-fold memutar peran data validasi sehingga setiap sampel dinilai tepat satu kali, lalu skornya dirata-ratakan.
StratifiedKFoldmenjaga proporsi kelas setiap fold, penting pada data dengan kelas tak seimbang.- Rata-rata dan deviasi standar antar fold memberi ukuran sebaran yang tidak dimiliki satu pemisahan.
- Cross-validation digunakan untuk memilih model, sedangkan data uji tetap terpisah sampai evaluasi akhir.
Referensi#
- Cross-validation: evaluating estimator performance — scikit-learn — Panduan resmi cross-validation beserta k-fold dan varian stratified.
- sklearn.model_selection.cross_val_score — scikit-learn — Dokumentasi fungsi penilai satu metrik antar fold.
- sklearn.model_selection.cross_validate — scikit-learn — Dokumentasi varian multi-metrik cross-validation.
- sklearn.model_selection.StratifiedKFold — scikit-learn — Dokumentasi pembagi fold penjaga proporsi kelas.