Stratified splitting

Stratified Splitting trong Machine Learning

Stratified Splitting in Machine Learning

Giới thiệu

Khi phát triển mô hình machine learning, một trong những bước quan trọng nhất là chia dữ liệu thành các tập khác nhau: tập huấn luyện (training set), tập kiểm thử (test set) và có thể cả tập xác thực (validation set). Quá trình chia dữ liệu này ảnh hưởng trực tiếp đến hiệu suất và độ tin cậy của mô hình.

Stratified Splitting (hay còn gọi là Chia tách phân tầng) là một kỹ thuật chia dữ liệu đặc biệt, trong đó phân phối của biến mục tiêu được giữ nguyên giữa các tập dữ liệu. Kỹ thuật này đặc biệt quan trọng khi làm việc với dữ liệu không cân bằng (imbalanced data) hoặc khi cần đảm bảo tính đại diện của mẫu.

Tại sao cần Stratified Splitting?

Vấn đề với phương pháp chia ngẫu nhiên thông thường

Khi sử dụng phương pháp chia dữ liệu ngẫu nhiên thông thường (Random Splitting), chúng ta có thể gặp phải một số vấn đề:

  1. Phân phối không đồng đều: Nếu dữ liệu gốc có sự mất cân bằng giữa các lớp, một số lớp có thể bị thiếu hoặc thậm chí không xuất hiện trong tập test hoặc validation.

  2. Không đại diện: Tập train có thể không đại diện cho toàn bộ dữ liệu, dẫn đến mô hình học không tốt.

  3. Đánh giá không chính xác: Tập test không đại diện có thể dẫn đến đánh giá không chính xác về hiệu suất của mô hình.

Ví dụ minh họa

Giả sử chúng ta có một bộ dữ liệu phân loại với 1000 mẫu, trong đó:

  • Lớp A: 900 mẫu (90%)

  • Lớp B: 80 mẫu (8%)

  • Lớp C: 20 mẫu (2%)

Nếu sử dụng phương pháp chia ngẫu nhiên với tỷ lệ 70/30 (train/test):

Kết quả có thể xảy ra với Random Splitting:

  • Training set: Lớp A (630 mẫu), Lớp B (55 mẫu), Lớp C (15 mẫu)

  • Test set: Lớp A (270 mẫu), Lớp B (25 mẫu), Lớp C (5 mẫu)

Trong trường hợp xấu nhất, lớp C có thể chỉ có rất ít mẫu hoặc thậm chí không có mẫu nào trong tập test, khiến việc đánh giá mô hình trên lớp này trở nên không đáng tin cậy.

Kết quả với Stratified Splitting:

  • Training set: Lớp A (630 mẫu = 90% của train set), Lớp B (56 mẫu = 8%), Lớp C (14 mẫu = 2%)

  • Test set: Lớp A (270 mẫu = 90% của test set), Lớp B (24 mẫu = 8%), Lớp C (6 mẫu = 2%)

Stratified Splitting đảm bảo rằng cả tập train và test đều có cùng tỷ lệ phân phối các lớp như dữ liệu gốc.

Cách thực hiện Stratified Splitting

1. Stratified Splitting cho bài toán phân loại

Với bài toán phân loại, chúng ta chia dữ liệu dựa trên sự phân bố của biến mục tiêu (nhãn).

from sklearn.model_selection import train_test_split

# Chia dữ liệu theo phương pháp stratified
X_train, X_test, y_train, y_test = train_test_split(
    X, y, 
    test_size=0.3,           # 30% dữ liệu cho test set
    stratify=y,              # Stratify dựa trên nhãn y
    random_state=42          # Để kết quả có thể tái tạo
)

Kiểm tra phân phối sau khi chia:

import pandas as pd

# Kiểm tra phân phối trước khi chia
print("Original class distribution:")
print(pd.Series(y).value_counts(normalize=True))

# Kiểm tra phân phối trong tập train
print("\nTraining set class distribution:")
print(pd.Series(y_train).value_counts(normalize=True))

# Kiểm tra phân phối trong tập test
print("\nTest set class distribution:")
print(pd.Series(y_test).value_counts(normalize=True))

2. Stratified K-Fold Cross-Validation

Trong cross-validation, stratified splitting cũng đặc biệt quan trọng để đảm bảo mỗi fold đều có phân phối tương tự nhau.

from sklearn.model_selection import StratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

# Khởi tạo mô hình
model = RandomForestClassifier(random_state=42)

# Khởi tạo StratifiedKFold
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# Thực hiện cross-validation
fold_accuracies = []

for train_index, test_index in skf.split(X, y):
    X_fold_train, X_fold_test = X[train_index], X[test_index]
    y_fold_train, y_fold_test = y[train_index], y[test_index]
    
    # Huấn luyện mô hình
    model.fit(X_fold_train, y_fold_train)
    
    # Dự đoán và tính accuracy
    y_pred = model.predict(X_fold_test)
    fold_accuracy = accuracy_score(y_fold_test, y_pred)
    fold_accuracies.append(fold_accuracy)

# Hiển thị kết quả
print(f"Fold accuracies: {fold_accuracies}")
print(f"Mean accuracy: {sum(fold_accuracies) / len(fold_accuracies):.4f}")

3. Stratified Splitting cho bài toán hồi quy

Đối với bài toán hồi quy, biến mục tiêu là liên tục, không phải là các lớp rời rạc. Để thực hiện stratified splitting, chúng ta cần chuyển đổi biến mục tiêu thành các bin (khoảng).

import numpy as np
from sklearn.model_selection import train_test_split

# Tạo bins từ biến mục tiêu liên tục
def create_bins(y, n_bins=5):
    return np.digitize(y, np.histogram(y, bins=n_bins)[1][:-1])

# Tạo các bin từ biến mục tiêu
y_binned = create_bins(y, n_bins=10)  # Chia thành 10 khoảng

# Thực hiện stratified split dựa trên các bin
X_train, X_test, y_train, y_test = train_test_split(
    X, y,
    test_size=0.3,
    stratify=y_binned,
    random_state=42
)

# Kiểm tra phân phối của biến mục tiêu trong các tập
print(f"Training set mean: {np.mean(y_train)}, std: {np.std(y_train)}")
print(f"Test set mean: {np.mean(y_test)}, std: {np.std(y_test)}")

4. Stratified Splitting với nhiều biến

Trong một số trường hợp, chúng ta muốn stratify dựa trên nhiều biến (ví dụ: cả giới tính và nhóm tuổi). Để làm điều này, chúng ta có thể tạo một biến kết hợp.

# Giả sử chúng ta có hai biến phân loại cần stratify: gender và age_group
combined_strat = df['gender'] + '_' + df['age_group']

# Thực hiện stratified split dựa trên biến kết hợp
train_df, test_df = train_test_split(
    df,
    test_size=0.3,
    stratify=combined_strat,
    random_state=42
)

# Kiểm tra phân phối trong từng tập
print("Original distribution:")
print(df.groupby(['gender', 'age_group']).size() / len(df))

print("\nTraining set distribution:")
print(train_df.groupby(['gender', 'age_group']).size() / len(train_df))

print("\nTest set distribution:")
print(test_df.groupby(['gender', 'age_group']).size() / len(test_df))

Stratified Splitting cho dữ liệu dạng chuỗi thời gian

Với dữ liệu chuỗi thời gian, chúng ta thường cần duy trì tính liên tục về mặt thời gian. Trong trường hợp này, chúng ta có thể kết hợp stratified splitting với các kỹ thuật time series splitting.

from sklearn.model_selection import TimeSeriesSplit

# Tạo TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)

# Trong mỗi fold, thực hiện stratified sampling
for train_index, test_index in tscv.split(X):
    X_fold_train, X_fold_test = X[train_index], X[test_index]
    y_fold_train, y_fold_test = y[train_index], y[test_index]
    
    # Nếu cần stratify trong mỗi fold
    # (Ví dụ: trong mỗi khoảng thời gian, chúng ta vẫn muốn giữ nguyên phân phối của một biến nào đó)
    strat_var = some_categorical_variable[train_index]
    
    # Chia nhỏ tập train thành train và validation với stratify
    X_fold_train_final, X_fold_val, y_fold_train_final, y_fold_val = train_test_split(
        X_fold_train, y_fold_train,
        test_size=0.2,
        stratify=strat_var,
        random_state=42
    )

Stratified Group K-Fold

Trong một số bài toán, dữ liệu có cấu trúc nhóm (ví dụ: nhiều mẫu từ cùng một bệnh nhân). Chúng ta muốn đảm bảo rằng các mẫu từ cùng một nhóm không xuất hiện ở cả tập train và test, đồng thời vẫn duy trì phân phối của các lớp.

from sklearn.model_selection import StratifiedGroupKFold

# Giả sử groups là array chỉ định nhóm của mỗi mẫu
sgkf = StratifiedGroupKFold(n_splits=5, shuffle=True, random_state=42)

# Thực hiện cross-validation
for train_index, test_index in sgkf.split(X, y, groups=groups):
    X_fold_train, X_fold_test = X[train_index], X[test_index]
    y_fold_train, y_fold_test = y[train_index], y[test_index]
    
    # Kiểm tra phân phối
    print(f"Train class distribution: {np.bincount(y_fold_train) / len(y_fold_train)}")
    print(f"Test class distribution: {np.bincount(y_fold_test) / len(y_fold_test)}")
    
    # Kiểm tra groups không bị chồng chéo
    train_groups = set(groups[train_index])
    test_groups = set(groups[test_index])
    intersection = train_groups.intersection(test_groups)
    print(f"Number of overlapping groups: {len(intersection)}")  # Nên bằng 0

Lưu ý quan trọng

  1. Số lượng mẫu tối thiểu: Stratified splitting đòi hỏi mỗi lớp phải có số lượng mẫu tối thiểu. Nếu một lớp có rất ít mẫu, có thể không đủ để phân phối vào tất cả các fold trong cross-validation.

  2. Kích thước bin cho hồi quy: Khi áp dụng stratified splitting cho bài toán hồi quy, việc chọn số lượng bin phù hợp rất quan trọng. Quá ít bin sẽ không nắm bắt được phân phối đầy đủ, quá nhiều bin có thể dẫn đến các bin quá nhỏ.

  3. Kiểm tra tính hiệu quả: Luôn kiểm tra phân phối trước và sau khi chia để đảm bảo stratified splitting hoạt động đúng.

  4. Class Imbalance Extreme: Khi dữ liệu cực kỳ mất cân bằng, stratified splitting vẫn có thể không đủ, và cần kết hợp với các kỹ thuật sampling khác như SMOTE.

So sánh hiệu suất

Để minh họa tầm quan trọng của stratified splitting, dưới đây là một ví dụ so sánh hiệu suất của mô hình khi sử dụng stratified splitting và random splitting thông thường:

from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import f1_score, accuracy_score
from sklearn.model_selection import train_test_split

# Tạo dữ liệu không cân bằng
X, y = make_classification(
    n_samples=1000, 
    n_features=20,
    n_informative=2, 
    n_redundant=2,
    n_classes=3,
    weights=[0.8, 0.15, 0.05],  # Tạo class imbalance
    random_state=42
)

# Random splitting
X_train_random, X_test_random, y_train_random, y_test_random = train_test_split(
    X, y, test_size=0.3, random_state=42
)

# Stratified splitting
X_train_strat, X_test_strat, y_train_strat, y_test_strat = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=42
)

# Huấn luyện và đánh giá với random splitting
model_random = RandomForestClassifier(random_state=42)
model_random.fit(X_train_random, y_train_random)
y_pred_random = model_random.predict(X_test_random)
accuracy_random = accuracy_score(y_test_random, y_pred_random)
f1_random = f1_score(y_test_random, y_pred_random, average='weighted')

# Huấn luyện và đánh giá với stratified splitting
model_strat = RandomForestClassifier(random_state=42)
model_strat.fit(X_train_strat, y_train_strat)
y_pred_strat = model_strat.predict(X_test_strat)
accuracy_strat = accuracy_score(y_test_strat, y_pred_strat)
f1_strat = f1_score(y_test_strat, y_pred_strat, average='weighted')

# So sánh kết quả
print(f"Random Splitting - Accuracy: {accuracy_random:.4f}, F1 Score: {f1_random:.4f}")
print(f"Stratified Splitting - Accuracy: {accuracy_strat:.4f}, F1 Score: {f1_strat:.4f}")

# Kiểm tra phân phối lớp
from collections import Counter
print("\nOriginal class distribution:")
print(Counter(y))
print("\nRandom Split - Training set class distribution:")
print(Counter(y_train_random))
print("\nRandom Split - Test set class distribution:")
print(Counter(y_test_random))
print("\nStratified Split - Training set class distribution:")
print(Counter(y_train_strat))
print("\nStratified Split - Test set class distribution:")
print(Counter(y_test_strat))

Kết luận

Stratified Splitting là một kỹ thuật quan trọng trong quá trình chia dữ liệu, đặc biệt khi làm việc với dữ liệu không cân bằng. Bằng cách duy trì phân phối của biến mục tiêu trong tất cả các tập dữ liệu, kỹ thuật này giúp:

  1. Đảm bảo tính đại diện của mẫu

  2. Cải thiện độ tin cậy của đánh giá mô hình

  3. Giảm thiểu bias trong quá trình huấn luyện và đánh giá

Trong thực tế, stratified splitting nên được coi là một phần tiêu chuẩn trong quy trình phát triển mô hình machine learning, đặc biệt khi làm việc với dữ liệu phân loại hoặc dữ liệu có sự phân bố không đồng đều.

Last updated