소품집

[TimeSeries] 시계열 교차검증 with Python : TSCV (Time Serise Cross Validation) 본문

AI

[TimeSeries] 시계열 교차검증 with Python : TSCV (Time Serise Cross Validation)

sodayeong 2023. 2. 15. 16:10
728x90

Cross-Validation이 필요한 이유

  • 데이터는 기본적으로 label이 있는 Train / Test set으로 구성되어 있음.
  • 모델 검증을 할 때 Train set을 Train / Validation set으로 분리하지 않으면 Test set으로 사용함.
    • 이 때, 고정된 Test set만 사용해 모델 성능을 확인하면 Test set에만 적합한 모델이 됨.
    • 따라서 Test set에 과적합되어 실제 데이터를 예측할 때 성능이 안 좋을 수 있음.
  • 그래서 고정된 Train / Test set으로 학습하고 평가하다보면 과적합을 일으킬 수 있기 때문에 이를 해결하고자 나온 개념이 Cross-Validation.

교차검증의 종류

(1) K-fold cross-validation

  • 전체 데이터 셋을 k의 subset으로 나누고 k번 평가를 실행함.
  • 단, test set을 중복 없이 바꿔가면서 평가를 진행해야함.
  • 최종 평가는 k번 평가한 결과의 평균 값으로 성능을 평가함.

(2) Stratified K-fold cross validation

  • class A, B가 각 90/10 비율을 가질 때, 계층별 k-fold cross validation에서 각 분할의 90%가 class A, 10%는 class B로 실행한다는 의미.

(3) Group K-fold

  • Train / Test set에서 동일한 그룹이 표시되지 않도록 하는 K-fold 의 변형 방식.
  • 각 그룹은 다른 test fold에 있고, 같은 그룹은 Train / Test에 동시에 포함되지 않음.
  • 즉, 두 개의 다른 폴드에 동일한 그룹이 나타나지 않음.

시계열 교차검증의 종류

(1) Time Serise Cross-Validation


k 번째 fold 까지 Train, k+1 번째 까지 Test하는 k-fold 방식

import numpy as np
from sklearn.model_selection import TimeSeriesSplit 
from matplotlib.patches import Patch
import matplotlib.pyplot as plt

cmap_data = plt.cm.Paired
cmap_cv = plt.cm.coolwarm
plt.style.use('fivethirtyeight')

def plot_cv_indices(cv, X, n_splits, lw=10):

    fig, ax = plt.subplots()
    """Create a sample plot for indices of a cross-validation object."""

    # Generate the training/testing visualizations for each CV split
    for ii, (tr, tt) in enumerate(cv.split(X=X)):
        # Fill in indices with the training/test groups
        indices = np.array([np.nan] * len(X))
        indices[tt] = 1
        indices[tr] = 0

        # Visualize the results
        ax.scatter(range(len(indices)), [ii + .5] * len(indices),
                   c=indices, marker='_', lw=lw, cmap=cmap_cv,
                   vmin=-.2, vmax=1.2)

    # Formatting
    yticklabels = list(range(n_splits))
    ax.set(yticks=np.arange(n_splits) + .5, yticklabels=yticklabels,
           xlabel='Sample index', ylabel="CV iteration",
           ylim=[n_splits+0.1, -.1], xlim=[0, len(X)])
    ax.set_title('{}'.format(type(cv).__name__), fontsize=15)

    ax.legend([Patch(color=cmap_cv(.8)), Patch(color=cmap_cv(.02))],
          ['Testing set', 'Training set'], loc=(1.02, .8))

# source code site : https://gmnam.tistory.com/230
x = np.arange(100)

tscv = TimeSeriesSplit(n_splits = 5)
plot_cv_indices(tscv, x, n_splits = 5)
  • np.array 함수로 0~99 까지 데이터 셋을 만들어보자. 이 데이터 셋은 시간순으로 기록된 시계열 데이터라 가정한다.
    • sklearn에 TimeSeriesSplit 함수를 사용한다.
    • n_split: 몇 번의 반복으로 교차검증을 할건지 정한다.
    • n_split = n : n개의 훈련용 + 검증용 데이터 셋을 만듦.

(2) Blocked Cross-Validation

  • Train Data와 Test Data의 크기를 모든 교차검증 횟수에서 고정시키는 것.
  • Generator 클래스를 정의해야 함.
class BlockingTimeSeriesSplit():
    def __init__(self, n_splits):
        self.n_splits = n_splits

    def get_n_splits(self, groups):
        return self.n_splits

    def split(self, X, y=None, groups=None):
        n_samples = len(X)
        k_fold_size = n_samples // self.n_splits
        indices = np.arange(n_samples)

        margin = 0
        for i in range(self.n_splits):
            start = i * k_fold_size
            stop = start + k_fold_size
            mid = int(0.8 * (stop - start)) + start
            yield indices[start: mid], indices[mid + margin: stop]
btscv = BlockingTimeSeriesSplit(n_splits=5)
plot_cv_indices(btscv, x, n_splits=5)
728x90
Comments