도찐개찐

[머신러닝] 07. ROC 그래프 본문

PYTHON/데이터분석

[머신러닝] 07. ROC 그래프

도개진 2023. 1. 3. 09:42

ROC

  • Receiver Operation Characteristic
  • 수신자 판단 곡선
  • 세계 2차 대전 통신 장비 성능 평가를 위해 고안된 수치
    • 레이더 데이터를 분석하여 적 항공기와 신호 소음을 구별하는 데 사용
    • 수신기의 감도가 증가함에 따라 거짓 긍정의 수(즉, 특이성)이 감소함
  • 의학분야에 많이 사용되지만, 머신러닝의 이진 분류 모델 예측 성능 평가에도 사용
  • ROC곡선의 y축은 '민감도TPR'를, x축은 '1-재현율FPR'로 그림
    • FPR : FP/(TN+FP) : 아군항공기 소리를 적항공기 소리라고 오판한 비율 - 낮아야 함
    • TPR : TP/(TP+FN) : 적항공기 소리를 적항공기 소리라고 잘판단한 비율 - 높아야 함
    • FPR이 변할때 TPR이 어떻게 변하는지 알아봄
  • 모델이 양성/긍정으로 예측했을때 얼마나 잘 맞추고 있는지를 설명한 것

ROC 그래프 그리기

  • 타이타닉 데이터셋으로 로지스틱회귀 분석한 후
  • 특이도, 재현율을 구한후 ROC 그래프 작성
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.metrics import recall_score
from sklearn.metrics import precision_score
from sklearn.preprocessing import LabelEncoder
def getAge(x):
    result = 'a'
    if 80 < x <= 90:
        result = 'b'
    elif 70 < x <= 80:
        result = 'c'
    elif 60 < x <= 70:
        result = 'd'
    elif 50 < x <= 60:
        result = 'e'
    elif 40 < x <= 50:
        result = 'f'
    elif 30 < x <= 40:
        result = 'g'
    elif 20 < x <= 30:
        result = 'h'
    elif 10 < x <= 20:
        result = 'i'
    elif 5 < x <= 10:
        result = 'j'
    elif x <= 5:
        result = 'k'
    return result
def getFare(x):
    result = 'a'
    if 400 < x <= 500:
        result = 'b'
    elif 300 < x <= 400:
        result = 'c'
    elif 200 < x <= 300:
        result = 'd'
    elif 100 < x <= 200:
        result = 'e'
    elif x <= 100:
        result = 'f'
    return result
random_state = 2211171140

titanic = pd.read_csv('../data/titanic2.csv')
titanic['Age'] = titanic.age.apply(lambda x: getAge(x))
titanic['Fare'] = titanic.fare.apply(lambda x: getFare(x))

encoder = LabelEncoder()
encoder.fit(titanic.Age)
titanic['Age'] = encoder.transform(titanic.Age)

encoder = LabelEncoder()
encoder.fit(titanic.Fare)
titanic['Fare'] = encoder.transform(titanic.Fare)
titanic

field = ['pclass', 'sibsp', 'parch', 'Title', 'gender', 'Embarked', 'age', 'fare']
data = titanic.loc[:, field]
target = titanic.survived
X_train, X_test, y_train, y_test = train_test_split(data, target, train_size = 0.7,
                stratify=target, random_state=random_state)

lrclf = LogisticRegression(random_state=random_state, max_iter=500)
lrclf.fit(X_train, y_train)

pred = lrclf.predict(X_test)

accuracy_score(y_test, pred)
0.8061224489795918

분류 알고리즘 종류

  • 판별함수 모형
    • 주어진 데이터를 범주category에 따라 서로 다른 영역으로 나누는 경계면decision boundary을 찾아낸 후, 이 경계면을 기준으로 데이터가 어디에 있는지를 계산하는 함수를 이용
  • 확률적 판별/생성 모형
    • 주어진 데이터에 대해 각 범주category/레이블이 정답일 조건부확률을 계산하는 방법에 따라 조건부 확률함수를 추정하거나 베이즈 정리를 사용해서 분류하는 모형
  • 따라서, 분류기의 예측 불확실성을 추정하려면 scikit-learn에서 제공하는 2가지 함수를 사용
    • decision function, predict_proba
### 타이타닉 생존 예측확률 알아보기1
X_test[:5]
  pclass sibsp parch Title gender Embarked age fare
951 3 0 0 13 1 0 22.0 7.2250
364 2 1 0 16 1 2 54.0 26.0000
1125 3 0 0 13 1 2 19.0 7.8958
1170 3 8 2 10 0 2 28.0 69.5500
393 2 0 1 10 0 2 18.0 23.0000
# 판별함수 모형으로 생존 여부 확인 : 이산형 수치 출력
lrclf.predict(X_test[:5])
array([0, 0, 0, 0, 1])
# 확률적 판별/생성 모형으로 생존여부 확인
lrclf.predict_proba(X_test[:5])
array([[0.81780204, 0.18219796],
       [0.90538997, 0.09461003],
       [0.87151718, 0.12848282],
       [0.81173904, 0.18826096],
       [0.17735944, 0.82264056]])

타이타닉 승객 생존 여부 확률값 추출

  • 타이나닉 승객 생존 여부에서 생존(1)이 주된 관심사이므로
  • 생존에 대한 확률값만 따로 추출
pred_proba = lrclf.predict_proba(X_test)
pred_proba[:5, 1]
array([0.18219796, 0.09461003, 0.12848282, 0.18826096, 0.82264056])
# 실제값, 확률값, 예측값을
# 하나의 데이터 프레임에 작성
table = pd.DataFrame()
table['실제값'] = y_test
table['생존 확률값'] = pred_proba[:, 1]
table['예측값'] = lrclf.predict(X_test)
table.head()
  실제값 생존 확률값 예측값
951 1 0.182198 0
364 0 0.094610 0
1125 0 0.128483 0
1170 0 0.188261 0
393 1 0.822641 1

각 예측값 별 특이도fprs, 민감도(재현률)tprs, 임계값을 구함

  • tprs : 실제값이 양성(승객 생존)이고 예측값이 양성(승객 생존)으로 예측되는 정도
  • fprs : 실제값이 음성(승객 사망)인데 예측값이 양성(승객 생존)으로 예측되는 정도
  • thresholds : 임계값
from sklearn.metrics import roc_curve
fprs, tprs, thresholds = roc_curve(y_test, pred_proba[:, 1])
print('양성 tprs 결과', tprs[:20])
print('음성 fprs 결과', fprs[:20])

print('임계 값', thresholds[:20])
# 임계값에 따라 tprs, fprs가 달라져 보임
양성 tprs 결과 [0.         0.00671141 0.20134228 0.20134228 0.36912752 0.36912752
 0.39597315 0.39597315 0.42281879 0.43624161 0.45637584 0.45637584
 0.4966443  0.4966443  0.51677852 0.52348993 0.52348993 0.53020134
 0.53691275 0.55033557]
음성 fprs 결과 [0.         0.         0.         0.00411523 0.00411523 0.00823045
 0.00823045 0.01234568 0.01234568 0.01234568 0.01234568 0.02057613
 0.02057613 0.02469136 0.02469136 0.02469136 0.02880658 0.02880658
 0.03292181 0.03292181]
임계 값 [1.94471902 0.94471902 0.88688535 0.87539431 0.78355664 0.77898588
 0.76331131 0.76050806 0.73562007 0.73506918 0.72066805 0.70270998
 0.68020194 0.67717055 0.67551737 0.67360769 0.67311293 0.67163371
 0.66679513 0.66114106]
# 특이도 fprs, 민감도/ 재현율 tprs 시각화
roc = pd.DataFrame()
roc['fprs'] = fprs
roc['tprs'] = tprs
roc['thresholds'] = thresholds
roc.head()
  fprs tprs thresholds
0 0.000000 0.000000 1.944719
1 0.000000 0.006711 0.944719
2 0.000000 0.201342 0.886885
3 0.004115 0.201342 0.875394
4 0.004115 0.369128 0.783557
import seaborn as sns

sns.displot(roc.iloc[:, [0, 1]], kind='kde')
# 하단 그래프의 fprs, tprs의 겹침 영역이 넓을 수록 분류가 잘 되지 않는 그래프로 판단 해야함.
# 즉, 하단 그래프는 분류가 잘 되지 않는 그래프로 임계치 조정등으로 수정 필요
<seaborn.axisgrid.FacetGrid at 0x7fc965947220>

최적 임계값 알아보기

  • argmax : 가장 큰 값을 지닌 요소의 index값 출력
optidx = np.argmax(tprs - fprs)
thresholds[optidx]
0.6331740880282526

ROC 그래프 그리기

  • 분류를 잘 하는 모델일수록 커브는 1에 가깝게 표시
  • fpr, tpr 그래프의 겹친 부분이 크다 - 변별력 낮음 - 곡선이 0에 가깝게 붙어서 출력
  • fpr, tpr 그래프가 겹친 부분이 작다 - 변별력 높음 - 곡선이 1에 가깝게 붙어서 출력
plt.plot(fprs, tprs)
plt.plot([0,1], [0,1], 'k--')
plt.xlabel('FPRS')
plt.ylabel('TPRS')
plt.plot()

AUC

  • Area Under Curve
  • ROC 곡선 밑의 면적을 구한 값
  • 1에 가까울 수록 좋은 수치를 의미함
    • 0.9 ~ 1 : excellent
    • 0.8 ~ 0.9 : good
    • 0.7 ~ 0.8 : normal
from sklearn.metrics import roc_auc_score
roc_auc_score(y_test, pred)
0.7852072803601514
728x90
Comments