In ML, Data Leakage - 2

Page content

머신러닝 전처리 자주하는 안 좋은 습관들 모음

Sample 데이터

  • 먼저 가상의 데이터를 하나 생성합니다.
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split

random_state = 42
X, y = make_regression(random_state = random_state, n_features = 1, noise = 1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.4, random_state = random_state)

Inconsistent preprocessing

  • 모델을 학습시킬 때 이러한 데이터 변환을 사용하는 경우 테스트 데이터든 프로덕션 시스템의 데이터든 후속 데이터셋에도 사용해야 합니다. 그렇지 않으면 피쳐 공간이 변경되고 모델이 효과적으로 수행되지 않습니다.

Wrong

  • 먼저, 잘못된 방식을 소개합니다.
  • train 데이터에는 scaler가 적용되었지만, 테스트 데이터에는 적용되지 않았습니다. 따라서, 이러한 경우 모델 성능이 예상보다 떨어질 수 있습니다.
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_transformed = scaler.fit_transform(X_train)
model = LinearRegression().fit(X_train_transformed, y_train)
mean_squared_error(y_test, model.predict(X_test))
62.80867119249524
  • Non-Transformed X_test 데이터를 예측값으로 넣으려면 X_train과 마찬가지로 동일하게 transform을 적용해야 한다.
scaler = StandardScaler()
X_train_transformed = scaler.fit_transform(X_train)
X_test_transformed = scaler.transform(X_test)
model = LinearRegression().fit(X_train_transformed, y_train)
mean_squared_error(y_test, model.predict(X_test_transformed))
0.9027975466369481
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline

random_state = 42
X, y = make_regression(random_state = random_state, n_features = 1, noise = 1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.4, random_state = random_state)

model = make_pipeline(StandardScaler(), LinearRegression())
model.fit(X_train, y_train)
mean_squared_error(y_test, model.predict(X_test))
0.9027975466369481

Data Leakage

  • 모델을 구축할 때 예측 시점에 사용할 수 없는 정보가 사용될 때 데이터 누출이 발생함.
  • 이로 인해 교차 검증 시, 매우 낙관적인 성능 추정치가 발생하기도 하며, 실제 새로운 데이터와 만날 때는 성능이 매우 크게 저하 되기도 함.
  • 훈련 및 테스트 데이터 하위 집합 모두 이전 섹션에서 설명한 것과 동일한 전처리 변환을 받아야 하지만,
  • 이러한 변환은 훈련 데이터에서만 학습되는 것이 중요하다. 예를 들어 평균값으로 나누는 정규화 단계가 있는 경우 평균은 모든 데이터의 평균이 아니라 훈련 데이터 하위 집합의 평균이어야 합니다. 테스트 하위 집합이 평균 계산에 포함되는 경우 테스트 하위 집합의 정보가 모델에 영향을 줍니다.
  • Data Leakage가 발생하는 몇가지 상황을 살펴본다.

Data Leakage During Pre-Processing

  • 우선 가상의 데이터를 만듭니다.
import numpy as np 
n_samples, n_features, n_classes = 200, 100000, 2
rng = np.random.RandomState(42)
X = rng.standard_normal((n_samples, n_features))
y = rng.choice(n_classes, n_samples)

print(X.shape, y.shape)
(200, 100000) (200,)

Wrong

  • 이제 transformation부터 머신러닝 학습, 그리고 평가까지 진행한다.
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectKBest
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score

# 여기 부분이 잘못 되었다. 
X_selected = SelectKBest(k=25).fit_transform(X, y)

X_train, X_test, y_train, y_test = train_test_split(X_selected, y, random_state=42)
gbc = GradientBoostingClassifier(random_state=1)
gbc.fit(X_train, y_train)

y_pred = gbc.predict(X_test)
accuracy_score(y_test, y_pred)
0.72

Right

  • Data Leakage를 예방하기 위해 먼저 train 데이터와 test 데이터를 분리한다.
  • fit이나 fit_transform은 train 데이터에만 적용한다.
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 42)
select = SelectKBest(k=25)

X_train_selected = select.fit_transform(X_train, y_train)
gbc = GradientBoostingClassifier(random_state = 1)
gbc.fit(X_train_selected, y_train)
GradientBoostingClassifier(ccp_alpha=0.0, criterion='friedman_mse', init=None,
                           learning_rate=0.1, loss='deviance', max_depth=3,
                           max_features=None, max_leaf_nodes=None,
                           min_impurity_decrease=0.0, min_impurity_split=None,
                           min_samples_leaf=1, min_samples_split=2,
                           min_weight_fraction_leaf=0.0, n_estimators=100,
                           n_iter_no_change=None, presort='deprecated',
                           random_state=1, subsample=1.0, tol=0.0001,
                           validation_fraction=0.1, verbose=0,
                           warm_start=False)
X_test_selected = select.transform(X_test)
y_pred = gbc.predict(X_test_selected)
accuracy_score(y_test, y_pred)
0.5
  • 이번에는 Pipeline을 통해 구성하도록 한다.
from sklearn.pipeline import make_pipeline

X_train, X_test, y_train, y_test = train_test_split(
    X, y, random_state=42)

pipeline = make_pipeline(SelectKBest(k=25), 
                         GradientBoostingClassifier(random_state=1))

pipeline.fit(X_train, y_train)
Pipeline(memory=None,
         steps=[('selectkbest',
                 SelectKBest(k=25,
                             score_func=<function f_classif at 0x7f56a7c7f5f0>)),
                ('gradientboostingclassifier',
                 GradientBoostingClassifier(ccp_alpha=0.0,
                                            criterion='friedman_mse', init=None,
                                            learning_rate=0.1, loss='deviance',
                                            max_depth=3, max_features=None,
                                            max_leaf_nodes=None,
                                            min_impurity_decrease=0.0,
                                            min_impurity_split=None,
                                            min_samples_leaf=1,
                                            min_samples_split=2,
                                            min_weight_fraction_leaf=0.0,
                                            n_estimators=100,
                                            n_iter_no_change=None,
                                            presort='deprecated',
                                            random_state=1, subsample=1.0,
                                            tol=0.0001, validation_fraction=0.1,
                                            verbose=0, warm_start=False))],
         verbose=False)
  • 테스트 데이터는 예측을 할 때만 사용하도록 한다.
y_pred = pipeline.predict(X_test)
accuracy_score(y_test, y_pred)
0.5
  • Pipeline은 cross_val_score에 직접 사용할 수도 있다.
from sklearn.model_selection import cross_val_score
scores = cross_val_score(pipeline, X, y)
print(f"Mean accuracy: {scores.mean():.2f}+/-{scores.std():.2f}")
Mean accuracy: 0.52+/-0.04