Validasi silang k-fold bertingkat menggunakan python

Apa itu pengambilan sampel bertingkat?

Dalam sampel terstratifikasi, peneliti membagi populasi menjadi subpopulasi homogen yang disebut strata berdasarkan karakteristik tertentu (misalnya ras, identitas gender, lokasi, dll.). Setiap anggota populasi yang diteliti harus berada tepat pada satu strata.

Peneliti mengandalkan pengambilan sampel bertingkat ketika karakteristik suatu populasi beragam dan mereka ingin memastikan bahwa setiap karakteristik terwakili dengan baik dalam sampel.

Validasi silang k-fold bertingkat

Validasi silang k-fold bertingkat adalah metode untuk mengevaluasi performa model pembelajaran mesin. Ini adalah variasi dari validasi silang k-fold, yaitu teknik untuk menilai keakuratan model dengan membagi kumpulan data menjadi k subset (atau “lipatan”) dan menggunakan salah satu lipatan sebagai set validasi sedangkan k yang tersisa -1 lipatan digunakan sebagai set pelatihan. Proses ini diulang sebanyak k kali sehingga setiap lipatan digunakan sebagai set validasi tepat satu kali.

Dalam validasi silang k-fold bertingkat, lipatan dibuat sedemikian rupa sehingga setiap lipatan merupakan sampel yang mewakili kumpulan data. Secara khusus, data dibagi menjadi k lipatan sehingga setiap lipatan berisi proporsi sampel yang kira-kira sama dari setiap kelas dengan kumpulan data asli. Hal ini penting ketika berhadapan dengan kumpulan data yang tidak seimbang, dimana jumlah sampel di setiap kelas tidak sama.

Keuntungan utama validasi silang k-fold bertingkat adalah memberikan estimasi performa model yang lebih akurat pada data yang tidak terlihat, terutama saat menangani kumpulan data yang tidak seimbang. Hal ini karena hal ini memastikan bahwa setiap lipatan mewakili keseluruhan kumpulan data, dan setiap kelas terwakili secara merata dalam kumpulan pelatihan dan validasi.

Validasi silang k-fold bertingkat adalah teknik umum dalam pembelajaran mesin dan banyak digunakan dalam berbagai aplikasi, termasuk klasifikasi dan regresi. Ini sering digunakan untuk menyesuaikan hyperparameter suatu model atau untuk membandingkan performa berbagai model pada kumpulan data tertentu.

Beberapa tes

Definisi dataset, untuk sklearn ini menyediakan fungsi menarik bernama “make_classification” yang dapat digunakan untuk memperoleh suatu dataset.

Secara khusus dataset yang digunakan terbuat dari 100 sampel dengan 10 fitur, satu label yang menggambarkan 4 kelas tipe titik, bobot tiap kelas dalam dataset sebenarnya tidak sama.

from sklearn.datasets import make_classification
from sklearn.model_selection import KFold, StratifiedKFold
import numpy as np

X,y = make_classification(n_samples=100, n_features=10, n_informative=4, n_classes=4,weights=[.2,.4,.1,.4])

import pandas as pd

dataset = pd.DataFrame(X)
dataset = dataset.add_prefix('f')
dataset['y'] = y
dataset

Perhatikan bahwa karena bobot relatif kelas-kelas berbeda dalam kumpulan data, kita tidak akan menemukan poin yang sama untuk masing-masing kelas, melainkan kita akan menemukan, misalnya, lebih banyak poin dari kelas “1” dan sedikit dari kelas “2.

dataset['y'].value_counts()
1    40
3    29
0    21
2    10

lipat

Kita mulai dengan menggunakan Kfold klasik yang tidak menggunakan opsi bertingkat dan melihat bagaimana kelas didistribusikan dengan membandingkan lipatan kereta dan lipatan tes.

kfold = KFold(n_splits=3,random_state=11,shuffle=True)
splits = kfold.split(dataset, dataset['y']) # each split has a train indexes and test indexes pair
print(f'PROPORTION OF TARGET IN THE ORIGINAL DATA\n{dataset["y"].value_counts() / len(dataset)}\n\n')

ll = []
for n,(train_index,test_index) in enumerate(splits):
    print(f'SPLIT NO {n+1}\nTRAINING SET SIZE: {np.round(len(train_index) / (len(train_index)+len(test_index)),2)}'+
          f'\tTEST SET SIZE: {np.round(len(test_index) / (len(train_index)+len(test_index)),2)}\nPROPORTION OF TARGET IN THE TRAINING SET\n'+
          f'{dataset.iloc[train_index,10].value_counts() / len(dataset.iloc[train_index,10])}\nPROPORTION OF TARGET IN THE TEST SET\n'+
          f'{dataset.iloc[test_index,10].value_counts() / len(dataset.iloc[test_index,10])}\n\n')
    ll.append({'SPLIT NO {}'.format(n+1): {
        'train':dataset.iloc[train_index,10].value_counts() / len(dataset.iloc[train_index,10]),
        'test': dataset.iloc[test_index,10].value_counts() / len(dataset.iloc[test_index,10])
              }})

Kita dapat langsung melihat bagaimana distribusi kelas dalam pelatihan dan pengujian berubah, tetapi kita dapat melihatnya lebih baik dengan menggabungkan hasilnya ke dalam kerangka data.

PROPORTION OF TARGET IN THE ORIGINAL DATA
1    0.4
3    0.3
0    0.2
2    0.1
Name: y, dtype: float64


SPLIT NO 1
TRAINING SET SIZE: 0.66	TEST SET SIZE: 0.34
PROPORTION OF TARGET IN THE TRAINING SET
1    0.439394
3    0.272727
0    0.181818
2    0.106061
Name: y, dtype: float64
PROPORTION OF TARGET IN THE TEST SET
3    0.352941
1    0.323529
0    0.235294
2    0.088235
Name: y, dtype: float64


SPLIT NO 2
TRAINING SET SIZE: 0.67	TEST SET SIZE: 0.33
PROPORTION OF TARGET IN THE TRAINING SET
1    0.343284
3    0.328358
0    0.238806
2    0.089552
Name: y, dtype: float64
PROPORTION OF TARGET IN THE TEST SET
1    0.515152
3    0.242424
2    0.121212
0    0.121212
Name: y, dtype: float64


SPLIT NO 3
TRAINING SET SIZE: 0.67	TEST SET SIZE: 0.33
PROPORTION OF TARGET IN THE TRAINING SET
1    0.417910
3    0.298507
0    0.179104
2    0.104478
Name: y, dtype: float64
PROPORTION OF TARGET IN THE TEST SET
1    0.363636
3    0.303030
0    0.242424
2    0.090909
Name: y, dtype: float64
import pandas as pd

# extract data from the list
data = []
for d in ll:
    for key, value in d.items():
        split_num = key.split()[-1]
        train_data = value['train']
        test_data = value['test']
        data.append([split_num, train_data, test_data])

# create a DataFrame for the train data
train_df = pd.DataFrame({d[0]: d[1] for d in data}, index=data[0][1].index)
train_df = train_df.add_prefix('split_')
train_df = train_df.set_index('class_'+ train_df.index.astype(str))

# create a DataFrame for the test data
test_df = pd.DataFrame({d[0]: d[2] for d in data}, index=data[0][2].index)
test_df = test_df.add_prefix('split_')
test_df = test_df.set_index('class_'+ test_df.index.astype(str))

# display the DataFrames
train_df.merge(test_df, left_index=True, right_index=True,
          suffixes=('_train', '_test'))

Faktanya dari dataframe pengaruhnya sangat jelas. Sekarang mari kita coba merepresentasikannya menggunakan grafik batang yang jelas hanya akan melacak satu kelas dan menunjukkan perbedaan antara komposisinya dalam pelatihan dan pengujian di berbagai pemisahan.

import plotly.graph_objects as go

# create the bar trace
bar_trace = go.Bar(x=train_df.columns, y=train_df.iloc[0])

# create the bar trace
bar_trace1 = go.Bar(x=test_df.columns, y=test_df.iloc[0])


# create the layout
layout = go.Layout(title='Train vs Test target composition', xaxis_title='Split', yaxis_title='%')

# create the figure
fig = go.Figure(data=[bar_trace, bar_trace1], layout=layout)

# display the figure
fig.show()

Efeknya sangat jelas dan tidak menyenangkan jika kita mencoba memvalidasi model kita karena hal ini akan membuat validasi tidak memenuhi syarat. Tentunya dalam contoh yang saya berikan, saya hanya menunjukkan satu kelas tetapi semuanya dapat dilakukan ulang dengan kelas lainnya.

Lipatan bertingkat

Ulangi prosedur yang sama dengan menggunakan pendekatan bertingkat, pada dasarnya satu-satunya baris yang harus diubah adalah baris yang dilaporkan di bawah ini.

kfold = StratifiedKFold(n_splits=3,shuffle=True,random_state=11)
PROPORTION OF TARGET IN THE ORIGINAL DATA
1    0.4
3    0.3
0    0.2
2    0.1
Name: y, dtype: float64


SPLIT NO 1
TRAINING SET SIZE: 0.66	TEST SET SIZE: 0.34
PROPORTION OF TARGET IN THE TRAINING SET
1    0.393939
3    0.303030
0    0.196970
2    0.106061
Name: y, dtype: float64
PROPORTION OF TARGET IN THE TEST SET
1    0.411765
3    0.294118
0    0.205882
2    0.088235
Name: y, dtype: float64


SPLIT NO 2
TRAINING SET SIZE: 0.67	TEST SET SIZE: 0.33
PROPORTION OF TARGET IN THE TRAINING SET
1    0.402985
3    0.298507
0    0.208955
2    0.089552
Name: y, dtype: float64
PROPORTION OF TARGET IN THE TEST SET
1    0.393939
3    0.303030
0    0.181818
2    0.121212
Name: y, dtype: float64


SPLIT NO 3
TRAINING SET SIZE: 0.67	TEST SET SIZE: 0.33
PROPORTION OF TARGET IN THE TRAINING SET
1    0.402985
3    0.298507
0    0.194030
2    0.104478
Name: y, dtype: float64
PROPORTION OF TARGET IN THE TEST SET
1    0.393939
3    0.303030
0    0.212121
2    0.090909
Name: y, dtype: float64

Di sini kita melihat dengan jelas perbedaannya dan bagaimana sekarang berbagai rangkaian pelatihan dan pengujian jauh lebih konsisten satu sama lain.

Efeknya seperti sebelumnya dapat ditampilkan pada grafik batang. Anda dapat langsung melihatnya dengan melakukan penghitungan menggunakan grafik sebelumnya tentang bagaimana semua batang sekarang berada pada ketinggian yang sama, yang jelas akan mengevaluasi pemisahan individu.

Kesimpulan

Dari hasil tersebut terlihat jelas bahwa penggunaan pendekatan stratifikasi secara intrinsik lebih tepat jika kita ingin memvalidasi suatu model. Maksud saya, jika model dilatih pada datum train tertentu maka diharapkan dapat divalidasi pada datum validasi yang sedekat mungkin dengan datum train. Dengan menggunakan pendekatan standar, hal ini tidak mungkin dilakukan, jadi penting untuk mengetahui pendekatan validasi ini juga karena mungkin berguna.