Python / / 2022. 10. 17. 17:17

Python - 머신러닝 모델 평가 [최종]

from random import random
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import f1_score
import seaborn as sns
from sklearn.preprocessing import LabelEncoder, binarize
import warnings

'''교차검증만으로 끝난게 아니다. 

모델을 신뢰 할 수 없기 때문에 교차검증, 모델튜닝을 하며 최적화를 진행한후 
실질적인 모델 평가를 진행한다.

<분류 모델 평가 지표> 

       

True Negative(TN) : Negative로 예측했는데 맞은거  | False Positive (FP): Positive로 예측했는데 틀린거
------------------------------------------
False Negative (FN) : Negative로 예측했는데 틀린것 | True Positive (TP): Postitive로 예측했는데 , 맞은거

FN이 잘못된 경우 굉장히 위험하다.


TN는 고려대상이 아니기 때문에 , FN / FP / TP를 주로 확인한다.

예를들어, 암 환자 예측에서 암환자는 반드시 Positive(0)로 해야한다.  정상인은 Negative(1)

--> 반드시 예측 대상을 Positive로 설정해야한다. 

<회귀 성능 평가 지표 >
근사치 확인

<분류 성능 평가 지표>
*1. 정확도 (accuracy)

2. 오차행렬
3. 정밀도 & 재현율 
4. F1 스코어 

*5. ROC AUC (신뢰성 측정)

정확도, ROC AUC를 확인하면 쓸 수 있는 모델인지 평가가 가능하다.

'''
warnings.filterwarnings('ignore')
df = sns.load_dataset('titanic')
df.drop(['class', 'alive', 'embark_town', 'who', 'adult_male', 'alone'], axis=1, inplace=True)
df['family'] = df.sibsp + df.parch
df.drop(['sibsp', 'parch'], axis=1, inplace=True)
df1 = df.copy()
df1.embarked.fillna('S', inplace=True)
m1_med = df1.loc[(df1.sex == 'male') & (df1.pclass == 1), 'age'].median()
m2_med = df1.loc[(df1.sex == 'male') & (df1.pclass == 2), 'age'].median()
m3_med = df1.loc[(df1.sex == 'male') & (df1.pclass == 3), 'age'].median()
f1_med = df1.loc[(df1.sex == 'female') & (df1.pclass == 1), 'age'].median()
f2_med = df1.loc[(df1.sex == 'female') & (df1.pclass == 2), 'age'].median()
f3_med = df1.loc[(df1.sex == 'female') & (df1.pclass == 3), 'age'].median()
df1.loc[(df1.sex == 'male')&(df1.pclass == 1), 'age'].fillna(29, inplace=True)
df1.loc[(df1.sex == 'male')  &(df1.pclass == 1)&(df1.age.isna()), 'age'] = m1_med
df1.loc[(df1.sex == 'male')  &(df1.pclass == 2)&(df1.age.isna()), 'age'] = m2_med
df1.loc[(df1.sex == 'male')  &(df1.pclass == 3)&(df1.age.isna()), 'age'] = m3_med
df1.loc[(df1.sex == 'female')&(df1.pclass == 1)&(df1.age.isna()), 'age'] = f1_med
df1.loc[(df1.sex == 'female')&(df1.pclass == 2)&(df1.age.isna()), 'age'] = f2_med
df1.loc[(df1.sex == 'female')&(df1.pclass == 3)&(df1.age.isna()), 'age'] = f3_med
df1.drop('deck', axis=1, inplace=True)
df1.age_new = 0
# 노인의 생존율 (50세 이상) 유아의 생존율 (10세 미만)
df1.loc[df1.age >= 50, 'age_new'] = 'old'
df1.loc[(df1.age < 50) & (df1.age>=10), 'age_new'] = 'young'
df1.loc[df1.age < 10, 'age_new'] = 'baby'
for i in ['sex', 'embarked', 'age_new']:
    globals()[f'df1_{i}_encoder'] = LabelEncoder()
    globals()[f'df1_{i}_encoder'].fit(df1[i])
    df1[i] = globals()[f'df1_{i}_encoder'].transform(df1[i])
df1_sex_encoder = LabelEncoder()
df1_embarked_encoder = LabelEncoder()
df1_agenew_encoder = LabelEncoder()
df1_sex_encoder.fit(df1['sex'])
df1_embarked_encoder.fit(df1['embarked'])
df1_agenew_encoder.fit(df1['age_new'])
df1['sex'] = df1_sex_encoder.transform(df1['sex'])
df1['embarked'] = df1_embarked_encoder.transform(df1['embarked'])
df1['age_new'] = df1_agenew_encoder.transform(df1['age_new'])
X = df1.drop('survived', axis=1)
y = df1.survived


from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score


#X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Tuning Multiple Hyperparameters
# read in hyperopt values


titanic_dtclf = DecisionTreeClassifier()
titanic_dtclf.fit(X,y)
# pred = titanic_dtclf.predict(y, X)


#오차 행렬 확인 
from sklearn.metrics import confusion_matrix

#예측
print(confusion_matrix(y, titanic_dtclf.predict(X)))
'''

True Negative(TN)  | False Positive (FP)
     (547)                   (2) 
------------------------------------------
False Negative (FN) | True Positive (TP)
    (15)                     (327)

'''

#예시 
'''더미 데이터를 생성 1,1000까지 10단위로 random
만든후 ,20개는 1로 설정 1000개를 0으로 채워서 정확도 예측 하면 98%가 나온다. 

하지만, 정확도가 높다고 하더라도 제대로 된 결과가 아님

confusion_matrix를 확인하면 
[[980   0]  /열의 오른쪽이 0 , 0이 나온 순간부터 이미 잘못되었다는것을 알 수 있다.
 [ 20   0]]
'''
data={'a':np.random.randint(1,10,1000),
      'b':np.random.randint(1,10,1000)}
new = pd.DataFrame(data)
new['result'] = 0 
new.iloc[-20:] = 1
# print('정확도', accuracy_score(new.result, np.zeros(1000)))
# print('컨퓨전 매트릭스', confusion_matrix(new.result,np.zeros(1000)))
'''
기존의 정확도 : 전체 예측 데이터 건수 , 예측결과가 동일한 데이터 건수를 확인했지만 

Confusion_matrix로  확인을 해야 한다 .

(TN+TP)
--------------
(TN+FN+FP+TP)

의료쪽에선 TP가 상당히 중요한 요소이다.

''''''
정밀도(PRECISION): 실제 음성인 데이터 예측을 양성으로 잘못 판단 시 업무상 큰 영향이 발생하는 경우 (FP)
이런경우, FP를 낮추는게 중요하다. False Positive를 낮출수록, 정밀도는 상승한다.

재현율(RECALL): 실제 양성인 데이터를 음성으로 잘못 판단시 업무상 큰 영향이 발생하는 경우, (FN)

정밀도든 재현율이든 Positive를 맞추는게 중요하기 때문에, TP를 올리는게 중요하다.

TN은 갖다버리자 Nagative는 예측해봤자 아무짝에도 쓸모없다.
'''

from sklearn.metrics import accuracy_score,precision_score,recall_score,confusion_matrix
#오차행렬, 정확도 , 정밀도 , 재현율을 출력해주는 함수 생성 

def get_clf_eval(y_test , pred):
    #오차행렬
    confusion = confusion_matrix(y_test, pred)
    #정확도
    accuracy = accuracy_score(y_test , pred)
    #정밀도
    precision = precision_score(y_test , pred)
    #재현율
    recall = recall_score(y_test , pred)
    
    print('오차 행렬',confusion)
    print('정확도: {}, 정밀도: {}, 재현율: {}'.format(accuracy , precision ,recall))
    
print(get_clf_eval(y, titanic_dtclf.predict(X)))
from sklearn.metrics import classification_report

print(classification_report(y, titanic_dtclf.predict(X)))

print(get_clf_eval(y, titanic_dtclf.predict(X)))
'''정밀도 & 재현율'''


#생성한 함수 사용 의사결정 나무 학습 후 결과 확인해보기
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score, recall_score, precision_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import plot_tree
import warnings
warnings.filterwarnings('ignore')
df = sns.load_dataset('titanic')
df.drop(['class', 'alive', 'embark_town', 'who', 'adult_male', 'alone'], axis=1, inplace=True)
df1 = df.copy()
df1.deck = df1.deck.astype('O')
# 결측치 처리
df1.embarked.fillna('S', inplace=True)
df1.age.fillna(df1.age.mean(), inplace=True)
df1.deck.fillna('N', inplace=True)
def label_encoding(df):
    ecs = df.columns[(df.dtypes=='O')|(df.dtypes=='category')|(df.dtypes=='bool')]
    for i in ecs:
        globals()[f'{df}_{i}_encoder'] = LabelEncoder()
        globals()[f'{df}_{i}_encoder'].fit(df[i])
        df[i] = globals()[f'{df}_{i}_encoder'].transform(df[i])
label_encoding(df1)
X = df1.drop('survived', axis=1)
y = df1.survived
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

def get_clf_eval(y_test, pred):
    confusion = confusion_matrix(y_test, pred)
    accuracy  = accuracy_score(y_test, pred)
    recall    = recall_score(y_test, pred)
    precision = precision_score(y_test, pred)
    print('-'*15)
    print('1. 오차행렬')
    print(confusion)
    print('2. 정확도:', round(accuracy, 2))
    print('3. 정밀도:', round(precision, 2))
    print('4. 재현율:', round(recall, 2))
    print('-'*15)
dtclf = DecisionTreeClassifier(max_depth=5 , min_samples_leaf=3)
dtclf.fit(X_train, y_train)
pred = dtclf.predict(X_test)
get_clf_eval(y_test, pred)
plt.figure(figsize=(20,10))
plot_tree(dtclf, class_names=['0', '1'], fontsize=8)
#plt.show()

'''TRADE - OFF 


'''

#predict 메소드 대신 predict_proba로 예측해보기

pred_proba = dtclf.predict_proba(X_test)
pred_proba = pred_proba[:,1]
pred  = dtclf.predict(X_test)
len(dtclf.predict(X_test))
pp_df = pd.DataFrame([pred,pred_proba]).T
pp_df.columns = ['pred','pred_proba']
print(pp_df.head(20))

from sklearn.preprocessing import Binarizer
x= [[1,-1,2], [2,0,0],[0,1.1,1.2]]

#Binarizer 클래스 활용 1.1 보다 같거나 작으면 0 을 크면 1을 반환 하기
# >>> transformer = Binarizer().fit(X)  # fit does nothing.
# >>> transformer
# Binarizer()
# >>> transformer.transform(X)

Binarizer_ts = Binarizer(threshold=1.1)
print(Binarizer_ts.fit_transform(x))

#Binarizer 활용 기준을 0~1까지 0.1단위로 이동하면서 get_clf_eval 함수 사용 결과 출력
dtclf = DecisionTreeClassifier(max_depth=3, min_impurity_decrease=0.01)
dtclf.fit(X_train, y_train)
pred_proba = dtclf.predict_proba(X_test)
thresholds = range(1, 11)
def get_eval_by_threshold(y_test, pred_proba, thresholds):
    ts_accuracy = []
    ts_precision = []
    ts_recall = []
    ts_f1score = []
    ts_datas = {}
    for custom_threshold in thresholds:
        tt_binarizer = Binarizer(threshold=custom_threshold * 0.1)
        custom_predict = tt_binarizer.fit_transform(pred_proba)[:, 1]
        f1score = f1_score(y_test, custom_predict)
        accuracy  = accuracy_score(y_test, custom_predict)
        recall    = recall_score(y_test, custom_predict)
        precision = precision_score(y_test, custom_predict)
        
        ts_accuracy.append(accuracy)
        ts_precision.append(precision)
        ts_recall.append(recall)
        ts_f1score.append(f1score)
    ts_datas['thresholds'] = [i * 0.1 for i in thresholds]
    ts_datas['accuracy'] = ts_accuracy
    ts_datas['precision'] = ts_precision
    ts_datas['f1score'] = ts_f1score
    ts_datas['recall'] = ts_recall
    pd.DataFrame(ts_datas).set_index('thresholds').plot()
  #  plt.show()

dtclf = DecisionTreeClassifier(max_depth=5 , min_samples_leaf=3)
dtclf.fit(X_train, y_train)
pred_proba = dtclf.predict_proba(X_test)
get_eval_by_threshold(y_test,pred_proba,thresholds)

#F1 스코어 추가후, 함수 재구성 후 정확도, 정밀도 ,재현율, F1 스코어 그래프 출력후 최적의 기준점 확인하기 
'''
ROC : 수신자 판단 곡선 

2차대전때 통신 장비 성능 평가를 위해 고안된 수치 

의학분야에서 많이 사용 

머신러닝 이진 분류 모델의 예측 성능 판단에 중요한 평가 지표 


'''

from sklearn.metrics import roc_curve
# 레이블 값이 1일 때의 예측 확률을 추출
pred_prob_class1 = dtclf.predict_proba(X_test)[:,1]
fprs, tprs, thresholds = roc_curve(y_test, pred_prob_class1)
# 반환된 임곗값 배열 로우가 47건 추출
thr_index = np.arange(0, thresholds.shape[0])
print('임곗값 배열의 index:', thr_index)
print('전체 임곗값:', np.round(thresholds[thr_index], 2))
# 추출된 임곗값에 따른 FPR, TPR 값
print('전체 임곗값별 FPR:', np.round(fprs[thr_index], 3))
print('전체 임곗값별 TPR:', np.round(tprs[thr_index], 3))
def roc_curve_plot(y_test, pred_prob_c1):
    # 임곗값에 따른 FPR, TPR 값을 반환받음
    fprs, tprs,thresholds = roc_curve(y_test, pred_prob_c1)
    # ROC 곡선을 그래프 곡선으로 그림
    plt.plot(fprs, tprs, label='ROC')
    # 가운데 대각선 직선을 그림
    plt.plot([0,1], [0,1], 'k--', label='Random')
    # FPR X 축의 Scale을 0.1 단위로 변경, X, Y측 명 설정 등
    start, end = plt.xlim()
    plt.xticks(np.round(np.arange(start, end, 0.1), 2))
    plt.xlim(0,1); plt.ylim(0,1)
    plt.xlabel('FPR(1-Sensitivity)'); plt.ylabel('TPR(Recall)')
    plt.legend()
   # plt.show()
roc_curve_plot(y_test, pred_proba[:,1])

'Python' 카테고리의 다른 글

Python - Grid Search Hyper parameter  (0) 2022.10.19
Python - 랜덤포레스트 iris  (0) 2022.10.19
Python - 모델평가 함수  (0) 2022.10.17
Python - 모델 평가 오차행렬  (0) 2022.10.17
Python - 교차 검증 정리  (0) 2022.10.17
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유