기본 콘텐츠로 건너뛰기

[math] 정적분의 특성

[ML] 이진 분류 추정기의 평가

이진분류기의 평가

이진 분류를 위해 로지스틱 모델을 사용합니다. 모델 생성을 위해 사용한 데이터셋은 pima-indians-diabetes.csv로서 Kaggle에서 가져올 수 있습니다. 이 파일은 .csv 형식이므로 pandas.read_csv("경로")를 통해 호출할 수 있습니다.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score, roc_auc_score
from sklearn.metrics import f1_score, confusion_matrix, precision_recall_curve, roc_curve
from sklearn.preprocessing import StandardScaler, Binarizer
from sklearn.linear_model import LogisticRegression
data = pd.read_csv('pima-indians-diabetes.csv')
data.head(3)
preg plas pres skin test mass pedi age class
0 6 148 72 35 0 33.6 0.627 50 1
1 1 85 66 29 0 26.6 0.351 31 0
2 8 183 64 0 0 23.3 0.672 32 1

데이터는 라벨은 2개의 클래스로 구성되며 그 빈도는 다음과 같습니다.

data["class"].value_counts()
class
0    500
1    268
Name: count, dtype: int64
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 768 entries, 0 to 767
Data columns (total 9 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   preg    768 non-null    int64  
 1   plas    768 non-null    int64  
 2   pres    768 non-null    int64  
 3   skin    768 non-null    int64  
 4   test    768 non-null    int64  
 5   mass    768 non-null    float64
 6   pedi    768 non-null    float64
 7   age     768 non-null    int64  
 8   class   768 non-null    int64  
dtypes: float64(2), int64(7)
memory usage: 54.1 KB

위 결과로 자료에 Null 값은 존재하지 않음을 알 수 있습니다.

sklearn.linear_model.LogisticRegression() 클래스를 사용하여 로지스틱 모델을 생성합니다. 모델 생성전 훈련과 검증 데이터셋을 구분합니다.

Xtr, Xte, ytr, yte=train_test_split(data.iloc[:,:-1], data["class"], test_size=0.3)
lrClf=LogisticRegression(max_iter=10000, random_state=3).fit(Xtr, ytr)
predTr=lrClf.predict(Xtr)
predTr[:3]
array([0, 0, 0], dtype=int64)

위 모델을 평가하기위해 혼동행렬(confusion matrix), 정확도, 정밀도, 재현율, F1 score, ROC를 조사합니다. 이들을 계산하기 위해 다음 함수를 생성합니다.

def clf_eval(y, y_pre):
    confMat=confusion_matrix(y, y_pre)
    acc=accuracy_score(y, y_pre)
    preci=precision_score(y, y_pre)
    recall=recall_score(y, y_pre)
    f1=f1_score(y, y_pre)
    re=pd.Series([acc, preci, recall, f1], index=["accuracy","precision","recall", "F1 score"])
    return(confMat, re)

정확도, 정밀도, 재현율, F1-Score는 다음과 같습니다.

표 1. 교차표
관측\예측PN
P TP FN
N FP TN
  • 정확도(Accuracy): 전체 중에 올바른 분류의 정도
  • 정밀도(Precision): 긍정값(목표값)을 올바르게 분류하는 정도(실제 목표값 중에 예측한 목표값) 직관적으로 분류자가 음성(목표가 아닌 값)인 샘플을 양성(목표값)으로 분류하지 않는 성능을 나타냄
  • 재현율(Recall): 긍정값(목표값)을 올바르게 분류하는 정도(예측한 목표값 중에 실제 목표값) 직관적으로 양성(목표값)을 찾는 성능을 나타냄
  • F1-score: 재현율과 정밀도의 조화평균입니다.

\begin{align}\text{Accuracy}&=\frac{\text{TP + TN}}{\text{TP + FP +TN + FN}}\\\tag{식 1}\text{Precision}&=\frac{\text{TP}}{\text{TP + FP}}\\\text{Recall}&=\frac{\text{TP}}{\text{TP + FN}}\\\text{F1-score}&=\frac{2\cdot\text{Precsion}\cdot\text{Recall}}{\text{Precsion}+\text{Recall}} \end{align}

조화평균(harmonic mean)은 각 값의 역수에 대한 산술평균입니다. 예를 들어 두 수 a,b의 조화평균은 식 2와 같이 계산됩니다.

$$\tag{식 2}\text{조화평균}=\frac{2}{\frac{1}{a} + \frac{1}{b}}=\frac{2ab}{a+b}$$

train_eval=clf_eval(ytr, predTr)
train_eval
(array([[313,  37],
        [ 78, 109]], dtype=int64),
 accuracy     0.785847
 precision    0.746575
 recall       0.582888
 F1 score     0.654655
 dtype: float64)
preTe=lrClf.predict(Xte)
test_eval=clf_eval(yte, preTe)
test_eval
(array([[131,  19],
        [ 32,  49]], dtype=int64),
 accuracy     0.779221
 precision    0.720588
 recall       0.604938
 F1 score     0.657718
 dtype: float64)

정밀도와 재현율을 나타내는 그래프를 생성합니다. 이분류 모델은 식 3과 같습니다.

$$ \tag{식 3} f(x_i)=\begin{cases}1 & \hat{p}(Y=1 | x_i) > c\\ 0& \text{otherwise}\end{cases}$$

즉, 식 3에서 나타낸것과 같이 실제 관측값의 클래스가 1인 조건에서 모델에 의한 x(features)에 대한 예측확률이 임계값(threshold, c)보다 클때 1 즉, P로 그렇지 않을 경우 0(N)으로 예측합니다. sklearn의 대부분의 분류 추정기는 predict_proba() 함수에 의한 0과 1에 대한 예측확률을 계산합니다. SGDClassifier()와 같은 선형모형을 기반으로 분류하는 경우 decision_function() 메서드로 그 선형모델에 의한 결과값을 반환합니다. 이 두 결과는 각 샘플에 대한 모델의 결과로서 분류 경계를 나타낼 수 있는 임계값(threshold)으로 사용할 수 있습니다. 예를 들어 precision_recall_curve(y, y_score) 함수는 한계값의 변화에 의한 계산된 정밀도와 재현율을 반환합니다. 이 함수의 y_score는 predict_proba()의 결과로부터 1이 될 확률 또는 decision_function() 의 결과가 됩니다.

함수 precision_recall_curve( y, y_score)는 precision, recall, threshold를 반환하는 데 threshold는 y_score를 올림차순으로 정렬한 것입니다.

preTrProba=lrClf.predict_proba(Xtr)
preTrProba[:3]
array([[0.88212769, 0.11787231],
       [0.7058086 , 0.2941914 ],
       [0.95943764, 0.04056236]])
ytr_score=preTrProba[:,1]
precisions, recalls, thresholds=precision_recall_curve( ytr, ytr_score)
np.sort(ytr_score)[:5]
array([0.00224104, 0.00408697, 0.00825182, 0.01150737, 0.01381425])
thresholds[:5]
array([0.00224104, 0.00408697, 0.00825182, 0.01150737, 0.01381425])
precisions.shape, recalls.shape, thresholds.shape
((538,), (538,), (537,))
plt.figure(figsize=(4,3))
limit=thresholds.shape[0]
plt.plot(thresholds, precisions[:limit], color="b", label="precision")
plt.plot(thresholds, recalls[:limit], color="r", label="recall")
plt.xlabel("threshold")
plt.ylabel('values')
plt.xticks(np.arange(0, 1.1, 0.1))
plt.yticks(np.arange(0, 1.1, 0.1))
plt.legend(loc="best")
plt.show()

위 과정을 함수로 작성하면 다음과 같습니다.

def precisionRecallFigure(y, y_score):
    precisions, recalls, thresholds=precision_recall_curve(y, y_score)
    plt.figure(figsize=(4,3))
    limit=thresholds.shape[0]
    plt.plot(thresholds, precisions[:limit], color="b", label="precision")
    plt.plot(thresholds, recalls[:limit], color="r", label="recall")
    plt.xlabel("threshold")
    plt.ylabel('values')
    plt.xticks(np.arange(0, 1.1, 0.1))
    plt.yticks(np.arange(0, 1.1, 0.1))
    plt.legend(loc="best")
    plt.show()
    re=pd.DataFrame([precisions, recalls], index=["precision", "recall"]).T
    return(re)
preTeProba=lrClf.predict_proba(Xte)
yte_score=preTeProba[:,1]
yte_eval=precisionRecallFigure(yte, yte_score)
yte_eval.head(3)
precision recall
0 0.350649 1.0
1 0.352174 1.0
2 0.353712 1.0

ROC curve(Receiver-Operation Chracteristic curve)는 민감도(recall)와 실제 음성중 가짜음성을 예측하는 비율(1- 특이도)를 시각화 한 것입니다. 함수 roc_curve(y, y_score, drop_intermediate=True)는 1-특이도, 민감도, 임계값를 반환합니다.

  • 민감도(sensitivity)는 recall이라고도 하면 실제 양성중 양성을 예측하는 비율
  • 특이도(spcificity)는 실제 음성중 음성을 예측하는 비율
  • FPR은 실제 음성중 가짜음성의 비율

표 1의 교차표로부터 민감도와 특이도는 식 4와 같이 계산됩니다.

\begin{align}\text{sensitivity(recall)}&=\frac{\text{TP}}{\text{TP}+\text{FN}}\\\tag{식 4}\text{spcificity}&=\frac{\text{TN}}{\text{TN}+\text{FP}}\\\text{FPR}&=1-\text{spcificity}\\&=\frac{\text{FP}}{\text{TN}+\text{FP}} \end{align}

결과중 임계값는 y_score의 내림차순으로 정렬한 것입니다. thresh[0]에서 양성(P) 예측 매우 작을 것이므로 TP 또는 FP는 매우 낮을 것입니다. 그러므로 민감도와 1 - 특이도는 올림차순으로 반환됩니다. 또한 함수 roc_curve()의 인수 drop_intermediate에 의해 일부 임계값의 제거 여부를 지정할 수 있습니다. 기본값은 True로 제거되어 반환됩니다.

fpr, sen, thresh=roc_curve(ytr, ytr_score, drop_intermediate=False)
thresh.shape, fpr.shape, sen.shape
((538,), (538,), (538,))
thresh[:10]
array([1.98998104, 0.98998104, 0.98106614, 0.9614324 , 0.9580388 ,
       0.95603086, 0.95416965, 0.95221597, 0.94948189, 0.94771266])
np.sort(ytr_score)[::-1][:10]
array([0.98998104, 0.98106614, 0.9614324 , 0.9580388 , 0.95603086,
       0.95416965, 0.95221597, 0.94948189, 0.94771266, 0.93416477])
plt.figure(figsize=(4,3))
plt.plot(fpr, sen, color="b", label="ROC")
plt.plot([0,1], [0,1], color="r", ls="dotted", label="random")
plt.xlabel("1-specificity")
plt.ylabel('sensitivy')
plt.xticks(np.arange(0, 1.1, 0.1))
plt.yticks(np.arange(0, 1.1, 0.1))
plt.legend(loc="best")
plt.show()

위 그래프의 전체 면적은 1이며 그 중에 ROC 그래프의 아래의 면적(Area Under Curve, AUR)의 비율로서 이 추정기의 평가를 측정합니다. 위 그래프는 동전던기와 같은 랜덤변수의 이진 분류에서 기대할 수 있는 AUC 값으로 0.5를 나타냅니다. 일반적으로 추정기(분류모델)은 이 선위에 존재합니다. 위 그림에서 ROC의 AUC는 함수 roc_auc_score(y, y_score)로 확인할 수 있습니다.

auc=roc_auc_score(ytr, ytr_score)
auc
0.8389915966386554

위 과정을 함수로 작성해 봅니다.

def rocFigure(y, y_score):
    fpr, sen, thresh=roc_curve(y, y_score)
    plt.figure(figsize=(4,3))
    plt.plot(fpr, sen, color="b", label="ROC")
    plt.plot([0,1], [0,1], color="r", ls="dotted", label="random")
    plt.xlabel("1-specificity")
    plt.ylabel('sensitivy')
    plt.xticks(np.arange(0, 1.1, 0.1))
    plt.yticks(np.arange(0, 1.1, 0.1))
    plt.legend(loc="best")
    plt.show()
    auc=roc_auc_score(ytr, ytr_score)
    return({"AUC":auc})
rocFigure(yte, yte_score)
{'AUC': 0.8389915966386554}

댓글

이 블로그의 인기 게시물

[Linear Algebra] 유사변환(Similarity transformation)

유사변환(Similarity transformation) n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사행렬(similarity matrix)이 되며 행렬 A를 가역행렬 P와 B로 분해하는 것을 유사 변환(similarity transformation) 이라고 합니다. $$\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B $$ 식 2는 식 1의 양변에 B의 고유값을 고려한 것입니다. \begin{align}\tag{식 2} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align} 식 2의 행렬식은 식 3과 같이 정리됩니다. \begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \textsf{det}(I)\end{aligned}\end{align} 유사행렬의 특성 유사행렬인 두 정방행렬 A와 B는 'A ~ B' 와 같

[matplotlib] 히스토그램(Histogram)

히스토그램(Histogram) 히스토그램은 확률분포의 그래픽적인 표현이며 막대그래프의 종류입니다. 이 그래프가 확률분포와 관계가 있으므로 통계적 요소를 나타내기 위해 많이 사용됩니다. plt.hist(X, bins=10)함수를 사용합니다. x=np.random.randn(1000) plt.hist(x, 10) plt.show() 위 그래프의 y축은 각 구간에 해당하는 갯수이다. 빈도수 대신 확률밀도를 나타내기 위해서는 위 함수의 매개변수 normed=True로 조정하여 나타낼 수 있다. 또한 매개변수 bins의 인수를 숫자로 전달할 수 있지만 리스트 객체로 지정할 수 있다. 막대그래프의 경우와 마찬가지로 각 막대의 폭은 매개변수 width에 의해 조정된다. y=np.linspace(min(x)-1, max(x)+1, 10) y array([-4.48810153, -3.54351935, -2.59893717, -1.65435499, -0.70977282, 0.23480936, 1.17939154, 2.12397372, 3.0685559 , 4.01313807]) plt.hist(x, y, normed=True) plt.show()

R 미분과 적분

내용 expression 미분 2차 미분 mosaic를 사용한 미분 적분 미분과 적분 R에서의 미분과 적분 함수는 expression()함수에 의해 생성된 표현식을 대상으로 합니다. expression expression(문자, 또는 식) 이 표현식의 평가는 eval() 함수에 의해 실행됩니다. > ex1<-expression(1+0:9) > ex1 expression(1 + 0:9) > eval(ex1) [1] 1 2 3 4 5 6 7 8 9 10 > ex2<-expression(u, 2, u+0:9) > ex2 expression(u, 2, u + 0:9) > ex2[1] expression(u) > ex2[2] expression(2) > ex2[3] expression(u + 0:9) > u<-0.9 > eval(ex2[3]) [1] 0.9 1.9 2.9 3.9 4.9 5.9 6.9 7.9 8.9 9.9 미분 D(표현식, 미분 변수) 함수로 미분을 실행합니다. 이 함수의 표현식은 expression() 함수로 생성된 객체이며 미분 변수는 다음 식의 분모의 변수를 의미합니다. $$\frac{d}{d \text{변수}}\text{표현식}$$ 이 함수는 어떤 함수의 미분의 결과를 표현식으로 반환합니다. > D(expression(2*x^3), "x") 2 * (3 * x^2) > eq<-expression(log(x)) > eq expression(log(x)) > D(eq, "x") 1/x > eq2<-expression(a/(1+b*exp(-d*x))); eq2 expression(a/(1 + b * exp(-d * x))) > D(eq2, "x") a * (b * (exp(-d * x) * d))/(1 + b