모델 세부 튜닝
- 그리드 탐색으로 사이킷런의 GridSearchCV를 사용하면 된다. 그러면 가능한 모든 하이퍼파라미터 조합에 대해 교차 검증을 사용해 평가한다.
from sklearn.model_selection import GridSearchCV
param_grid = [
{'n_estimators': [3, 10, 30], 'max_features': [2, 4, 6, 8]},
{'bootstrap': [False], 'n_estimators': [3, 10], 'max_features': [2, 3, 4]},
]
forest_reg = RandomForestRegressor()
gird_search = GridSearchCV(forest_reg, param_grid, cv=5,
scoring='neg_mean_sqaured_error',
return_train_score=True)
grid_search.fit(housing_prepared, housing_labels)
# 최적의 추정기에 직접 접근
grid_search.best_estimator_
# 평가 점수 확인
cvres = grid_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
print(np.sqrt(-mean_score), params)
(참고) GridSearchCV(scoring='neg_mean_squared_error')
sklearn의 교차검증은 MSE에 음수를 취해서 오차를 점수로 바꾼다. 즉 본래는 MSE가 낮아야 좋은 것인데, 여기에 음수를 취하기 때문에 sklearn에선 해당 점수가 높아야 모델이 좋다고 판단한다.
param_grid 설정에 따라 먼저 첫 번째 dict에 있는 n_estimators와 max_features 하이퍼파라미터의 조합인 3 x 4 = 12개를 평가한다. 그 다음 하이퍼파라미터의 조합인 2 x 3 = 6개를 시도한다. 모두 합하면 18개 하이퍼파라미터 값 조합을 탐색하고, 각각 다섯 번 모델을 훈련시킨다(5겹 교차 검증 사용) 따라서 전체 훈련 횟수는 90이 된다.
그리드 탐색 방법은 비교적 적은 수의 조합을 탐구할 때 괜찮다. 하지만 하이퍼파라미터 탐색 공간이 커지면 RandomizedSearchCV를 사용하는 것이 좋으며, 이는 가능한 모든 조합을 시도하는 대신 각 반복바다 하이퍼파라미터에 임의의 수를 대입하여 지정한 횟수만큼 평가한다.
- 랜덤 탐색을 1000회 반복 실행하면 하이퍼파라미터마다 각기 다른 1000개의 값을 탐색(그리드 탐색은 하이퍼파라미터마다 몇 개의 값만 탐색)
- 단순히 반복 횟수를 조절하는 것만으로 하이퍼파라미터 탐색에 투입할 컴퓨팅 자원 제어
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
param_distribs = {
'n_estimatros': randint(low=1, high=200), # 변수의 범위를 랜덤
'max_feautres': randint(low=1, high=8)
}
forest_reg = RandomForestRegressor(random_state=42)
rnd_search = RandomizedSearchCV(forest_reg, param_distributions=param_distribs,
n_iter=10, cv=5, scoring='neg_mean_squared_error', random_state=42)
rnd_search.fit(housing_prepared, housing_labels)
cvres = rnd_search.cv_results_
for mean_score, params in zip(cvres['mean_test_score'], cvres['params']):
print(np.sqrt(-mean_score), params)
최상의 모델과 오차 분석
- 각 특성의 상대적인 중요도 파악
feature_importances = grid_search.best_estimator_.feature_importances_
feature_importances
# 중요도 다음에 그에 대응하는 특성 이름 표시
extra_attribs = ["rooms_per_hhold", "pop_per_hhold", "bedrooms_per_room"]
# wrap up: full_pipeline에서 cat_attribs=["ocean_proximity"]을 원-핫 인코딩으로 처리
cat_encoder = full_pipeline.named_transformers_["cat"]
cat_one_hot_attribs = list(cat_encoder.categories_[0])
attributes = num_attribs + extra_attribs + cat_one_hot_attribs
sorted(zip(feature_importances, attributes), reverse=True)
테스트 세트로 시스템 평가
final_model = grid_search.best_estimator_
X_test = start_test_set.drop("median_house_value", axis=1)
y_test = start_test_set["median_house_value"].copy()
X_test_prepared = full_pipeline.transform(X_test)
final_predictions = final_model.predict(X_test_prepared)
final_mse = mean_squared_error(y_test, final_predictions)
final_rmse = np.sqrt(final_mse)
테스트 세트에서 예측 변수와 레이블을 얻은 후 full_pipeline을 사용해 데이터를 변환한다. 이때 주의할 점은 테스트 세트는 훈련하면 안 되므로 fit_transform()이 아닌 transform()을 호출해야 한다.
이렇게 구한 일반화 오차의 추정이 론칭을 결정하기에 충분하지 않을 것이다. 추정값이 얼마나 정확한지 알고 싶다면 scipy.stats.t.interval()을 사용해 일반화 오차의 95% 신뢰 구간을 계산할 수 있다.
from scipy import stats
confidence = 0.95
squared_errors = (final_predictions - y_test) ** 2
np.sqrt(stats.t.interval(confidence, len(squared_errors) - 1,
loc=squared_errors.mean(),
scale=stats.sem(squared_errors)))
하이퍼파라미터 튜닝을 많이 했다면 교차 검증을 사용해 측정한 것보다 조금 성능이 낮은 것이 보통이다. 우리 시스템이 검증 데이터에서 좋은 성능을 내도록 세밀하게 튜닝되었기 때문에 새로운 데이터셋에는 잘 작동하지 않을 가능성이 크다.
챕터 3: 분류
MNIST
코드를 이용하여 MNIST 데이터셋을 다운로드한다.
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1)
mnist.keys()
>>> dict_keys(['data', 'target', 'feature_names', 'DESCR', 'details', 'categories', 'url'])
X, y = mnist["data"], mnist["target"]
X.shape
>>> (70000, 784) # 특성 784개 = 이미지 28 x 28 픽셀
y.shape
>>> (70000,)
y = y.astype(np.unit8) # 문자열인 레이블을 정수로 변환
사이킷런에서 읽어들인 데이터셋들은 일반적으로 비슷한 딕셔너리 구조를 가지고 있다.
- 데이터셋을 설명하는 DESCR 키
- 샘플이 하나의 행, 특성이 하나의 열로 구성된 배열을 가진 data 키
- 레이블 배열을 담은 target 키
실제 이미지 확인해보기
import matplotlib as mpl
import matplotlib.pyplot as plt
# 샘플의 특성 벡터를 추출하여 28 x 28 배열로 변환
some_digit = X[0]
some_digit_image = some_digit.reshape(28, 28)
plt.imshow(some_digit_image, cmap="binary")
plt.axis("off")
plt.show()
MNIST 데이터셋은 이미 훈련 세트(앞쪽 60,000개 이미지)와 테스트 세트(뒤쪽 10,000개 이미지)로 나누어놓은 상태다.
이진 분류기 훈련
'5'와 '5 아님' 두 개 의 클래스를 구분하는 이진 분류기를 만들어보자.
y_train_5 = (y_train == 5) # 5는 True고, 다른 숫자는 모두 False
y_test_5 = (y_test == 5)
사이킷런의 SGDClassifier 클래스를 사용해 확률적 경사 하강법(Stochastic Gradient Descent) 분류기로 시작해본다. SGD는 한 번에 하나씩 훈련 샘플을 독립적으로 처리한다. SGDClassifier와 SGDRegressor는 기본적으로 에포크마다 훈련 데이터를 다시 섞는다.
from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train, y_train_5)
from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring="accuracy")
가끔 사이킷런이 제공하는 기능보다 교차 검증 과정을 더 많이 제어해야 할 필요가 있다. 이때 아래와 같이 교차 검증 기능을 직접 구현하면 된다.
from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone
# shuffle=False 기본값을 두고 random_state를 지정하면 경고 발생, 사이킷런 0.24 버전부터 에러 발생 예정
skfolds = StratifiedKFold(n_splits=3, random_state=42, shuffle=True)
for train_index, test_index in skfolds.split(X_train, y_train_5):
clone_clf = clone(sgd_clf)
X_train_folds = X_train[train_index]
y_train_folds = y_train_5[test_index]
X_test_fold = X_train[test_index]
y_train_fold = y_train_5[test_index]
clone_clf.fit(X_train_folds, y_train_folds)
y_pred = clone_clf.predict(X_test_fold)
n_correct = sum(y_pred == y_test_fold)
print(n_correct / len(y_pred))
정확도를 분류기의 성능 측정 지표로 선호하지 않는 이유
- 특히 불균형한 데이터셋(즉, 어떤 클래스가 다른 것보다 월등히 많은 경우)을 다룰 때 주의해야 한다. 이럴 경우, 정확도는 무의미하게 엄청 높게 나오기 때문이다.
from sklearn.base import BaseEstimator
class Never5Classifier(BaseEstimator):
def fit(self, X, y=None):
return self
def predict(self, X):
return np.zeros((len(x), 1), dtype=bool)
'문돌이 존버 > 데이터 분석' 카테고리의 다른 글
핸즈온 머신러닝 2 복습하기(챕터 4: 모델 훈련) (0) | 2020.09.30 |
---|---|
핸즈온 머신러닝 2 연습 문제 코드(Classification) (0) | 2020.09.08 |
핸즈온 머신러닝 2 복습하기(챕터 3: 분류) (0) | 2020.09.02 |
핸즈온 머신러닝2 복습하기(챕터 2: 머신러닝 프로젝트 처음부터 끝까지) (0) | 2020.08.29 |
멀티 레이블 분류(랜덤포레스트 및 딥러닝 keras 사용기) feat.파이썬 (0) | 2020.03.04 |