본문 바로가기
AI / DL/엘리스 AI 데이터 분석 트랙

[AI 데이터 분석] 머신러닝 시작하기 04. 지도학습 - 분류

by bri9htstar 2022. 10. 7.

분류

주어진 입력 값이 어떤 클래스(범주, label)에 속할지에 대한 결과 값을 도출하는 알고리즘이다. 다양한 분류 알고리즘이 존재하며, 예측 목표와 데이터 유형에 따라 적용한다.

분류 문제에 다양한 머신러닝 모델을 사용하여 해결한다. 가장 간단한 모델인 의사결정나무가 있다. 간단함에도 성능도 좋고, 이후에 확장할 수 있는 모델도 많다. 앙상블 기법과 함께 고급 기법까지 나무를 통해 만들 수 있다.

트리 구조 기반 의사결정나무, 랜덤포레스트, …
확률 모델 기반 나이브 베이즈 분류기, …
결정 경계 기반 선형 분류기, 로지스틱 회귀 분류기, SVM, …
신경망 퍼셉트론, 딥러닝 모델, …

선형 회귀 식-∞ ~ +∞결괏값을 가질 수 있다. 따라서 일반적인 회귀 알고리즘은 분류 문제에 그대로 사용할 수 없다. 그렇기에 선형 회귀 식을 분류에 사용하기 위해선 적절한 변형이 필요하다.

 

의사결정나무

의사결정나무(Decision Tree)란 스무고개와 같이 특정 질문들을 통해 정답을 찾아가는 모델이다. 최상단의 뿌리 마디에서 마지막 끝 마디까지 아래 방향으로 진행한다. 예측을 할 수 있게끔 질문 구조를 만드는 거라 할 수 있다. 뿌리 마디(Root Node)와 끝 마디(Terminal Node) 사이에 있는 마디를 중간 마디(Internal Node)로 부른다. 아래 그림에선 '질문 2'가 중간 마디라고 볼 수 있다.


의사결정나무 구조

2 ~ 4 사이에만 Yes를 출력해야 될 때의 예시

2개 이상의 feature 데이터가 오는 경우가 대부분인데, 이 때도 적용 가능하다. 각각 변수에 대해 질문을 하면 된다.

2개 이상의 feature 데이터의 경우 의사결정나무

import pandas as pd
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split

# 1. sklearn에 저장된 데이터를 불러 옵니다.
X, Y = load_iris(return_X_y = True)

# 2. DataFrame으로 변환
df = pd.DataFrame(X, columns=['꽃받침 길이','꽃받침 넓이', '꽃잎 길이', '꽃잎 넓이'])
df['클래스'] = Y

X = df.drop(columns=['클래스'])
Y = df['클래스']
    
# 3. 학습용 평가용 데이터로 분리합니다
train_X, test_X, train_Y, test_Y = train_test_split(X, Y, test_size=0.2, random_state = 42)

# 4. DTmodel에 의사결정나무 모델을 초기화 하고 학습합니다
DTmodel = DecisionTreeClassifier()
DTmodel.fit(train_X, train_Y)

# 5. test_X에 대해서 예측합니다.
pred_X = DTmodel.predict(test_X)
print('test_X에 대한 예측값 : \n{}'.format(pred_X))

위의 코드를 실행했을 때 출력

참고로 학습한 결과를 출력하면 이렇게까지 구조를 만들 수 있구나, 볼 수 있다.

# 학습한 결과를 출력합니다
plt.rc('font', family='NanumBarunGothic')
fig = plt.figure(figsize=(25,20))
_ = tree.plot_tree(DTmodel, 
                   feature_names=['꽃받침 길이','꽃받침 넓이', '꽃잎 길이', '꽃잎 넓이'],  
                   class_names=['setosa', 'versicolor', 'virginica'],
                   filled=True)

네 가지 변수와, 세 종류의 붓꽃 클래스로 구성된 Iris 데이터의 의사결정나무

DTmodel = DecisionTreeClassifier() 로 초기화를 수행할 때 max_depth를 설정하여 의사결정나무의 최대 깊이를 조절할 수 있다. 깊이를 적게 하면 단순한 구조가 나오지만, 그만큼 불순도가 생겨서 정확하지 않은 모델이 된다.

import pandas as pd

# 풍속을 threshold 값에 따라 분리하는 의사결정나무 모델 함수
def binary_tree(data, threshold):
    
    yes = []
    no = []
    
    # data로부터 풍속 값마다 비교를 하기 위한 반복문
    for wind in data['풍속']:
    
        # threshold 값과 비교하여 분리합니다.
        if wind > threshold:
            yes.append(wind)
        else:
            no.append(wind)
    
    # 예측한 결과를 DataFrame 형태로 저장합니다.
    data_yes = pd.DataFrame({'풍속': yes, '예상 지연 여부': ['Yes']*len(yes)})
    data_no = pd.DataFrame({'풍속': no, '예상 지연 여부': ['No']*len(no)})
    
    return data_no.append(data_yes,ignore_index=True)

# 풍속에 따른 항공 지연 여부 데이터
Wind = [1, 1.5, 2.5, 5, 5.5, 6.5]
Delay  = ['No', 'No', 'No', 'Yes', 'Yes', 'Yes']

# 위 데이터를 DataFrame 형태로 저장합니다.
data = pd.DataFrame({'풍속': Wind, '지연 여부': Delay})
print(data,'\n')

"""
1. binary_tree 모델을 사용하여 항공 지연 여부를 예측합니다.
"""
data_pred = binary_tree(data, threshold = 4)
print(data_pred,'\n')

 

혼동 행렬(Confusion Matrix)

분류 모델의 성능을 평가하기 위해 가장 기본적인 밑바탕인 지표들을 계산하는 행렬이다.

    예측
    Positive Negative
실제 Positive True Positive (TP)

실제 Positive인 값을
Positive라고 예측 (정답)
False Negative (FN)

실제 Positive인 값을
Negative라고 예측 (2형 오류)
Negative False Positive (FP)

실제 Negative인 값을
Positive라고 예측 (1형 오류)
True Negative (TN)

실제 Negative인 값을
Negative라고 예측 (정답)
import numpy as np
import seaborn as sns
from matplotlib import pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix

# sklearn에 저장된 데이터를 불러 옵니다.
X, Y = load_breast_cancer(return_X_y = True)
X = np.array(X)
Y = np.array(Y)

# 데이터 정보를 출력합니다
print('전체 샘플 개수: ',len(X))
print('X의 feature 개수: ',len(X[0]))

# 학습용 평가용 데이터로 분리합니다
train_X, test_X, train_Y, test_Y = train_test_split(X, Y, test_size=0.2, random_state = 42)

# 분리된 평가용 데이터 정보를 출력합니다
print('평가용 샘플 개수: ',len(test_Y))
print('클래스 0인 평가용 샘플 개수: ',len(test_Y)-sum(test_Y))
print('클래스 1인 평가용 샘플 개수: ',sum(test_Y),'\n')

# DTmodel에 의사결정나무 모델을 초기화 하고 학습합니다
DTmodel = DecisionTreeClassifier()
DTmodel.fit(train_X, train_Y)

# test_X을 바탕으로 예측한 값을 저장합니다
y_pred = DTmodel.predict(test_X)

"""
1. 혼동 행렬을 계산합니다
"""
cm = confusion_matrix(test_Y, y_pred) # confusion_matrix() 를 활용하여 혼동행렬을 계산합니다.
print('Confusion Matrix : \n {}'.format(cm))

# 혼동 행렬을 출력합니다
fig = plt.figure(figsize=(5,5))
ax = sns.heatmap(cm, annot=True)
ax.set(title='Confusion Matrix',
            ylabel='True label',
            xlabel='Predicted label')
fig.savefig("decistion_tree.png")

위 코드 실행 결과

테스트 데이터의 혼동 행렬

정확도(Accuracy)

전체 데이터 중에서 제대로 분류된 데이터의 비율로, 모델이 얼마나 정확하게 분류하는지를 나타낸다. 일반적으로 분류 모델의 주요 평가 방법으로 사용되나 클래시 비율이 불균형 할 경우 평가 지표의 신뢰성을 잃을 가능성이 있다. 전체 대비 잘 맞힌 데이터의 비율이다!

정확도 식. 분모는 전체 데이터이다.

from sklearn.tree import DecisionTreeClassifier

DTmodel = DecisionTreeClassifier()

acc_train = DTmodel.score(train_X, train_Y) # score() 함수를 활용하여 정확도를 계산합니다.
acc_test = DTmodel.score(test_X, test_Y)

# 정확도를 출력합니다.
print('train_X Accuracy: %f' % (acc_train))
print('test_X Accuracy: %f' % (acc_test))


위 코드 실행 결과

훈련 데이터의 혼동 행렬. 정확도가 1이 나올 수밖에 없다.

정밀도(Precision)

모델이 Positive라고 분류한 데이터 중에서 실제로 Positive인 데이터의 비율이다. Negative가 중요한 경우인데, 즉 실제로 Negative인 데이터를 Positive라고 판단(FP)하면 안되는 경우 사용되는 지표이다.

정밀도 식

재현율(Recall, TPR)

실제로 Positive인 데이터 중에서 모델이 Positive로 분류한 데이터의 비율이다. Positive가 중요한 경우인데, 즉 실제로 데이터를 Negative라고 판단하면 안되는 경우 사용되는 지표이다.

재현율 식


분류 목적에 따라 다양한 지표를 계산하여 평가한다. 분류 결과를 전체적으로 보고 싶다면 혼동 행렬을, 정답을 얼마나 잘 맞혔는지 보고 싶다면 정확도를, FP 또는 FN의 중요도가 높다면 정밀도와 재현율을 계산하면 된다.

from sklearn.metrics import precision_score
from sklearn.metrics import recall_score

"""
1. 정밀도를 계산합니다.
"""
precision_train = precision_score(train_Y, y_pred_train) # precision_score()를 활용하여 정밀도를 계산합니다.
precision_test = precision_score(test_Y, y_pred_test)

# 정밀도를 출력합니다.
print('train_X Precision: %f' % (precision_train))
print('test_X Precision: %f' % (precision_test),'\n')

"""
2. 재현율을 계산합니다.
"""
recall_train = recall_score(train_Y, y_pred_train) # recall_score()를 활용하여 재현율을 계산합니다.
recall_test = recall_score(test_Y, y_pred_test)

# 재현율을 출력합니다.
print('train_X Recall: %f' % (recall_train))
print('test_X Recall: %f' % (recall_test))

위 코드 실행 결과

FPR(False Positive Rate)

실제로 Negative인 데이터 중에서 모델이 Positive로 잘못 분류한 데이터의 비율이다. FPR이 높으면 해당 데이터에 대한 신뢰도를 떨어뜨리는 요인이 될 수 있다.

FPR 식

게임에서 비정상 사용자 검출 시 FPR이 높다. = 정상 사용자를 비정상 사용자로 검출하는 경우가 많다.이 때 비정상 사용자에 대해서 계정정지 등 페널티를 부여할 경우 선의의 사용자가 피해를 입게 될 가능성이 높다.→ 이는 곧 게임에 대한 충성도를 떨어뜨리는 계기가 될 수 있다.

ROC Curve와 AUC

X축을 FPR(False Positive Rate), Y축을 TPR(True Positive Rate, Recall)로 두고 시각화한 그래프이다.

ROC Curve와 AUC

ROC Curve 아래 면적인 AUC(Area Under Curve)를 이용해 성능을 평가한다.


실제 Negative 데이터에 중요도가 높다면 FPR, FPR과 TPR의 변화에 따른 모델의 전체적인 성능이 중요하다면 ROC Curve 및 AUC를 지표로 계산하여 평가하면 된다.