모델평가와 성능평가 _미완성

2018. 1. 15. 09:03ML(머신러닝)/BASIC

도움이 되셨다면, 광고 한번만 눌러주세요.  블로그 관리에 큰 힘이 됩니다 ^^

지도와 비지도 다양한 머신러닝 알고리즘 살펴봄
-> 모델 평가와 매개변수 선택에 대해 알아보자

비지도 학습은 선택하는 일 정성적인 일 = 정량적이지 않다는 말 
지도 학습 위주로 살펴봄

평가를 위해서는 train_test_split  = > train / test
모델 생성 => fit
test 모델 평가 => score 메서드
score 메서드 -> 정확히 분류된 샘플의 비율을 계산하는 방법
train / test 나누는 이유 -> 새로운 데이터가 얼마나 일반화되어 있는지 측정하기 위해

안정적인 일반화를 위해 -> 교차 검증(cross validation)

score R^2 이외에 다른 방법

from sklearn.datasets import make_blobs
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

# 인위적인 데이터셋을 만듭니다
X, y = make_blobs(random_state=0)
# 데이터와 타깃 레이블을 훈련 세트와 테스트 세트로 나눕니다
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 모델 객체를 만들고 훈련 세트로 학습시킵니다
logreg = LogisticRegression().fit(X_train, y_train)
# 모델을 테스트 세트로 평가합니다 
print("테스트 세트 점수: {:.2f}".format(logreg.score(X_test, y_test)))

 


 

교차 검증(Cross Validation)

 

일반화 성능 재기 위해 훈련 세트 테스트 세트 한번 나누는 것보다 더 안정적인 방법

 

데이터를 여러 번 반복해서 나누고 여러 모델을 학습

 

주로 k-겹 교차 검증 | k는 특정 숫자 보통 5, 10 

ex) 5-겹 교차 검증 

데이터를 먼저 fold 라고  하는 거의 비슷한 크기의 부분 집합 "다섯" 개로 나눔

 

-> 그 다음 모델 생성  

첫번째 모델은 1번째 fold - Test / 나머지 train

2번째 fold -Test /...

....

이런 식으로 다섯 번의 분할마다 정확도 측정하여 다섯 개의 정확도 얻음

 

scikit-learn 교차검증 방법

from sklearn.model_selection import cross_val_score from sklearn.datasets import load_iris from sklearn.linear_model import LogisticRegression iris = load_iris() logreg = LogisticRegression() scores = cross_val_score(logreg, iris.data, iris.target) # default = 3 - fold print("교차 검증 점수: {}".format(scores))

교차 검증 점수: [ 0.961  0.922  0.958]

scores = cross_val_score(logreg, iris.data, iris.target, cv=5) print("교차 검증 점수: {}". format(scores)

교차 검증 점수: [ 1.     0.967  0.933  0.9    1.   ]

# 보통 교차 검증 점수 나온 점수들의 평균으로 정확도를 일반화시킨다.

print("교차 검증 평균 점수: {:.2f}".format(scores.mean()))
 
교차 검증 평균 점수: 0.96

5 fold crossvalidation 100~90% 다양 -> 데이터 셋 너무 작거나 훈련 폴드에 매우 의존적이거나

 

 


 

교차 검정의 장점

 

한번 나누는 것보다 -> 교차검증하면 1. train_test_split 이용하면 데이터 무작위  

-> 운 좋게 훈련 세트에는 분류하기 어려운 샘플만 담을 수 있다는 가정 해보자 -> 당연히 정확도는 매우 떨어질 것이다

# 그러나 교차 검증을 사용하면 테스트 세트에 각 샘플이 정확하게 한 번씩 들어간다 

-> 각 샘플은 폴드 중 하나에 속하며 각 폴드는 하나의 테스트가 된다. -> 그래서 교차 검증 점수를 높이기 위해서 -> 잘 일반화됨

 

또 데이터를 여러 개로 나누면 모델이 훈련 데이터에 얼마나 민감한지 알 수 있다.

최악의 경우와 최선의 경우를 각각 확인할 수 있다.

 

2. 한번 분할했을 때보다 더 데이터를 효과적으로 사용 가능하다.

보통 train_test_split -> 75 % train / 25 % test하고 평가를 학습 -> 5겹을 사용하면 매 반복에서 80 % train 20% test

 

# 단점

연산 시간이 늘어난다 -> k-fold 면 한번 할 때보다 시간이 k 배가 걸린다.

 

교차 검증 함수는 -> 모델 반환 x -> cross_val_score 함수 호출 -> 내부적으로 여러 모델 만들지만 

교차 검증 목적 -> 단지 주어진 데이터 셋에 학습된 알고리즘이 얼마나 일반화될 수 있는지를 평가

sklearn.model_seletion 모듈의 cross_val_predict 함수 사요하여 교차 검증으로 만든 예측값을 구할 수 있다. 

cross_val_predict(logreg.iris.data, iris.target, cv=5) -> 각 폴드가 테스트 세트일 때 예측된 값을 반환해준다.


 

계층별 K-겹 교차 검증과 그 외 전략들

항상 좋은 것은 아님 

만약에 iris 데이터 -> 3겹 교차 검증 사용 -> 첫 번째 반복에서 테스트에는 클래스 0 / 2번째에는 테스트에는 1 / 3번째는 테스트에는 2

-> 정확도 0 -> 단순한 k 겹 교차 검증 문제가 있어서 -> scikit-learn에서는 이 방법 대신 계층별 k 겹 교차검증 (stratified) 

cross_val_score 함수에서 기본적으로 분류에는 StratifiedKFold 사용하여 훈련 / 테스트 나누고 | 회귀에서는 단순한 KFold

KFold에서 shuffle 매개변수를 기본 값 FALSE 대신 TRUE  지정하면 폴드 나누기 전 무작위로 섞임 

하지만 cross_val_score에서 KFold는 제어 방법 x ->  따로 KFold 만들어 -> cv 매개변수로 전달


 

교차검증 상세 옵션

 

cross_val_score 사용할 때 CV 매개변수 이용해 폴드 개수 정함 

그러나 SCIKIT-LEARN에서는 CV 매개변수에 교차 검증 분할기(cross_validation spliter) 전달함으로써 더 세밀하게 제어해 분할 가능

회귀 : k-겹 교차 검증 / 분류 : 계층별 k-겹 교차 검증 기본값이 잘 작동한다 

-> 하지만 다른 사람의 결괏값을 재현할 때는 분류 데이터 셋에 기본 k-겹 교차 검증 사용할 때 

-> 먼저 model_selection 모듈에서 KFold 분할기를 import 하고  원하는 폴드 수를 넣어 객체 성성

from sklearn.model_selection import KFold
kfold = KFold(n_splits=5)
print("교차 검증 점수:\n{}".format(
      cross_val_score(logreg, iris.data, iris.target, cv=kfold)))
 
교차 검증 점수:
[ 1.     0.933  0.433  0.967  0.433]

iris에서 3겹 안 좋은지 실제로 확인하기

kfold = KFold(n_splits=3)
print("교차 검증 점수:\n{}".format(
      cross_val_score(logreg, iris.data, iris.target, cv=kfold)))
 
교차 검증 점수:
[ 0.  0.  0.]

 

각 폴드는 iris 데이터셋에 클래스중 대응 x -> 학습 x -> 계층별 폴드를 만드는 대신 다른 해결 방법  

데이터를 섞어서 (shuffle) -> 샘플의 순서를 뒤죽박죽 만들기

KFold에서 shuffle FALSE -> TRUE 바꿔주기

 

random_state로 고정해서 똑같은 작업 재현 가능 

kfold = KFold(n_splits=3, shuffle=True, random_state=0)
print("교차 검증 점수:\n{}".format(
    cross_val_score(logreg, iris.data, iris.target, cv=kfold)))
 
교차 검증 점수:
[ 0.9   0.96  0.96]

 

LOOCV

 

LOOCV 교차 검증은 폴드 하나에 샘플 하나만 들어있는 K 겹 교차 검증으로 생각할 수 있다. 

각 반복에서 하나의 데이터 포인트를 선택해 테스트 세트로 사용한다. 작은 데이터 셋에서는 효과적, 큰 데이터에서는 시간이 걸림

from sklearn.model_selection import LeaveOneOut
loo = LeaveOneOut()
scores = cross_val_score(logreg, iris.data, iris.target, cv=loo)
print("교차 검증 분할 횟수: ", len(scores))
print("평균 정확도: {:.2f}".format(scores.mean()))
 
교차 검증 분할 횟수:  150
평균 정확도: 0.95

 

 


임의 분할 교차검증

 

유연한 또 다른 방법 임의 분할 교차 검증 ( shuffle-split cross-validation) 

임의 분할 교차 검증에서 train_size 만큼의 포인트로 훈련 세트를 만들고, test_size 만큼의 (훈련 세트와 중복되지 않는) 포인트로 테스트 세트

이 분할은 n_splits 횟수만큼 반복 

 

 

다음 코드는 50 %  훈련 50 % test 10번 분할 반복

from sklearn.model_selection import ShuffleSplit
shuffle_split = ShuffleSplit(test_size=.5, train_size=.5, n_splits=10)
scores = cross_val_score(logreg, iris.data, iris.target, cv=shuffle_split)
print("교차 검증 점수:\n{}".format(scores))
 
교차 검증 점수:
[ 0.92   0.947  0.987  0.92   0.88   0.96   0.933  0.987  0.987  0.96 ]

임의 분할 교차 검증은 반복 횟수를 훈련 세트나 테스트 세트의 크기와 독립적으로 조절해야 할 때 유용

또한 train_size test_size의 합을 전체와는 다르게 함으로써 전체 데이터 일부만 사용할 수 있다

이렇게 하는 것을 부분 샘플링(subsampling) 대규모 데이터셋 할 때 도움 가능

 

마지막으로 ShuffleSplit의 계층별 버전 분류 작업에서 더 적합한 StratifiedShuffleSplit 도 있음

 


그룹별 교차 검증

 

데이터 안에 매우 연관된 그룹 -> 교차검증 널리 사용

 

얼굴 사진 인식 100명 모음 -> 한 사람을 찍은 여러 장의 사진 , 여러 표정으로 가지고 있다 

-> 이 데이터셋에 없는 사람의 표정을 정확히 구분하는 것이 목표

 

성능 측정하기 위해 계층별 교차 검증 사용할 수 있지만 -> 같은 사람의 사진이 훈련 세트와 테스트 세트에 모두 나타날 수 있다

완전히 새로운 얼굴보다 훈련 세트에 잇던 얼굴의 표정은 훨씬 쉽게 식별할 수 있을 것이다.

얼굴에 대한 일반화 성능 더 정확하게 평가하려면 훈련 세트와 테스트 세트에서 서로 다른 사람의 사진이 들어가야 한다.

 

이를 위해 사진의 사람이 누구인지 배열을 groups 매개변수로 전달받을 수 있는 GroupKFold를 사용 가능

groups 배열은 훈련 세트와 테스트 세트를 만들 때 분리되지 않아야 할 그룹을 지정하는 것이며 , 클래스 레이블은 아니다. 

 

데이터에 그룹 있는 것들 ex ) 의료 애플리케이션 , 같은 환자로부터 얻은 여러 샘플을 가지고 새로운 환자에게 일반화하는 것이 목적

마찬가지로 음성인식에서도 같은 사람의 목소리가 여러 개 저장 -> 새로운 사람의 대화를 인식하고 싶다

 

from sklearn.model_selection import GroupKFold
# 인위적 데이터셋 생성
X, y = make_blobs(n_samples=12, random_state=0)
# 처음 세 개의 샘플은 같은 그룹에 속하고
# 다음은 네 개의 샘플이 같습니다.
groups = [0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3]
scores = cross_val_score(logreg, X, y, groups, cv=GroupKFold(n_splits=3))
print("교차 검증 점수:\n{}".format(scores))
 
교차 검증 점수:
[ 0.75   0.8    0.667]

12개의 포인트 -> groups는 각 데이터에 대해 각 포인트가 어떤 그룹에 속하는지  (어떤 환자) 4개 그룹 

굳이 정렬할 필요가 없다 -> 여기서 예를 들어서 한 것 그룹 레이블 기반으로 계산해줌 

다양한 상황에서 적용할 수 있는 다양한 교차 검증 전략 있다 그중 기본으로 KFold / StratifiedKFold / GroupKFold

 


 

그리드 서치

 

앞에서는 일반화 -> 지금부터는 매개변수를 튜닝하여 일반화 성능 높이기

 

매개변수란 무엇인가

 

모델에서 중요한 매개변수(일반화 성능 높이는) 값 찾는 것은 어렵지만 모든 모델과 데이터셋에서 해야 할 필수적인 것

 

GRID_SEARCH 로서 관심 있는 매개변수들을 대상으로 가능한 모든 조합 시도하는 것

 

EX) RBF 커널 SVM 할 때 커널의 폭 : gamma / 규제 매개변수 C 중요  총 조합수 36개

# 간단한 그리드 서치 구현
from sklearn.svm import SVC
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target,
                                                    random_state=0)
print("훈련 세트의 크기: {}   테스트 세트의 크기: {}".format(
      X_train.shape[0], X_test.shape[0]))

best_score = 0

for gamma in [0.001, 0.01, 0.1, 1, 10, 100]:
    for C in [0.001, 0.01, 0.1, 1, 10, 100]:
        # 매개변수의 각 조합에 대해 SVC를 훈련시킵니다
        svm = SVC(gamma=gamma, C=C)
        svm.fit(X_train, y_train)
        # 테스트 세트로 SVC를 평가합니다
        score = svm.score(X_test, y_test)
        # 점수가 더 높으면 매개변수와 함께 기록합니다
        if score > best_score:
            best_score = score
            best_parameters = {'C': C, 'gamma': gamma}
            
print("최고 점수: {:.2f}".format(best_score))
print("최적 파라미터: {}".format(best_parameters))
 
훈련 세트의 크기: 112   테스트 세트의 크기: 38
최고 점수: 0.97
최적 파라미터: {'C': 100, 'gamma': 0.001}

 

매개변수 과대 적합과 검증 세트

 

모델 정확도 97 -> 낙관적인 주장이다 -> 이 정확도가 새로운 데이터에는 이어지지 않을 수 있다

 

매개변수를 조정하기 위해 테스트 세트를 이미 사용했기 때문에 -> 평가를 할 수 없다

-> 그래서 훈련 / 검증 나눈 이유 -> 즉 평가를 위해서 모델을 만들 때 사용하지 않은 독립 데이터셋 필요

 

이 문제 해결하기 위해 훈련 : 모델 / 검증 : 매개변수 선택 / 테스트 : 매개변수 평가

 

검증 세트를 사용해 최적의 매개변수 선택 -> 그 매개변수에서 훈련 세트와 검증 세트 데이터를 모두 이용해 모델을 다시 만든다.

이렇게 하는 이유 -> 모델을 만들 때 가능한 많은 데이터를 이용하기 위해서 

from sklearn.svm import SVC
# 데이터를 훈련+검증 세트 그리고 테스트 세트로 분할
X_trainval, X_test, y_trainval, y_test = train_test_split(
    iris.data, iris.target, random_state=0)
# 훈련+검증 세트를 훈련 세트와 검증 세트로 분할
X_train, X_valid, y_train, y_valid = train_test_split(
    X_trainval, y_trainval, random_state=1)
print("훈련 세트의 크기: {}   검증 세트의 크기: {}   테스트 세트의 크기:"
      " {}\n".format(X_train.shape[0], X_valid.shape[0], X_test.shape[0]))

best_score = 0

for gamma in [0.001, 0.01, 0.1, 1, 10, 100]:
    for C in [0.001, 0.01, 0.1, 1, 10, 100]:
        # 매개변수의 각 조합에 대해 SVC를 훈련시킵니다
        svm = SVC(gamma=gamma, C=C)
        svm.fit(X_train, y_train)
        # 검증 세트로 SVC를 평가합니다
        score = svm.score(X_valid, y_valid)
        # 점수가 더 높으면 매개변수와 함께 기록합니다
        if score > best_score:
            best_score = score
            best_parameters = {'C': C, 'gamma': gamma}

# 훈련 세트와 검증 세트를 합쳐 모델을 다시 만든 후
# 테스트 세트를 사용해 평가합니다
svm = SVC(**best_parameters)
svm.fit(X_trainval, y_trainval)
test_score = svm.score(X_test, y_test)
print("검증 세트에서 최고 점수: {:.2f}".format(best_score))
print("최적 파라미터: ", best_parameters)
print("최적 파라미터에서 테스트 세트 점수: {:.2f}".format(test_score))
 
훈련 세트의 크기: 84   검증 세트의 크기: 28   테스트 세트의 크기: 38

검증 세트에서 최고 점수: 0.96
최적 파라미터:  {'C': 10, 'gamma': 0.001}
최적 파라미터에서 테스트 세트 점수: 0.92

 

더 일반화가 잘되게 한 것을 알 수 있다 새로운 데이터에 대해 92% 정확하게 분류

 

훈련 세트 / 검증 세트 / 테스트 세트 -> 아주 중요 ###

 

거의 모든 모델을 짤 때 기본이 되므로 저런 코드를 이용해하는 것이 중요!!

 


 

교차 검증을 사용한 그리드 서치

 

훈련 세트 / 검증 세트 / 테스트 세트  이렇게 데이터를 나눴지만 나누는 방법에 매우 민감!!!

 

-> 일반화 성능 올리기 위해 교차검증 사용하기

 

for gamma in [0.001, 0.01, 0.1, 1, 10, 100]:
    for C in [0.001, 0.01, 0.1, 1, 10, 100]:
        # 매개변수의 각 조합에 대해 SVC를 훈련시킵니다
        svm = SVC(gamma=gamma, C=C)
        # 교차 검증을 적용합니다
        scores = cross_val_score(svm, X_trainval, y_trainval, cv=5)
        # 교차 검증 정확도의 평균을 계산합니다
        score = np.mean(scores)
        # 점수가 더 높으면 매개변수와 함께 기록합니다
        if score > best_score:
            best_score = score
            best_parameters = {'C': C, 'gamma': gamma}
# 훈련 세트와 검증 세트를 합쳐 모델을 다시 만듭니다
svm = SVC(**best_parameters)
svm.fit(X_trainval, y_trainval)
Out[22]:

SVC(C=100, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape='ovr', degree=3, gamma=0.01, kernel='rbf', max_iter=-1, probability=False, random_state=None, shrinking=True,  tol=0.001, verbose=False)

 

 

정확도 평가하려면 5 X 6 X 6 = 180... 컴퓨터를 좋은 걸로 바꿔야 하나...

 

 

 

교차 검증은 -> 주어진 데이터 셋에서 알고리즘을 평가하는 방법

 

하지만 그리드 서치와 같은 탐색방법과 합쳐서 많이 사용 -> 그래서 많은 사람이 교차 검증이란 용어를

 

교차 검증을 사용한 그리드 서치라고 많이 한다고 한다.

 

 

그래서 SCIKIT-LEARN -> GridSearchCV 제공한다 

 GridSearchCV  먼저 딕셔너리 행태로 검색 대상 매개변수 지정해야 한다

param_grid = {'C': [0.001, 0.01, 0.1, 1, 10, 100],
              'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
print("매개변수 그리드:\n{}".format(param_grid))
 
매개변수 그리드:
{'C': [0.001, 0.01, 0.1, 1, 10, 100], 'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
grid_search = GridSearchCV(SVC(), param_grid, cv=5, return_train_score=True)
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target,
                                                    random_state=0)
grid_search.fit(X_train, y_train)

# fit 사용하면 param_grid에 설정된 매개변수 조합에 대한 교차검증 실시

GridSearchCV(cv=5, error_score='raise',
       estimator=SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False),
       fit_params=None, iid=True, n_jobs=1,
       param_grid={'C': [0.001, 0.01, 0.1, 1, 10, 100], 'gamma': [0.001, 0.01, 0.1, 1, 10, 100]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score=True,
       scoring=None, verbose=0)
GridSearchCV 객채에 FIT 매서드는 최적의 매개변수를 찾는 일 뿐만 아니라 교차 검증 성능이 가장 좋은 매개변수로 

전체 훈련 데이터 세트에 대해 새로운 모델을 자동을 만든다.

 

GridSearchCV는 전체 데이터로 학습한 모델에 접근할 수 있도록 PREDICT와 SCORE 메서드 제공

## GridSearchCV 분류 : StratifiedKFold 회귀 : KFold

meta_esimator_mixin -> 메타 추정 기라고 한다. 랜덤 포레스트., 그래디언트 부스팅, RFE 다 포함됨

사용에 따라서는 predict_proba, decision_function 도 제공

 

# 매개변수를 사용하지 않았을 때

print("테스트 세트 점수: {:.2f}".format(grid_search.score(X_test, y_test)))
 
테스트 세트 점수: 0.97

 

# 매개변수를 사용할 때

print("최적 매개변수: {}".format(grid_search.best_params_))
print("최고 교차 검증 점수: {:.2f}".format(grid_search.best_score_))
 
최적 매개변수: {'C': 100, 'gamma': 0.01}
최고 교차 검증 점수: 0.97

# 특성 중요도를 살펴보라고 할 때입니다

# 최적 매개 변수에서 전체 훈련 세트를 사용하여 학습한 모델은 best_estimator_

# grid_search 객체가 predict와 score 메서드를 가지고 있으므로 예측이나 모델을 평가하기 위해

# best_estimator_ 속성을 사용할 필요가 없다.

print("최고 성능 모델:\n{}".format(grid_search.best_estimator_))
 
최고 성능 모델:
SVC(C=100, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=0.01, kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)

교차 검증 결과 분석

 
교차 검증 시각화 -> 검색 대상 매개변수가 모델의 일반화 이해 하는데 도움줌
# 적은 수 그리드로 시작하는 것이 좋음 # 여러 정보가 함께 저장 되어 있는 cv_results 속성에 담겨져 있다. 
# 상세 정보가 들어 있다 
# Randomized Search CV 의 결과가 GridSearchCV 보다 크게 뒤떠러 지지 않는다 . 
# 매개변수 샘플링 기본 횟수 10개 이며 n_iter 
cv_results_ 총 23zl 
import pandas as pd
pd.set_option('display.max_columns', None)
# DataFrame으로 변환합니다
results = pd.DataFrame(grid_search.cv_results_)
# 처음 다섯 개 행을 출력합니다
display(np.transpose(results.head()))

 

#C와 gamma 시각화

 

scores = np.array(results.mean_test_score).reshape(6, 6)

# 교차 검증 평균 점수 히트맵 그래프
mglearn.tools.heatmap(scores, xlabel='gamma', xticklabels=param_grid['gamma'],
                      ylabel='C', yticklabels=param_grid['C'], cmap="viridis")

 

 

 

정확도가 높으면 -> 밝은 색 / 낮으면 -> 어두운 색

SVC 가 매개변수에 민감 

 

바람직하지 못한 예

 

fig, axes = plt.subplots(1, 3, figsize=(13, 5))
param_grid_linear = {'C': np.linspace(1, 2, 6),
                     'gamma':  np.linspace(1, 2, 6)}

param_grid_one_log = {'C': np.linspace(1, 2, 6),
                     'gamma':  np.logspace(-3, 2, 6)}

param_grid_range = {'C': np.logspace(-3, 2, 6),
                     'gamma':  np.logspace(-7, -2, 6)}

for param_grid, ax in zip([param_grid_linear, param_grid_one_log,
                           param_grid_range], axes):
    grid_search = GridSearchCV(SVC(), param_grid, cv=5)
    grid_search.fit(X_train, y_train)
    scores = grid_search.cv_results_['mean_test_score'].reshape(6, 6)

    # 교차 검증 평균 점수의 히트맵 그래프
    scores_image = mglearn.tools.heatmap(
        scores, xlabel='gamma', ylabel='C', xticklabels=param_grid['gamma'],
        yticklabels=param_grid['C'], cmap="viridis", ax=ax)
    
plt.colorbar(scores_image, ax=axes.tolist())

첫 번째 그래프는 점수 변화가 없어서 -> 전체 매개변수 그리드가 같은 색 -> 부적절하게 발생

 

그러나 매개변수 설정이 바뀌어도 정확에 변화 없다면 -> 매개변수가 전혀 중요하지 않은 것 

 

매우 극단적인 값 적용해보고 -> 매개변수 바꿔가며 정확도 살펴봄 

## 두 번째 그래프 세로 띠 -> Gamma 매개 변수만 정확도에 영향을 준다는 뜻 

# 교차 검증 점수를 토대로 매개변수 그리드를 튜닝하는 것 -> 아주 안전한 방법 

 

비대칭 매개변수 그리드 탐색

 

어떤 경우에는 모든 매개변수 조합 GridSearchCV 수행하는 것 좋지 않을 수 있음

SVC는 KERNEL 매개변수를 가지고 있는데 어떤 커널을 사용하는지에 따라 관련 있는 매개변수들이 결정

KERNEL =" linear" 이면 선형 모델 C 매개 변수만 사용

KERNEL = "RBF" C와 GAMMA를 모두 사용 (C, GAMMA , KERNEL 매개변수의 모든 조합을 조사 X)

KERNEL ="linear" 이면 gamma를 사용하지 않으므로 -> 시간 낭비

 

이런 조건부 매개변수 조합을 적용하려면 gridsearchcv에 전달할 param_grid를 딕셔너리의 리스트로 만들어줌

각 딕셔너리는 독립적인 그리드로 적용됩니다.

param_grid = [

{'kernel': ['rbf'], 'C': [0.001, 0.01, 0.1, 1, 10, 100], 'gamma': [0.001, 0.01, 0.1, 1, 10, 100]},

 

{'kernel': ['linear'], 'C': [0.001, 0.01, 0.1, 1, 10, 100]}] print("그리드 목록:\n {}". format(param_grid))

그리드 목록:
[{'C': [0.001, 0.01, 0.1, 1, 10, 100], 'gamma': [0.001, 0.01, 0.1, 1, 10, 100], 'kernel': ['rbf']}, {'C': [0.001, 0.01, 0.1, 1, 10, 100], 'kernel': ['l

 

grid_search = GridSearchCV(SVC(), param_grid, cv=5, return_train_score=True)
grid_search.fit(X_train, y_train)
print("최적 파라미터: {}".format(grid_search.best_params_))
print("최고 교차 검증 점수: {:.2f}".format(grid_search.best_score_))
 
최적 파라미터: {'C': 100, 'gamma': 0.01, 'kernel': 'rbf'}
최고 교차 검증 점수: 0.97

 

results = pd.DataFrame(grid_search.cv_results_)
# 좀 더 나은 출력을 위해 결과를 전치시킵니다
display(results.T)

 

그리드 서치에 다양한 교차 검증 적용

 

cross_val_score와 비슷하게 gridsearchcv는 분류에는 기본적으로 계층형 k-겹 교차 검증을 사용하고 회귀에는 k-겹 교차 검증 사용

 

중첩 교차 검증

훈련 , 검증, 테스트 세트 / 훈련 세트와 테스트 세트로 나눈 다음 훈련 세트로 교차 검증 수행하는 방식으로 바꿈

하지만 GridSearchCV를 사용할 때 데이터를 훈련 세트와 테스트 세트로 한 번만 나누기 때문에  분할에 크게 의존

중합 교차 검증 - 바깥쪽 루프 ( 훈련 / 테스트 ) 그리고 각 훈련 세트에 대해 그리드 서치 실행

 

 

param_grid = {'C': [0.001, 0.01, 0.1, 1, 10, 100],
              'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
scores = cross_val_score(GridSearchCV(SVC(), param_grid, cv=5),
                         iris.data, iris.target, cv=5)
print("교차 검증 점수: ", scores)
print("교차 검증 평균 점수: ", scores.mean())
print(param_grid)
 
교차 검증 점수:  [ 0.967  1.     0.967  0.967  1.   ]
교차 검증 평균 점수:  0.98
{'C': [0.001, 0.01, 0.1, 1, 10, 100], 'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}

안쪽 루프와 바깥쪽 루프에 각각 계층형 5겹 교차 검증 사용 

param_grid 매개변수 조합은 42개 , 42x5x5 = 1050개나 되므로 연산 매우 비싼 방법

 
def nested_cv(X, y, inner_cv, outer_cv, Classifier, parameter_grid):
    outer_scores = []
    # outer_cv의 분할을 순회하는 for 루프
    # (split 메소드는 훈련과 테스트 세트에 해당하는 인덱스를 리턴합니다)
    for training_samples, test_samples in outer_cv.split(X, y):
        # 최적의 매개변수를 찾습니다
        best_parms = {}
        best_score = -np.inf
        # 매개변수 그리드를 순회합니다
        for parameters in parameter_grid:
            # 안쪽 교차 검증의 점수를 기록합니다
            cv_scores = []
            # inner_cv의 분할을 순회하는 for 루프
            for inner_train, inner_test in inner_cv.split(
                    X[training_samples], y[training_samples]):
                # 훈련 데이터와 주어진 매개변수로 분류기를 만듭니다
                clf = Classifier(**parameters)
                clf.fit(X[inner_train], y[inner_train])
                # 검증 세트로 평가합니다
                score = clf.score(X[inner_test], y[inner_test])
                cv_scores.append(score)
            # 안쪽 교차 검증의 평균 점수를 계산합니다
            mean_score = np.mean(cv_scores)
            if mean_score > best_score:
                # 점수가 더 높은면 매개변수와 함께 기록합니다
                best_score = mean_score
                best_params = parameters
        # 바깥쪽 훈련 데이터 전체를 사용해 분류기를 만듭니다
        clf = Classifier(**best_params)
        clf.fit(X[training_samples], y[training_samples])
        # 테스트 세트를 사용해 평가합니다
        outer_scores.append(clf.score(X[test_samples], y[test_samples]))
    return np.array(outer_scores)
from sklearn.model_selection import ParameterGrid, StratifiedKFold
scores = nested_cv(iris.data, iris.target, StratifiedKFold(5),
                   StratifiedKFold(5), SVC, ParameterGrid(param_grid))
print("교차 검증 점수: {}".format(scores))
 
교차 검증 점수: [ 0.967  1.     0.967  0.967  1.   ]

 

교차 검증과 그리드 서치 병렬화


그리드 서치는 데이터 용량이 크고 매개변수 수도 많을 때는 -> 상당한 연산 부하

=> 병렬화 가능 

 

하나의 교차 검증 분할에서 특정 매개변수를 설정 사용 -> 모델을 만드는 일은 다른 매개변수 설정이나 모델과 전혀 상관 입어 진행할 수 있기 때문이다.

 

그러므로 그리드 서치와 교차 검증은 여러 cpu 코어 또는 클러스터에 병렬화하기 좋습니다.

 

GridSearchCV와 cross_val_score에서 n_jobs 사용해 지정

skitlearn - 병렬화를 중첩해서 사용 x  -> 그래서 n_jobs 옵션 사용하면 -> 병렬화 옵션 사용 X

 


 

평가 지표와 측정

 

이진 분류의 평가 지표  

이진 분류에서 양성 클래스가 - 관심 클래스

 

에러의 종류 

 

정확도 만으로는 예측 성능 측정하기 부족함 

 

불균형 데이터 셋

한 클래스가 다른 클래스보다 많을 때 

from sklearn.datasets import load_digits digits = load_digits() y = digits.target == 9 # 0~8 FALSE 9 TRUE X_train, X_test, y_train, y_test = train_test_split( digits.data, y, random_state=0)

from sklearn.dummy import DummyClassifier
dummy_majority = DummyClassifier(strategy='most_frequent').fit(X_train, y_train)
pred_most_frequent = dummy_majority.predict(X_test)
print("예측된 레이블의 고유값: {}".format(np.unique(pred_most_frequent)))
print("테스트 점수: {:.2f}".format(dummy_majority.score(X_test, y_test)))
 
예측된 레이블의 고유값: [False]
테스트 점수: 0.90

DUMMYCLASSIFIER 실제 모델과 비교하기 위해 간단한 규칙 지원 STRATEGY 옵션 - STRATIFIED - 클래스 비율에 맞게 / MOST_FREQUENT - 가장 많은 레이블

 

# -> 학습하지 않고 90 % 얻음 

 

from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(max_depth=2).fit(X_train, y_train)
pred_tree = tree.predict(X_test)
print("테스트 점수: {:.2f}".format(tree.score(X_test, y_test)))
 
테스트 점수: 0.92
from sklearn.linear_model import LogisticRegression

dummy = DummyClassifier().fit(X_train, y_train)
pred_dummy = dummy.predict(X_test)
print("dummy 점수: {:.2f}".format(dummy.score(X_test, y_test)))

logreg = LogisticRegression(C=0.1).fit(X_train, y_train)
pred_logreg = logreg.predict(X_test)
print("logreg 점수: {:.2f}".format(logreg.score(X_test, y_test)))
 
dummy 점수: 0.83
logreg 점수: 0.98

 

불규칙 데이터셋에서는 정확도로 측정하는 것은 옳지 못한다

PRED_MOST_FREQUENT와 PRED_DUMMY처럼 빈도나 무직 위 기반 예측보다 얼마나 더 나은지 알려주는 평가 지표 필요

-> 비상식적 예측 피할 수 있다.

 

 

 

 

 


 

오차 행렬

 

- 이진 분류 평가 결과를 나타낼 때 가장 널리 사용하 넌 법

CONFUSION_MATRIX 함수 사용해서 이용함

from sklearn.metrics import confusion_matrix

confusion = confusion_matrix(y_test, pred_logreg)
print("오차 행렬:\n{}".format(confusion))
 
오차 행렬:
[[401   2]
 [  8  39]]

 

오차 행렬의 대각형렬 - 정확히 분류된 경우 

 

다른 항목들 한 클래스의 샘플들이 다른 클래스로 잘 본 된 것이 얼마나 많은지

 

print("빈도 기반 더미 모델:")

print(confusion_matrix(y_test, pred_most_frequent))
print("\n무작위 더미 모델:")
print(confusion_matrix(y_test, pred_dummy))
print("\n결정 트리:")
print(confusion_matrix(y_test, pred_tree))
print("\n로지스틱 회귀")
print(confusion_matrix(y_test, pred_logreg))
 
빈도 기반 더미 모델:
[[403   0]
 [ 47   0]]

무작위 더미 모델:
[[371  32]
 [ 43   4]]

결정 트리:
[[390  13]
 [ 24  23]]

로지스틱 회귀
[[401   2]
 [  8  39]]

# 결정 트리와 로티스 틱 회귀가 그나마 정상적임

재현율= 민감도, 적중률, 진짜 양성 비율

 

재현율 최적화와 정밀도 최적화는 "상충" 

 

IF  FN를 없애서 -> 재현율 완벽 -> 하지만 샘플 양성으로 예측하면 -> FP 증가 -> 정밀도 떨어짐

 

IF FP를 깡그리 없애면 -> 정밀도 완벽 ->  하지만 재현율 떨어짐

 

정밀도와 재현율의 조화 평균 인 f - 점수 

 

 

from sklearn.metrics import f1_score
print("빈도 기반 더미 모델의 f1 score: {:.2f}".format(
    f1_score(y_test, pred_most_frequent)))
print("무작위 더미 모델의 f1 score: {:.2f}".format(f1_score(y_test, pred_dummy)))
print("트리 모델의 f1 score: {:.2f}".format(f1_score(y_test, pred_tree)))
print("로지스틱 회귀 모델의 f1 score: {:.2f}".format(
    f1_score(y_test, pred_logreg)))
 
빈도 기반 더미 모델의 f1 score: 0.00
무작위 더미 모델의 f1 score: 0.10
트리 모델의 f1 score: 0.55
로지스틱 회귀 모델의 f1 score: 0.89

# 2가지 주목 : 하나는 양성 클래스로 예측된 것이 하나도 없어서 빈도 기반 더미 모델 -> 에러 발생

 

또한 무작위 더미 분류기와 트리 분류기 사이에서도 - 뚜렷한 차이 

 

f-1 점수가 직관적으로 판단할 때 유용하다. 하지만 이해하거나 설명하기 어려움

 

from sklearn.metrics import classification_report
print(classification_report(y_test, pred_most_frequent,
                            target_names=["9 아님", "9"]))
 
             precision    recall  f1-score   support

       9 아님       0.90      1.00      0.94       403
          9       0.00      0.00      0.00        47

avg / total       0.80      0.90      0.85       450

 

print(classification_report(y_test, pred_dummy,
                            target_names=["9 아님", "9"]))
 
             precision    recall  f1-score   support

       9 아님       0.90      0.92      0.91       403
          9       0.11      0.09      0.10        47

avg / total       0.81      0.83      0.82       450

 

print(classification_report(y_test, pred_logreg, target_names=["9 아님", "9"]))

 
             precision    recall  f1-score   support

       9 아님       0.98      1.00      0.99       403
          9       0.95      0.83      0.89        47

avg / total       0.98      0.98      0.98       450

불확실성 고려 

 

오차 행렬과 분류 리포트가 - 예측 결과 자세히 분석하게 도와줌

 

-> 하지만 예측값은 모델에 담긴 많은 정보가 이미 손실된 상태 -> 대부분 분류기 예측의 확신 가늠하기 위한 (decision_function. decision_proba 제공)

 

예측을 만들어 내는 것은 decision_function이나 predict_proba 출력의 임계값을 검증

decision-function =0 // predict_proba = 0.5

from mglearn.datasets import make_blobs 
X, y = make_blobs(n_samples=(400, 50), centers=2, cluster_std=[7.0, 2],        
                  random_state=22)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
svc = SVC(gamma=.05).fit(X_train, y_train)                                     
mglearn.plots.plot_decision_threshold()

print(classification_report(y_test, svc.predict(X_test)))
 
             precision    recall  f1-score   support

          0       0.97      0.89      0.93       104
          1       0.35      0.67      0.46         9

avg / total       0.92      0.88      0.89       113

 

# -> 클래스 1의 재현율을 높이는 게 중요하다고 가정해보자

-> 클래스 1로 잘못 분류된 FP(거짓 양성)이 늘어나더라도 TP를 늘리자는 뜻 

 

임계값을 바꿔 클래스 1의 재현율을 높이도록 예측 조정

기본적으로 decision_function 값이 0보다 큰 포인트는 클래스 1로 분류되므로 클래스 1로 분류되려면 좀 더 임계값을 낮춰야 한다.

 

y_pred_lower_threshold = svc.decision_function(X_test) > -.8
print(classification_report(y_test, y_pred_lower_threshold))
 
             precision    recall  f1-score   support

          0       1.00      0.82      0.90       104
          1       0.32      1.00      0.49         9

avg / total       0.95      0.83      0.87       113

# 실전에서는 테스트 데이터 사용 x -> 검증 세트와 교차 검증 (너무 낙관적으로 나오게 하는 거 방지)

 

predict_proba 출력이 0~1 고정되어 있어서 -> 선택하기 쉬움

기본 값 0.5 임계값 0.5 이상 확신 -> 양성 임계값 높이면 -> 양성 클래스 분류할 때 더 큰 확신 필요

 

 

728x90