การตรวจสอบความถูกต้องข้ามแบบแบ่งชั้น k-fold โดยใช้ python

การสุ่มตัวอย่างแบบแบ่งชั้นคืออะไร?

ในตัวอย่างแบบแบ่งชั้น นักวิจัยแบ่งประชากรออกเป็นประชากรย่อยที่เป็นเนื้อเดียวกันที่เรียกว่า ชั้น ตามลักษณะเฉพาะ (เช่น เชื้อชาติ อัตลักษณ์ทางเพศ สถานที่ ฯลฯ) สมาชิกทุกคนของประชากรที่ศึกษาควรอยู่ในชั้นเดียว

นักวิจัยอาศัยการสุ่มตัวอย่างแบบแบ่งชั้นเมื่อลักษณะของประชากรมีความหลากหลาย และพวกเขาต้องการให้แน่ใจว่าทุกคุณลักษณะจะแสดงอย่างถูกต้องในกลุ่มตัวอย่าง

การตรวจสอบความถูกต้องข้ามแบบแบ่งชั้น k-fold

การตรวจสอบความถูกต้องข้ามแบบแบ่งชั้น k-fold เป็นวิธีการประเมินประสิทธิภาพของโมเดลแมชชีนเลิร์นนิง เป็นรูปแบบหนึ่งของการตรวจสอบความถูกต้องแบบข้าม k-fold ซึ่งเป็นเทคนิคในการประเมินความแม่นยำของแบบจำลองโดยการแบ่งชุดข้อมูลออกเป็นชุดย่อย k (หรือ "folds") และใช้หนึ่งในพับเป็นชุดการตรวจสอบในขณะที่ k ที่เหลือ -ใช้พับ 1 ทบเป็นชุดฝึกซ้อม กระบวนการนี้ทำซ้ำ k ครั้ง เพื่อให้แต่ละพับถูกใช้เป็นชุดการตรวจสอบความถูกต้องหนึ่งครั้ง

ในการตรวจสอบความถูกต้องข้ามแบบแบ่งชั้น k-fold การพับจะถูกสร้างขึ้นในลักษณะที่แต่ละการพับเป็นตัวอย่างที่เป็นตัวแทนของชุดข้อมูล โดยเฉพาะอย่างยิ่ง ข้อมูลจะถูกแบ่งออกเป็น k เท่า โดยแต่ละส่วนจะมีสัดส่วนตัวอย่างจากแต่ละคลาสโดยประมาณเท่ากับชุดข้อมูลดั้งเดิม นี่เป็นสิ่งสำคัญเมื่อต้องรับมือกับชุดข้อมูลที่ไม่สมดุล ซึ่งจำนวนตัวอย่างในแต่ละคลาสไม่เท่ากัน

ข้อได้เปรียบหลักของการตรวจสอบความถูกต้องข้ามแบบแบ่งชั้น k-fold คือ ให้การประมาณประสิทธิภาพของแบบจำลองกับข้อมูลที่มองไม่เห็นได้แม่นยำยิ่งขึ้น โดยเฉพาะอย่างยิ่งเมื่อต้องรับมือกับชุดข้อมูลที่ไม่สมดุล เนื่องจากช่วยให้แน่ใจว่าแต่ละพับเป็นตัวแทนของชุดข้อมูลทั้งหมด และแต่ละคลาสจะถูกนำเสนออย่างเท่าเทียมกันในชุดการฝึกอบรมและการตรวจสอบความถูกต้อง

การตรวจสอบความถูกต้องข้ามแบบแบ่งชั้น k-fold เป็นเทคนิคทั่วไปในแมชชีนเลิร์นนิงและมีการใช้กันอย่างแพร่หลายในการใช้งานที่หลากหลาย รวมถึงการจำแนกประเภทและการถดถอย มักใช้เพื่อปรับแต่งไฮเปอร์พารามิเตอร์ของโมเดลหรือเพื่อเปรียบเทียบประสิทธิภาพของโมเดลต่างๆ บนชุดข้อมูลที่กำหนด

การทดสอบบางอย่าง

คำจำกัดความของชุดข้อมูล สำหรับ sklearn นี้มีฟังก์ชันที่น่าสนใจที่เรียกว่า "make_classification" ซึ่งสามารถใช้เพื่อรับชุดข้อมูลได้

โดยเฉพาะชุดข้อมูลที่ใช้ประกอบด้วยตัวอย่าง 100 ตัวอย่าง มีคุณสมบัติ 10 ประการ หนึ่งป้ายกำกับที่อธิบายประเภทจุดคลาส 4 รายการ น้ำหนักของแต่ละคลาสในชุดข้อมูลนั้นไม่เท่ากันจริงๆ

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

โปรดทราบว่าเนื่องจากชุดข้อมูลมีน้ำหนักสัมพัทธ์ของคลาสที่แตกต่างกัน จึงไม่พบคะแนนที่เท่ากันของแต่ละคลาส แต่จะพบคะแนนเพิ่มเติมจากคลาส “1” และอีกสองสามคะแนนจากคลาส “2”

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

Kfold

เราเริ่มต้นด้วยการใช้ Kfold แบบคลาสสิกที่ไม่ได้ใช้ตัวเลือก stratified และดูว่าคลาสมีการกระจายอย่างไรโดยการเปรียบเทียบ train fold และ test fold

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])
              }})

เราจะเห็นได้ทันทีว่าการกระจายคลาสใน Train และ Test เปลี่ยนแปลงไปอย่างไร แต่เราสามารถเห็นสิ่งนี้ได้ดีขึ้นด้วยการรวมผลลัพธ์ไว้ใน Dataframe

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'))

ในความเป็นจริงจาก dataframe เอฟเฟกต์นั้นชัดเจนมาก ตอนนี้ให้เราลองแสดงสิ่งนี้โดยใช้กราฟแท่งที่จะติดตามคลาสเดียวอย่างชัดเจนและแสดงความแตกต่างระหว่างองค์ประกอบของคลาสใน Train และในการทดสอบในการแยกต่างๆ

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()

ผลลัพธ์นั้นชัดเจนมากและไม่น่าพอใจหากเราพยายามตรวจสอบโมเดลของเรา เนื่องจากจะทำให้การตรวจสอบไม่มีสิทธิ์ แน่นอนในตัวอย่างที่ฉันให้ไว้ ฉันแสดงเพียงคลาสเดียว แต่สิ่งทั้งหมดสามารถทำซ้ำร่วมกับคลาสอื่นๆ ทั้งหมดได้

แบ่งชั้น kfold

ทำซ้ำขั้นตอนเดียวกันโดยใช้วิธีแบ่งชั้น โดยพื้นฐานแล้วมีเพียงแถวเดียวเท่านั้นที่ต้องเปลี่ยนคือแถวที่รายงานด้านล่าง

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

ที่นี่เราสังเกตเห็นความแตกต่างอย่างชัดเจน และตอนนี้ชุดฝึกและชุดทดสอบต่างๆ มีความสอดคล้องกันมากขึ้นอย่างไร

เอฟเฟกต์เหมือนเมื่อก่อนสามารถแสดงบนกราฟแท่งได้ คุณสามารถดูได้ทันทีโดยการคำนวณด้วยกราฟก่อนหน้าว่าแท่งทั้งหมดมีความสูงเท่ากันอย่างไร เพื่อประเมินการแยกแต่ละส่วนอย่างชัดเจน

ข้อสรุป

จากผลลัพธ์ เห็นได้ชัดว่าการใช้แนวทางแบบแบ่งชั้นมีความถูกต้องมากขึ้นหากเราจะตรวจสอบแบบจำลอง จากสิ่งนี้ ฉันหมายความว่าหากโมเดลได้รับการฝึกบน Datum รถไฟจำนวนหนึ่ง ก็คาดว่าจะได้รับการตรวจสอบความถูกต้องบน Datum การตรวจสอบความถูกต้องที่ใกล้เคียงกับ Datum รถไฟมากที่สุด การใช้แนวทางมาตรฐานนี้เป็นไปไม่ได้ ดังนั้นจึงเป็นเรื่องสำคัญที่จะต้องทราบแนวทางการตรวจสอบนี้เช่นกัน เนื่องจากอาจมีประโยชน์