머신러닝 교과서 10장
선형 회귀 모델에 대해 알아보자. 보스턴 집 가격 예측 문제를 예시로 들어서 설명하겠다.
2025/01/09
Jinsoolve.
Categories
Tags
1월 안에는 꼭...
About
해당 내용은 머신러닝 교과서 with 파이썬, 사이킷런, 텐서플로의 장 내용을 기반으로 작성되었다.
우리는 모델을 훈련시킬 때 데이터를 전처리한 후에 학습시킨다. 그럼 테스트 데이터셋이나 검증 데이터셋을 모델에 넣을 때도 같은 전처리 과정을 거쳐야 한다.
매번 같은 과정을 한 번에 표현할 수 있도록 도와주는 것이 파이프라인이다. 예시로 설명하겠다.
1from sklearn.preprocessing import StandardScaler 2from sklearn.decomposition import PCA 3from sklearn.linear_model import LogisticRegression 4from sklearn.pipeline import make_pipeline 5 6pipe_lr = make_pipeline(StandardScaler(), 7 PCA(n_components=2), 8 LogisticRegression(random_state=1)) 9 10pipe_lr.fit(X_train, y_train) 11y_pred = pipe_lr.predict(X_test) 12print('테스트 정확도: %.3f' % pipe_lr.score(X_test, y_test))
위 코드는 정규화 -> PCA 차원 축소 -> 로지스틱회귀 순으로 전처리 및 모델링 하였다.
이를 파이프라인을 이용해 test 데이터셋에 동일하게 적용시켜 예측값을 반환하는 코드다. 매우 편리하게 사용할 수 있다.
적절한 편향-분산 트레이드 오프를 찾고 하이퍼 파라미터 튜닝을 통해서 적절한 모델을 선택하려면 해당 모델의 성능이 어떠한지를 평가해야 한다.
이때 테스트 데이터셋으로 평가 및 모델 조정 과정을 여러번 하게 되면 테스트 데이터셋도 사실상 훈련 데이터처럼 학습되고 과대적합될 수 있다. 때문에 훈련 데이터 안에서 검증 데이터를 따로 뗴어내어 해당 검증 데이터만으로 모델을 조정하고 한 번도 사용하지 않은 테스트 데이터셋으로 모델이 일반화가 잘 되었는지 확인해야 건강한(?) 모델링이라 할 수 있다.
즉, 검증 데이터로 모델이 더 좋은 결과값을 얻도록 조정하고 깨끗한(?) 테스트 데이터로 모델의 일반화 능력을 확인하는 것이다.
그럼 이제 검증 과정에 대해서 자세히 알아보자.
전통적이고 널리 알려져 있는 홀드아웃은 단순하게 훈련데이터에서 일부를 뗴어내고 이를 검증 데이터로 사용하는 것이다.
그러나 이 방법은 검증 데이터를 추출하는 경우마다 점수가 달라진다는 단점이 있다.
이를 해결하기 위해 훈련 데이터를 k개로 나눠서 k번의 홀드아웃을 하는 방식이 있는데 이를 k-fold 교차 검증이라 한다.
k-fold는 k개로 나눠서 k번 진행한 홀드아웃에 대한 평균값을 반환하기 때문에 추출하는 케이스에 따라 값이 달라지는 정도가 훨씬 덜 하다는 정도가 있다.
또한 데이터를 k개로 나눌 때 중복을 허용하지 않고 샘플링하기 때문에 k번 홀드아웃한다고 해서 하나의 데이터를 여러 번 검증에 사용하지 않는다. (여러 번 사용하면 분산이 커지고 편향이 낮아짐. 즉, 과대적합될 수 있다.)
가장 기본적이고 많이 사용되는 k값은 10으로 대체적으로 좋은 성능을 보인다.
데이터가 너무 작다면 k를 증가시키는 것이 좋지만 과도하게 하면 과대적합(분산이 카지고, 편향이 작아짐)이 될 수 있으므로 주의해야 한다.
반면에 대규모 데이터인 경우 k값이 5정도만 돼도 충분하다.
참고로 k-fold를 통해서 데이터 검증을 마치고 모델 조정(= 모델 선택)을 마쳤다면 검증 데이터를 다시 훈련 데이터에 합쳐서 이 전체 훈련 데이터로 모델을 다시 훈련시키는 것이 좋다. 훈련 샘플이 많을 수록 모델 성능이 더 안정화되고 정확해지기 때문이다.
기존 K-Fold는 target 데이터인 클래스의 비율이 균일하지 않을 때 편향-분산 트레이드 오프가 안 좋아지는데, 이를 계층화 k-fold로 어느 정도 해결할 수 있다.
stratified k-fold는 데이터를 k개로 나눌 때 클래스 비율이 전체 훈련 데이터의 비율과 최대한 동일하게 나눈다.
코드는 다음과 같다.
1import numpy as np 2from sklearn.model_selection import StratifiedKFold 3 4kfold = StratifiedKFold(n_splits=10).split(X_train, y_train) 5 6scores = [] 7for k, (train, test) in enumerate(kfold): 8 pipe_lr.fit(X_train[train], y_train[train]) 9 score = pipe_lr.score(X_train[test], y_train[test]) 10 scores.append(score) 11 print('폴드: %2d, 클래스 분포: %s, 정확도: %.3f' % (k+1, 12 np.bincount(y_train[train]), score)) 13
k-fold의 점수를 알려면 stratified k-fold를 사용한 후에 각 폴드에 대한 점수를 파이프라 인을 이용하여 계산 후에 저장해야 한다. 이를 사이킷런에서 만든 함수가 있는데 cross_val_score이다.
사이킷런에 cross_val_score는 stratified k-fold를 해서 검증 데이터셋을 준비한 후에 이에 대한 점수값을 출력해주는 함수다.
1from sklearn.model_selection import cross_val_score 2 3# estimator에는 우리가 사용한 파이프라인을 넣어준다. 4# cv에는 몇 개의 폴드를 나눌 지를 정한다. 5scores = cross_val_score(estimator=pipe_lr, 6 X=X_train, 7 y=y_train, 8 cv=10, 9 n_jobs=1) 10print('CV 정확도 점수: %s' % scores) 11print('CV 정확도: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))
cross_val_score의 기본 지표는 회귀: , 분류: 정확도 이다. scoring 파라미터를 이용해서 이를 변경할 수 있다.
대체로 cross_val_score를 통해서 교차검증을 해결할 수 있지만 좀 더 많은 정보가 필요한 경우에 cross_validate함수를 사용한다. 모델 훈련 및 테스트 시간, 여러 성능 메트릭, 원하는 작업(예: 예측 결과 저장)을 수행할 수 있다.
1from sklearn.model_selection import cross_validate 2 3scores = cross_validate(estimator=pipe_lr, 4 X=X_train, 5 y=y_train, 6 scoring=['accuracy'], 7 cv=10, 8 n_jobs=-1) 9print('CV 정확도 점수: %s' % scores['test_accuracy']) 10print('CV 정확도: %.3f +/- %.3f' % (np.mean(scores['test_accuracy']), 11 np.std(scores['test_accuracy'])))
cross_val_score가 예측값에 따른 점수를 반환했다면,
cross_val_predict는 바로 그 예측값을 반환한다.
1from sklearn.model_selection import cross_val_predict 2 3# method='predict_proba': 예측값을 얼마의 확률로 결과를 얻었는지 알게 된다. 4preds = cross_val_predict(estimator=pipe_lr, 5 X=X_train, 6 y=y_train, 7 cv=10, 8 method='predict_proba', 9 n_jobs=-1) 10preds[:10]
method='predict_proba' 없을 때
array([0, 0, 0, 0, 0, 0, 0, 1, 1, 1])
method='predict_proba' 있을 떄
array([[9.93982352e-01, 6.01764759e-03],
[7.64328337e-01, 2.35671663e-01],
[9.72683946e-01, 2.73160539e-02],
[8.41658121e-01, 1.58341879e-01],
[9.97144940e-01, 2.85506043e-03],
[9.99803660e-01, 1.96339882e-04],
[9.99324159e-01, 6.75840609e-04],
[2.12145074e-06, 9.99997879e-01],
[1.28668437e-01, 8.71331563e-01],
[7.76260670e-04, 9.99223739e-01]])
학습곡선과 검증곡선을 그래프로 그려서 모델이 편향과 분산은 어떠한지, 과소적합인지, 과대적합인지를 해석할 수 있다.
물론 데이터 자체 수를 늘리는 것이 해결할 때도 많지만 이는 현실적으로 어렵고,
데이터에 노이즈가 많거나 모델이 이미 최적화된 경우 데이터양을 늘리는 것으로는 해결이 어렵다.
이때 학습곡선과 검증곡선을 분석해 이를 해결해 볼 수 있다.
마지막 그림처럼 기대 정확도에 가까우면서 학습곡선과 검증곡선 사이의 간격이 좁을 때가 좋은 편향과 분산을 가질 때다.
이처럼 학습데이터의 크기에 따른 학습데이터와 검증데이터의 성능을 그린 그래프를 학습곡선이라 한다.
훈련 데이터셋의 크기에 학습곡선을 그려 보자.
1import matplotlib.pyplot as plt 2from sklearn.model_selection import learning_curve 3 4 5pipe_lr = make_pipeline(StandardScaler(), 6 LogisticRegression(penalty='l2', random_state=1, 7 max_iter=10000)) 8 9train_sizes, train_scores, test_scores =\ 10 learning_curve(estimator=pipe_lr, 11 X=X_train, 12 y=y_train, 13 train_sizes=np.linspace(0.1, 1.0, 10), 14 cv=10, 15 n_jobs=1) 16 17train_mean = np.mean(train_scores, axis=1) 18train_std = np.std(train_scores, axis=1) 19test_mean = np.mean(test_scores, axis=1) 20test_std = np.std(test_scores, axis=1) 21 22plt.plot(train_sizes, train_mean, 23 color='blue', marker='o', 24 markersize=5, label='Training accuracy') 25 26plt.fill_between(train_sizes, 27 train_mean + train_std, 28 train_mean - train_std, 29 alpha=0.15, color='blue') 30 31plt.plot(train_sizes, test_mean, 32 color='green', linestyle='--', 33 marker='s', markersize=5, 34 label='Validation accuracy') 35 36plt.fill_between(train_sizes, 37 test_mean + test_std, 38 test_mean - test_std, 39 alpha=0.15, color='green') 40 41plt.grid() 42plt.xlabel('Number of training examples') 43plt.ylabel('Accuracy') 44plt.legend(loc='lower right') 45plt.ylim([0.8, 1.03]) 46plt.tight_layout() 47# plt.savefig('images/06_05.png', dpi=300) 48plt.show()
훈련데이터의 크기가 250 이상일 때 좋은 편향, 분산을 갖게 되는 것으로 보인다.
하이퍼 파라미터에 따른 훈련 데이터와 검증 데어티의 성능을 그린 그래 프를 검증곡선이라 한다.
이번에는 로지스틱 회귀의 파라미터 C의 값에 따른 검증 곡선을 그려보자.
1from sklearn.model_selection import validation_curve 2 3 4param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0] 5 6# param_name='logisticregression__C': 로지스틱 회귀의 파라미터 C를 x축으로 사용한다. 7train_scores, test_scores = validation_curve( 8 estimator=pipe_lr, 9 X=X_train, 10 y=y_train, 11 param_name='logisticregression__C', 12 param_range=param_range, 13 cv=10) 14 15train_mean = np.mean(train_scores, axis=1) 16train_std = np.std(train_scores, axis=1) 17test_mean = np.mean(test_scores, axis=1) 18test_std = np.std(test_scores, axis=1) 19 20plt.plot(param_range, train_mean, 21 color='blue', marker='o', 22 markersize=5, label='Training accuracy') 23 24plt.fill_between(param_range, train_mean + train_std, 25 train_mean - train_std, alpha=0.15, 26 color='blue') 27 28plt.plot(param_range, test_mean, 29 color='green', linestyle='--', 30 marker='s', markersize=5, 31 label='Validation accuracy') 32 33plt.fill_between(param_range, 34 test_mean + test_std, 35 test_mean - test_std, 36 alpha=0.15, color='green') 37 38plt.grid() 39plt.xscale('log') 40plt.legend(loc='lower right') 41plt.xlabel('Parameter C') 42plt.ylabel('Accuracy') 43plt.ylim([0.8, 1.0]) 44plt.tight_layout() 45# plt.savefig('images/06_06.png', dpi=300) 46plt.show()
하이퍼 파라미터를 튜닝하는 데 사용하는 gridsearch가 있다.
GridSearchCV함수를 이용하여 교차검증 폴드의 평균 점수를 이용하여 하이퍼파라미터를 튜닝시킨다.
그러나 GridSearchCV는 너무 많은 계산 비용이 드는데 이를 RandomizedSearchCV를 이용해 어느 정도 해결할 수 있다. 특히 매개변수 탐색 범위가 넓거나 연속적인 매개변수를 탐색해야 할 때 효과적이다.
HalvingGridSearchCV는 몇 개의 샘플에 대해서 교차검증을 하고 좋은 결과 나온 후보만을 골라서 다시 교차검증을 하는 방식이다. GridSearchCV보다는 결과가 아주 미세하게 안 좋을 수 있지만 계산 비용이 훨씬 저렴하다.
일반 교차 검증이 훈련 셋과 테스트 셋으로 나누어 검증한 것이라면,
중첩 교차 검증은 나누어진 훈련셋과 테스트셋들에 대해서 훈련셋을 다시 검증폴드와 훈련폴드로 나누어 교차검증을 진행한다.
중첩 교차 검증이 당연히 일반 교차 검증보다 뛰어날 수 밖에 없다.
주로 'accuracy' 정확도 지표를 사용하여 모델을 평가하는 것이 모델 성능에 도움이 된다.
주어진 모델이 적합한지 측정할 수 있는 다른 성능 지표도 여럿 있다. 정밀도(precision), 재현율(recall), F1-점수이 있다.
오차행렬은 예측값과 실제값에 대한 비교 행렬이다.
아래와 같은 코드로 사용할 수 있다.
악성 종양 감지 문제에서 악성 종양:1, 양성 종양:0 이라고 하자.
정밀도를 최적화시키면 건강한 환자에게 악성 종양을 감지하는 일이 줄어들고,
재현율을 최적화시키면 아픈 환자에게 건강하다고 하는 일이 줄어든다.
이러한 PRE와 REC 최적화 균형을 맞추기 위해 이를 조합한 F1-score 지표를 자주 사용한다.
아래와 같이 make_scorer()를 이용해 scorer 변수를 만들 수 있다.
pos_label=0: 레이블이 0이 양성 클래스가 되도록 바꿔준다.
ROC 곡선은 FPR, TPR을 축으로 해서 그리는 그래프이다.
auc(Area Under the ROC Curve)는 ROC curve의 밑면적을 말한다. 해당 값이 높을수록 높은 정확도를 갖는다.
roc_curve 함수를 사용하면 fpr, tpr를 얻을 수 있다. 이를 그래프를 그리는 사용하거나 auc함수에 넘겨줘서 roc_auc 점수를 얻을 수 있다.
roc_auc_score 함수를 사용하면 바로 ROC-AUC 점수를 얻을 수 있다.
이진 분류 말고 다중 클래스에 대한 분류를 할 때의 성능지표에 대해서 말하겠다.
micro 평균은 기존의 PRE를 k개의 클래스에 대한 PRE로 만든 것이고 이는 각 샘플이나 예측에 동일한 가중치를 부여하고자 할 때 사용된다.
macro 평균은 단순하게 클래스별 PRE의 평균값이다. 가중치가 적용된 macro 평균은 레이블마다 샘플 개수가 다른 불균형한 클래스를 다룰 때 유용하다.
불균형한 클래스를 처리하지 않고 모델을 훈련시키면 학습 알고리즘 자체에 영향을 끼친다.
이를 소수 클래스에서 발생한 예측 오류에 큰 벌칙을 부여하는 방식으로 해결해 볼 수 있다. 대부분의 분류기에 구현된 **class_weight='balanced'**로 설정해서 조정할 수 있다.
혹은 소수클래스의 샘플을 늘리거나(업샘플링) 다수클래스의 샘플을 줄이는(다운샘플링) 방식을 사용하여 해결해 볼 수 있다.
다음은 다운샘플링에 대한 코드다. resample 함수를 사용한다. 업샘플링을 하려면 y_imb == 0과 y_imb == 1의 값을 뒤바꿔주면 된다.
선형 회귀 모델에 대해 알아보자. 보스턴 집 가격 예측 문제를 예시로 들어서 설명하겠다.
2025/01/09
레이블이 없는 데이터들을 분석하여 비슷한 데이터들끼리 그룹으로 묶을 것이다.이를 군집으로 묶는다하여 클러스터링(clustering)이라 한다. - k-평균 알고리즘을 이용하여 클러스터 중심 찾기- 상향식 방법으로 계층적 군집 트리 만들기- 밀집도 기반의 군집 알고리즘을 사용하여 임의 모야을 가진 대상 구분하기
2025/01/09
데이터 전처리에 대해서 정리해 보았다.
2025/01/09
차원 축소 기법에 대해서 알아보자.
2025/01/09