머신러닝에서 분류는 특정 값이 O인지 X인지를 예측하는 것입니다
MNIST 데이터베이스
MNIST 데이터베이스는 분류에 있어서 기본적인 데이터셋입니다.
- MNIST 데이터셋은 손글자 숫자 이미지 데이터 (70000개)를 포함하고 있습니다
- 각 이미지는 대표하는 숫자 즉 '레이블'을 가집니다
- ML 분야의 "Hello, World" 라고 불립니다.
- 데이터는 28 by 28 = 784 픽셀 데이터로 구성되어 있습니다
- 한 픽셀은 0-255 값을 가지며, 해당 값은 픽셀 밝기를 의미합니다. (0은 흰색, 255은 검은색)
데이터 분석하기
먼저 MNIST 데이터를 불러옵니다.
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', as_frame=False)
이후 Key 값으로는 어떤 것이 있는지 파악하고 데이터셋과 타겟(정답)셋의 이름을 파악하고 X와 y로 나눠 담습니다.
이제 mnist의 data 와 target (data의 정답)을 나눠서 각각 확인합니다.
물론 X는 784 픽셀 데이터가 숫자 갯수(70000)만큼 들어있으므로 70000, 784 를 의미하며
y는 784 픽셀 데이터가 어떤 숫자를 의미하는지를 포함하는 결과를 의미합니다.
실제 데이터를 나타내보면 다음과 같습니다.
import matplotlib.pyplot as plt
def plot_digit(image_data):
image = image_data.reshape(28, 28)
plt.imshow(image, cmap="binary")
plt.axis("off")
some_digit = X[0]
plot_digit(some_digit)
plt.show()
모델 훈련하기
70000개의 데이터를 훈련셋과 테스트셋으로 나눕니다.
- 70000개의 사진 데이터를 60000개(훈련셋) 10000개(테스트셋)으로 나눕니다.
- 70000개의 사진에 대한 정답 데이터를 60000개(훈련셋) 10000(테스트셋)으로 나눕니다.
X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:]
이진 분류기 훈련
이진 분류기는 두 클래스가 타겟으로 주어지며 새 인스턴스를 두 클래스 중 하나로 분류하는 것입니다.
다음은 숫자 5인 경우에는 True, 5가 아닌 숫자는 모두 False로 벡터 인스턴스를 만듭니다.
y_train_5 = (y_train == '5') # True for all 5s, False for all other digits
y_test_5 = (y_test == '5')
이제 훈련셋인 X_train을 가지고 숫자가 5일 때 정답으로 인식하는 y_train_5로 학습(fit)을 시킵니다.
- SGDClassifier (확률적 경사 하강법)란 이진 분류기 중 하나로 분류 모델 중 하나입니다.
- SGDClassifier는 매우 큰 데이터셋을 효율적으로 처리하는 장점을 지니고 있습니다
- 이진 분류기 종류인 SGD(확률적 경사 하강법) 분류기는 훈련하는데 무작위성을 사용합니다.
from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train, y_train_5)
이진 분류기 훈련 예측
훈련 시킨 모델을 사용하여 결과를 예측할 수 있습니다.
sgd_clf.predict([some_digit]) # some_digit = X[0]
성능 측정
성능 지표로는 정확도, 정밀도, 재현율, F1 점수 등이 있으며 이를 위해서는 오차 행렬을 조사해야 합니다.
교차 검증을 사용한 정확도 측정
교차 검증이란?
- 훈련셋을 k-폴드로 나눕니다
- k-1 폴드를 이용해서 ML 모델을 훈련하고 나머지 폴드를 이용해 모델 성능을 검증합니다
- 위 과정을 k번 반복하여 최적의 파라미터 (모델, 하이퍼파라미터)를 찾아 모델 확정합니다
- sklearn에서 k-폴드 교차 검증 CV 함수인 cross_val_score 를 제공하고 있습니다.
교차 검증은 모델을 평가하는데 좋은 방법 중 하나입니다, 이는 cross_val_score를 통해서 교차 검증을 진행할 수도 있으며
데이터를 공정하게 사용하여 편향되지 않도록 StratifiedKFold 를 사용하여 계층적 샘플링을 수행할 수도 있습니다.
cross_val_score(훈련시킬 fit된 ML모델, 훈련데이터셋, 타겟값, 폴드개수, 사용할성능지표)
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.linear_model import SGDClassifier
model = RandomForestClassifier(random_state=42)
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# cross_val_score를 사용하여 교차 검증 수행
scores = cross_val_score(model, X, y, cv=3, scoring='accuracy')
# 계층적 샘플링을 사용하여 교차 검증 수행
# scores = cross_val_score(model, X, y, cv=skf, scoring='accuracy')
print(scores)
# array([0.90965, 0.90965, 0.90965])
정확도를 성능 측정 지표로 선호하면 안되는 상황
MNIST와 같이 불균형한 데이터셋을 다룰 때(즉, 어떤 클래스가 다른 것보다 월등히 많은 경우) 정확도는 분류기의 성능 지표로 적합하지 않습니다. 이유는 숫자가 0부터 9까지 존재하며 이미지의 10% 정도가 숫자 5인데 그러면 무조건 5가 아니다를 예측하게 되면 정확도가 90%가 넘어버리는 문제점이 발생하게 됩니다.
- 예를 들어 90%의 데이터가 클래스 A이고 10%의 데이터가 클래스 B인 불균형한 데이터셋이 있다고 가정합니다.
- 모델이 모든 데이터를 클래스 A로 예측한다고 해도, 이 모델의 정확도는 90%가 됩니다.
- 그러나 이 모델은 클래스 B를 전혀 올바르게 예측하지 못하므로 실제로는 유용하지 않습니다.
오차 행렬
정확도로 평가하는 것보다 더 좋은 방법은 오차 행렬을 조사하는 것입니다.
오차 행렬을 만들기 위해서는 실제 정답과 비교할 수 있도록 먼저 cross_val_predict() 를 사용하여 예측값을 만들어야 합니다.
- 아래는 실제 정답 클래스인 y_train_5 를 사용하여 값이 5면 True 5가 아니면 False 를 백터 값으로 y_train_pred에 담깁니다.
from sklearn.model_selection import cross_val_predict
y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)
이제 예측값과 실제 값을 이용하여 오차 행렬을 만들 수 있게 됐습니다
- sklearn에서 제공하는 confusion_matrix 함수를 사용하면 오차 행렬을 쉽게 만들 수 있습니다.
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_train_5, y_train_pred)
cm
위에 데이터는 아래 사진으로 정리될 수 있습니다. 예측한 y_train_pred 값으로 정답 y_train_5와 대조해본 결과
- 53892: (y_train_5 = False y_train_pred = False) 실제값 음성, 예측값 음성 (TN)
- 687: (y_train_5 = False y_train_pred = True) 실제값 음성, 예측값 양성 (FP)
- 1891: (y_train_5 = True y_train_pred = False) 실제값 양성, 예측값 음성 (FN)
- 3530: (y_train_5 = True y_train_pred = True) 실제값 양성, 예측값 양성 (TP)
오차 행렬로 요약된 지표들도 존재하며 상황에 따라서 중요도가 달라집니다.
다음은 오차 행렬로 구할 수 있는 지표들입니다.
- 정밀도: 전체 양성 예측의 정확도 TP / (TP + FP)
- 분류한 양성 중 진짜 양성(TP)이 차지하는 비율
- 기계가 암환자라고 말한 암환자들 중에서 진짜 암환자는 얼마나 되는가? - 정확도: 실제 양성의 정확도(실제 양성 데이터에서 진짜 양성(TP)의 비율) (TN + TP) / (TN + TP + FN + FP)
- (전체 데이터에서 진짜 음성(TN)과 진짜 양성(TP)을 찾은 비율)
- 기계가 암환자를 얼마나 잘 분류했는가?
- 재현율와 같이 사용하는 것이 일반적 - 재현율: 예측 양성의 정확도(예측 양성 데이터에서 진짜 양성(TP)의 비율) TP / (TP + FN)
- 예측한 값에서 진짜 양성(TP)의 비율
- 암환자 중 기계가 암환자라고 분류하고 싶은 사람을 얼마나 제대로 재현하였는가?
- 정밀도와 같이 사용하는 것이 일반적
정밀도와 재현율
sklearn은 정밀도와 재현율을 포함하여 분류기의 지표를 계산하는 여러 함수를 제공합니다.
- precision_score 란 정밀도 점수 함수입니다.
- recall_score 란 재현율 점수 함수입니다.
from sklearn.metrics import precision_score, recall_score
precision_score(y_train_5, y_train_pred) # == 3530 / (687 + 3530)
# 0.8370879772350012
recall_score(y_train_5, y_train_pred) # == 3530 / (1891 + 3530)
# 0.6511713705958311
이때 정밀도와 재현율을 F1 점수라고 하는 하나의 숫자로 만들면 편리할 때가 많습니다.
- F1 점수는 정밀도와 재현율의 조화 평균입니다. (일반 평균은 모든 값을 동등히 다루지만, 조화 평균은 작은 값에 더 큰 비중을 둠)
- F1 점수는 모델이 얼마나 잘 예측하는지를 종합적으로 평가하는 데 유용하며, 특히 불균형 데이터셋에서 유용합니다.
정밀도와 재현율이 비슷한 분류기에서는 F1 점수가 높지만 이것이 항상 바람직한 것은 아닙니다. 상황에 따라서 정밀도가 중요할 수도 있고 재현율이 중요할 수도 있습니다.
넷플릭스처럼 어린이를 위한 영상만 필터링해서 만들어 보여줄 때는 재현율 비중을 높이면 성인용 비디오를 보여줄 수 있습니다
- 좋은 동영상이 많이 제외되더라도(늦은 재현율) 안전한 것들만 노출시키는 (높은 정밀도) 분류기를 선호할 것입니다
- 실제는 음성(성인)이지만 예측은 양성(아동) 인 FP로 예측할 수 있기 때문에 정밀도가 중요
암환자같은 경우에는 잠재적인 암환자를 안좋치는 것이 중요하기 때문에 재현율 비중을 높이는 것이 좋습니다.
- 암환자 같은 경우 암이 아닌 사람들라고 했지만 실제로는 암일 수도 있는 잠재적 암환자를 안놓치는 것이 중요하기 때문에 재현율이 중요
- 실제 암환자들이 중요하므로 FN, TP 를 다루는 재현율이 중요
중요한 점은 정밀도를 올리면 재현율이 줄고 정밀도를 내리면 재현율이 올리는 반비례 관계라고 합니다. 트레이드 오프 필요!
정밀도/재현율 트레이드오프
정밀도와 재현율을 그래프로 그려내 적당한 임계값을 설정해야 합니다
트레이드 오프란 한쪽이 오르면 한쪽이 내리는 것을 의미하며 정밀도와 재현율이 반비례 관계로 한쪽을 위해 한쪽을 버리는 의미입니다.
SGDClassifier 분류기는 결정 함수(decision function)를 사용하여 각 샘플의 점수를 계산합니다. 이 점수가 임곗값보다 크면 샘플을 양성 클래스에 할당하고 그렇지 않으면 음성 클래스에 할당합니다.
재현율: 예측 양성의 정확도(예측 양성 데이터에서 진짜 양성(TP)의 비율) TP / (TP + FN)
정밀도: 전체 양성 예측의 정확도 TP / (TP + FP)
아래 사진을 확인해보면 정밀도와 재현율은 서로 트레이드오프가 필요한 것을 확인할 수 있습니다.
sklearn에서 임곗값을 직접 지정할 수는 없지만 예측에 사용한 점수는 확인할 수 있습니다. 분류기의 decision_function() 메서드를 호출하면 각 샘플의 점수를 얻을 수 있으며 이 점수를 기반으로 임곗값을 정해 예측을 만들 수 있습니다.
다음처럼 임곗값을 0으로 지정하면 당연히 True가 반환될 것입니다.
y_scores = sgd_clf.decision_function([some_digit])
y_scores
# array([2164.22030239])
threshold = 0
y_some_digit_pred = (y_scores > threshold)
y_some_digit_pred
# array([True])
하지만 만약 임곗값이 2164보다 큰 값이면 False를 반환할 것입니다. 이 결과는 임곗값을 높이면 재현율이 줄어든다는 것을 보여줍니다.
적절한 임곗값을 구하기 위해서는 cross_val_predict 함수를 통해 훈련셋의 모든 샘플의 점수를 구해야 합니다.
y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3, method="decision_function")
이 점수로 precision_recall_curve() 함수를 사용하여 간으한 모든 임곗값에 대해 정밀도와 재현율을 계산할 수 있습니다.
from sklearn.metrics import precision_recall_curve
# precisions: 정밀도 recalls: 재현율 thresholds: 계산에 사용된 여러 임계값
precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)
plt.figure(figsize=(8, 4)) # extra code – it's not needed, just formatting
plt.plot(thresholds, precisions[:-1], "b--", label="정밀도", linewidth=2)
plt.plot(thresholds, recalls[:-1], "g-", label="재현율", linewidth=2)
plt.vlines(threshold, 0, 1.0, "k", "dotted", label="threshold")
plt.show()
또는 정밀도와 재현율을 다음과 같이 그래프를 만들어 확인할 수 있으며
프로젝트의 특성에 따라서 정밀도와 재현율을 각각 알맞게 트레이드오프를 통해서 조절해야 합니다.
import matplotlib.patches as patches # extra code – for the curved arrow
plt.figure(figsize=(6, 5)) # extra code – not needed, just formatting
plt.plot(recalls, precisions, linewidth=2, label="Precision/Recall curve")
plt.plot([recalls[idx], recalls[idx]], [0., precisions[idx]], "k:")
plt.plot([0.0, recalls[idx]], [precisions[idx], precisions[idx]], "k:")
plt.plot([recalls[idx]], [precisions[idx]], "ko",
label="Point at threshold 3,000")
plt.gca().add_patch(patches.FancyArrowPatch(
(0.79, 0.60), (0.61, 0.78),
connectionstyle="arc3,rad=.2",
arrowstyle="Simple, tail_width=1.5, head_width=8, head_length=10",
color="#444444"))
plt.text(0.56, 0.62, "Higher\nthreshold", color="#333333")
plt.xlabel("Recall")
plt.ylabel("Precision")
plt.axis([0, 1, 0, 1])
plt.grid()
plt.legend(loc="lower left")
plt.show()
ROC 곡선
ROC 곡선 (수신기 조작 특성)
ROC 곡선도 이진 분류에서 널리 사용하는 도구로 정밀도/재현율 곡선과 달리 거짓 양성 비율(FPR)에 대한 진짜 양성 비율(TPR)의 곡선
즉 재현율에 대한 1-특이도 그래프입니다.
ROC 곡선을 그리려면 roc_curve() 함수를 사용해 여러 임곗값에서 TPR과 FPR을 계산해야합니다.
from sklearn.metrics import roc_curve
#roc_curve() 함수를 사용해 여러 임곗값에서 TPR과 FPR을 계산해야한다.
#roc_curve(정답 레이블,결정함수 점수)
fpr, tpr, thresholds = roc_curve(y_train_5, y_scores)
다음 맷플릇립을 사용해 TPR(진짜 양성 비율, 재현율)에 대한 FPR(거짓 양성 비율, 1-특이도) 곡선을 나타낼 수 있습니다.
def plot_roc_curve(fpr, tpr, label=None): #위에서 구한 TPR과 FPR로 ROC 그래프를 만든다.
plt.plot(fpr, tpr, linewidth=2, label=label)
plt.plot([0, 1], [0, 1], 'k--') # dashed diagonal
plt.axis([0, 1, 0, 1]) # Not shown in the book
plt.xlabel('False Positive Rate (Fall-Out)', fontsize=16) # Not shown
plt.ylabel('True Positive Rate (Recall)', fontsize=16) # Not shown
plt.grid(True) # Not shown
plt.figure(figsize=(8, 6)) # Not shown
plot_roc_curve(fpr, tpr)
fpr_90 = fpr[np.argmax(tpr >= recall_90_precision)] # Not shown
plt.plot([fpr_90, fpr_90], [0., recall_90_precision], "r:") # Not shown
plt.plot([0.0, fpr_90], [recall_90_precision, recall_90_precision], "r:") # Not shown
plt.plot([fpr_90], [recall_90_precision], "ro") # Not shown
save_fig("roc_curve_plot") # Not shown
plt.show()
#그래프를 확인해보면 재현율이 높아질수록 가짜 양성이 늘어난다.
#점선은 완벽한 랜덤 분류기의 ROC 곡선을 뜻한다.
곡선 아래의 면적(AUC)을 측정하면 분류기들을 비교할 수 있습니다. 완벽한 분류기는 ROC의 AUC가 1이고, 완전한 랜덤 분류기는 0.5다. sklearn에서는 AUC를 계산하는 함수를 제공하고 있습니다.
from sklearn.metrics import roc_auc_score
#y_train_5: array([False, False, False, ..., False, True, False]) 이런식으로 되어 있는 정답
#y_scores: sgd_clf.decision_function([some_digit])로 sgd_clf 모듈을 통해서 얻은 예측 점수
#즉 y_scores 예측 점수를 기반으로 y_train_5 정답을 맞췄을때 얻게 되는 AUC 넓이
roc_auc_score(y_train_5, y_scores)
# 0.9983436731328145
다중 분류
이진 분류가 두 개의 클래스를 구별하는 반면에 다중 분류기는 둘 이상의 클래스를 분류할 수 있습니다.
- SGD 분류기, 랜덤 포레스트 분류기 같은 일부 알고리즘은 여러 개의 클래스를 직접 처리할 수 있지만 반면, 로지스틱 회귀나 서포트 백터 머신 분류기 같은 다른 알고리즘은 이진 분류만 가능합니다
하지만 이진 분류기를 여러 개 사용해 다중 클래스를 분류하는 기법도 많습니다.
OvR(좌) OvO(우)
OvR(one-versus-the-rest)
예를 들어 특성 숫자 하나만 구분하는 숫자별 이진 분류기 10개(0~9까지)를 훈련시켜 클래스가 10개인 숫자 이미지 분류 시스템을 만들 수 있습니다. 이미지를 분류할 때 각 분류기의 결정 점수 중에서 가장 높은 것을 클래스로 선택하는 기법
ex: 1이 5인가?, 2가 5인가?, 3이 5인가?, 4가 5인가?, 5가 5인가?, 6이 6인가? ••• 9가 6인가?
OvR 특징 요약
- 각 클래스와 나머지 모든 클래스르 비교하는 방식. N 개의 이진 분류기가 필요합니다.
- 간단하지만 그만큼 데이터 불균형 문제가 발생할 수 있습니다.
OvO(one-versus-one)
0과 1 구별, 0과 2 구별, 1과 2 구별 등과 같이 각 숫자의 조합마다 이진 분류기를 훈련시키는 것입니다.
-> MNIST 문제에서는 45개의 분류기를 훈련시키는 것을 의미, 45개 분류기 모두 통과시켜서 가장 많이 양성으로 분류된 클래스 선택
-> OvO(one-versus-one) 일기토라고 생각하면 됩니다.
ex: 5가 0인가 1인가?, 5가 0인가 5인가?, 5가 0인가 9인가? ••• 5가 1인가 9인가? ••• 5가 5인가 6인가? ••• 5가 8인가 9인가?
OvO 특징 요약
- 클래스가 N개라면 분류기는 N x (N - 1) / 2개가 필요합니다.
- 데이터 불균형 문제가 적지만 비용함수가 높습니다. (데이터가 적으면 괜찮지만 많아지면 기하급수적으로 증가)
다중 클래스 분류 작업에서 이진 분류 알고리즘을 선택하면 sklearn이 알고리즘에 따라서 자동으로 OvR 또는 OvO를 실행합니다.
다음은 sklearn.svm.SVC 클래스를 사용해 서포트 벡터 머신 분류기를 테스트 하는 코드입니다
- 기존에는 5를 맞추는 분류기를 사용했기 때문에 y_train_5를 사용했지만 y_train을 사용하여 모든 숫자 데이터를 적용합니다.
- 내부에서는 sklearn이 OvO 전략을 사용해 45개의 이진 분류기를 훈련시키고 각각의 결정 점수를 얻어 점수가 가장 높은 클래스를 선택
서포트 벡터 머신(SVM)에서 OvO(One-vs-One) 방식을 선호하는 이유는 다음과 같습니다:
1. 계산 효율성
- OvO 방식은 클래스 간에 1:1로 비교하여 다수의 이진 분류기를 학습시킵니다. 이때 각 분류기는 두 클래스 간의 데이터를 사용하기 때문에 전체 데이터 크기가 줄어들어 학습 및 예측 속도가 빠릅니다.
- 반면 OvR(One-vs-Rest) 방식은 각 클래스마다 전체 데이터를 사용해야 하므로 데이터 크기가 커지고 계산량이 많아집니다.
2. 불균형 데이터 처리
- OvO 방식은 각 이진 분류기에서 비교되는 데이터의 클래스가 균형을 이루는 경우가 많습니다. 이는 불균형 데이터를 다룰 때 성능 향상에 도움이 됩니다.
- OvR 방식은 불균형 데이터에서 특정 클래스가 전체 데이터의 대부분을 차지할 때 성능 저하를 겪을 수 있습니다.
3. 결정 경계의 세밀함
- OvO 방식은 클래스 간의 경계를 보다 세밀하게 조정할 수 있어, 복잡한 데이터 분포에서도 더 정밀한 분류가 가능합니다.
- OvR 방식은 하나의 클래스와 나머지 클래스를 구분하는 방식으로, 경계 설정이 다소 단순화될 수 있습니다.
4. 확장성
- OvO 방식은 클래스 수가 많아질수록 분류기의 수가 기하급수적으로 증가하지만, 각 분류기가 단순해질 수 있습니다.
- OvR 방식은 클래스 수가 늘어나면 분류기의 복잡도가 증가하여 학습과 예측의 효율성이 떨어질 수 있습니다.
from sklearn.svm import SVC
#SVM 분류기 생성
svm_clf = SVC(gamma="auto", random_state=42)
svm_clf.fit(X_train[:1000], y_train[:1000]) # y_train, not y_train_5
svm_clf.predict([some_digit])
#array([5], dtype=uint8)
decision_function() 메서드를 호출하면 1개가 아니라 샘플당 10개의 점수를 반환합니다. 이 점수는 클래스마다 하나입니다.
- 가장 높은 점수가 some_digit 값인 5에 해당하는 값이 됩니다.
some_digit_scores = svm_clf.decision_function([some_digit])
some_digit_scores
0부터 9까지 45번 일기토를 했을때 5가 9번 이겼다는 의미입니다.
인덱스 5에 해당하는 해당하는 클래스 값은 5인 것을 확인할 수 있습니다.
np.argmax(some_digit_scores)
#5
위에서는 내부적으로 SVM(서포트 벡터 머신)에 유리한 OvO를 선택했지만 OvR를 강제할 수 있지만 OvR 대신 OvO로 강제 가능합니다
- OneVsOneClassifier나 OneVsRestClassifier를 사용합니다.
- 간단하게 이진 분류기 인스턴스를 만들어 객체를 생성할때 전달하면 됩니다.
다음은 SVC 기반으로 OvR 전략을 사용하는 다중 분류기를만드는것 입니다.
from sklearn.multiclass import OneVsRestClassifier
ovr_clf = OneVsRestClassifier(SVC(gamma="auto", random_state=42))
ovr_clf.fit(X_train[:1000], y_train[:1000])
ovr_clf.predict([some_digit])
다음 코드로 분류기의 갯수도 알 수 있습니다
len(ovr_clf.estimators_)
# 10
이진 분류기 중 하나인 SGD(확률적 경사 하강법)을 훈련시키는 것도 간단합니다
#이진 분류기 종류인 SGD(확률적 경사 하강법) 분류기는 훈련하는데 무작위성을 사용한다.
#sgd_clf = SGDClassifier(max_iter=1000, tol=1e-3, random_state=42)
sgd_clf.fit(X_train, y_train)
sgd_clf.predict([some_digit])
이 경우 SGD 분류기는 직접 샘플을 다중 클래스로 분류할 수 있기 때문에 별도의 sklearn의 OvR이나 OvO를 적용할 필요가 없습니다.
deicision_function() 메서드는 클래스마다 하나의 값을 반환합니다.
sgd_clf.decision_function([some_digit])
위 결과에서 분류기가 예측 결과(정답은 3이라고)에 강한 확신을 보이고 있음을 알 수 있습니다.
교차검증
하지만 위에 SGD(확률적 경사 하강법) 분류기로 얻은 점수가 얼마나 정확한지를 알고 싶다면 이때 사용하는 것이 교차 검증입니다.
- 분류기 평가에는 일반적으로 교차 검증을 사용합니다.
cross_val_score() 함수를 사용해 SGDClassifier의 정확도를 평가할 수 있습니다.
cross_val_score(sgd_clf, X_train, y_train, cv=3, scoring="accuracy")
다음으로는 값을 표준화 시키고 다시 교차 검증을 하면 더 정확한 정확도를 얻을 수 있습니다.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.astype(np.float64))
cross_val_score(sgd_clf, X_train_scaled, y_train, cv=3, scoring="accuracy")
에러 분석
에러를 분석하기 위해서는 여러 가지 방법을 사용할 수 있으며 적절하게 사용하면 정확도를 높일 수 있습니다
오차 행렬
오차 행렬을 사용하면 데이터의 예측이 얼만큼 잘 됐는지를 직관적으로 확인할 수 있습니다.
다음 코드는 오차 행렬을 만들어주는 코드로 실제 정답과 예측 값이 얼만큼 정확하고 어디서 얼만큼 잘못 예측됐는지 확인할 수 있습니다.
from sklearn.metrics import ConfusionMatrixDisplay
y_train_pred = cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3)
plt.rc('font', size=9)
ConfusionMatrixDisplay.from_predictions(y_train, y_train_pred)
# %로 표현하기
# ConfusionMatrixDisplay.from_predictions(y_train, y_train_pred, normalize="true", values_format=".0%")
plt.show()
이렇게 오차 행렬을 분석하면 성능 향상 방안에 대한 통찰을 얻을 수 있게 됩니다. 위에 그래프를 확인했을 때 실제로는 5지만 8로 잘못 예측 되는 영향이 큰 것을 확인할 수 있습니다. 이런 문제 패턴을 파악하여 올바르게 값을 예측할 수 있도록 수정할 수 있게 됩니다.
'AI > Machine Learning' 카테고리의 다른 글
사이킷런 scikit-learn 이란? (0) | 2024.07.09 |
---|---|
여러 개의 레이블을 갖는 하나의 데이터 분류하기 - 다중 레이블 분류 (2) | 2024.07.08 |
머신러닝 모델 튜닝하기 (0) | 2024.06.28 |
머신러닝 회귀 (집값 예측하기) (0) | 2024.06.27 |
머신러닝 시작하기 (0) | 2024.06.26 |