갈루아의 반서재

scikit-learn (3) - 선형분류 (결과평가)


1. 예측하기


꽃받침 너비가 4.7이고 꽃받침 길이가 3.1인 새로운 꽃이 있다고 가정하고, 이 꽃의 범주를 예측해보자. 예측 메소드는 인스턴스 배열을 입력받고, 예측 범주의 리스트를 반환한다.  다음과 같다

1
2
>>> print clf.predict(scaler.transform([4.73.1]))
[0]
cs

분류기가 정확했다면, 이 꽃의 범주는 세토사이다. 앞선 포스팅에서 선형 모델은 두 개의 분류를 나누는 것을 기억했다. 그러므로 예측절차는 세 개 이진 분류기의 결과를 합하고 좀 더 확실한 범주를 선택한다. 이 경우 인스턴스에서 멀리 떨어진 경계선을 선택한다. 분류기의 decision_function 메소드를 사용해 확인할 수 있다.

1
2
3
>>> print clf.decision_function(scaler.transform([4.73.1]))
[[ 24.25497645  -9.30416563 -42.71765579]
 
cs



2. 결과평가


1) 성능측정

분류기의 성능은 유효성의 측정이다. 가장 단순한 성능 측정은 정확도다. 분류기와 평가데이터를 고려해, 분류기가 정확하게 분류한 인스턴스의 비율을 측정한다. 먼저 훈련데이터에서 정확도를 측정해보자.

1
2
3
4
>>> from sklearn import metrics
>>> y_train_pred = clf.predict(X_train)
>>> print metrics.accuracy_score(y_train, y_train_pred)
0.8125
cs

이 수치는 분류기가 데이터 인스턴스의 81.25%만 정확하게 분류했음을 말해준다.


2) 과적합화

훈련데이터의 정확도만을 측정하는 것은 좋지 않다. 훈련데이터를 사용해 모델을 만들었고 모델은 이 데이터에 잘 적응되었지만 미래에 형편없이 예측할 수 있다. 이 현상을 과적합화overfitting 이라고 한다.

훈련데이터를 기반으로 측정한다면 과적합화를 절대 발견할 수 없다. 그래서 훈련데이터를 기반으로 절대 측정하지 않는다. 원본 데이터셋에서 일부를 보관한 이유가 여기에 있다. 이전에 보지 못한 데이터에 대한 성능을 평가하기 위해서이다. 이번에는 평가데이터로 정확도를 다시 측정해보자.

1
2
3
>>> y_pred = clf.predict(X_test)
>>> print metrics.accuracy_score(y_test, y_pred)
0.605263157895
cs

테스트데이터에 대해 60%의 정확도를 얻었다. 일반적으로 테스트데이터의 정확도는 훈련데이터보다 낮은 정확도를 보인다. 목표는 훈련데이터로 훈련할 때 과적합화를 피하는 모델을 생성하는 일이다.


3. 평가함수


1) 평가함수

scikit-learn 에는 몇 가지 평가함수가 있다. 이러한 함수 문제는 이진 분류 문제와 두 범주 (긍정과 부정)로 가정한다. 앞의 예를 들자면, 긍정 범주는 아이리스 세토사인 반면, 두 품종은 합해 하나의 부정 범주가 된다.

정밀도 precision

 정확한 긍정으로 예측된 인스턴스의 비율 계산

재현율 recall

 정확학게 평가된 긍정 인스턴스의 비율

F1-점수

 정밀도와 재현율의 조화평균 두 수를 결합해 한 자리수로 만든다


참과 거짓, 긍정과 부정의 관점으로 이러한 측정을 다음과 같이 정의할 수 있다.

 

예측 : 긍정

예측 : 부정

 목적 경우 : 긍정

참 긍정 True Positive (TP)

거짓 부정 False Negative (FN)

 목적 경우 : 부정

거짓 긍정 False Positive (FP)

참 부정 True Negative (TN)


2) 평가함수의 수식

표본의 크기 m 과 다음 식을 만든다.

m = TP + FP + FN + TN

정확도

 (TP + TN ) / m

 정밀도

 TP / (TP + FP)

 재현율

 TP / (TP + FN)

 F-1 점수

 2 * 정밀도 * 재현율 / (정밀도 + 재현율)


1
2
3
4
5
6
7
8
>>> print metrics.classification_report(y_test, y_pred, target_names=iris.target_names)
            precision    recall  f1-score   support
 
     setosa       1.00      1.00      1.00         8
 versicolor       0.38      0.27      0.32        11
  virginica       0.64      0.74      0.68        19
 
avg / total       0.64      0.66      0.64        38
cs

분류기는 세토사 범주에 대해 1.0인 정밀도와 재현율을 얻었다.

정밀도는 세토사로 분류한 인스턴스의 100%가 실제 세토사 인스턴스였고, 재현율은 세토사 인스턴스의 100%가 세토사로서 분류되었음을 뜻한다.

한편 버시칼라 범주에서는 결과가 좋지 않았다. 43%의 정밀도는 단지 버시칼라로 분류된 인스턴스의 43%가 실제 버시칼라 인스턴스로 분류되었다. 또한 0.27%의 재현율은 버시칼라의 27% 인스턴스가 정확하게 분류했다.


3) 혼돈 매트릭스 confusion matrix

다른 유용한 메트릭은 혼돈 매트릭스다. ( i, j ) 칸은 범주 j 로 예측한 반면, 실제 범주가 i 인 인스턴스의 개수를 나타낸다. 좋은 분류기는 정확하게 분류된 혼돈 매트릭스의 대각선에 인스턴스 개수가 많다.

1
2
3
4
>>> print metrics.confusion_matrix(y_test, y_pred)
[[ 8  0  0]
 [ 0  0 11]
 [ 0  0 19]]
cs

분류기는 범주가 0인 꽃을 분류할 때 평가에서 절대 틀리지 않았다. 그러나 범주가 1과 2인 꽃을 직면해서 이 꽃들을 혼동했다. 혼돈 매트릭스는 분류기가 행한 실수의 종류에 대해 유용한 정보를 준다.


4) 교차 검증 cross-validation

데이터의 분할은 훈련하는 인스턴스의 개수와 특정 속성의 분할에 의존적이므로 최종 결과에 영향을 미친다. 교차 검증은 이러한 특정 경우를 피할 수 있게 하며, 결과의 변화를 줄이고 모델에 대해 현실적인 점수를 만든다. k-중첩 교차검증의 일반적인 단계는 다음과 같다.

  • k 개의 부분집합으로 나눈다.
  • k-1 개 부분집합으로 훈련하고 나머지 부분 집합으로 테스트한 k 개의 모델을 만든다
  • k 모델에 대해 성능을 측정하고 평균을 구한다.

먼저 선형 모델과 표준화를 파이프라인으로 합성한 에스터메이터를 만든다. 파이프라인을 사용해 각 반복에서 데이터를 표준화하고, 변환된 데이터로 훈련 및 테스트를 한다. Pipeline 클래스는 변환을 연속적으로 적용해 좀 더 복잡한 모델을 단순하게 생성하는데 사용된다.

k = 5 중첩으로, 매번 데이터의 80%로 훈련하고 남은 20%를 사용하게 한다. 기본적으로 교차검증은 성능 측정으로서 정확도를 사용하지만, 매개변수에 대해 다른 점수 함수를 지정해 측정을 선택할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
>>> from sklearn.cross_validation import cross_val_score, KFold
>>> from sklearn.preprocessing import StandardScaler
>>> from sklearn.pipeline import Pipeline
>>> clf = Pipeline({
>>>         ('scaler', StandardScaler()),
>>>         ('linear_model', SGDClassifier())        
>>>     }
>>> )
>>> cv = KFold(X.shape[0], 5, shuffle=True, random_state=33)
>>> scores = cross_val_score(clf, X, y, cv=cv)
>>> print scores
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-4c521b642cbf> in <module>()
      8 )
      9 cv = KFold(X.shape[0], 5, shuffle=True, random_state=33)
---> 10 scores = cross_val_score(clf, X, y, cv=cv)
     11 print scores
 
/root/anaconda/envs/tensorflow/lib/python2.7/site-packages/sklearn/cross_validation.pyc in cross_val_score(estimator, X, y, scoring, cv, n_jobs, verbose, fit_params, pre_dispatch)
   1509 
   1510     cv = check_cv(cv, X, y, classifier=is_classifier(estimator))
-> 1511     scorer = check_scoring(estimator, scoring=scoring)
   1512     # We clone the estimator to make sure that all the folds are
   1513     # independent, and that it is pickle-able.
 
/root/anaconda/envs/tensorflow/lib/python2.7/site-packages/sklearn/metrics/scorer.pyc in check_scoring(estimator, scoring, allow_none)
    271         raise TypeError(
    272             "If no scoring is specified, the estimator passed should "
--> 273             "have a 'score' method. The estimator %r does not." % estimator)
    274 
    275 
 
TypeError: If no scoring is specified, the estimator passed should have a 'score' method. The estimator Pipeline(steps=[('linear_model', SGDClassifier(alpha=0.0001, average=False, class_weight=None, epsilon=0.1,
       eta0=0.0, fit_intercept=True, l1_ratio=0.15,
       learning_rate='optimal', loss='hinge', n_iter=5, n_jobs=1,
       penalty='l2', power_t=0.5, random_state=None, shuffle=True,
       verbose=0, warm_start=False)), ('scaler', StandardScaler(copy=True, with_mean=True, with_std=True))]) does not.
cs


k 개 점수로 배열을 구했다. 최종 수치를 얻기 위해 표준 편차와 평균을 계산한다.

1
2
3
4
>>> from scipy.stats import sem
>>> def mean_score(scores):
>>>     return ("Mean score: {0:.3f} (+/-{1:.3f})").format(np.mean(scores), sem(scores))
>>> print mean_score(scores)
cs