[Python] 데이터 사이언스 스쿨 - 5.2 교차검증
Updated:
데이터 사이언스 스쿨 자료를 토대로 공부한 내용입니다.
실습과정에서 필요에 따라 내용의 누락 및 추가, 수정사항이 있습니다.
기본 세팅
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
mpl.rc('font', family='NanumGothic') # 폰트 설정
mpl.rc('axes', unicode_minus=False) # 유니코드에서 음수 부호 설정
# 차트 스타일 설정
sns.set(font="NanumGothic", rc={"axes.unicode_minus":False}, style='darkgrid')
plt.rc("figure", figsize=(10,8))
warnings.filterwarnings("ignore")
5.2 교차검증
회귀분석 모형을 주요 목적 중 하나는 종속 변수의 값을 모르고 독립변수만 알 때 종속변수를 예측하는 것이다.
보통은 학습 데이터(train)로 모델을 학습 시킨 후 검증 데이터(test)로 모델의 성능을 평가한다.
이를 교차검증이라 한다.
5.2.1 statsmodels 패키지의 교차검증
statsmodels 패키지에는 교차검증을 위한 기능이 별도로 준비되어 있지 않고 사용자가 직접 코드를 작성해야 한다.
from sklearn.datasets import load_boston
boston = load_boston()
dfX = pd.DataFrame(boston.data, columns=boston.feature_names)
dfy = pd.DataFrame(boston.target, columns=["MEDV"])
df = pd.concat([dfX, dfy], axis=1)
# 학습/검증 데이터 분리
N = len(df) # boston row
ratio = 0.7
np.random.seed(0)
idx_train = np.random.choice(np.arange(N), np.int(ratio * N)) # 원 데이터의 70% train set
idx_test = list(set(np.arange(N)).difference(idx_train)) # 30% test set
df_train = df.iloc[idx_train]
df_test = df.iloc[idx_test]
import statsmodels.api as sm
model = sm.OLS.from_formula("MEDV ~ " + "+".join(boston.feature_names), data=df_train)
result = model.fit()
print(f"train set 결정계수: {result.rsquared:0.3f}")
train set 결정계수: 0.757
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
print(f"test set 결정계수: {rsquared:0.3f}")
test set 결정계수: 0.688
5.2.2 scikit-learn 패키지의 교차검증
sklearn.model_selection
의 train_test_split()
명령어로 데이터 분리가 가능하다.
train_test_split(X_data, y_data, test_size, train_size, random_state)
-
X_data: 독립 변수 데이터 배열 또는 pandas 데이터프레임
-
y_data: 종속 변수 데이터. data 인수에 종속 변수 데이터가 같이 있으면 생략할 수 있다.
-
test_size: 검증용 데이터 개수. 1보다 작은 실수이면 비율을 나타낸다.
-
train_size: 학습용 데이터의 개수. 1보다 작은 실수이면 비율을 나타낸다. test_size와 train_size 중 하나만 있어도 된다.
-
random_state: 난수 시드
# 데이터 프레임에 독립변수, 종속변수가 같이 있는 경우
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
((354, 14), (152, 14))
-
train 70%, test 30%의 비율로 데이터가 잘 분리되었다.
-
독립변수, 종속변수 데이터가 같이 있으면 train 데이터 프레임과 test 데이터 프레임을 반환한다.
# 독립변수, 종속변수 데이터가 따로 있는 경우
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
((354, 13), (354, 1), (152, 13), (152, 1))
- 독립변수, 종속변수 데이터가 따로 있으면 순서대로 X_train, X_test, y_train, y_test를 반환한다.
5.2.3 K-폴드 교차검증
sklearn.model_selection
의 KFold()
를 이용해 K-폴드 교차검증을 진행할 수 있다.
sklearn.metrics
에는 예측성능을 평가하기 위한 다양한 함수가 내제되어있다.
from sklearn.model_selection import KFold
from sklearn.metrics import r2_score
scores = np.zeros(5)
cv = KFold(5, shuffle=True, random_state=0) # k=5
# .split으로 train, test 인덱스 반환
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)
rsquared = r2_score(df_test.MEDV, pred)
scores[i] = rsquared
print("학습 R2 = {:.8f}, 검증 R2 = {:.8f}".format(result.rsquared, rsquared))
학습 R2 = 0.77301356, 검증 R2 = 0.58922238
학습 R2 = 0.72917058, 검증 R2 = 0.77799144
학습 R2 = 0.74897081, 검증 R2 = 0.66791979
학습 R2 = 0.75658611, 검증 R2 = 0.66801630
학습 R2 = 0.70497483, 검증 R2 = 0.83953317
5.2.4 교차검증 반복
sklearn.model_selection
의 cross_val_score()
를 이용해 교차검증 반복을 쉽게 진행할 수 있다.
cross_val_score()
는 sklearn
의 모형만 사용가능하다.
cross_val_score(model, X, y, scoring=None, cv=None)
-
model: 회귀 분석 모형
-
X: 독립 변수 데이터
-
y: 종속 변수 데이터
-
scoring: 성능 검증에 사용할 함수 이름
-
cv: 교차검증 생성기 객체 또는 숫자로 None이면 KFold(3) 숫자 k이면 KFold(k)
statsmodels 모형을 사용할 경우
cross_val_score()
에 statsmodels의 모형을 사용하려면 다음과 같이 scikit-learn의 RegressorMixin으로 래퍼 클래스를 만들어주어야 한다.
from sklearn.base import BaseEstimator, RegressorMixin
import statsmodels.formula.api as smf
import statsmodels.api as sm
class StatsmodelsOLS(BaseEstimator, RegressorMixin):
def __init__(self, formula):
self.formula = formula
self.model = None
self.data = None
self.result = None
def fit(self, dfX, dfy):
self.data = pd.concat([dfX, dfy], axis=1)
self.model = smf.ols(self.formula, data=self.data)
self.result = self.model.fit()
def predict(self, new_data):
return self.result.predict(new_data)
from sklearn.model_selection import cross_val_score
model = StatsmodelsOLS("MEDV ~ " + "+".join(boston.feature_names))
cv = KFold(5, shuffle=True, random_state=0)
r2_scores = cross_val_score(model, dfX, dfy, scoring="r2", cv=cv)
r2_score = np.mean(r2_scores)
print(f"각 fold별 결정계수: {np.round(r2_scores,3)}")
print(f"평균 결정계수: {r2_score:.3f}")
각 fold별 결정계수: [0.589 0.778 0.668 0.668 0.84 ]
평균 결정계수: 0.709
sklearn 모형을 사용할 경우
from sklearn.linear_model import LinearRegression
model = LinearRegression(fit_intercept=True).fit(dfX,dfy)
r2_scores = cross_val_score(model, dfX, dfy, scoring="r2", cv=cv)
r2_score = np.mean(r2_scores)
print(f"각 fold별 결정계수: {np.round(r2_scores,3)}")
print(f"평균 결정계수: {r2_score:.3f}")
각 fold별 결정계수: [0.589 0.778 0.668 0.668 0.84 ]
평균 결정계수: 0.709
- 결과는 동일하며 여기선 평균 결정계수로 모델의 성능을 평가한다.
Leave a comment