Classification 모델 만들기

2024. 2. 8. 20:27TIL

Classification 모델링이란?

 Classification 모델은 데이터를 다양한 클래스로 분류하는데 사용한다. Classification 모델의 종류는 이진 분류(Binary Classification, 두 개의 클래스로 분류), 다중 클래스 분류(Multiclass Classification, 3개 이상의 클래스로 분류), 다중 레이블 분류(Multilabel Classification, 한 레코드가 여러 클래스에 할당될 수 있음)가 있다. 알고리즘의 종류는 다음과 같다.

  • Logistic Regression - 이진 분류 문제에 자주 사용.
  • Decision Tree - 직관적이고 시각화하기 쉬움.
  • Random Forests 
  • Support Vector Machines - 복잡한 분류 문제에 효과적.
  • Deep Learning - 고급 분류 문제, 특히 이미지 및 음성 인식에 사용
Classification 모델 종류
  • Logistic Regression: binary classification(예: 공부 시간에 따른 시험 합격 예측)
  • Softmax Regression: multi-label classification(예: 공부 시간에 따른 학점 예측)
  • Support Vector Machine: Feature space를 변환하여 분류 작업 수행
  • Decision Tree: Regression으로도 사용 가능, Feature의 중요도가 나오고 동작방식을 상대적으로 이해하고 설명 가능
Classification 사용법 학습

Confusion Matrix

  • Precision = TP / (TP+FP)
    • spam으로 예측된 리뷰 중 얼마나 실제로 spam인가? 
  • Recall = TP / (TP+FN)
    • : 실제 spam중의 얼마나 spam으로 예측되었나?
  • Accuracy: (TP + TN)/(TP + TN + FP + FN)

 스팸리뷰 Classification의 경우 True Positive는 스팸리뷰를 스팸리뷰라 판정하는 것(TPR: TP/(TP+FN))이고 False Positive는 정상리뷰를 스팸리뷰라 판정하는 것(FPR: FP/(FP+TN))이다. 모든 리뷰를 스팸이라고 판정하면 TPR은 100%이지만 FPR도 100%이다. 모든 리뷰를 정상이라고 판정한다면 FPR은 0%가 되지만 TPR도 0%이다. Binary Classifier는 확률을 리턴하는데 어떤 threshold를 사용할지 결정하는데 사용하는 것이 ROC커브와 AUC이다. 

 

 ROC(Receiver Operating Characteristic) 커브는 Binary classifier의 정확도를 보기위한 용도로 사용한다. True Positive Rate을 Y축으로 스팸 판정했을 때 맞을 확률 , False Positive Rate을 X축으로 스팸이 아닌 것들을 스팸 판정하는 확률이 그려진 그래프이다. 이 그래프를 통해 TPR(높게)과 FPR(낮게)을 최적의 위치에서 조정한다. 그래프에서 ROC 커브 밑의 면적을 계산한 것이 AUC(Area Under the Curve)이다. AUC는 0부터 1 사이의 값을 리턴한다. 1에 가까우면 ML 모델이 굉장히 정확함을 의미하고 0.5는 랜덤 추정에 가깝고 0에 가까우면 데이터에 무엇인가 문제가 있음을 나타낸다.

 

scikit-learn 학습 1

 scikit-learn으로 머신러닝을 하는 큰 흐름은 먼저 머신러닝 알고리즘과 모델 성과 지표를 결정한다. 그리고 데이터셋 로딩하고 데이터셋을 훈련 데이터와 테스트 데이터로 분리한다. 훈련 데이터를 전처리하고 훈련 데이터로 모델을 빌딩한다. 다음으로 테스트 데이터를 전처리한다. 레이블 정보 제외 테스트 데이터를 모델에 입력으로 지정하고 결과물과 정답 레이블을 비교하여 성과를 계산한다. 

 

 피쳐 추출과 변환은 피쳐 값들을 모델 훈련에 적합한 형태로 바꾸는 것을 지칭한다. 예를 들어 카테고리를 숫자로 변환하는 것이다. sklearn.preprocessing 모듈 아래 여러 인코더 존재한다. OneHotEncoder는 서로 관계없는 카테고리들을 인코딩하는 경우, LabelEncoder는 레이블 필드를 인코딩하는 경우, OrdinalEncoder는 순서가 있는 카테고리들을 인코딩하는 경우에 사용한다. 

# Encoder를 사용하는 일반적인 방법
from sklearn.preprocessing import XxxEncoder
X = [['seoul'], ['tokyo'], ['beijing']]
# 방법 1
enc = XxxEncoder()
enc.fit(X)
X_enc = enc.transform(X)
# 방법 2
X_enc = XxxEncoder().fit_transform(X)

# OrdinalEncoder vs. OneHotEncoder
from sklearn.preprocessing import OrdinalEncoder
from sklearn.preprocessing import OneHotEncoder
X = [['seoul'], ['tokyo'], ['beijing']]
----
enc = OrdinalEncoder()
enc.fit(X)
enc.transform(X) # array([[1.], [2.], [0.]])
----
enc = OneHotEncoder()
enc.fit(X)
enc.transform(X) # array([[0., 1., 0.],[0., 0., 1.],[1., 0., 0.]])

# LabelEncoder
from sklearn.preprocessing import LabelEncoder
y = ['recurrence-events', 'no-recurrence-events', 'recurrence-events']
enc = LabelEncoder()
enc.fit(y)
y_labeled = enc.transform(y)
print(y_labeled) # [1, 0, 1]

 

scikit-learn 학습 2

 숫자 필드값의 범위 표준화는 숫자 필드 값의 범위를 특정 범위(예를 들면 0부터 1)로 변환하는 것이다. 피쳐 스케일링(Feature Scaling) 혹은 정규화 (Normalization)라 부른다. sklearn.preprocessing 모듈 아래 두 개의 스케일러 존재한다. StandardScaler은 각 값에서 평균을 빼고 이를 표준편차로 나누고 값의 분포를 정규분포를 따르게 변환한다. MinMaxScaler은 모든 값을 0과 1사이로 스케일하며 각 값에서 최솟값을 빼고 (최댓값-최솟값)으로 나눈다.

from sklearn.preprocessing import StandardScaler
import numpy as np

X_train = np.array([[ 1., -1., 2.],
 [ 2., 0., 0.],
 [ 0., 1., -1.]])
scaler = StandardScaler().fit(X_train)
X_scaled = scaler.transform(X_train)

 

 값이 존재하지 않는 레코드들이 존재하는 필드들의 경우 기본값을 정해서 채우는 것을 Impute한다고 부른다. sklearn.preprocessing 모듈 아래 2개의 Imputer 존재한다. SimpleImputer은 간단한 통계(평균, 중앙값, 최다 빈도 또는 상수)를 지정하여 누락된 값을 대체한다. IterativeImputer은 결측값이 있는 각 피처를 다른 피처들의 함수로 지정한다.

# SimpleImputer
import numpy as np
from sklearn.impute import SimpleImputer
# Example dataset with missing values
data = np.array([
 [1, np.nan, 3],
 [4, 3, np.nan],
 [np.nan, 6, 9],
 [8, 5, 2]
])
imputer = SimpleImputer(missing_values=np.nan, strategy='mean')
imputed_data = imputer.fit_transform(data)
print(imputed_data)

# IterativeImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
data = np.array([
 [1, np.nan, 3],
 [4, 3, np.nan],
 [np.nan, 6, 9],
 [8, 5, 2]
])
iterative_imputer = IterativeImputer(max_iter=10, random_state=0)
imputed_data = iterative_imputer.fit_transform(data)
print(imputed_data)

 

Hold out 테스트, Cross Validation (X-Fold) 테스트, 파이프라인, Classification 리포트 생성

# 모델 테스트
# Hold out 테스트: train_test_split
# Cross Validation (X-Fold) 테스트: cross_validate
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_validate

# 파이프라인 사용해보기
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

X, y = make_classification(random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

pipe = make_pipeline(StandardScaler(), LogisticRegression())
pipe.fit(X_train, y_train)
pipe.score(X_test, y_test)

# Classification 리포트 생성
from sklearn.metrics import classification_report
classificationReport = classification_report(expected, predicted)

 

Classification 모델 실습 1

 분류(Classification) 머신 러닝은 지도 학습 (Supervised Learning)의 일부로 예측 대상이 유한한 경우에 사용한다. 예를 들면 타이타닉 승객 생존 예측처럼 예측값이 두 가지인 경우(생존 혹은 비생존)이다. 이진분류 학습의 경우 AUC 혹은 F1을 성능지표로 사용한다.  예측 대상이 무한한 경우(연속되는 값) 이를 회귀(Regression) 머신 러닝이라 한다. 예를 들면 주식가격 예측이 있다. 

 

 타이타닉 승객 생존 예측은 Binary Classification을 사용할 것이다. AUC (Area Under the Curve)의 값이 중요한 성능 지표가 된다. 총 892개의 레코드로 구성되며 11개의 피쳐와 레이블 필드(생존여부)로 구성되어 있고 2번째 필드(Survived)가 예측해야 하는 승객 생존 여부이다. 

 

 성별에 따른 생존 여부를 보면 남성보다 여성이 생존자가 많았다. 승선 항구에 따른 생존 여부는 S등급이 사망자가 많았다. 머신러닝 알고리즘들은 숫자타입 밖에 이해하지 못하므로 성별을 numpy의 where를 이용해 숫자 타입으로 변환하였다. 각 필드마다 상관 관계를 보면 생존 여부와 성별, 티켓클래스, 운임의 값 순으로 상관 계수가 높았다.

 

 나이 정보가 없는 승객 레코드는 dataframe의 fillna 함수를 이용해서 평균값으로 채웠다. 일반적으로는 Scikit-Learn에서 Imputer라는 클래스를 이용한다. 숫자 값의 범위를 0과 1 혹은 -1과 1 처럼 특정 범위로 scikit-learn에서는 Scaler를 이용해 

정규화하는 것이 일반적이다. 

 

 Hold out 테스트: train_test_split로 모델 성능을 검증하였다. F1-score는 생존자 예측은 0.68이었고 사망자 예측은 0.86이었다. ROC 커브를 그려보고 AUC는 0.84이었다.

 

Classification 모델 실습 2

 미국 인디언 부족 여성들의 당뇨병 여부를 예측하는 분류 문제를 실습할 것이다. 사용된 데이터 셋은 UCI 대학에서 제공한 미국 피마 인디언 여성의 당뇨병 데이터(21세 이상)이다. '피마 인디언'은 생활 습관상의 문제로 절반이 당뇨병 환자다.

총 767개의 레코드가 제공되고 각 레코드에는 모델의 훈련을 위해 사용할 수 있는 8개의 피쳐와 최종 결과 (당뇨병 여부)가 레이블로 제공된다. 즉 마지막 Outcome이 바로 예측해야 하는 정답이 들어있는 필드가 된다. 

 

 먼저 파이썬 모듈을 로딩하고 EDA를 한다. 만약 Outcome 필드의 분포가 90:10이라면 모델은 항상 90인 레이블로 예측을 하므로 유의해야 한다. 그리고 컬럼별로 감지된 타입과 값이 존재하는 레코드의 수 등의 통계를 보고 컬럼별로 값이 존재하지 않는 레코드들이 몇 개가 있는지 살펴본다. 

 

 특히 BMI 컬럼을 예제로 다양한 분석을 수행해보았다. bmi 값을 0과 1사이의 범위로 바꾼 후를 이를 bmi_norm이라는 새로운 컬럼에 저장하였다. 이를 Scikit-learn에서는 MinMaxScaler를 사용하여 보정 가능하다. bmi_norm과 Outcome 컬럼 간 상관계수는 약 0.29이었다. 

 

 LogisticRegression 모델을 사용해 당뇨병 분류기를 구현해보았다. 학습된 모델은 홀드아웃 테스트셋을 사용해 평가했다. 모든 피쳐가 이미 숫자값이라 별다른 보정은 필요 없지만 0과 1사이로 정규화해주는 것은 필요해 보인다. 모델 검증을 해보면 당뇨병을 판별하는 F1-score가 0.65이었다. AUC는 약 0.84이다. 

 

'TIL' 카테고리의 다른 글

SageMaker 사용해보기  (1) 2024.02.11
Regression 모델 만들기  (0) 2024.02.09
데이터 EDA와 머신러닝 소개  (2) 2024.02.06
Kaggle 소개  (0) 2024.02.05
성능 평가  (0) 2024.02.02