오차 행렬(confusion matrix)로 분류 성능 평가

2021. 4. 24. 15:24캐리의 데이터 세상/캐리의 데이터 공부 기록

반응형

빅데이터 분석기사 시험에서 여러 형태로 출제되었던 confusion matrix 관련 기본 개념과 더불어 사이킷런 함수를 활용하여 분류기의 지표를 계산해 보겠습니다. 단순히 행렬 표와 정밀도, 재현율, F1 score 식만 외웠다면 시험용 한 번에 불과할 테니 어떻게 쓰이는지 핸즈온 머신러닝 책에 수록된 예시로 정리합니다. 그리고 MNIST 숫자 이미지 데이터셋을 지문으로 주어진 다음에 어떤 알고리즘이 쓰일 것인가 묻는 문제도 빅분기에서 출제되었는데요, 같이 활용해 보겠습니다^^

참고 : MNIST 데이터셋은 고등학생과 미국 인구조사국 직원들이 손으로 쓴 70,000개의 작은 숫자 이미지를 모은 것입니다. 각 이미지에는 어떤 숫자를 나타내는지 레이블이 되어 있으며, 학습용으로 아주 많이 쓰입니다.

 

1. MNIST 데이터셋 불러오기

사이킷런에서 읽어 들인 데이터셋은 일반적으로 비슷한 딕셔너리 구조를 가지고 있음

  • 데이터셋을 설명하는 DESCR 키
  • 샘플이 하나의 행, 특성이 하나의 열로 구성된 배열을 가진 data 키
  • 레이블 배열을 담은 target 키
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1)
mnist.keys()
dict_keys(['data', 'target', 'frame', 'categories', 'feature_names', 'target_names', 'DESCR', 'details', 'url'])

배열들을 살펴보면

X, y = mnist["data"], mnist["target"]
X.shape
(70000, 784)
y.shape
(70000,)

- 이미지가 70,000개 있고 각 이미지에는 28X28 픽셀이기 때문에  784개의 특성이 있음. 

- 각 특성은 0(흰색)부터 255(검은색)까지의 픽셀 강도를 나타냄.

 

 

 

 

 

* 데이터셋에서 이미지 하나를 확인해보기. 샘플의 특성 벡터를 추출해서 28 x 28 배열로 크기를 바꾸고 matplotlib의 imshow() 함수를 사용해서 그리기

import matplotlib as mpl
import matplotlib.pyplot as plt

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 샘플

 

숫자 5로 보이는데 실제 레이블도 똑같은지 확인해 보기

y[0]
'5'

레이블은 문자열입니다. 대부분 머신러닝 알고리즘은 숫자를 기대하므로 y를 정수로 변환하면

y=y.astype(np.uint8)

Train / Test 세트 나누기 (앞쪽 60,000개 이미지를 훈련 세트로, 뒤쪽 10,000개 이미지를 테스트 세트로)

X_train, X_test, y_train, y_test = X[:60000], X[60000:],y[:60000], y[60000:]

 

2. 이진 분류기 훈련

문제를 단순화해서 숫자 5만 식별하는 이진 분류기(binary classifier)를 만들어 보면, 

y_train_5 = (y_train == 5) #5는 True, 다른 숫자는 모두 False
y_test_5 = (y_test == 5)

분류 모델 중 하나인 SGD (Stochastic Gradient Descent) 확률적 경사 하강법을 선택해 훈련시키기. SGDClassifier는 훈련하는 데 무작위성을 사용하기 때문에 이름에 '확률적'이 붙음. 결과를 재현하고 싶다면 random_state 매개변수를 지정해야 함.

from sklearn.linear_model import SGDClassifier

sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train, y_train_5)
SGDClassifier(random_state=42)

 

모델을 사용해서 앞서 다뤘던 숫자 5의 이미지를 감지해 보면 이 이미지가 5를 나타낸다고 추측했네요(True)

sgd_clf.predict([some_digit])
array([ True])

 

 

3. 오차 행렬로 성능 평가

오차 행렬(confusion matrix)의 기본적인 아이디어는 클래스 A의 샘플이 클래스 B로 분류된 횟수를 세는 것. 예를 들어 분류기가 숫자 5의 이미지를 3으로 잘못 분류한 횟수를 알고 싶다면 오차 행렬의 5행 3열을 보면 된다.

오차 행렬을 만들려면 실제 타깃과 비교할 수 있도록 먼저 예측값을 만들어야 함. 사이킷런에서는 cross_val_predict() 함수 사용!

from sklearn.model_selection import cross_val_predict

y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)

타깃 클래스(y_train_5)와 예측 클래스(y_train_pred)를 넣고 호출

from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5, y_train_pred)
array([[53892, 687],
        [ 1891, 3530]], dtype=int64)

 

오차 행렬의 은 실제 클래스를 나타내고 열은 예측한 클래스를 나타냅니다.

결과 행렬의 첫 번째 행은 '5 아님' 이미지(음성 클래스 negative class)에 대한 것으로 53,892 개를 '5 아님'으로 정확하게 분류했고(진짜 음성 True negative) 나머지 687개는 '5'라고 잘못 분류 (거짓 양성, False positive)

두 번째 행은 '5' 이미지 (양성 클래스 positive class)에 대한 것으로 1,891개를 '5' 아님으로 잘못 분류(False negative) 나머지 3,530개를 정확히 '5'라고 분류(true positive)

 

오차행렬

 

 

 

완벽한 분류기라면 진짜 양성과 진짜 음성만 가지고 있을 것이므로 오차 행렬의 주대 각선(왼쪽에서 오른쪽 아래로)만 0이 아닌 값이 된다.

y_train_perfect_predictions = y_train_5 #완벽한 분류기일 경우
confusion_matrix(y_train_5, y_train_perfect_predictions)
array([[54579, 0],
       [ 0, 5421]], dtype=int64)

 

  • 오차 행렬 샘플

 

오차행렬 샘플

 

 

빅분기(수제비) 책에서 정리해 놓은 평가지표 식들 이미지로 대체합니다.  TP, FP 등의 숫자를 주고 지표를 계산하는 문제들이 출제되는데요, 사이킷런에서는 지표를 계산하는 함수를 제공하고 있습니다. 아래 예시 참고:)

 

오차행렬 평가지표

 

 

4. 정밀도와 재현율 함수로 도출

  • 정밀도(precision) 식 TP / TP+FP 에 대입해보면  3530 / 3530 + 687  = 0.84(반올림) :  5로 예측한 이미지 중 84%만 정확
from sklearn.metrics import precision_score, recall_score
precision_score(y_train_5, y_train_pred)
0.8370879772350012

 

  • 재현율(recall) = 민감도(sensitivity) = 진짜 양성 비율(true positive rate=TPR)  TP / TP+FN 
  • 3530 / 3530 + 1891 = 0.65  : 정확하게 5로 감지한 양성 샘플의 비율이 65%니 전체 숫자 5에서 65%만 감지
recall_score(y_train_5, y_train_pred)
0.6511713705958311

 

 

F1 score는 정밀도와 재현율의 조화 평균(harmonic mean). 정밀도와 재현율이 비슷한 분류기에서는 F1 점수가 높다. 하지만 이게 항상 바람직한 것은 아님. 상황에 따라 정밀도가 중요할 수도 있고 재현율이 중요할 수도 있다. 

  • F1 점수를 계산하면,
from sklearn.metrics import f1_score
f1_score(y_train_5, y_train_pred)
0.7325171197343846

 

정밀도와 재현율 트레이드오프 관계와 ROC 곡선은 다음 포스팅에서...

 

Sources : 핸즈온 머신러닝(2판) 3장 분류

 

▼ 참고글

2021.04.17 - [캐리의 도전/시험] - 빅데이터 분석기사 후기

2021.04.19 - [캐리의 도전/시험] - SQL 개발자 (SQLD) 독학 합격 후기

2021.04.09 - [캐리의 도전/시험] - 데이터분석 준전문가(ADsP) 합격 후기

2021.04.23 - [캐리의 데이터 세상/캐리의 데이터 공부 기록] - 파이썬 리스트 명령어

2021.02.21 - [캐리의 도전/시험] - ADSP, SQLD 기출 문제 자료 모음

반응형