교차 검증

이 포스트는 fastcampus에서 강의를 하고 계시는 김도형 박사님의 강의록을 따라 쓰며 연습한 포스트입니다.

데이터 사이언스 스쿨

표본 내 성능과 표본 외 성능

회귀분석 모형을 만들기 위해서는 모수 추정 즉 학습을 위한 데이터 집합이 필요하다. 보통 회귀분석 성능을 이야기할 때는 이 학습 데이터 집합의 종속 변수값을 얼마나 잘 예측하였는지를 결정 계수(codefficient of determination) 등을 이용하여 따진다. 이러한 성능을 표본 내 성능 검증(in-sample testing)이라고 한다.

그런데 회귀분석 모형을 만드는 목적 중 하나는 종속 변수의 값을 아직 알지 못하고 따라서 학습에 사용하지 않은 표본에 대해 종속 변수의 값을 알아내고자 하는 것 즉 예측(prediction)이다. 이렇게 학습에 쓰이지 않는 표본 데이터 집합의 종속 변수 값을 얼마나 잘 예측하는가를 검사하는 것을 표본 외 성능 검증(out-of-sample testing) 혹은 교차 검증(cross validation)이라고 한다.

과최적화

일반적으로 표본 내 성능과 표본 외 성능은 비슷한 수준을 보이지만 경우에 따라서는 표본 내 성능은 좋으면서 표본 외 성능이 상대적으로 많이 떨어지는 수도 있다. 이러한 경우를 과최적화(overfitting)라고 한다. 과최적화가 발생하면 학습에 쓰였던 표본 데이터에 대해서는 종속변수의 값을 잘 추정하지만 새로운 데이터를 주었을 때 전혀 예측하지 못하기 때문에 예측 목적으로는 쓸모없는 모형이 된다.

검증용 데이터 집합

교차 검증을 하려면 두 종류의 데이터 집합이 필요하다.

  • 모형 추정 즉 학습을 위한 데이터 집합 (training data set)
  • 성능 검증을 위한 데이터 집합 (test data set)

두 데이터 집합 모두 종속 변수값이 있어야 한다. 따라서 보통은 가지고 있는 데이터 집합을 학습용과 검증용으로 나누어 학습용 데이터만을 사용하여 회귀분석 모형을 만들고 검증용 데이터로 성능을 계산하는 학습/검증 데이터 분리(train-test split) 방법을 사용한다.

statsmodels 패키지에서의 교차 검증

사실 소수의 입력 변수와 소규모 데이터를 사용하는 전통적인 회귀분석에서는 다항 회귀 등의 방법으로 모형 차수를 증가시키지 않는 한 과최적화가 잘 발생하지 않는다. 따라서 statsmodels 패키지에는 교차 검증을 위한 기능이 별도로 준비되어 있지 않고 사용자가 직접 코드를 작성해야 한다.

scikit-learn의 교차 검증 기능

독립 변수의 개수가 많은 빅데이터에서는 과최적화가 쉽게 발생한다. 따라서 scikit-learn 의 model_selection 서브 패키지는 교차 검증을 위한 다양한 명령을 제공한다.

단순 데이터 분리

train_test_split 명령은 데이터를 학습용 데이터와 검증용 데이터로 분리한다.

1
train_test_split(data, data2, test_size, train_size, random_state)
  • data: 독립 변수 데이터 배열 또는 pandas 데이터 프레임
  • data2: 종속 변수 데이터. data인수에 종속 변수 데이터가 같이 있으면 생략할 수 있다.
  • test_size: 검증용 데이터 개수. 1보다 작은 실수이면 비율을 나타낸다.
  • train_size: 학습용 데이터의 개수. 1보다 작은 실수이면 비율을 나타낸다. test_sizetrain_size중 하나만 있어도 된다.
  • random_state: 난수 시드
1
2
3
4
from sklearn.model_selection import train_test_split

df_train, df_test = train_test_split(df, test_size=0.3, random_state=0)
df_train.shape, df_test.shape
1
2
dfx_train, dfx_test, dfy_train, dfy_test = train_test_split(dfx, dfy, test_size=0.3, random_state=0)
dfx_train.shape, dfy_train.shape, dfx_test.shape, dfy_test.shape

K- 폴드 교차 검증

데이터의 수가 적은 경우에는 이 데이터 중의 일부인 검증 데이터의 수도 적기 때문에 검증 성능의 신뢰도가 떨어진다. 그렇다고 검증 데이터의 수를 증가시키면 학습용 데이터의 수가 적어지므로 정상적인 학습이 되지 않는다. 이러한 딜레마를 해결하기 위한 검증 방법이 K-폴드(K-fold) 교차 검증 방법이다.

K-폴드 교차 검증에서는 전체 데이터를 K개의 부분집합($\{1, 2, \cdots , K\}$)로 나눈 뒤 다음과 같이 학습과 검증을 반복한다.

  • 데이터 $\{1, 2, \cdots, K - 1\}$를 학습용 데이터로 사용하여 회귀분석 모형을 만들고 데이터 $\{K\}$ 로 교차 검증을 한다.
  • 데이터 $\{1, 2, \cdots, K - 2, K\}$를 학습용 데이터로 사용하여 회귀분석 모형을 만들고 데이터 $\{K-1\}$로 교차 검증을 한다.
  • $\vdots$
  • 데이터 $\{2, \cdots, K\}$를 학습용 데이터로 사용하여 회귀분석 모형을 만들고 데이터 $\{1\}$로 교차 검증을 한다.

이렇게 하면 총 K개의 모형과 K개의 교차 검증 성능이 나온다. 이 K개의 교차 검증 성능을 평균하여 최종 교차 검증 성능을 계산한다.

scikit-learn 패키지의 model_selection 서브 패키지는 KFold 클래스를 비롯한 다양한 교차 검증 생성기를 제공한다. 이 생성기의 split 메서드는 학습용과 검증용의 데이터 인덱스를 출력하는 파이썬 반복자(iterator)를 반환한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.model_selection import KFold

scores = np.zeros(5)
cv = KFold(5, shuffle=True, random_state=0)
for i, (idx_train, idx_test) in enumerate(cv.split(df)):
df_train = df.iloc[idx_train]
df_test = df.iloc[idx_test]

model = sm.OLS.from_formula("MEDV ~ " + "+".join(boston.feature_names), data=df_train)
result = model.fit()

pred = result.predict(df_test)
rss = ((df_test.MEDV - pred) ** 2).sum()
tss = ((df_test.MEDV - df_test.MEDV.mean()) ** 2).sum()
rsquared = 1 - rss/tss

scores[i] = rsquared
print("train R2 = {:.8f}, test R2 = {:.8f}".format(result.rsquared, rsquared))
Share