본문 바로가기

머신러닝/지도학습

[Machine learning 평가] Pipeline 형태, Classifier의 평가지표{confusion matrix, 혼동오류, f1 score 개념}

320x100

 

머신러닝의 모델 중 표현방식을 다르게 바꿔주는 set_config와 pipeline에 대해서 알아보겠습니다.

또한 Classifier(분류) 모델의 여러가지 평가 지표를 확인해보고자 합니다.

 

이번 시간에는 데이터셋이나 feature engineering에 대해 많은 지면을 할애하진 않고

모델 자체에 중점을 두고자 합니다. 너무나 잘 알려진 데이터셋이기도 하고, 이제까지 많은 머신러닝,

캐글 관련 서적에서 다뤄본 적 있는 주제이기 때문이지요.

 

바로, 타이타닉 생존자 데이터셋 입니다.

Kaggle Competitions

 

Titanic - Machine Learning from Disaster | Kaggle

 

www.kaggle.com

 

잠깐 설명하자면, 타이타닉에 올라탔었던 승객들과 승객에 대한 정보, 그리고 여기서 궁금해하는 생존여부에

대해서 설명하고 있는 데이터셋 입니다.

 

# https://www.kaggle.com/competitions/titanic/data
train = pd.read_csv("/content/train.csv")
train

 

 

궁금하신 분들은 위의 캐글 사이트에서 확인해보셔도 좋고, 아래의 한글 블로그도 추천드립니다.

확인해보니 kaggle에서 공식 한국 블로그도 운영하고 있었습니다!

타이타닉 튜토리얼 1 - Exploratory data analysis, visualization, machine learning (tistory.com)

 

타이타닉 튜토리얼 1 - Exploratory data analysis, visualization, machine learning

My_kernel_chapter_data_check_EDA /*!** Twitter Bootstrap**//*! * Bootstrap v3.3.7 (http://getbootstrap.com) * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) *//*! normalize.css v3.0.3 | MIT Li

kaggle-kr.tistory.com

 

'타이타닉은 이제 흔해~ 하지만 비슷한 데이터는 없을까?' 라는 생각이 드는 분들도 있을 수 있겠지요.

그를 위해서 타이타닉의 상위호환 버전 'Space titanic'도 소개되고 있습니다.

 

[Machine Learning] Kaggle_연습사례 분석_Spaceship_titanic (tistory.com)

 

[Machine Learning] Kaggle_연습사례 분석_Spaceship_titanic

서론 이번시간에는 Kaggle의 완전 기초, 시작단계(Getting Started) 컴피티션에 놓여져있는 Spaceship Titanic 데이터에 대해서 개인적으로 분석한 머신러닝 사례를 진행해 보고자 합니다. 먼저, 원본 데이

astart.tistory.com

여기서는 난파된 사람을 바다가 아니라 차원 저 편(alternate dimension)으로 보내버립니다.

 


타이타닉 데이터셋에서 기본적인 데이터 전처리는 빠르게 압축하고 지나가겠습니다.

결과적으로 Survived가 Target이자 y이구요. 그 외의 column들이 Feature이자 X가 됩니다.

 

train = pd.get_dummies(train, columns=['Sex', 'Embarked'], drop_first=True)
train.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 1 to 891
Data columns (total 9 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   Survived    891 non-null    int64  
 1   Pclass      891 non-null    int64  
 2   Age         891 non-null    float64
 3   SibSp       891 non-null    int64  
 4   Parch       891 non-null    int64  
 5   Fare        891 non-null    float64
 6   Sex_male    891 non-null    uint8  
 7   Embarked_Q  891 non-null    uint8  
 8   Embarked_S  891 non-null    uint8  
dtypes: float64(2), int64(4), uint8(3)
memory usage: 51.3 KB

 

object 타입 데이터는 모두 int로 만들기 위해 원-핫 인코딩을 한 상태입니다.

 

from sklearn.model_selection import train_test_split

X = train.drop('Survived', axis=1)
y = train['Survived']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=16)

 

여기서 약간 달라지는데요. 먼저 make_pipeline 라이브러리를 소개합니다.

 

파이프라인 생성

 

사이킷런 공식 설명

sklearn.pipeline.make_pipeline — scikit-learn 1.2.1 documentation

 

sklearn.pipeline.make_pipeline

Examples using sklearn.pipeline.make_pipeline: Release Highlights for scikit-learn 1.2 Release Highlights for scikit-learn 1.2 Release Highlights for scikit-learn 1.1 Release Highlights for scikit-...

scikit-learn.org

 

make pipeline은 일종의 설계도를 만듭니다.

기존까지는 그냥 모델을 텍스트로 단순하게 표현했지만, pipeline을 사용하면 시각적으로

어떻게 구성되어 있는지 알 수 있는데요?

 

중간중간 데이터를 스케일링 하는 방식을 바꾸거나, 모델을 교체한 후에 어떤 모델을 사용했는지

확인할 때 유용하게 쓰입니다.

 

여기서는 스테일링으로 Robustscaler, 분류 모델로 Light GBM을 사용했습니다.

from sklearn.pipeline import make_pipeline


# 로부스트 스케일러 사용
from sklearn.preprocessing import RobustScaler

# 필요한 lightgbm 패키지 호출
import lightgbm as lgb

# 모델 = 파이프라인
pipe_lr = make_pipeline(RobustScaler(), lgb.LGBMClassifier(random_state = 1))
lgb = lgb.LGBMClassifier()
model = pipe_lr
model

 

이렇게 저장되어 있는 모델을 반환합니다. 화살표를 누르면 열고 닫을 수 있습니다.

 

 

이 형태는 현재 Diagram으로 설정된 것입니다.

sklearn set_config에서 형태를 텍스트로 바꾸면 우리가 익히 아는 '그 형태'로 조회됩니다.

 

(텍스트 조회 시)

from sklearn import set_config
set_config(display = 'text')

model
Pipeline(steps=[('robustscaler', RobustScaler()),
                ('lgbmclassifier', LGBMClassifier(random_state=1))])

 

(다이어그램)

 

 

좋습니다.

 

일단 LightGBM을 그대로 평가하도록 넘어가겠습니다.

 

# Lightgbm 학습 시작

model.fit(X_train, y_train)
pred = model.predict(X_test)

 

train데이터 예측하구요.

 

# 예측값
pred
array([1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
       0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1,
       0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0,
       0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
       0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1,
       0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1,
       0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0,
       1, 0, 0])

 

분류 모델의 Evaluation score

 

먼저 accuracy score입니다.

accuracy score는 (TP + TN) / (TP + FN + FP + TN)을 나타낸 것입니다.

즉, 모델이 바르게 분류할 비율입니다.

 

TP = 양성을 참으로 예측할 확률
FN = 음성을 거짓으로 예측할 확률
FP = 양성을 거짓으로 에측할 확률
TN = 음성을 참으로 예측할 확률
# 정확도 점수
score = accuracy_score(y_test, pred)
print(f'※ lightGBM NO 패러미터의 정확도 점수 : {score}')
※ lightGBM NO 패러미터의 정확도 점수 : 0.8268156424581006

 

그리고 Confusion matrix입니다. 

위에서 제시한 TP, FN, FP, TN 값을 모두 표현합니다.

 

# confusion matrix (혼동행렬)

from sklearn.metrics import confusion_matrix

cf_matrix = confusion_matrix(y_test,pred)
group_names = ['TN','FP','FN','TP']
group_counts = ["{0:0.0f}".format(value) for value in
                cf_matrix.flatten()]
group_percentages = ["{0:.2%}".format(value) for value in
                     cf_matrix.flatten()/np.sum(cf_matrix)]
labels = [f"{v1}\n{v2}\n{v3}" for v1, v2, v3 in
          zip(group_names,group_counts,group_percentages)]
labels = np.asarray(labels).reshape(2,2)
sns.heatmap(cf_matrix, annot=labels, fmt='', cmap='coolwarm')
plt.title('Space titanic had Transported? \n')
plt.ylabel('True')
plt.xlabel('Predicted')
plt.show()

1종오류/2종오류 잠깐 설명

 

좋습니다. 여기서 FP가 곧 1종 오류를 나타냅니다. 보통 'False Alarm'으로 일컫어 지는 일종의 

'아닌데 잡아냈다'의 경우 입니다.

 

FN은 2종 오류입니다. 2종 오류는 'Missing'으로 기억해두시면 좋겠네요.

'맞는데 놓쳤다'의 경우입니다.

 

실제로 FN과 FP가 혼동되는 경우가 많죠?

False Negative인데 negative(음성)을 거짓으로 판단했다면 Missing이 아니지 않나? 

또한 False Positive인데 positive(양성)을 거짓으로 판단했다면 False Alarm이 아니라 다른 용어가 아닐까?

 

하지만 이 confusion matrix는 주장의 역인 귀무가설을 대상으로 판단하는 것이기 때문에 

거꾸로 생각해봐야 합니다. 귀무가설 없이는 시작이 안되는 개념인 것이죠.

 

귀무가설은 가설이 실제로 발생했냐, 발생하지 않았냐를 검증하기 위해서 세상을 뒤바꿔 보는 가정입니다.

 

그것이 현실에서 일어났다

 

이 주장은 무엇을 뜻할까요?

 

그것은 현실에서 일어나지 않았다

 

이 주장의 '역'일 것입니다.

 

A : 그것이 현실에서 일어나지 않았다

 

 

A의 주장을 여러 근거나 증거들을 통해 거짓이라고 추정할 경우 처음의 주장이 참이라고 볼 수 있게됩니다.

 

A가 거짓이면 거짓일수록, A를 깎아 내리는 설명이 많으면 많을수록, A가 제시한 모든 데이터가 거짓임을 주장할수록,

 

그것이 현실에서 일어났다

 

이 의견이 정답에 가까워지게 됩니다.

 

 

이것이 귀무가설 입니다.


귀무가설의 의도를 그림으로 한번 봐보겠습니다.

 

이 이미지의 귀무가설은 "여성이 임신하지 않았다" 입니다.

 

False Negative는 귀무가설이 Negative 인데, 그냥 보내 버리는 오류입니다.

즉 현실은 참입니다. 참을 놓쳤죠? 그래서 Missing을 내놓는 것이구요.

 

2종 오류, 그리스어로 베타 β는 알파벳 B의 뜻도 있지만 순서상 두 번째를 의미합니다.

그래서 2종 오류는 베타 오류 (β error)라고도 합니다.

 

 

이 이미지의 귀무가설은 "흑인 남성이 임신하지 않았다" 입니다.

 

False Positive는 귀무가설은 Positive인데 실제 세계에 존재한다는 주장을 내보낸 경우입니다.

현실은 거짓입니다. 근데 참으로서 존재한다고 했으니 False alarm이 헛되이 나갔을 뿐입니다.

 

1종 오류는 그리스 어로 알파 α, 첫 번째를 의미한다는 점에서 알파 오류(α error)라고도 합니다.


다시 평가지표로 돌아와보겠습니다.

 

Cross validation을 통해 데이터를 여러 회 반복하고 나누어 여러 모델을 학습합니다.

이것을 fold라고 합니다. 필자가 설정한 횟수만큼 데이터를 갈라서(fold) 부분 집합으로 만들고

나머지를 test 세트로 사용하여 학습하는 것인데요. 여기서 우리가 설정하는 횟수를 K라 합니다.

그래서 사이킷런에서도 용어를 KFold로 사용하고 있습니다.

 

 

이 이미지가 필자 생각에는 가장 좋습니다.

Cross-validation (statistics) - Wikipedia

 

Cross-validation (statistics) - Wikipedia

From Wikipedia, the free encyclopedia Statistical model validation technique Comparing the cross-validation accuracy and percent of false negative (overestimation) of five classification models. Size of bubbles represent the standard deviation of cross-val

en.wikipedia.org

 

여기서는 cross val score를 통해서 5개로 나눈 형태를 보았습니다.

 

from sklearn.model_selection import cross_val_score

scores = cross_val_score(model, X, y, cv=5)
print("교차 검증 점수: ", scores)
교차 검증 점수:  [0.81564246 0.81460674 0.84831461 0.78089888 0.85393258]

 

다음으로 F1 스코어까지 살펴보겠습니다.

 

f1 스코어는 confusion matrix에서 바로 계산하기 힘든 precision과 recall의 조화 평균을 뜻합니다.

 

2 * (precision * recall)/(precision + recall) 인데요? 

이렇게 보니 어렵네요.

 

 

위 공식입니다.

F1 score가 높으면 점수가 높다고 볼 수 있겠습니다.

 

일반적으로 사용되는 말이지만 어느 하나가 특출나게 높거나, 어느 하나만 특출나게 낮다고 해서

모델이 잘 학습되었다고 보기는 어렵습니다. 그 target의 목적이나 환경에 따라서 

잘 학습되었는지, 엉뚱하게 학습되었는지 일련의 판단을 내릴 수 있을 것입니다.

 

아래 모델은 GridSearchCV를 사용해 점수를 f1 스코어로 지정한 모델입니다.

 

# 수정
model = make_pipeline(LGBMClassifier())

gs = GridSearchCV(LGBMClassifier(), param_grid = param_grid, scoring = 'f1',
                  cv = 5, n_jobs=-1)
gs

베스트 점수는 무엇일까요?

 

gs = gs.fit(X_train, y_train)
print(f'베스트 점수 : {gs.best_score_}')
베스트 점수 : 0.7744411112300849

 

이렇게 해서, 분류 모델의 평가지표를 확인해보았습니다.

728x90