다중분류 알고리즘 - dajungbunlyu algolijeum

이제껏 우리는 이진분류에 관한 데이터만 다루었습니다. 12장은 레이블 데이터가 3개 이상의 범주형 데이터를 다룰 때를 위한 챕터입니다.

앞에서 딥러닝을 공부하면서 레이블의 범주가 3개 이상이면 어떤 식으로 알고리즘이 분류할지 계속 궁금했습니다.

출력층은 활성화 함수(sigmoid)가 1 or 0의 값을 뱉어내는데 과연 이럴 때는 어떻게 알고리즘을 설계하거나 작동할지가 진짜 의문이었는데 답은 간단하더군요! ㅋㅋ

그냥 출력층의 개수를 범주의 개수와 똑같이 맞춥니다!

이제 데이터를 보겠습니다. 이번 데이터는 꽃잎의 모양과 길이 등의 정보를 토대로 꽃의 3종류를 분류하는 문제입니다.

<pair plot>

pair plot은 몇개의 피쳐들에 한하여 범주형 데이터를 시각화 할때, hue를 지정해주고 그리면 굉장히 유용합니다. 범주별 특성을 한눈에 파악할 수 있습니다.

정말 버릴 피쳐가 없을 정도로 꽃잎의 종류에 따라 plot이 아주 명확하게 나오네요!

#필요한 모듈 import
import seaborn as sns
import matplotlib.pyplot as plt

sns.pairplot(df, hue='species') # hue에 범주형 피쳐를 넣어 산점도를 범주로 구분하여 시각화
다중분류 알고리즘 - dajungbunlyu algolijeum

<원-핫 인코딩>

 우리는 꽃잎의 정보들을 가지고 품종을 예측해야합니다. 하지만 품종 피쳐인 'species'가 문자열로 되어 있어요. 컴퓨터는 문자열보다는 숫자를 좋아합니다. 숫자로 바꿔주면 속도 측면에서도 유리할 수 있기 때문에 우리는 문자열을 숫자로 바꿔주는 작업을 해야합니다.

문자열로 표현된 범주형 변수를 숫자로 바꿔주면 '정수 인코딩'

0, 1 값으로 값을 변경해면 '원-핫 인코딩'이라고 합니다.

굳이 용어를 구분하는건 중요치 않지만 이번 데이터 셋은 출력층에 있어서 소프트 맥스라는 활성화 함수를 이용해야 하기 때문에 0,1로 바꿔주는 원-핫 인코딩을 수행할겁니다.

# 원-핫 인코딩

import tensorflow as tf

dataset = df.values
X = dataset[:,0:4].astype(float)
Y_obj = dataset[:,4]

from sklearn.preprocessing import LabelEncoder

e = LabelEncoder()
e.fit(Y_obj)
Y = e.transform(Y_obj)


#from tensorflow.keras.utils import np_utils

Y_encoded = tf.keras.utils.to_categorical(Y)

저는 항상 데이터를 다룰 때, pandas를 이용해서 데이터 프레임 형태로 많이 다뤘는데, 책에서는 이렇게 array배열로 많이 다루더군요. 이미 습관이 많이 베어서... 하지만 분명히 속도 측면에서 훨씬 좋을 것 같아요.

다중분류 알고리즘 - dajungbunlyu algolijeum
다중분류 알고리즘 - dajungbunlyu algolijeum

주석처리한 np_utils 모듈은 아마 버전차이 때문인지 임포트되지 않지만, to_categorical 함수는 잘 작동합니다. 신경 안써도 될 것 같아요!

원-핫 인코딩으로 Y를 만들면 끝이 아니라, 활성화 함수를 적용하기 위해서는 Y_encoded의 형태로 바꿔줘야 합니다. 이는 3가지로 분류하는 데에 각 범주들에게 확률을 개별적으로 부여하지 않고, 세 범주 확률값의 합을 1로 계산해놓기 위함입니다. 그래서 활성화 함수도 소프트맥스(softmax) 함수를 이용합니다.

아래 링크를 보면 데이터 셋도 똑같이 iris데이터를 예로 들어서 쉽게 이해할 수 있습니다. 정수 인코딩을 하게 되면 발생하는 문제점에 관한 내용도 있습니다.

https://wikidocs.net/35476

<모델 최적화>

오차 함수는 다중 분류이기 때문에 categorical_crossentropy를 이용했고, 최적화 함수는 9장에서 나왔던 adam으로 설정했습니다.

# 필요한 모듈 import
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from sklearn.preprocessing import LabelEncoder


# 모델의 설정

model = Sequential()
model.add(Dense(16,input_dim=4,activation='relu'))
model.add(Dense(3,activation='softmax'))

# 모델 컴파일

model.compile(loss='categorical_crossentropy',
             optimizer='adam',
             metrics=['accuracy'])

# 모델 실행
model.fit(X,Y_encoded, epochs=50, batch_size=1)

# 결과 출력
print("\n Accuracy: %.4f"%(model.evaluate(X, Y_encoded)[1]))
다중분류 알고리즘 - dajungbunlyu algolijeum

책이랑 같은 정확도가 나옴을 알 수 있습니다.

'다중 분류 문제 해결하기'12장도 포스팅을 마칩니다.

동삼이의 노트북

Projects

MNIST 분류 (4) - 다중 분류

동삼이 2020. 11. 23. 19:04

이전 포스팅까지 다뤘던 머신러닝 모델은 '5' 인지, '5가아님' 인지를 분류하는 이진 분류기 였다. 하지만 우리가 궁극적으로 분류해내고자 하는 것은 각 데이터가 어떤 숫자에 해당하는지를 분류해내는 것이다. 따라서 우리는 이진 분류가 아닌 다중 분류기(multiclass classifier)를 사용할 것이다. 이전에 사용했던 SGDClassifier나, RandomForestClassifier, 혹은 나이브 베이즈 분류기 등은 여러 클래스를 직접 처리할 수 있지만, 로지스틱 회귀나, SVM 같은 머신러닝 알고리즘은 이진 분류만 가능하다.

다중분류 알고리즘 - dajungbunlyu algolijeum
로지스틱 회귀

하지만 이러한 이진 분류기를 이용하여 여러 클래스를 분류할 수 있게 하는 다중 클래스 분류 기법이 있다. 예를 들어, 하나의 숫자만 구분하는 숫자별 이진 분류기 10개(0~9까지)를 훈련시켜 클래스가 10개인 숫자 이미지 분류 시스템을 만들 수 있다. 이미지를 분류할 때 각 분류기의 결정 점수 중에서 가장 높은 것을 클래스로 선택하면 되는데, 이를 OvR(one-versus-the-rest)전략이라고 부른다. (또는 one-versus-all) 

다중분류 알고리즘 - dajungbunlyu algolijeum

또 다른 전략으로는 0과 1 구별, 0과 2 구별, 1과 2 구별 ... 등과 같이 각 숫자 조합마다 이진 분류기를 훈련시키는 것이며, 이를 OvO(one-versus-one)전략이라고 한다.

다중분류 알고리즘 - dajungbunlyu algolijeum

클래스가 N개 라면 이진 분류기는 N * (N-1)/2 개가 필요하다. 이번 MNIST 분류에서는 총 45개의 분류기를 훈련시켜야 한다. 이미지 하나를 분류하려면 45개 분류기를 모두 통과시켜서 가장 많이 양성으로 분류된 클래스를 선택한다. OvO의 장점은 각 분류기의 훈련에 전체 훈련 세트 중 구별할 두 클래스에 해당하는 샘플만 필요하다는 것이다. 

일부 알고리즘은 훈련 세트의 크기에 민감해서 큰 훈련 세트에서 몇 개 분류기를 훈련시키는 것보다 작은 훈련 세트에서 많은 분류기를 훈련시키는 쪽이 빠르므로 OvO를 선호한다. 하지만 대부분은 OvR을 선호한다. 다중 클래스 분류 작업에서 이진 분류 알고리즘을 선택하면, 사이킷런이 알고리즘에 따라 자동으로 OvR, OvO를 실행한다.

다중분류 알고리즘 - dajungbunlyu algolijeum

사이킷런에 있는 서포트 벡터 머신 분류기를 통해 다중 분류기를 생성하였다. 내부에서는 사이킷런이 OvO전략을 사용하여 0부터 9까지 45개의 조합을 생성하여 이진분류기를 훈련시키고 각각의 결정 점수를 얻어서 점수가 가장 높은 클래스를 선택하였다.

다중분류 알고리즘 - dajungbunlyu algolijeum

decision_function() 메서드를 호출하여 샘플당 10개의 점수를 반환한다. 그 중 가장 높은 9.313 에 해당하는 클래스 '5'가 선택되어 머신러닝 모델이 '5'라는 클래스로 예측한 것이다. 만약 OvO가 아닌 OvR 전략을 사용하길 원한다면 사이킷런에서 OneVsOneClassifier나 OneVsRestClassifier를 사용하여 이진분류기 객체를 생성할 때 전달하면 된다. 

다중분류 알고리즘 - dajungbunlyu algolijeum

SVC모델을 OvR 전략으로 분류기를 생성하는 코드이다. SGDClassifier를 다중 분류기로 훈련시키는 것도 간단하다.

다중분류 알고리즘 - dajungbunlyu algolijeum

SGDClassifier는 직접 샘플을 다중 클래스로 분류할 수 있기 때문에 별도의 OvO나 OvR을 적용하지 않아도 된다. 위 경우 5의 해당하는 샘플데이터가 주어졌으나 3으로 예측한 것을 알 수있다. 우리는 decision_function()메소드를 통해 클래스마다의 값을 반환시켜 이러한 결과가 나타난 이유를 확인할 수 있다.

다중분류 알고리즘 - dajungbunlyu algolijeum

'3'에 해당하는 값이 1823으로 가장 높은 점수인 것을 알 수 있다. 실제 레이블 값인 '5'에 대한 점수는 -1385로 두 번째로 높게 나타난 것을 알 수 있었다.

다중분류 알고리즘 - dajungbunlyu algolijeum

교차 검증을 통해 분류기의 성능을 평가해본 결과 모든 테스트 폴드에서 85% 이상의 정확도를 얻었다. 성능을 약간 더 향상시키기 위해 스케일을 조정해보자.

다중분류 알고리즘 - dajungbunlyu algolijeum

StandardScaler를 사용하여 분류기의 성능을 89%이상으로 끌어올렸다. 실제 프로젝트라고 가정한다면 여러 모델을 시도해보고, 가장 좋은 몇 가지를 골라 GridSearchCV를 통해 하이퍼파라미터를 튜닝하고 파이프라인을 통해 자동화해야한다. 하지만 이번 프로젝트에서는 가장 좋은 모델을 찾았다고 가정하고 이 모델의 성능을 향상시킬 방법을 찾아보겠다. 그 중 하나는 다음 포스팅인 에러 분석을 통해 진행해보고자 한다.