2018. 7. 11. 22:55ㆍPython-이론/python-인공지능2
classification 2편
3.3 정밀도와 재현율
정밀도와 재현율 중 정밀도는 5라고 판단한 숫자중에 진짜 5의 비율이었고 (TP / (TP+NP))였고 재현율은 숫자 5중 5라고 판단한 5의 비율이었다. 이런 것 말고도 F1점수라는 하나의 평가지표가 있다. F1 점수는 정밀도와 재현율의 조화 평균입니다.
우선 정밀도와 재현율을 scikit learn에서 제공하는 함수를 통해 알아보겠습니다.
from sklearn.metrics import precision_score, recall_score
precision_score(y_train7, y_train_pred)#0.7878065872459705 recall_score(y_train7, y_train_pred) #0.8972067039106145
F1 점수 표현법
코드로 표현하기
from sklearn.metrics import f1_score
f1_score(y_train7, y_train_pred) # 0.838955223880597
재현율과 정밀도가 비슷할 수록 f1점수는 높게 나옵니다.
3.4 정밀도/재현율 트레이드오프
SGDClassifier(확률적 경사하강법)은 결정 함수를 사용하여 각 샘플의 점수를 계산합니다. 각 샘플의 점수가 임계값 보다 크면 양성으로 판단하고 작으면 음성으로 판단합니다.
8 7 3 9 5 2 5 5 6 5 5 5
| | |
왼쪽 부터
1번에 놓게 된다면 정밀도는 6/8 75% 재현율은 6/6 100%가 됩니다.
2번은 정밀도는 4/5 80%, 재현율은 4/6 67%가 됩니다.
3번은 정밀도는 3/3 100% 재현율은 3/6 입니다.
임계값이 낮아 질 수 록 재현율이 높아지고 높아질 수록 정밀도가 올라갑니다.
임계값을 어디에 놓냐에 따라 정밀도와 재현율이 디르게 판단됩니다.
scikit learn에서 직접 임계값을 지정해줄 수는 없지만 예측에 사용한 점수를 알 수 있습니다. decsision_function 메서드를 호출하면 각 샘프의 점수를 얻을 수 있습니다. 이 점수를 기반으로 원하는 임계값을 정해 예측을 만들 수 있습니다.
y_scores = sgd_clf.decision_function([x_train[0]])
thresholds = -150000 #임계값 설정
y_some_digit_pred = (y_scores > thresholds)
y_some_digit_pred, thresholds, y_scores #(array([ True]), -150000, array([-149475.42385269]))
-150000에서 임계값을 높이게 된다면 재현율이 갑소합니다. 따라서 0번째 인덱스를 7이라고 인식하지 못하게 됩니다.
적절한 임계값을 정하기
훈련세트에 있는 모든 샘플의 점수를 구하기
from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_train7, y_scores)
precisions.shape, recalls.shape, thresholds.shape #((57692,), (57692,), (57691,)) 원래 숫자가 같아야 하지만 특이하게 하#threshold가 하나 적게 나와서 그래프를 그려줄때 한개 적게 넣어주어야 합니다.
그후 그래프를 그려보겠습니다.
from matplotlib import pyplot as plt
def plot_precision_recall_vs_threshold(precisions, recalls, thresholds):
plt.plot(thresholds, precisions[:57691], "b--", label='precision')
plt.plot(thresholds, recalls[:57691], "g-", label='recall')
plt.xlabel("threshold")
plt.legend(loc="center left")
plt.ylim([0, 1])
plot_precision_recall_vs_threshold(precisions, recalls, thresholds)
plt.show()
이그래플 이용해서 threshold를 잘선택하여 정밀도와 재현율을 선택할 수 있습니다.
y_train_pred_90 = (y_scores > 70000)
precision_score(y_train7, y_train_pred_90)#0.8652729384436701
recall_score(y_train7, y_train_pred_90)#0.8324022346368715
3.5 ROC곡선
Roc곡선은 양성으로 잘못판단한 것(FPR)에 의한 진짜 양성의 비율 함수(TNR)입니다. FPR은 1에서 TNR를 뺴준값입니다. 우선 roc 곡선을 그리기 위해선 fpr과 tpr을 구해야 합니다. 이는 roc곡선을 통해서 그릴 수 있습니다.
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_train7, y_scores)
그러면 matplotlib을 통해 그래프로 나타낼 수 있습니다.
def plot_roc_curve(fpr, tpr, label=None):
plt.plot(fpr, tpr, linewidth=2, label=label)
plt.plot([0, 1], [0, 1], 'k--')
plt.axis([0, 1, 0, 1])
plt.xlabel('FPR')
plt.ylabel('TPR')
plot_roc_curve(fpr, tpr)
plt.show()
ROC 그래프 생성
이 그래프에도 트레이드오프가 보입니다. TPR이 높을 수록 FPR이 늘어납니다. 점선은 완전한 랜덤 분류기의 ROC곡선이다. 좋은 분류기는 이 점선으로 최대한 멀리 떨어져있어야 합니다. 곡선아래의 면적을 측정하면 분류기들을 비교할 수 있습니다.
from sklearn.metrics import roc_auc_score
roc_auc_score(y_train7, y_scores) #0.9842730386497363
앞서 공부한 정밀도와 재현율을 통해 이진분류(PR곡선)을 사용하는 것과 ROC곡선을 사용하는 것 중 어떤 것을 사용하면 좋을까 라고 했을때 설명 드리면 FN과 FT이 더중요한 데이터 들이라면 PR곡선을 사용하고 그렇지 않다면 ROC곡선을 사용하면됩니다.
RandomForestClassifier를 훈련시켜 SGDClassifier의 ROC곡선과 ROC AUC 점수를 비교하겠습니다.
앞서 descision_function을 활용해서 점수를 만든 것과 같이 SGDClassifier에서도 점수를 만들어주어야 하지만 이와 같은 함수가 없습니다. 따라서 각 데이터당 확률을 구해서 확률을 점수로 사용해보겠습니다.
from sklearn.ensemble import RandomForestClassifier
forest_clf = RandomForestClassifier(random_state=42)
y_probs_forest = cross_val_predict(forest_clf, x_train, y_train7, cv=3,
method="predict_proba")
각 배열에 확률을 구했지만 확률이 아닌 점수가 필요합니다. 점수로 바꾸어 보겠습니다.
y_scores_forest = y_probs_forest[:, 1] #array([0., 0., 0., ..., 0., 0., 0.])
fpr_forest, tpr_forest, thresholds_forest = roc_curve(y_train7, y_scores_forest)
그래프 작성 코드
plt.plot(fpr, tpr, "b:",label="SGD")
plot_roc_curve(fpr_forest, tpr_forest, label='RandomForest')
plt.legend(loc="lower right")
plt.show()
아까 완전한 분류기로 부터 멀수록 좋은 분류기라고 했습니다. 따라서 랜덤포레스트를 통해 ROC 그래프를 만들게 되면 더 효과좋은 분류기를 만들 수 있다는 뜻입니다.
roc_auc_score(y_train7, y_scores_forest) #0.9922149762316045
이전 ROC 그래프에서는 98%가나왔는데 랜덤포레스트를 사용하면 더 좋은 0.99가 나왔다.
3.4 다중분류
이진 분류가 한개만 구분하는 것이 였다면 다중 분류는 2개 이상의 결과로 분류하는 것이다. 다중 분류에도 여러가지 방법이 있는데
1. 일대 다(OVA(one verse All)): 숫자 하나만 구분하는 숫자별 이진 분류기 10개를 훈련시켜 클래스가 10개인 숫자 이미지 분류 시스템을 만들 수 있다. 이미지를 분류할 때 분류기의 결정 점수 중에서 가장 높은 것을 클래스로 선택하면 됩니다.
2. 일대 일(OVO(one versus one))1 과 0을 구분 1과 2 구별 등과 같이 숫자의 조합마다 이진 분류기를 훈련시키는 것입니다. 그러면 숫자가 10개니 각 숫자마다 모두 분류해주면 분류기는 45개가 만들어집니다. 45개의 분류기를 모두 통과해서 가장 많이 양성으로 구분된 것을 반환하면 됩니다.
OVO의 장점은 각 분류기의 훈련에 전체 훈련 세트 중 구별할 두 클래스에 해당하는 데이터만 필요하다는 것 입니다. 하지만 데이터가 커지고 분류기가 많아지면 오래걸리기 때문에 대부분 이진 분류 알고리즘에서는 OVA를 선호합니다.
데이터를 구분시켜 보겠습니다.
sgd_clf.fit(x_train, y_train)
sgd_clf.predict([x_test[0]]) #7 sgd.desicison_function([x_test[0]])
array([[ -443628.54049358, -1032831.48143599, -383319.96444514,
-130155.95644381, -478829.91572157, -286786.09045763,
-746799.97605978, 275059.67444649, -365955.85575451,
-315958.69904369]])
numpy의 argmax를 사용하면 점수가 가장높은 분류기의 인덱스 값을 구할 수 있습니다.
np.argmax(some_digit_scores) #7
각 구분기 중에 7의 점수가 가장 큰 것을 알 수 있습니다.
만약에 scikit learn에서 OVO나 OVA를 강제로 사용하고 싶다면 아래와 같은 방법을 사용하면 된다.
from sklearn.multiclass import OneVsOneClassifier
ovo_clf = OneVsOneClassifier(SGDClassifier(max_iter=5, random_state=42))
ovo_clf.fit(X_train, Y_train)
ovo_clf.predict([X_test[0]])#7을 출력 len(ovo_clf)로 치면 45가 나오는 것을 알 수 있다.
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")
array([0.91066787, 0.90619531, 0.91148672])
3.5 에러 분석
모델의 성능을 향상시키기 위해 에러를 분석해보겠습니다.
교차검증을 통해서 예측을 한 후 오차 배열을 만들어보겠습니다.
y_train_pred = cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3)
conf_mx = confusion_matrix(y_train, y_train_pred)
conf_mx
array([[5745, 3, 19, 9, 12, 44, 36, 10, 41, 4],
[ 2, 6461, 47, 24, 5, 41, 5, 10, 132, 15],
[ 52, 37, 5311, 98, 87, 23, 91, 65, 177, 17],
[ 51, 42, 132, 5327, 1, 250, 34, 60, 140, 94],
[ 20, 30, 34, 11, 5355, 7, 52, 30, 83, 220],
[ 69, 41, 32, 177, 78, 4621, 107, 24, 186, 86],
[ 37, 25, 42, 3, 37, 86, 5633, 8, 46, 1],
[ 26, 19, 65, 27, 53, 15, 5, 5801, 15, 239],
[ 55, 151, 64, 148, 15, 164, 52, 26, 5032, 144],
[ 43, 29, 24, 83, 153, 38, 2, 215, 81, 5281]],
matplotlib을 통해 그래프를 만들어 보겠습니다.
plt.matshow(conf_mx, cmap=plt.cm.gray)
plt.show()
숫자 5는 다른 곳보다 조금 어둡게 보입니다. 이는 잘못 분류한 것인지 이미지가 적은지 두경우 모두 알아봐야 합니다.
row_sums = conf_mx.sum(axis=1, keepdims=True)
norm_conf_mx = conf_mx/row_sums
np.fill_diagonal(norm_conf_mx, 0)#대각선 모두 0으로 만든다. 어두울 수록 정확
plt.matshow(norm_conf_mx, cmap=plt.cm.gray) plt.show()
3.6 다중레이블 분류
하나의 데이터에 여러개의 결과를 반환해야 할때가 있습니다. 예를 들어서 사진속 얼굴 판단기에서 3명이 찍혔는데 다음 사진에는 두명만 찍혀서 결과에 [0, 1, 0]을 출력해야합니다. 이런식으로 여러 개의 이진 레이블을 출력하는 분류시스템을 다중 레이블 분류 시스템이라고 합니다.
from sklearn.neighbors import KNeighborsClassifier
y_train_large = (y_train >= 7)
y_train_odd = (y_train % 2 ==1)
y_multilabel = np.c_[y_train_large, y_train_odd]
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train, y_multilabel) knn_clf.fit([x_test[0]]) # True, True 출력
test[0]은 7인데 7보다 크거나 같고 홀수이기 때문에 참이다.
평가하는 방법
평가법은 많은데 예를 들어서 각 레이블의 F1 점수를 구하고 간단하게 평균 점수를 계산합니다. 모든 레이블에 대한 F1 점수의 평균을 계산합니다.
y_train_pred = cross_val_predict(knn_clf, x_train, y_multilabel, cv=3, n_jobs=-1)
f1_score(y_multilabel, y_train_pred, average="marco") #가중치가 같다고 가정
가중치가 같다고 가정했는데 하나의 데이터가 더 많다면 많은 데이터에 더 높은 가중치를 두어야 합니다. 클래스의 지지도(타깃 레이블에 속한 샘플 수)를 가중치로 두는 것입니다. average 옵션에(average="weighted")로 설정해주어야 합니다.
'Python-이론 > python-인공지능2' 카테고리의 다른 글
[핸즈온 머신러닝] 4강 모델학습 2편 (0) | 2018.07.15 |
---|---|
[핸즈온 머신러닝] 4강 모델학습 1편 (0) | 2018.07.14 |
[핸즈온 머신러닝] 3강 scikitLearn Classification를 공부하고 1편 (3) | 2018.07.10 |
[Rnn]lstm을 이용해서 악보예측해보기 (0) | 2018.06.11 |
[keras]퍼셉트론을 활용해서 악보 만들기 (0) | 2018.06.10 |