Programming & Machine Learning/풀어쓰는 머신러닝
학습, 검증곡선과 편향-분산 트레이드오프(Bias-Variance Tradeoff)
Yamarae
2017. 8. 11. 02:57
본 포스팅을 읽기 전에, 교차검증이 무엇인지에 대해 모르는 분들은 이곳을 참고해주시길 바랍니다.
학습, 검증곡선과 편향-분산 트레이드오프
편향-분산 트레이드오프 (Bias-Variance Tradeoff)
머신 러닝에서의 error는 크게 두 분류로 나뉜다.
bias(편향), 그리고 variance(분산)이다.
bias는 흔히 생각할 수 있는 error로, 선형 회귀같은 문제에서의 SSE를 떠올리면 쉽다.
모델이 학습데이터를 충분히 설명할 수 없는 상황에서 커지는 에러이다.
이 상황을 흔히 underfitting이라고 한다.
variance는 그 반대로 모델이 학습데이터를 과도하게 잘 설명하는 상황이다.
모집단을 추정하고자 표본집단을 이용하여 모델을 만들어놨더니, 표본집단만을 거창하게 잘 설명하는 모델이 된 것이다.
머신러닝을 공부하는 사람이라면 한 번 쯤 들어본, overfitting의 상황이다.
variance에 대해 수학적으로 조금 더 직관적으로 얘기하자면,
Training Score와 Cross Validation의 차이가 심해지는 상황이 variance가 증가하는 상황이다.
아래의 그림을 보면 매우 쉽게 알 수 있다.
빨간 곡선은 cross validation error이고, training error가 녹색 곡선이다.
학습이 진행됨에 따라 오버피팅이 되기 때문에 트레이닝 스코어는 계속해서 증가하는 경향을 보이지만,
교차검증값은 점점 악화된다. 모델이 일반화 성능을 잘 보여주는 영역에서 벗어나고 있는 상황이다.
(참고 - 트레이닝 스코어 : 학습데이터로 모델을 테스트했을때의 스코어,
교차검증 스코어 : 모델의 일반적 성능을 추정하기 위해, 테스트셋을 resampling 하는 등의 모델 일반화 검증을 하는 방법에
대한 스코어)
일반적인 머신러닝에서는 편향-분산의 관계는 편향이 올라가면 분산은 내려가고,
분산이 올라가면 편향이 내려가는 시소 관계에 놓여있다. 이를 편향-분산 트레이드오프라 한다.
흔히 머신러닝에 있어서 중요한 것으로 비용함수를 최적화 하거나 차원을 축소한다거나 하는 이야기를 하기 쉽다.
하지만 편향-분산 트레이드오프를 잘 맞춰주는 것이 훨씬, 머신러닝의 정말 기본중의 기본이라고 할 수 있겠다.
편향-분산 트레이드오프를 잘 맞춰주기 위해서는 그리드검색등을 이용한 파라미터 튜닝의 좋은 기법등이 있을 수 있겠으나
역시 기본이 되면서 사람에게 설득력을 갖는 것은 눈으로 보여지는 것이다.
학습곡선과 검증곡선을 이용하여 바이어스와 분산 문제를 진단하는 것이 여전히 좋은 방법이다.
sklearn에서는 learning-curve를 아주 쉽게 그려주는 모듈을 제공한다.
이것을 이용하는 것이 전자보다 더 의미가 있다.
그리드 검색의 하이퍼 파라미터 튜닝, 중첩 교차검증 등의 고급 기법은 학습곡선과 검증곡선을 토대로
이 문제를 사람의 직관으로 판단하는 요인을 코드로 구현한 것에 지나지 않기 때문이다.
아래의 예제는 학습곡선과 검증곡선으로 편향-분산 트레이드 오프 상황을 핸들링하는 상황을 재현한 것이다.
예제에서는 편향-분산 트레이드오프가 x축의 값의 증감에 따라 크게 민감하지 않기 때문에,
데이터 수나 샘플링에 큰 영향을 받지 않고 모델의 성능을 잘 유지하는 좋은 모델이라고 할 수 있다.
예제와 달리 위의 두번째 그림같은 상황이 된다면 트레이드 오프 상황이 벌어지기 시작하는, 두 곡선이 갈래길로 분기하는
상황에서 모델의 조건이 가장 좋다고 할 수있는 것이다.
사람의 패턴 인식능력을 십분 활용하는 이런 방식이 개인적으로는 더 좋은 것 같다.
물론 더욱 고급기법의 경우는 오히려 코드로는 훨씬 쉽다.
또한 GridSearchCV등의 클래스에 매우 직관적이면서 쉽게 정리가 되어있기 때문에 sklearn의 튜토리얼을
한 번 쯤 따라해보는 것만으로도 충분하다.
%matplotlib inline
import matplotlib.pyplot as plt
from sklearn.learning_curve import learning_curve
pipe_lr = Pipeline([('scl', StandardScaler()),
('clf', LogisticRegression(penalty='l2', random_state=0))])
train_sizes, train_scores, test_scores =\
learning_curve(estimator=pipe_lr,
X=X_train,
y=y_train,
train_sizes=np.linspace(0.1, 1.0, 10),
cv=10,
n_jobs=1)
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)
plt.plot(train_sizes, train_mean,
color='blue', marker='o',
markersize=5, label='training accuracy')
plt.fill_between(train_sizes,
train_mean + train_std,
train_mean - train_std,
alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
color='green', linestyle='--',
marker='s', markersize=5,
label='validation accuracy')
plt.fill_between(train_sizes,
test_mean + test_std,
test_mean - test_std,
alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training samples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.0])
plt.tight_layout()
# plt.savefig('./figures/learning_curve.png', dpi=300)
plt.show()