Study/머신러닝

머신러닝 Advanced_ 데이터 셋 분할

김 도경 2024. 11. 8. 20:53

[2024.11.08] 필수 온라인 강의 Part16 Machine Learning Advanced CH07 데이터 셋 분할

이론
  • 데이터 분할
    - 모델을 학습하기 위해 전체 데이터를 Train, Valid, Test 총 3가지의 데이터로 나누는 과정

    - 일반적으로 사용할 수 있는 데이터는 전체 데이터의 일부분
    - 학습 데이터의 높은 성능이 실제 미래 데이터에 대한 보장이 되지 않는다
    - 이러한 모델의 성능을 보장해 줄 데이터가 필요하며 평가 데이터가 이런 역할

    -> 데이터는 미래의 데이터나 그 후 단계의 성능은 확인할 수 없는데, 평가셋을 미리 구분하여, 모델이 얼마나 유의미한지 확인할 필요성이 있고, 배포때 확인을 하면 위험성이 많아서, 배포에는 최적의 모델을 보낼 수 있음

  • 구성요소
    - 학습(Train) 데이터 : 모델이 데이터의 기능/패턴을 학습하는데 사용
    - 검증(Validation) 데이터 : 학습 중 모델 성능을 검증하고 조정해주는 역할
            - 모델의 일반화 능력이나 parameter 튜닝 등의 작업을 할때 사용
    - 테스트(Test) 데이터 :  학습을 마친 후 모델을 최종적으로 평가

    - 평가 데이터나 검증 데이터가 원본 데이터를 잘 반영하지 못하면
       :  모델의 학습과 실험이 올바르게 진행되지 않음
       - 검증 데이터와 평가 데이터의 분포가 다르거나 서로 다른 패턴을 보유하고 있을 경우, 학습 데이터를 올바르게 검증해도 평가 데이터에 대해서는 성능이 좋지 않은 결과
       - 따라서, 검증 데이터와 평가 데이터를 잘 설정하는 것이 중요

    - 검증데이터가 올라가면, 평가데이터도 같이 올라가고, 내려가면 같이 내려가게. 상관관계를 잘 보는 게 중요함.

다양한 분할 방법

  • Holdout
    - 가장 기본적인 분할 방법
    - 전체 데이터를 8:2 (7:3, 5:5) 등과 같이 일정 비율로 분리하는 기법
    - 랜덤하게 분리하거나, 경우에 따라 가장 최근 20%를 검증데이터로 사용
    - 주어진 데이터의 특징에 따르거나 목적에 따라 다르기에, 데이터와 도메인에 대한 이해를 바탕으로 진행

    - 장점
             - 빠른 속도로 모델 검증 가능하여, 가장 기본적이지만 많이 사용되는 방법론
    - 단점
             - 일부 데이터(20%의 검증 데이터)가 학습에 참여하지 못함으로 성능이 일반적으로 낮은 편
          - 전체 데이터가 아닌 일부 데이터에 대해서만 검증했기에 완전히 신뢰성 있다고 보장할 수 없음
  • K-Fold
    - K번의 Holdout으로 데이터를 분리하는 방법
    - K-1개를 학습 세트로, 1개를 검증 세트로 사용
    - 동일한 Fold가 없도록 설정
    - 최적의 학습,검증 Fold셋을 찾아내거나 모델들의 결과를 앙상블해서 사용

    - 장점
             - 데이터셋 내의 모든 데이터를 훈련에 활용 가능
             - 전체 데이터에 대한 검증을 하기에, 신뢰성을 보장
             - Fold별 결과물을 앙상블 하기에, 대부분의 경우 Holdout대비 높은 성능
    - 단점
             - 모델 훈련 및 평가 소요시간 증가 (Holdout대비 K배)
             - K의 선택이 필요
  • Stratified K-Fold
    - K개의 Fold를 구성하는 방법은 K-Fold와 동일
    - 하지만, Fold별 Y의 비율도 동일하게 하는 분할 방법
    - 데이터의 양이 적거나 불균형이 심한 데이터에 대해 K-Fold적용 시 분포가 크게 달라지는 현상을 방지

    - 장점
             - 데이터셋 내의 모든 데이터를 훈련에 활용 가능.
             - 전체 데이터에 대한 검증을 하기에, 신뢰성을 보장.
             - Fold별 결과물을 앙상블 하기에, 대부분의 경우 Holdout대비 높은 성능.
             - Y의 분포가 동일하기에, 불균형에서 오는 부분이 일부 해소.
    - 단점 
             - 모델 훈련 및 평가 소요시간 증가 (Holdout대비 K배) 
             - K의 선택이 필요.
  • Group K-Fold
    - 특정 목적하에 사용되는 확장된 K-Fold 방법
    - Train과 Valid에 같은 값이 들어가지 않도록 Group을 구성 후, Fold별로 Group을 분배
    - 새로운 사람에 대해 예측하는 경우나 일반적으로 환자 데이터에 사용하기 유리
    - 새로운 환자의 질병을 예측해야하는 데이터에서 Train과 Valid에 같은 환자의 정보가 들어있으면, 모델이 해당 환자의 패턴을 미리 파악하게 되는 문제가 발생할 수 있음

    - 장점
             - 데이터셋 내의 모든 데이터를 훈련에 활용 가능
             - 전체 데이터에 대한 검증을 하기에, 신뢰성을 보장
             - Fold별 결과물을 앙상블 하기에, 대부분의 경우 Holdout대비 높은 성능
    - 단점
             - 모델 훈련 및 평가 소요시간 증가 (Holdout대비 K배)
             - K의 선택이 필요

  • Time-Series Split
    - 시계열 데이터에 사용되는 확장된 K-Fold 방법
    - 시간에 따른 순서가 존재하기에, 기존의 K-Fold를 사용하면 미래의 데이터로 과거를 예측하는 일이 발생
    - 따라서, 과거의 데이터를 통해 미래를 예측하는 데이터의 구조를 유지한 채 Fold를 나누는 방법

    - 장점
             - 데이터셋 내의 모든 데이터를 훈련에 활용 가능
             - 전체 데이터에 대한 검증을 하기에, 신뢰성을 보장
             - Fold별 결과물을 앙상블 하기에, 대부분의 경우 Holdout대비 높은 성능
             - 다양한 기간(Fold1=1주, Fold2=2주, …)에 대한 학습 결과를 얻을 수 있어, 일반화의 성능을 높일 수 있는 장점.

    - 단점
             - 모델 훈련 및 평가 소요시간 증가 (Holdout대비 K배)
             - K의 선택이 필요

  • 그외 기타
    - Leave-One-Out Cross Validation
    - Nested Cross Validation
    - Blocked Cross validation
    - https://scikit-learn.org/stable/modules/cross_validation.html
    - https://medium.com/geekculture/cross-validation-techniques-33d389897878

실전

 

1. 실습 데이터 준비
데이터 불러오기 및 전처리
- Yahoo Finance OHLCV 데이터셋

: https://glowdp.tistory.com/80 참조


2. 데이터 분할
Holdout

# 학습데이터를 학습/검증 데이터로 분리
# Sklearn의 Holdout을 이용해 랜덤으로 8:2로 분리

holdout_X_train, holdout_X_valid, holdout_Y_train, holdout_Y_valid = train_test_split(X_train, Y_train, test_size=0.2, shuffle=True) # default shuffle : True

display(f"Train Input : {holdout_X_train.shape}")
display(f"Train Target : {holdout_Y_train.shape}")
display(f"Valid Input : {holdout_X_valid.shape}")
display(f"Valid Target : {holdout_Y_valid.shape}")
display(f"Test Input : {X_test.shape}")
display(f"Valid Target : {Y_test.shape}")
# LightGBM을 선언
# 학습은 총 1000번을 반복
# 데이터셋 분할에 따른 성능비교를 위해 별도의 Parameter 튜닝은 적용하지 않고 기본값을 사용

gbm = lgb.LGBMRegressor(n_estimators=1000)
# 학습을 진행
# 비교를 위해 %%time을 이용해 학습시간을 측정

%%time
gbm.fit(holdout_X_train, holdout_Y_train,                                                  # 학습 데이터를 입력합
        eval_set=[(holdout_X_train, holdout_Y_train), (holdout_X_valid, holdout_Y_valid)], # 평가셋을 지정
        eval_metric ='rmse',                                                               # 평가과정에서 사용할 평가함수를 지정
        callbacks=[lgb.early_stopping(stopping_rounds=10),                                 # 10번의 성능향상이 없을 경우, 학습을 중지
                   lgb.log_evaluation(period=10, show_stdv=True)]                          # 매 iteration마다 학습결과를 출력
)
joblib.dump(gbm, 'holdout_gbm.pkl')  # 학습한 모델을 저장

gbm_trained = joblib.load('holdout_gbm.pkl') # 저장한 모델

# 불러온 모델을 통해 추론을 진행
# 이후, Test set에 대한 rmse를 측정
predicts = gbm_trained.predict(X_test)
RMSE = mean_squared_error(Y_test, predicts)**0.5
print(f"Test rmse : {RMSE}")

 

Holdout 학습의 최종결과는 아래와 같습니다.
Training Time : 29.9s
Train RMSE : 2097.48
Valid RMSE : 3015.71
Test RMSE : 3487.40

 

K-Fold

# 분할된 데이터를 fold별로 시각화하기 위한 함수를 구성
# Scikit-learn에서 https://scikit-learn.org/stable/auto_examples/model_selection/plot_cv_indices.html 사용한 코드를 가져와서 사용
cmap_data = plt.cm.Paired
cmap_cv = plt.cm.coolwarm

def plot_cv_indices(x, y, cv, ax, split_strategy='KFold', group=None, lw=10):
    """Create a sample plot for indices of a cross-validation object."""

    for ii, (tr, tt) in enumerate(cv.split(X=x, y=y, groups=group)):
        # Fill in indices with the training/test groups
        print(f"Fold {ii} :")
        print(f"  Train : index={tr[:5]}...")
        print(f"  Valid : index={tt[:5]}...")
        indices = np.array([np.nan] * len(x))
        indices[tt] = 1
        indices[tr] = 0
        # Visualize the results
        ax.scatter(
            range(len(indices)),
            [ii + 0.5] * len(indices),
            c=indices,
            marker="_",
            lw=lw,
            cmap=cmap_cv,
            vmin=-0.2,
            vmax=0.2,
        )

    # Formatting
    yticklabels = list(range(5))

    ax.set(
        yticks=np.arange(len(yticklabels)) + 0.5,
        yticklabels=yticklabels,
        xlabel="Sample index",
        ylabel="CV iteration",
        ylim=[len(yticklabels) + 0.2, -0.2],
        xlim=[0, len(x)],
    )
    ax.set_title(split_strategy, fontsize=15)
    return ax
kf = KFold(n_splits=5)    # Kfold 함수를 선언

# kfold를 시각화
fig, ax = plt.subplots()
plot_cv_indices(x=X_train,
                y=Y_train,
                cv=kf,
                ax=ax,
                split_strategy='K-Fold')
# 학습 데이터를 Kfold로 나눔
train_folds = kf.split(X_train, Y_train)
display(train_folds)

# 학습을 진행
%%time
fold_save_files = []

for fold_idx, (train_idx, valid_idx) in enumerate(train_folds):
    print(f"--------{fold_idx}번째 fold의 학습을 시작.--------")

    # index를 통해 fold의 학습세트
    X_train_fold = X_train.iloc[train_idx, :]
    Y_train_fold = Y_train[train_idx]

    # index를 통해 fold의 평가세트
    X_valid_fold = X_train.iloc[valid_idx, :]
    Y_valid_fold = Y_train[valid_idx]

    # fold의 데이터로 학습을 진행
    gbm = lgb.LGBMRegressor(n_estimators=1000)
    gbm.fit(X_train_fold, Y_train_fold,                                               # 학습 데이터를 입력.
        eval_set=[(X_train_fold, Y_train_fold), (X_valid_fold, Y_valid_fold)], # 평가셋을 지정.
        eval_metric ='rmse',                                                               # 평가과정에서 사용할 평가함수를 지정
        callbacks=[lgb.early_stopping(stopping_rounds=10),                                  # 10번의 성능향상이 없을 경우, 학습을 중지.
                   lgb.log_evaluation(period=10, show_stdv=True)]                           # 매 iteration마다 학습결과를 출력.
    )

    # 각 fold별 학습한 모델을 저장
    file_name = f"kfold{fold_idx}_gbm.pkl"
    joblib.dump(gbm, file_name)
    print(f"--------{fold_idx}번째 fold는 {file_name}에 저장되었습니다.--------\n\n")
    fold_save_files.append(file_name)
# 저장한 학습모델들을 불러와, Testset에 대한 추론을 진행
# 각 fold의 예측결과를 평균을 취하는 방식으로 진행
total_predicts = np.zeros(len(X_test))

for file_name in fold_save_files:
    gbm_trained = joblib.load(file_name)
    fold_predicts = gbm_trained.predict(X_test)

    # 각 fold의 rmse를 측정합니다.
    RMSE = mean_squared_error(Y_test, fold_predicts)**0.5
    display(f"{file_name} - Test rmse : {RMSE}")

    total_predicts += fold_predicts / len(fold_save_files)

RMSE = mean_squared_error(Y_test, total_predicts)**0.5
display(f"최종 Test rmse : {RMSE}")
# 위 학습 로그에서 검증세트를 기준으로 rmse가 가장 낮은 3개의 모델을 선택후 추론을 진행

top_3_files = ["kfold1_gbm.pkl", "kfold3_gbm.pkl", "kfold4_gbm.pkl"]
total_predicts = np.zeros(len(X_test))

for file_name in top_3_files:
    gbm_trained = joblib.load(file_name)
    fold_predicts = gbm_trained.predict(X_test)

    # 각 fold의 rmse를 측정.
    RMSE = mean_squared_error(Y_test, fold_predicts)**0.5
    display(f"{file_name} - Test rmse : {RMSE}")

    total_predicts += fold_predicts / len(top_3_files)

RMSE = mean_squared_error(Y_test, total_predicts)**0.5
display(f"최종 Test rmse : {RMSE}")

 

Kfold 학습의 최종결과
Training Time : 40.5sTrain
RMSE : fold마다 다름
Valid RMSE : fold마다 다름

Test RMSE(ALL) : 5290.11
Test RMSE(Top3) : 3243.60
Test RMSE(Top1) : 3447.76

 

Stratified K-Fold

# Stratified Kfold 함수를 선언
kf = StratifiedKFold(n_splits=5)

# Target값을 기준으로 분포에 따라 1000등분
# Stratified KFold에서 각 구간을 기준으로 Y의 비율
# 0, ... 9 = 0 / 10, ..., 19 = 1 / ..
cut_Y_train = pd.cut(Y_train,
                     1000, # 데이터를 최소 최대 구간으로 1000등분
                     labels=False)

# Stratified Kfold를 시각화
# 앞의 kfold에서 정의한 시각화 함수를 사용
fig, ax = plt.subplots()
plot_cv_indices(x=X_train,
                y=cut_Y_train,
                cv=kf,
                ax=ax,
                split_strategy='Stratified K-Fold')
# 원래 Target값의 분포를 확인
plt.hist(Y_train, bins=100, density=True)
plt.xlabel('Target')
plt.ylabel('Frequency')
plt.show()
# 각 Fold별 분포를 시각화
fig, axs = plt.subplots(1, 5, sharex=True, sharey=True, figsize=(10,4)) # 5개의 plot
for fold_idx, (train_idx, valid_idx) in enumerate(kf.split(X_train, cut_Y_train)): # fold별로 iteration
    axs[fold_idx].hist(Y_train[train_idx], bins=100, density=True, label=f'Fold-{fold_idx}') # 각 fold에 해당하는 Target값
    if fold_idx == 0:
        axs[fold_idx].set_ylabel('Frequency')
    if fold_idx == 2:
        axs[fold_idx].set_xlabel("Target")
    axs[fold_idx].legend(frameon=False, handlelength=0)
plt.tight_layout()
plt.show()
# 학습 데이터를 Stratified Kfold로 나눔
train_folds = kf.split(X_train, cut_Y_train)
display(train_folds)

# 학습을 진행
%%time
fold_save_files = []

for fold_idx, (train_idx, valid_idx) in enumerate(train_folds):
    print(f"--------{fold_idx}번째 fold의 학습을 시작합니다.--------")

    # index를 통해 fold의 학습세트를 가져옵니다.
    X_train_fold = X_train.iloc[train_idx, :]
    Y_train_fold = Y_train[train_idx]

    # index를 통해 fold의 평가세트를 가져옵니다.
    X_valid_fold = X_train.iloc[valid_idx, :]
    Y_valid_fold = Y_train[valid_idx]

    # fold의 데이터로 학습을 진행합니다.
    gbm = lgb.LGBMRegressor(n_estimators=1000)
    gbm.fit(X_train_fold, Y_train_fold, # 학습 데이터를 입력합니다.
        eval_set=[(X_train_fold, Y_train_fold), (X_valid_fold, Y_valid_fold)], # 평가셋을 지정합니다.
        eval_metric ='rmse', # 평가과정에서 사용할 평가함수를 지정합니다.
        callbacks=[lgb.early_stopping(stopping_rounds=10) # 10번의 성능향상이 없을 경우, 학습을 멈춥니다.
                   lgb.log_evaluation(period=10, show_stdv=True)] # 매 iteration마다 학습결과를 출력합니다.
    )

    # 각 fold별 학습한 모델을 저장합니다.
    file_name = f"Stratified_kfold{fold_idx}_gbm.pkl"
    joblib.dump(gbm, file_name)
    display(f"--------{fold_idx}번째 fold는 {file_name}에 저장되었습니다.--------\n\n")
    fold_save_files.append(file_name)

 

# 저장한 학습모델들을 불러와, Testset에 대한 추론을 진행
# 각 fold의 예측결과를 평균을 취하는 방식으로 진행.
total_predicts = np.zeros(len(X_test))

for file_name in fold_save_files:
    gbm_trained = joblib.load(file_name)
    fold_predicts = gbm_trained.predict(X_test)

    # 각 fold의 rmse를 측정합니다.
    RMSE = mean_squared_error(Y_test, fold_predicts)**0.5
    display(f"{file_name} - Test rmse : {RMSE}")

    total_predicts += fold_predicts / len(fold_save_files)

RMSE = mean_squared_error(Y_test, total_predicts)**0.5
display(f"최종 Test rmse : {RMSE}")

# 위 학습 로그에서 검증세트를 기준으로 rmse가 가장 낮은 3개의 모델을 선택후 추론을 진행.
top_3_files = ["Stratified_kfold1_gbm.pkl", "Stratified_kfold2_gbm.pkl", "Stratified_kfold3_gbm.pkl"]
total_predicts = np.zeros(len(X_test))

for file_name in top_3_files:
    gbm_trained = joblib.load(file_name)
    fold_predicts = gbm_trained.predict(X_test)

    # 각 fold의 rmse를 측정합니다.
    RMSE = mean_squared_error(Y_test, fold_predicts)**0.5
    display(f"{file_name} - Test rmse : {RMSE}")

    total_predicts += fold_predicts / len(top_3_files)

RMSE = mean_squared_error(Y_test, total_predicts)**0.5
display(f"최종 Test rmse : {RMSE}")

 

StratifiedKfold 학습의 최종결과
Training Time : 1분 41초
Train RMSE : fold마다 다름
Valid RMSE : fold마다 다름
Test RMSE(ALL) : 3292.13
Test RMSE(Top3) : 3357.02
Test RMSE(Top1) : 3237.07

 

Group K-Fold

# 종목(code)를 Group을 나누는 기준으로 사용
groups = list(X_train['LEncodedCode'].astype('int'))

# GroupKfold 함수를 선언
kf = GroupKFold(n_splits=5)

# GroupKfold를 시각화
fig, ax = plt.subplots()
plot_cv_indices(x=X_train.sort_values(by='LEncodedCode'),
                y=Y_train,
                cv=kf,
                group=groups,
                ax=ax,
                split_strategy='Code Group K-Fold')
# 학습 데이터를 GroupKfold로 나눔
train_folds = kf.split(X_train, Y_train, groups=groups)
display(train_folds)

# 학습을 진행
%%time
fold_save_files = []

for fold_idx, (train_idx, valid_idx) in enumerate(train_folds):
    display(f"--------{fold_idx}번째 fold의 학습을 시작합니다.--------")

    # index를 통해 fold의 학습세트를 가져옵니다.
    X_train_fold = X_train.iloc[train_idx, :]
    Y_train_fold = Y_train[train_idx]

    # index를 통해 fold의 평가세트를 가져옵니다.
    X_valid_fold = X_train.iloc[valid_idx, :]
    Y_valid_fold = Y_train[valid_idx]

    # fold의 데이터로 학습을 진행합니다.
    gbm = lgb.LGBMRegressor(n_estimators=1000)
    gbm.fit(X_train_fold, Y_train_fold,                                               # 학습 데이터를 입력합니다.
        eval_set=[(X_train_fold, Y_train_fold), (X_valid_fold, Y_valid_fold)], # 평가셋을 지정합니다.
        eval_metric ='rmse',                                                               # 평가과정에서 사용할 평가함수를 지정합니다.
        callbacks=[lgb.early_stopping(stopping_rounds=10),                                  # 10번의 성능향상이 없을 경우, 학습을 멈춥니다.
                   lgb.log_evaluation(period=10, show_stdv=True)]                           # 매 iteration마다 학습결과를 출력합니다.
    )

    # 각 fold별 학습한 모델을 저장합니다.
    file_name = f"groupd_kfold{fold_idx}_gbm.pkl"
    joblib.dump(gbm, file_name)
    display(f"--------{fold_idx}번째 fold는 {file_name}에 저장되었습니다.--------\n\n")
    fold_save_files.append(file_name)
# 저장한 학습모델들을 불러와, Testset에 대한 추론을 진행.
# 각 fold의 예측결과를 평균을 취하는 방식으로 진행.
total_predicts = np.zeros(len(X_test))

for file_name in fold_save_files:
    gbm_trained = joblib.load(file_name)
    fold_predicts = gbm_trained.predict(X_test)

    # 각 fold의 rmse를 측정합니다.
    RMSE = mean_squared_error(Y_test, fold_predicts)**0.5
    display(f"{file_name} - Test rmse : {RMSE}")

    total_predicts += fold_predicts / len(fold_save_files)

RMSE = mean_squared_error(Y_test, total_predicts)**0.5
display(f"최종 Test rmse : {RMSE}")

# 위 학습 로그에서 검증세트를 기준으로 rmse가 가장 낮은 3개의 모델을 선택후 추론을 진행
top_3_files = ["groupd_kfold0_gbm.pkl", "groupd_kfold2_gbm.pkl", "groupd_kfold4_gbm.pkl"]
total_predicts = np.zeros(len(X_test))

for file_name in top_3_files:
    gbm_trained = joblib.load(file_name)
    fold_predicts = gbm_trained.predict(X_test)

    # 각 fold의 rmse를 측정.
    RMSE = mean_squared_error(Y_test, fold_predicts)**0.5
    display(f"{file_name} - Test rmse : {RMSE}")

    total_predicts += fold_predicts / len(top_3_files)

RMSE = mean_squared_error(Y_test, total_predicts)**0.5
display(f"최종 Test rmse : {RMSE}")

 

종목별 Group Kfold 학습의 최종결과
Training Time : 50sTrain
RMSE : fold마다 다름
Valid RMSE : fold마다 다름

Test RMSE(ALL) : 4912.87
Test RMSE(Top3) : 3442.06
Test RMSE(Top1) : 3637.26

 

 

Time-Series Split

# 산업군(Industry)를 Group을 나누는 기준으로 사용
groups = list(X_train['LEncodedIndustry'].astype('int'))

# GroupKfold 함수를 선언합니다.
kf = GroupKFold(n_splits=5)

# GroupKfold를 시각화
fig, ax = plt.subplots()
plot_cv_indices(x=X_train.sort_values(by='LEncodedIndustry'),
                y=Y_train,
                cv=kf,
                group=groups,
                ax=ax,
                split_strategy='Industry Group K-Fold')
# 학습 데이터를 GroupKfold로 나눔
train_folds = kf.split(X_train, Y_train, groups=groups)
display(train_folds)

# 학습을 진행
%%time
fold_save_files = []

for fold_idx, (train_idx, valid_idx) in enumerate(train_folds):
    display(f"--------{fold_idx}번째 fold의 학습을 시작합니다.--------")

    # index를 통해 fold의 학습세트
    X_train_fold = X_train.iloc[train_idx, :]
    Y_train_fold = Y_train[train_idx]

    # index를 통해 fold의 평가세트
    X_valid_fold = X_train.iloc[valid_idx, :]
    Y_valid_fold = Y_train[valid_idx]

    # fold의 데이터로 학습을 진행
    gbm = lgb.LGBMRegressor(n_estimators=1000)
    gbm.fit(X_train_fold, Y_train_fold,                                               # 학습 데이터를 입력
        eval_set=[(X_train_fold, Y_train_fold), (X_valid_fold, Y_valid_fold)], # 평가셋을 지정
        eval_metric ='rmse',                                                               # 평가과정에서 사용할 평가함수를 지정
        callbacks=[lgb.early_stopping(stopping_rounds=10),                                  # 10번의 성능향상이 없을 경우, 학습 중단
                   lgb.log_evaluation(period=10, show_stdv=True)]                           # 매 iteration마다 학습결과를 출력.
    )

    # 각 fold별 학습한 모델을 저장
    file_name = f"industry_groupd_kfold{fold_idx}_gbm.pkl"
    joblib.dump(gbm, file_name)
    display(f"--------{fold_idx}번째 fold는 {file_name}에 저장되었습니다.--------\n\n")
    fold_save_files.append(file_name)
# 저장한 학습모델들을 불러와, Testset에 대한 추론을 진행
# 각 fold의 예측결과를 평균을 취하는 방식으로 진행
total_predicts = np.zeros(len(X_test))

for file_name in fold_save_files:
    gbm_trained = joblib.load(file_name)
    fold_predicts = gbm_trained.predict(X_test)

    # 각 fold의 rmse를 측정합니다.
    RMSE = mean_squared_error(Y_test, fold_predicts)**0.5
    display(f"{file_name} - Test rmse : {RMSE}")

    total_predicts += fold_predicts / len(fold_save_files)

RMSE = mean_squared_error(Y_test, total_predicts)**0.5
display(f"최종 Test rmse : {RMSE}")

# 위 학습 로그에서 검증세트를 기준으로 rmse가 가장 낮은 3개의 모델을 선택후 추론을 진행
top_3_files = ["industry_groupd_kfold0_gbm.pkl", "industry_groupd_kfold1_gbm.pkl", "industry_groupd_kfold2_gbm.pkl"]
total_predicts = np.zeros(len(X_test))

for file_name in top_3_files:
    gbm_trained = joblib.load(file_name)
    fold_predicts = gbm_trained.predict(X_test)

    # 각 fold의 rmse를 측정
    RMSE = mean_squared_error(Y_test, fold_predicts)**0.5
    display(f"{file_name} - Test rmse : {RMSE}")

    total_predicts += fold_predicts / len(top_3_files)

RMSE = mean_squared_error(Y_test, total_predicts)**0.5
display(f"최종 Test rmse : {RMSE}")

 

산업군별 Group Kfold 학습의 최종결과
Training Time : 29.9s
Train RMSE : fold마다 다름
Valid RMSE : fold마다 다름

Test RMSE(ALL) : 3529.99
Test RMSE(Top3) : 3288.77
Test RMSE(Top1) : 3347.86
분할 별 Test RMSE 결과
Holdout : 3487.40
KFold(Top3) : 3243.60
Stratified KFold(Top) : 3237.07
종목별 Group KFold(Top3) : 3442.06
산업군별 Group KFold(Top3) : 3288.77