Machine Learning (기계학습) - part 9 [교차 검증, 모델 최적화, 파이프라인]
2021년도에 대학교 과목으로 들었던 '기계학습' 강의를 복습 및 정리 + 추가적인 공부를 위해 이 글을 작성한다. (세종대학교 최유경 교수님 '2021 기계학습'수업)
이번 파트는 지금까지 배운 기계학습(지도학습, 비지도학습)을 실제로 적용할 때 유용하고 또 필요한 기술들에 대해 배우려고 한다. 이 기술에는 교차검증, 파이프라인, 모델 최적화, 최적의 하이퍼 파라미터 찾기 등이 있다.
1. 교차 검증 (Cross Validation)
이론
우리가 지금까지 학습된 모델을 검증했던 과정을 생각해보자. 학습데이터를 통해 우리가 정한 모델을 학습하고 검증데이터를 통해 해당 모델의 최적의 하이퍼파라미터를 선택했으며 마지막으로는 학습이나 검증에 이용한 적이 없는 실제 시험데이터의 정밀도를 측정했다. 이 과정에서 우린 전체 데이터를 학습데이터,검증데이터,테스트데이터의 3가지로 분할한 고정 자료 분할법을 사용했다. (우린 밑의 예시의 과정 중 학습데이터를 학습데이터, 검증데이터로 나누는 실습을 진행함)
하지만 이런 고정 자료 분할은 선택된 하나의 검증데이터에만 의존하여 검증하며 그 자료의 크기가 작을 경우 모형진단의 신뢰성이 떨어진다는 단점이 있다. 또한, 안그래도 데이터가 부족한 상황에서 이 데이터를 나누어 평가에 이용한다는 단점도 있다. 이런 단점을 극복하기 위해서는 교차 검증(대표적으로 K-분할 교차검증)이 나온 것이다.
K-분할 교차검증(K-fold cross validation)이란, 학습데이터의 K분할을 통해 K번 모델검증을 통해 최적의 모델을 선택하는 것을 말한다. (일반적으로 K=10, 대용량자료일 경우 K=5) 이는 해당 K개의 검증 중 가장 우수한 성능을 보이는 모델을 대표 모델로 결정한다.
즉, 위의 예시와 같이 학습 데이터를 K=10개로 나눈 다음 10개로 나누어진 각각의 데이터 중 9개는 학습데이터, 1개는 검증데이터로 사용하는 것이다. 결국 이런 교차검증을 이용하게 되면 모든 학습데이터에 대한 학습과 검증이 이루어지기 때문에 데이터를 낭비하는 일이 없으며, 일석이조로 더 좋은 일반화 성능을 얻을 수 있게 되는 것이다. 결국 이 교차검증으로 결정된 모델을 통해 학습에 이용되지 않은 실제데이터(테스트데이터)에 좋은 성능을 보일 수 있다. 그래서 일반적으로 기계 학습을 이용한 문제해결 과정에서 실제로 고정 자료 분할법이 아닌 교차검증을 자주 사용한다.
2. 모델 최적화
이론
최적화된 모델을 찾고자 한다면, 어떤 문제들을 고려해야 될까? 바로 과적합 문제와 해당 데이터에 맞는 최적의 하이퍼 파라미터 설정이다.
1) 과적합
과적합 문제는 과대적합, 과소적합 두가지로 나뉘게 된다. 이에 대해선 이전 파트에서도 많이 언급했던 것을 기억할 수 있을 것이다. 과대적합(overfitting)은 모델이 학습 데이터에만 너무 잘맞고 일반화(genrealization) 능력이 떨어지는 상황이다. [일반화(genrealization): 테스트 데이터에 대한 높은 성능을 갖추는 것] 반면에 과소적합(underfitting)은 모델이 너무 단순하여 데이터에 내재된 구조 조차 학습하지 못하는 현상을 말한다. 결국 좋은 모델에 있어서 과대적합 과소적합 모두를 피하거나 해결해야되는 것이다.
여기서 잠깐!
더 깊게 들어가기 전 우린 과적합에서 빠질 수 없는 개념인 bias와 variance에 대해선 짚고 넘어갈 필요가 있다. 기계학습에서는 낮은 바이어스와 낮은 분산을 가진 예측기 제작이 목표로 한다. 즉, 아래 예시의 왼쪽 아래 상황이 최적의 모델이라할 수 있다. 하지만 바이어스와 분산은 서로 Trade-off(절충관계)이기 때문에 바이어스 희생을 최소로 유지하며 분산을 최대로 낮추는 전략을 통해 최적의 모델을 찾는다. 즉, 최적의 모델은 good bias-variance trade-off 모델인 것이다.
그렇다면 이런 과대적합이나 과소적합이 일어났는지 어떻게 판단할까?
이는 학습 곡선의 편향(bias)와 분산(variance)를 분석하여 판단할 수 있다.
학습 곡선, 즉 샘플 데이터의 수에 따른 정확도 변화가 학습정확도, 검증정확도 둘 다 낮을 경우 과소적합을 의심할 수 있다. 만약 학습정확도만 높고 검증정확도는 낮다면 이때는 과대적합을 의심할 수 있다.
또는 매개변수에 따른 정확도 변화를 통해 판단할 수 있다. 로지스틱 회귀의 매개변수 C에 따른 정확도 변화를 보면서 이해해보자.
매개변수 C 즉, 규제강도이다. 우린 이 C값이 커지면 커질수록 규제강도가 낮아져 과대적합의 우려가 있다는 것을 이전에 배웠다. 이를 상기시키며 해당 그래프를 보자. 우리가 아는 것과 같이 C가 커질 수록 training과 validation accuracy값에 차이가 더 커진다. 즉, 학습데이터에만 집중하여 일반화를 놓친 과대적합이 일어난다. 하지만 C가 작을 경우는 둘의 accruacy 모두 낮다. 즉, 학습이 제대로 이루어지지 않은 결과인 과소적합이 일어난다. 이런 방식으로 우린 과대적합,과소적합이 일어난 것을 확인할 수 있다. 그럼 어느 C값이 최적의 C값일까? 바로 가장 accuracy가 높은 범위 내에서 training과 validation accuracy값의 차이가 크지 않은 구간을 선택하면 된다. 즉, 형광펜이 칠해진 구간의 C를 선택하면 된다.
C를 그래프에 따라 선택한 것과 같이 과대적합,과소적합을 해결하기 위한 다양한 방법이 있다. 먼저 과대적합을 해결하기 위해선 학습 데이터를 추가로 수집하여 학습에 이용하거나 기존의 학습 데이터의 잡음(오류 및 이상치)을 줄이는 방법이 있다. 혹은 모델의 규제 하이퍼파라미터 즉, 제약을 늘려 과대적합을 방지할 수 있다. 다음으로 과소적합은 특성변수(차원)을 늘려 학습에 이용되는 특성들을 추가하거나 학습이 부족하여 과소적합이 일어났을 수 있으니 과대적합 이전까지 충분히 학습 하는 방법이 있다. 흑은 너무나 과도한 제약으로 인해 학습이 제대로 이루어지지 않았을 수 있으니 모델의 규제 하이퍼파라미터를 줄여 제약을 줄여준다.
2) 최적의 하이퍼 파라미터 설정
모델 최적화를 위한 과적합 해결을 봤으니 이제 더 좋은 성능을 내기 위한 최적의 하이퍼 파라미터 설정에 대해 알아보자. 지금까지 설명한 거의 모든 기계학습의 학습 모델들의 하이퍼 파라미터(더 좋은 성능을 내기 위해 조정하던 인자들)들은 직접 우리가 실험적으로 다양한 경우를 구해서 더 좋은 결과를 보인 값들을 선택하는 과정을 겪었다. 이게 바로 최적의 하이퍼 파라미터 설정이다. 하지만, 매번 모든 경우를 우리가 직접하기에는 매개변수들도 많고 너무나 다양한 경우의 수들이 있을 것이다. 그래서 우린 이 경우의 수들을 실험적으로 확인해주고 최적의 결과를 산출해주는 기술을 사용할 필요가 있다. 이게 바로 GridSearch 이다. 이는 실습에서 어떻게 사용하는지 더 자세히 알아보자.
3. 파이프라인
이론
이전 파트까지 실습에서 우린 데이터 전처리나 학습과정 등을 각각 학습데이터 따로 검증데이터 따로 테스트데이터 따로 진행해왔다. 물론 우리가 지금까지 실습에서 다루었던 데이터들은 복잡하지 않기에 많은 전처리를 요구하지 않아 따로 진행하는데 큰 어려움이 없었다. 하지만, 실제로 다루는 데이터들은 상당히 복잡한 전처리들을 요구한다. 이때 이 복잡한 과정들을 데이터마다 따로 따로 진행한다면 시간도 오래걸릴 뿐만 아니라 실수 또한 늘어날 것이다. 그래서 이 여러 전처리 과정들을 하나의 통로로 만들어 각 학습데이터, 검증데이터, 테스트데이터를 넣으면 end-to-end로 바로 결과를 산출해주는 것이 필요할 것이다. 이게 바로 파이프라인이다. 이 또한 실습에서 더 자세히 확인해보자.
전체 실습
앞서 배운 교차검증과 모델 최적화, 파이프라인을 적용해 sklearn에서 제공하는 유방암 데이터를 예측해보는 실습을 진행해보자.
# 유방암 데이터 로드
import pandas as pd
from sklearn.datasets import load_breast_cancer
cancer_data = load_breast_cancer()
X = cancer_data.data
Y = cancer_data.target
print(X.shape) # (569, 30)
pd.DataFrame(X).head(3)
유방암 데이터는 30개의 특성이 있기에 차원축소와 각 특성의 값들의 scaler가 다르기 때문에 정규화가 필요해 보인다.
여기서 고정 자료 분할법이 아닌 교차검증을 사용할 것이기에 우리가 항상 진행했던 학습 및 검증데이터 분리는 진행하지 않고 바로 차원축소(주성분 분석)와 정규화를 진행해준다. 이때, 이론에서 설명한 파이프라인을 적용해주어 더 간편하게 학습데이터와 검증데이터를 end-to-end로 적용해준다.
# 데이터 정규화 및 차원축소(주성분 분석)과 모델 학습 및 예측까지 end-to-end로 진행하는 파이프라인 구축
from sklearn.preprocessing import StandardScaler # 데이터 정규화
from sklearn.decomposition import PCA # 차원축소(주성분 분석)
from sklearn.linear_model import LogisticRegression # 예측 모델 (로지스틱 회귀)
from sklearn.pipeline import make_pipeline # 파이프라인
# 파이프라인 구축
pipe = make_pipeline(StandardScaler(), PCA(n_components=4),LogisticRegression()) # 파이프라인
pipe.fit(x_train, y_train) # 학습데이터에 맞게 학습 진행
pipe.score(x_test, y_test) # 학습데이터에 맞는 파이프라인을 통해 검증데이터로 검증 진행
이렇게 pipeline을 통해 정규화에 사용되는 StandarScaler, 차원축소에 사용되는 PCA, 마지막으로 학습 및 예측에 사용되는 LogisticRegression 을 하나의 파이프로 묶어 학습데이터, 검증데이터를 각각 한번에 모든 과정이 진행되도록 설정할 수 있다. 해당 코드를 그림으로 표현한 것이다.
이제 교차검증을 진행해주고 교차검증의 결과를 학습데이터, 검증데이터 측면에서 확인해보자. 교차검증 시 데이터는 10개로 나누어서 9개는 학습, 1개는 검증 데이터로 사용하여 총 10번의 학습(10개의 검증데이터)을 이루게 된다.
# 교차검증
import numpy as np
from sklearn.model_selection import cross_validate
scores = cross_validate(pipe, X, Y, cv=10, return_train_score=True) # 10개의 다른 검증데이터를 통해서 검증
#return_train_score=True, default는 False 학습 과정에서 일어난 학습데이터의 모든 accuracy를 return해주는 기능
# 각 학습마다의 학습데이터를 통한 accuracy
print('CV Train Accuracy scores: ', scores['train_score'])
print('CV Train Accuracy: %.3f +/- %.3f' %(np.mean(scores['train_score']), np.std(scores['train_score'])))
# 각 학습마다의 검증데이터를 통한 accuracy
print('CV Validation Accuracy scores: ', scores['test_score'])
print('CV Validation Accuracy: %.3f +/- %.3f' %(np.mean(scores['test_score']), np.std(scores['test_score'])))
학습데이터와 검증데이터의 분산이 작은 것을 보니 학습이 잘 이루어졌고, 이 둘의 값이 비슷한걸 보니 과적합은 이루어지지 않은 것을 확인할 수 있다.
그럼 지금까지 진행했던 PCA, LogisticRegression의 하이퍼 파라미터는 최적일까? 그에 따라 현재 예측결과는 최적의 결과일까? 모르는 것이다. 우린 이 하이퍼 파라미터에 대해 실험적으로 다양한 값을 넣어보지 못했다. 이를 우린 직접하지 않고 GridSearchCV를 통해 구현할 수 있다. 즉, 이제 GridSearchCV를 통해 지금까지 진행되었던 차원축소, 학습 모델의 최적의 하이퍼 파라미터를 찾아보자.
# 최적의 하이퍼 파라미터 찾기
from sklearn.model_selection import GridSearchCV # 최적의 하이퍼 파라미터 찾아주는 매서드
params = {'pca__n_components':[4,6,8,10,12],
'logisticregression__C':[0.01, 0.1, 1.0, 10,100]}
# 언더바2개 __ 를 통해서 해당 속성과 연결해줌
clf = GridSearchCV(pipe, params, scoring='accuracy', cv=10, return_train_score=True, n_jobs=-1)
# 구축했던 pipe를 사용하며 미리 설정한 params의 요소들을 통해 실험적으로 확인.
clf.fit(X,Y) # 학습 -> 최적의 하이퍼 파라미터를 찾아줌. 즉, 최적의 모델을 만들어줌
GridSearch를 사용하기 위해선 먼저 내가 어떤 하이퍼 파라미터들을 실험할 것인지 설정해주어야 한다. 해당 실습에서는 PCA의 n_components, LogisticRegression의 C의 조합을 실험해 보았다. 각각의 하이퍼 파라미터를 접근하려면 해당 기능의 이름과 언더바 2개, 하이퍼 파라미터의 이름을 통해 접근이 가능하다. (이는 pipe.get_params().keys() [파이프라인이 가지고 있는 모든 하이퍼 파라미터들] 을 통해 내가 어떤 하이퍼 파라미터를 조절할 수 있는지 확인 가능)
이 GridSearchCV는 이름에서 알 수 있듯이 교차검증(CV)까지 지원한다. 따라서 GridSearchCV를 진행할 때는 따로 교차검증을 통한 검증과정을 추가할 필요는 없다.
이제 최적의 하이퍼 파라미터를 통해 생성된 최적의 모델을 확인해보자.
# 결과 확인
print('Train Score:',np.mean(clf.cv_results_['mean_train_score']))
print('Test Score:',np.mean(clf.cv_results_['mean_test_score']))
# 최적의 하이퍼 파라미터 확인
print('Result of Hyper Parmeters: ',clf.best_params_)
결과적으로 학습에 이용되지 않은 새로운 데이터에 대해 예측을 진행할 경우 (도메인 갭이 크지 않다면) 해당 Test Score와 같은 예측 정확도를 보일 수 있다는 것이다. 유방암 데이터에서는 사실 위의 하이퍼 파라미터를 건드리지 않은 것과 정확도의 큰차이는 없지만, 조정해야 될 파라미터가 많은 예측 모델 및 데이터 전처리를 사용하거나 특이한 데이터를 만날 경우 GridSearchCV의 효과는 확실할 것이다.