[STUDY] 5월 3주차 - 이진분류, 성능분석, 오차행렬, 정밀도, 재현율
STUDY_05_3.ipynb
---------------------------------------------------------------------------------------------------------------------------
from sklearn.datasets import fetch_mldata
# 데이터 추가
# 이상하게 책에 있는 대로 fetch_openml 함수를 쓰니까 없는 함수라고 뜸
# 알아보니까 책 예전 버전에는 fetch_mldata를 썼는데 이 부분이 무슨 일인지 fetch_openml로 바뀜
# 일단 안 되니까 fetch_mldata 이걸로 데이터 불러봄
mnist = fetch_mldata('MNIST original')
print(mnist)
# - DESCR : 이 데이터가 무엇인지 설명함
# - data : 이미지의 픽셀마다 특성(0~255 사이의 픽셀이 gray값, 0이면 완전 검은색/255면 완전 흰색)
# - target : 레이블, 이미지가 어떤 숫자인지 나타내는 값(이것은 1이다, 이것은 4다.. 이렇게)
# {
# 'DESCR': 'mldata.org dataset: mnist-original',
# 'COL_NAMES': ['label', 'data'],
# 'target': array([0., 0., 0., ..., 9., 9., 9.]),
# 'data': array([[0, 0, 0, ..., 0, 0, 0],
# [0, 0, 0, ..., 0, 0, 0],
# [0, 0, 0, ..., 0, 0, 0],
# ...,
# [0, 0, 0, ..., 0, 0, 0],
# [0, 0, 0, ..., 0, 0, 0],
# [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)
# }
# 데이터 셋을 변수에 저장
# X : 이미지의 픽셀 데이터들
# y : 이미지가 어떤 숫자인지 나타내는 데이터들
X, y = mnist["data"], mnist["target"]
print(X.shape)
print(y.shape)
import matplotlib
import matplotlib.pyplot as plt
# 36000번째 픽셀 이미지를 보여준다
# - 이미지 크기를 28X28 크기로 shape 지정
# - binary 컬러 맵을 사용해서 그린다
# - 각 픽셀은 nearest 보간 방식으로 사용해서 결정한다
# > nearest 보간 방식 : 새로운 지점 또는 한 지점 값을 결정할 때 주변 분포한 값을 사용해 결정하는 것
some_digit = X[36000]
some_digit_image = some_digit.reshape(28, 28)
plt.imshow(some_digit_image,
cmap = matplotlib.cm.binary,
interpolation="nearest")
# 36000번째 값 레이블 확인
print(y[36000])
# 훈련-테스트 데이터 셋 분리
# - X_train : 0부터 59999 인덱스의 이미지 픽셀 데이터 (모델 훈련시킬 때 사용함)
# - X_test : 60000부터 69999 인덱스의 이미지 픽셀 데이터 (모델 테스트할 때 사용함)
# - Y_train : 0부터 59999 인덱스의 이미지가 무슨 숫자인지 나타내는 데이터 (모델 훈련시킬 때 사용함)
# - Y_test : 60000부터 69999 인덱스의 이미지가 무슨 숫자인지 나타내는 데이터 (모델 테스트할 때 사용함)
X_train = X[:60000]
X_test = X[60000:]
y_train = y[:60000]
y_test = y[60000:]
import numpy as np
# 훈련 데이터를 랜덤으로 섞음
# [numpy.random.permutation(x)]
# <Parameters>
# - x : int 값 혹은 배열
# int 값이면 범위로 지장한 숫자를 랜덤하게 배열해서 반환함
# 배열 값이면 배열 인덱스를 랜덤하게 섞어서 반환함
# <Example>
# >>> np.random.permutation(10) -----------> array([1, 7, 4, 3, 0, 9, 2, 5, 8, 6])
# >>> np.random.permutation([1, 4, 9, 12, 15]) ------------> array([15, 1, 9, 4, 12])
shuffle_index = np.random.permutation(60000)
X_train, y_train = X_train[shuffle_index], y_train[shuffle_index]
# 이진 분류기 훈련 ---------> 이것은 5인가 아닌가
y_train_5 = (y_train == 5)
y_test_5 = (y_test == 5)
# 확률적 경사 하강법 분류기 만들기
# [경사하강법]
# y = wx + b라는 함수 식이 있으면 가장 오차가 적게되는 w와 b를 구하는 방법이다.
# 특히 함수가 어렵고 복잡하여 수학적 접근 방법으로 풀기 어려운 문제에도 잘 동작.
# 2차원 cost 함수 위에서 경사도 0에 근접하도록 쭉쭉 내려갈때 쓰는 그거
# [확률적 경사하강법은 그냥 경사하강법이랑 뭐가 다르나]
# 경사 하강법은 모든 훈련 데이터에서 대해서 값을 평가하고 매개변수 업데이트를 진행 --> 속도 드림
# 확률적 경사하강법은 확률적으로 선택한 데이터에 대해서 값을 평가하고 매개변수를 업데이트 --> 속도 더 빠름
# 확률적 경사하강법은 일반 경사하강법이랑 방식은 같긴 한데
# 데이터 다 하는게 아니고 랜덤하게 하나 찝어서 cost 함수 위에서 올라갈지 말지를 정하니까 속도 빠름
from sklearn.linear_model import SGDClassifier
# [SGDClassifier]
# <Parameter>
# - max_iter : 최대로 통과할 수 있는 데이터의 수
# - random_state : 데이터를 셔플 할 때 사용할 의사 난수 생성 프로그램의 시드
sgd_clf = SGDClassifier(max_iter=5, random_state=42)
# [fit]
# <Parameter>
# - X : 학습할 데이터
# - y : 목표 값
sgd_clf.fit(X_train, y_train_5)
# 만든 확률적 경사하강법 분류기로 예측해보기
# - 예측 실패하면 이렇게 나온다 -------> array([False])
# - 예측 성공하면 이렇게 나온다 -------> array([ True])
sgd_clf.predict([some_digit])
# 교차검증을 써서 모델 성능 평가하기
# [k-fold 교차검증]
# 학습 데이터를 K개의 폴드로 나눠서 교차검증하는 방식이다.
from sklearn.model_selection import cross_val_score
# [cross_val_score]
# - 첫번째 인자(sgd_clf) : 검증 할 모델
# - 두번째 인자(X_train) : 검증 할 훈련 데이터
# - 세번째 인자(y_train_5) : 예측하려고하는 대상 변수
# - cv : 검증 fold 수(디폴트로는 3개)
# - scoring : 모델 평가 지표(디폴트로는 "accuracy", 정확도)
# 호출 결과 이런 값이 나온다. 각 폴드 당 계산 된 모델 성능을 의미한다/ (매번 조금씩 다르다)
# > array([0.9618 , 0.9629 , 0.96545])
cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring="accuracy")
# 오차행렬 만들기
# [오차행렬]
# 클래스 A의 샘플이 B로 분류 된 횟수를 세어서 행렬로 나타낸 것
# ex> 숫자 1 이미지가 2로 분류 된 횟수를 알고싶다 ----> 오차행렬의 1행 2열을 본다
# 오차 행렬을 만드려면 실제 타깃(여기서는 y_train_5)과 비교할 수 있도록 예측값을 만들어야 한다.
from sklearn.model_selection import cross_val_predict
# [cross_val_predict]
# - 첫번째 인자(sgd_clf) : 대상 모델
# - 두번째 인자(X_train) : 적용할 데이터
# - 세번째 인자(y_train_5) : 예측하려고 하는 대상 변수
# - cv : 폴드 수
#[cross_val_predict와 cross_val_score의 다른 점]
# > cross_val_score함수는 평가 결과 점수를 반영하지만, corss_val_predict는 각 테스트 케이스의 결과를 반영한다
# > cross_val_score
# -> 폴드 1의 첫번째 데이터 검증
# -> 폴드 1의 두번째 데이터 검증
# -> 폴드 안에 데이터 모두 검증 해서 폴드 1의 성능을 확률(0.0~1.0사이) 값으로 나타냄
# > array([0.9618 , 0.9629 , 0.96545]) ---> 3개 폴드 적용한 평가치
# > cross_val_predict
# -> 폴드 1의 첫번째 데이터 검증
# -> 폴드 1의 두번째 데이터 검증
# -> 폴드 안에 데이터 각각 검증 한 값을 배열 안에다 저장함
# > [False False False ... False False False]
y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)
# confusion_matrix 만들어서 오차행렬 만들기
from sklearn.metrics import confusion_matrix
# [confusion_matrix]
# 분류의 정확성을 평가하기위한 오차 행렬을 반환함
# - 첫번째 인자(y_train_5) : 원래 정답
# - 두번째 인자(y_train_pred) : 모델이 분류 한 정답
# 호출하면 이렇게 나온다
# array([[54020, 559],
# [ 1708, 3713]])
# 각 위치에 값들이 뜻하는 것
# array([[5아닌데 5아니라고 잘 분류한 횟수, 5아닌데 5라고 잘못 분류한 수],
# 5맞는데 5아니라고 잘못 분류한 횟수, 5맞는데 5라고 잘 분류한 횟수])
confusion_matrix(y_train_5, y_train_pred)
# [정밀도]
# 정밀도 = TP/(TP+FP)
# > TP : 진짜 양성의 수(5 맞는데 5라고 잘 분류한 횟수)
# > FP : 거짓 양성의 수(5 아닌데 5라고 잘못 분류한 횟수)
# ----> FP가 0 에 가까울 수록 정확도가 높다
# [재현율]
# 분류기가 정확하게 감지한 양성 샘플의 비율
# 재현율 = TP / (TP+FN)
# > TP : 진짜 양성의 수(5 맞는데 5라고 잘 분류한 횟수)
# > FN : 거짓 음성의 수(5인데 5 아니라고 잘못 분류한 횟수)
# ----> 역시 FN가 0 에 가까울 수록 정확도가 높다
from sklearn.metrics import precision_score, recall_score
# 정밀도 출력
print(precision_score(y_train_5, y_train_pred))
# 재현율 출력
print(recall_score(y_train_5, y_train_pred))
# [F1 점수]
# 정밀도와 재현율의 조화평균
# F1 점수 = TP * ( TP + ( (FN + FP) / 2 ) )
from sklearn.metrics import f1_score
print(f1_score(y_train_5, y_train_pred))
# [왜 굳이 정밀도와 재현도를 나눠가면서 계산하나]
# 도둑 방범 시스템
# ---> 정확도는 낮더라도 재현률이 높으면
# 도둑 안들었을 때 도둑 들었다고 경비원이 귀찮게 호출 될 지언정(낮은 정밀도)
# 도둑 들었을 때 도둑 안들었다고 잘못 판별하는것은 막을 수 있다.(높은 재현율)
# [정밀도/재현율 트레이드오프]
# 정밀도를 올리면 재현율이 줄고 재현율을 올리면 정밀도가 내려가는 현상