train data는 모델을 만들 때 사용되고, validation data로 모델의 hyperparameter를 조정한 여러 model들의 성능을 비교해 최적의 모델을 선택하며, test data는 모델을 최종적으로 1회 평가하는 용도로 사용한다.
ML의 목적 : generalization이 잘 된 예측 모델을 만드는 것
- generalization : 모델이 새로운 데이터에 적절하게 적응하는 능력
새로운 데이터를 예측하는 모델의 generalization 능력을 검증하기 위해,
- hold-out validation : train, test data를 일정 비율로 한 번 나누어 모델을 만드는 방식
- cross-validation : 사용할 수 있는 데이터의 크기가 비교적 작을 때에는 hold out기법보다는 k-fold cross validation 사용하는게 나음(random하게 교차해주어 )
- k-fold cross validation : data를 k개의 그룹으로 나누어, random하게 k번 교차하여 train, test data를 만드는 방식k번 학습 후, k개의 예측 성능 결과를 평균→ 최종 검증 스코어 도출
🌀 Data Split & Validation
Hold out
: test set를 따로 떼어놓는 방법
- 2 way Hold-Out : train set / test set
test set는 한번만 사용해야함. test set 여러번 사용 시, 의도치 않게 test set에 적합한 model을 만들 수 있다.
모델이 보지 않은 데이터라는 의미가 퇴색되어버리지 않기 위해.
- 3 way Hold-Out : train set / valid set / test set
train set로 모델 훈련 후, valid set로 모델 여러 번 평가 후 개선, 단 한번의 test set이용해 결과 도출
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.linear_model import LinearRegression
x = df[[['OverallQual', 'GrLivArea', 'GarageCars', 'TotalBsmtSF', '1stFlrSF', 'FullBath']]
y = df['SalePrice']
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
x_train, x_val, y_train, y_val = train_test_split(x_train, y_train, test_size=0.2, random_state=42)
# 성능 평가 함수
def eval_models(y_true, y_pred):
mse = mean_squared_error(y_true, y_pred)
rmse = math.sqrt(mse)
mae = mean_absolute_error(y_true, y_pred)
r2 = r2_score(y_true, y_pred)
return mse, rmse, mae, r2
# 기초 모델
base = [y_train.mean()]*len(y_train)
# 단순선형회귀 모델
simple_model = LinearRegression()
simple_model.fit(x_train, y_train)
y_train_pred = simple_model.predict(x_train)
y_val_pred = simple_model.predict(x_val)
y_test_pred = simple_model.predcit(x_test)
comparison = pd.DataFrame(index=['mse','rmse','mae','r2'], column=['base','train','val','test'])
comparison['base'] = eval_models(y_train, base)
comparison['train'] = eval_models(y_train, y_train_pred)
comparison['val'] = eval_models(y_val, y_val_pred)
comparison['test'] = eval_models(y_test, y_test_pred)
comparison
3-way holdout-validation의 문제점 : 데이터의 크기가 작은 경우 적절하지 않은 방법이다.
- 모델학습에 사용되는 데이터의 양이 줄어들어, 모델이 데이터의 패턴을 정확히 파악할 수 없다.
- validation 크기가 충분하지 않다면 예측 성능에 대한 추정이 부정확함.
→ 이럴 때, Cross-validation 사용하자!
Cross-validation
교차검증(cv)
교차검증은 holdout과 달리, 훈련 세트의 모든 샘플이 검증에 사용된다.
일반적으로 가장 많이 사용하는 것은 k-fold cross-validation
- 데이터를 k개로 분할
- 1개는 valid set, 나머지 k-1개는 모두 train set
- k번의 검증 결과평균으로 전체 모델의 성능 평가
데이터셋의 크기가 작은 경우, cross validation 이용
예를 들면, 데이터셋의 크기가 1460개로 많지 않은 경우,
holdout-validation시, 실제 학습에 사용되는 데이터의 크기는 821밖에 되지 않는다.
But, cross-validation시, 1095개의 샘플을 학습에 사용할 수 있다.
KFold
sklearn의 KFold
함수를 통해, 데이터를 k개 분할
from sklearn.model_selection import KFold
kf = KFold(n_splits=5) # 데이터를 5개로 분
cv_result = []
for train_idx, test_idx in kf.split(X_train) :
X_train_cv, X_val_cv = X_train.iloc[train_idx], X_train.iloc[test_idx]
y_train_cv, y_val_cv = y_train.iloc[train_idx], y_train.iloc[test_idx]
# 위에서 선언한 linear regression
model.fit(X_train_cv, y_train_cv)
# 모델 예측
y_pred_cv = model.predict(X_val_cv)
# mae를 구해보겠습니다.
mae_cv = mean_absolute_error(y_val_cv, y_pred_cv).round(2)
cv_result.append(mae_cv)
print('k =', kf.get_n_splits()) # 몇개의 파트로 분리했는지 알려줌.
print('mae for each fold : ', cv_result)
print('average mae for model : ', np.mean(cv_result).round(2))
print('std of result : ', np.std(cv_result).round(2))
>> k = 5
mae for each fold : [29572.96, 28756.54, 25459.89, 29523.81, 28620.38]
average mae for model : 28386.72
std of result : 1513.79
Cross_val_score
위와 비슷한 방법으로, 간단한 방법을 사용해보자.
이 방법은 validation set을 따로 지정하지 않고 한다. 데이터 분할 작업이 자동으로 이루어진다.
결과는 각기 모델이 학습한 데이터셋이 조금씩 다를 수 있으므로, 결과값은 조금 차이가 있을 수 있다.
cross_val_score
를 통해 각 fold의 스코어 확인 가능
- 각 검증마다 구해진 평가지표 score를 list 형태로 반환
- 각 값은 음수로 되어있음
- 결과값은 valid 데이터의 성능을 나타냄.
from skelearn.model_selection import cross_val_score
cv_results = cross_val_score(model,
cv=5,
scoring='neg_mean_sqaured_error",
n_jobs=-1)
# n_jobs : (deafault=None, 단일 CPU 사용해 계산)
# n_jobs=-1 : 모든 가능한 CPU 코어 사용 -> 가능한 빠르게 작업
print('mae for each fold : ', -np.round(cv_results, 2))
print('average mae for model : ', -np.mean(cv_results).round(2))
print('std of result : ', np.std(cv_results).round(2))
>> mae for each fold : [28295.36 29845.32 26597.92 24041.59 32435.82]
average mae for model : 28243.2
std of result : 2846.33
k-fold cross validation은 k번 학습을 진행하기 때문에 학습 시간이 holdout보다 오래 걸린다.
→ 데이터의 사이즈에 따라 검증 방법을 달리 해야한다.
🌀 Bias-Variance Trade-off
Bias (편향)
: 모델의 예측값, 실제값의 차이
Variance (분산)
: 서로 다른 데이터가 들어왔을 경우 모델의 예측값이 변동되는 양
분산 에러 : train set의 결과에서
작은 변동이 있을 경우, 모델이 얼마나 민감하게 반응하는지에 따른 에러
분산이 큰 모델은 훈련 데이터의 노이즈까지 학습하여, overfitting이 일어날 수 있다.
Bias-Variance Trade-off
- MSE 식을 reducible, irreducible 에러로 나누어 표현하면 Bias 에러 + Variance 에러 + irreducible 에러로 나뉘게 됩니다.(irreducible error : 데이터 자체에 내재된 노이즈와 같은 불가피한 오차)
MSE가 고정되어있다는 가정 하에
Bias error가 커지면 Variance에러가 작아지고, 반대로 Variance에러가 커지면 Bias error가 작아지는 Trade-Off관계에 있다.
Model complex → Variance error 커짐. → Overfitting
Model simple → Bias error 커짐. → Underfitting
⇒ 모델의 복잡도를 변경하며, 중간의 Generalization이 잘되는 지점을 찾아야한다.
유연성이 높은 non-parametric 모델이나, nonlinear 모델에서 나타날 가능성이 높다. (선형회귀처럼 강력한 가정을 하지 않은 모델이라, 유연하게 데이터를 학습하기 때문)
다항선형회귀에서 과적합을 막기 위해 적합한 차수는 몇일까?
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import mean_absolute_error
from sklearn.linear_model import LinearRegression
def PolynomialRegression(**kwargs):
return make_pipeline(PolynomialFeatures(degree), LinearRegression(**kwargs))
degrees = [1,2,3,4,5]
mse_li = []
r2_li = []
for degree in degrees:
model = PolynomialRegression(degree)
model.fit(x_train, y_train)
y_train_pred = model.predict(x_train)
y_val_pred = model.predict(x_val)
mse_train = mean_absolute_error(y_train, y_train_pred)
mse_val = mean_absolute_error(y_val, y_val_pred)
r2_train = r2_score(y_train, y_train_pred)
r2_val = r2_score(y_val, y_val_pred)
mse_li.append([mse_train, mse_val])
r2_li.append([r2_train, r2_val])
fig, axs = plt.subplots(1,2)
axs[0].plot(degree, mse_li, label=['train','val'])
axs[0].get_title('MSE')
axs[0].legend()
axs[1].plot(degree, r2_li, label=['train', 'val'])
axs[1].get_title('R2')
axs[1].legend();
Overfitting 방지 방법
- 데이터의 개수 늘린다.
- 특성 개수(차수)를 줄인다.
- 규제 추가 (L1,L2..)
(편향을 희생하고, 분산을 줄여보자!)
More Study
R2_score 계산 방법 두가지
# 1번째 방법
from sklearn.metrics import r2_score
# predict 후, r2score 사용 가능
r2_score(y_train, y_pred)
# 2번째 방법
# predict 없이, fit 후 사용 가능
model.score(x_train, y_train)
시계열 데이터에 적합한 cross-validation 기법
: 시계열 데이터는 train, test data 사이의 시간 순서가 중요하기 때문에 무작위로 섞는 k-fold cross-validation을 사용할 수 없다.
[Reference]
- Generalization and Machine Learning.
- 여러 가지 예시를 보면서 일반화란 무엇인지 설명해보세요.
- Machine Learning Fundamentals: Bias and Variance
- low bias, low variance 모델은 어떤 모델을 말하나요? 훈련/테스트세트를 사용해 설명해 보세요.
훈련/검증/테스트 세트 분리
cross-validation
Bias-Variance Tradeoff
'Machine Learning' 카테고리의 다른 글
[The Based Models 2] Boosting (0) | 2023.04.14 |
---|---|
[The Based Models 1] Tree Based Model (0) | 2023.04.14 |
[Linear Models 4] Logistic Regression (0) | 2023.04.14 |
[Linear Models 3] Regularized Regression (0) | 2023.04.14 |
[Linear Models 1] Linear Regression (0) | 2023.04.14 |