[Tree Based Model] 모델선택(Model Selection)

교차검증(Cross-Validation)

  • 이 전 포스팅 까지 훈련/검증/테스트 세트로 나누어 학습했다.
  • 이 방법을 Hold-out 교차검증이라 하는데 이 방법의 문제점은
    • 학습에 사용가능한 데이터가 충분하다면 문제가 없겠지만, 훈련세트의 크기가 모델학습에 충분하지 않을 경우 문제가 될 수 있다.
    • 검증세트 크기가 충분히 크지 않다면 예측 성능에 대한 추정이 부정확할 것이다.
  • 모델 선택(Model Selection)문제
    • 문제를 풀기 위해 어떤 학습 모델을 사용해야 할 것인가?
    • 어떤 하이퍼파라미터를 사용할 것인가?
  • 데이터의 크기에 대한 문제, 모델선택에 대한 문제를 해결하기 위해 사용하는 방법 중 한가지는 교차검증이다.
    (교차검증은 시계열(time series))데이터에 적합하지 않다.)

스크린샷 2021-08-20 10 25 24

교차검증 사용

  • 교차검증을 위해서는 데이터를 k개로 등분해야 하는데 이를 k-fold cross validation(CV)라고 한다.
  • k개의 집합에서 k-1 개의 부분집합을 훈련에 사용하고 나머지 부분집합을 테스트 데이터로 검증하게 된다.
    • 데이터를 3등분하고 검증과 훈련세트를 총 세번 바꾸어가며 검증하는 것은 3-fold CV 이다.

선형모델 교차검증

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from category_encoders import OneHotEncoder
from sklearn.feature_selection import f_regression, SelectKBest
from sklearn.impute import SimpleImputer
from sklearn.linear_model import Ridge
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

target = 'SalePrice'

features = train.columns.drop([target])

X_train = train[features]
y_train = train[target]

X_test = test[features]
y_test = test[target]


pipe = make_pipeline(
    OneHotEncoder(use_cat_names=True), 
    SimpleImputer(strategy='mean'), 
    StandardScaler(), 
    SelectKBest(f_regression, k=20),
    Ridge(alpha=1.0)
)

# 3-fold 교차검증을 수행합니다.
k = 3
scores = cross_val_score(pipe, X_train, y_train, cv=k, 
                         scoring='neg_mean_absolute_error')

print(f'MAE ({k} folds):', -scores)
'''
MAE (3 folds): [19912.3716215  23214.74205495 18656.29713167]
'''

-scores.mean()
'''
20594.470269371817
'''

scores.std()
'''
1922.4635156881875
'''

랜덤포레스트 교차검증

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from category_encoders import TargetEncoder
from sklearn.ensemble import RandomForestRegressor

pipe = make_pipeline(
    # TargetEncoder: 범주형 변수 인코더로, 타겟값을 특성의 범주별로 평균내어 그 값으로 인코딩
    TargetEncoder(min_samples_leaf=1, smoothing=1),
    SimpleImputer(strategy='median'), 
    RandomForestRegressor(max_depth = 10, n_jobs=-1, random_state=2)
)

k = 3
scores = cross_val_score(pipe, X_train, y_train, cv=k, 
                         scoring='neg_mean_absolute_error')

print(f'MAE for {k} folds:', -scores)
-scores.mean()
'''
17018.19573706592
'''

scores.std()
'''
1797.7668143704145
'''

하이퍼파라미터 튜닝

  • 머신러닝 모델을 만들 때 중요한 이슈는 최적화(Optimization)와, 일반화(Generalization)이다.
    • 최적화 : 훈련 데이터로 더 좋은 성능을 얻기 위해 모델을 조정하는 과정
    • 일반화 : 학습된 모델이 처음 본 데이터에서 얼마나 좋은 성능을 내는 지
  • 모델의 복잡도를 높이는 과정에서 훈련/검증 세트의 손실이 함께 감소하는 시점은 과소적합(Underfitting)되었다고 한다.
  • 어느 시점부터 훈련데이터의 손실은 계속 감소하지만, 검증데이터의 손실이 증가하는 때는 과적합(Overfitting)되었다고 한다.
  • 이상적인 모델은 Underfitting과 Overfitting 사이에 존재한다.

스크린샷 2021-08-20 10 36 20

검증곡선(Validation curve)

  • 검증곡선 : 훈련/검증데이터에 대해 y는 score, x는 하이퍼파라미터로 그린 그래프
  • 하나의 파라미터만 가지고 검증곡선을 그리는 것이 현실적으로 유용하지는 않다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import matplotlib.pyplot as plt
from category_encoders import OrdinalEncoder
from sklearn.model_selection import validation_curve
from sklearn.tree import DecisionTreeRegressor

pipe = make_pipeline(
    OrdinalEncoder(), 
    SimpleImputer(), 
    DecisionTreeRegressor()
)

depth = range(1, 30, 2)
ts, vs = validation_curve(
    pipe, X_train, y_train
    , param_name='decisiontreeregressor__max_depth'
    , param_range=depth, scoring='neg_mean_absolute_error'
    , cv=3
    , n_jobs=-1
)

train_scores_mean = np.mean(-ts, axis=1)
validation_scores_mean = np.mean(-vs, axis=1)

fig, ax = plt.subplots()

# 훈련세트 검증곡선
ax.plot(depth, train_scores_mean, label='training error')

# 검증세트 검증곡선
ax.plot(depth, validation_scores_mean, label='validation error')

# 이상적인 max_depth
ax.vlines(5,0, train_scores_mean.max(), color='blue')

# 그래프 셋팅
ax.set(title='Validation Curve'
      , xlabel='Model Complexity(max_depth)', ylabel='MAE')
ax.legend()
fig.dpi = 100

스크린샷 2021-08-20 10 38 08

  • 이대로 트릐의 깊이를 정한다면 max_depth = 5 부근에서 설정해줘야 과적합을 막고 일반화 성능을 지킬 수 있다.

Randomized Search CV

  • GridSearchCV: 검증하고 싶은 하이퍼파라미터들의 수치를 정해주고 그 조합을 모두 검증한다.
  • RandomizedSearchCV: 검증하려는 하이퍼파라미터들의 값 범위를 지정해주면 무작위로 값을 지정해 그 조합을 모두 검증한다.

스크린샷 2021-08-20 10 39 52

Ridge 회귀모델 하이퍼파라미터 튜닝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from sklearn.model_selection import RandomizedSearchCV

pipe = make_pipeline(
    OneHotEncoder(use_cat_names=True)
    , SimpleImputer()
    , StandardScaler()
    , SelectKBest(f_regression)
    , Ridge()
)

# 튜닝할 하이퍼파라미터의 범위를 지정해 주는 부분
dists = {
    'simpleimputer__strategy': ['mean', 'median'], 
    'selectkbest__k': range(1, len(X_train.columns)+1), 
    'ridge__alpha': [0.1, 1, 10], 
}

clf = RandomizedSearchCV(
    pipe, 
    param_distributions=dists, 
    n_iter=50, 
    cv=3,
    scoring='neg_mean_absolute_error',
    verbose=1,
    n_jobs=-1
)

clf.fit(X_train, y_train);
'''
Fitting 3 folds for each of 50 candidates, totalling 150 fits
'''

print('최적 하이퍼파라미터: ', clf.best_params_)
print('MAE: ', -clf.best_score_)
'''
최적 하이퍼파라미터:  {'simpleimputer__strategy': 'median', 'selectkbest__k': 55, 'ridge__alpha': 10}
MAE:  18414.633797820472
'''

랜덤포레스트 하이퍼파라미터 튜닝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from scipy.stats import randint, uniform

pipe = make_pipeline(
    TargetEncoder(), 
    SimpleImputer(), 
    RandomForestRegressor(random_state=2)
)

dists = {
    'targetencoder__smoothing': [2.,20.,50.,60.,100.,500.,1000.], # int로 넣으면 error(bug)
    'targetencoder__min_samples_leaf': randint(1, 10),     
    'simpleimputer__strategy': ['mean', 'median'], 
    'randomforestregressor__n_estimators': randint(50, 500), 
    'randomforestregressor__max_depth': [5, 10, 15, 20, None], 
    'randomforestregressor__max_features': uniform(0, 1) # max_features
}

clf = RandomizedSearchCV(
    pipe, 
    param_distributions=dists, 
    n_iter=50, 
    cv=3, 
    scoring='neg_mean_absolute_error',  
    verbose=1,
    n_jobs=-1
)

clf.fit(X_train, y_train);
'''
Fitting 3 folds for each of 50 candidates, totalling 150 fits
'''

print('최적 하이퍼파라미터: ', clf.best_params_)
print('MAE: ', -clf.best_score_)
'''
최적 하이퍼파라미터:  {'randomforestregressor__max_depth': 20, 'randomforestregressor__max_features': 0.22612308958451122, 'randomforestregressor__n_estimators': 498, 'simpleimputer__strategy': 'mean', 'targetencoder__min_samples_leaf': 8, 'targetencoder__smoothing': 1000.0}
MAE:  15741.360087309344
'''

# 만들어진 모델에서 가장 성능이 좋은 모델
pipe = clf.best_estimator_

from sklearn.metrics import mean_absolute_error

y_pred = pipe.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
print(f'테스트세트 MAE: ${mae:,.0f}')
'''
테스트세트 MAE: $15,778
'''

features
'''
Index(['MSSubClass', 'MSZoning', 'LotFrontage', 'LotArea', 'Street', 'Alley',
       'LotShape', 'LandContour', 'Utilities', 'LotConfig', 'LandSlope',
       'Neighborhood', 'Condition1', 'Condition2', 'BldgType', 'HouseStyle',
       'OverallQual', 'OverallCond', 'YearBuilt', 'YearRemodAdd', 'RoofStyle',
       'RoofMatl', 'Exterior1st', 'Exterior2nd', 'MasVnrType', 'MasVnrArea',
       'ExterQual', 'ExterCond', 'Foundation', 'BsmtQual', 'BsmtCond',
       'BsmtExposure', 'BsmtFinType1', 'BsmtFinSF1', 'BsmtFinType2',
       'BsmtFinSF2', 'BsmtUnfSF', 'TotalBsmtSF', 'Heating', 'HeatingQC',
       'CentralAir', 'Electrical', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath',
       'HalfBath', 'BedroomAbvGr', 'KitchenAbvGr', 'KitchenQual',
       'TotRmsAbvGrd', 'Functional', 'Fireplaces', 'FireplaceQu', 'GarageType',
       'GarageYrBlt', 'GarageFinish', 'GarageCars', 'GarageArea', 'GarageQual',
       'GarageCond', 'PavedDrive', 'WoodDeckSF', 'OpenPorchSF',
       'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'PoolQC',
       'Fence', 'MiscFeature', 'MiscVal', 'MoSold', 'YrSold', 'SaleType',
       'SaleCondition', 'All_Flr_SF', 'All_Liv_SF'],
      dtype='object')
'''

선형회귀, 랜덤포래스트 모델들의 튜닝 추천 하이퍼파라미터

Random Forest

  • class_weight (불균형(imbalanced) 클래스인 경우)
  • max_depth (너무 깊어지면 과적합)
  • n_estimators (적을경우 과소적합, 높을경우 긴 학습시간)
  • min_samples_leaf (과적합일경우 높임)
  • max_features (줄일 수록 다양한 트리생성)

Logistic Regression

  • C (Inverse of regularization strength)
  • class_weight (불균형 클래스인 경우)
  • penalty

Ridge / Lasso Regression

  • alpha
0%