Cross-Validation(이하 CV)는 트레이닝셋을 여러 개의 폴드로 분할하여 트레이닝 하는 것이며 CV를 하는 이유 모델의 트레이닝 셋에 대한 Overfitting을 막기 위함이다.
데이터의 형태에 따라 cv를 처리하는 방법이 조금씩 다르다.
가장 대표적인 cv방법으로 k-fold가 있는데 트레이닝 셋을 여러개(k)개의 블럭으로 분할한 뒤 k-1개를 트레이닝셋으로, 1개를 검증셋으로 할당한다.
또한 이러한 분할을 총 k번 배타적으로 수행하기 때문에 모든 이벤트샘플은 하나의 검증 셋 인덱스에 속하게 된다.
다음 코드에서 보면 한번의 분할 결과마다 다른 폴드별 인덱스 (trn_
, val_
)을 리턴하는 것을 확인할 수 있다.
검증 세트 인덱스부분을 제외하면 모두 훈련세트 파트이므로 for문을 돌며 검증세트 파트에만 fold 인덱스 값을 입력해주면 모든 kfold컬럼에 해당하는 자신이 validation set일 경우의 fold index가 입력되게 된다.
이러한 심플 k-fold는 통상 회귀(regression) 문제에 사용한다.
#k-fold cv
from sklearn import model_selection
X["kfold"] = -1
X = X.sample(frac=1).reset_index(drop=True) # randomize the row of the data
kf = model_selection.KFold(n_splits=5)
# fill the validation set kfold column as kfold index num
for fold, (trn_, val_) in enumerate(kf.split(X=X)): # fold : index of fold
X.loc[val_, 'kfold'] = fold # trn_ : index of training set row, val_ : index of validation set row
X
skewed 데이터셋을 가진 분류 문제에서는 일부 fold에서 아예 타겟데이터가 없는 경우가 발생하게 된다. 이를 방지하기 위해 모든 폴드 블럭이 일정 비율의 타겟 데이터 비율을 유지하며 분할하게 하는 것이 Stratified K-fold cv이다. 통상 분류(classification)에 사용된다.
#Stratified k-fold cv
from sklearn import model_selection
X["kfold"] = -1
X = X.sample(frac=1).reset_index(drop=True) # Randomize the row of the data
kf = model_selection.StratifiedKFold(n_splits=5)
# fill the validation set kfold column as kfold index num
for fold, (trn_, val_) in enumerate(kf.split(X=X, y=y)): # fold : index of fold
X.loc[val_, 'kfold'] = fold # trn_ : index of training set row, val_ : index of validation set row
X
If you want to apply stratified k-fold to regression, it is first to divide the target(numerical data) to bins(categorical data). There are severral choices for selecting appropriate numbers for bins. If you have a lot of samples( > 10k, > 100k), then you don't need to care about it. If you do not have a lot of samples, you can use Sturge's Rule to calculate the apprpriate number of bins.
#Stratified k-fold cv with binning
from sklearn import model_selection
def create_folds_with_binned(data):
data["kfold"] = -1
data = data.sample(frac=1).reset_index(drop=True) # randomize
num_bins = int(np.floor(1+np.log2(len(data)))) # sturge'rule
data.loc[:, "bins"] = pd.cut(data['target'], bins=num_bins, labels=False) # bin target
kf = model_selection.StratifiedKFold(n_splits=5)
for fold, (trn_, val_) in enumerate(kf.split(X=data, y=data.bins.values)):
data.loc[val_, 'kfold'] = fold
#data = data.drop("bins", axis=1)
print(num_bins)
return data
data_binned = create_folds_with_binned(data)
b = sns.countplot(x='kfold', hue='bins', data=data_binned)
간단하게 베이스라인 모델을 만들때 사용하는 코드 샘플이다.
# baseline model - linear regression
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
evals = []
for fold in range(5):
X_train_folded = X_train[X_train['kfold'] != fold].reset_index(drop=True)
X_valid_folded = X_train[X_train['kfold'] == fold].reset_index(drop=True)
y_train = X_train_folded[target_col]
y_valid = X_valid_folded[target_col]
X_train_folded = X_train_folded.drop(target_col, axis=1)
X_valid_folded = X_valid_folded.drop(target_col, axis=1)
model = LinearRegression()
model.fit(X_train_folded, y_train)
pred = model.predict(X_valid_folded)
mse = mean_squared_error(y_valid, pred, squared=False)
print(f'{fold} : {mse}')
evals.append(mse)
print(np.array(evals).mean())
- Approaching (Almost) Any Machine Learning Problem, Abhishek Thakur