본문 바로가기

Theory/DeepLearning

[Keras] 붓꽃 Iris 데이터 분류해보기 Iris classification using Keras

최근 저는 케라스를 이용해서 선형 회귀도 살짝 공부할 겸, 다변수 입력에 대한 선형회귀 문제로 혈중 지방함량이라는 데이터를 어디서 구해서 케라스를 이용한 다변수 함수에 대한 선형회귀를 학습하고, 그리고 머신러닝을 학습하는 과정에서 필수라고 하는 MNIST 데이터 셋을 케라스를 이용해서 CNN으로 구분하는 학습을 해 보았습니다. 그리고 그렇게 CNN을 이용해서 학습한 모델을 저장한 후에, 그 모델만 다시 읽어와서 내가 손으로 쓴 글씨에 테스트도 해 보았네요^^ 그러다가, 이진 분류도 학습할 겸, 케라스를 이용해서 타이타닉 생존자도 예측을 해보았습니다.

오늘은 그 흐름에서 살짝 뒤로 가는 거긴 합니다만, 또 하나의 유명한 예제인 붓꽃 Iris 분류를 이야기하려고 합니다. 너무나 유명한 통계적 예제라서 통계와 관련된 모듈, 프로그램에서는 아예 이 데이터가 포함되어 있더라구요. 현재 쉽게 구할 수 있는 붓꽃 데이터는 150개 양입니다. 현대 통계학에서 위대한 업적을 많이 남기셨다고 하는 Roland Fisher라는 분이 수집한 데이터인데요.

출처 : 위키백과 아이리스 항목

뭐 일단, Iris가 어떤 아이인지는 알아야죠. 이쁘네요^^. 프랑스의 국화라고 하던데... 전 왜 이 꽃을 평상시 본적이 없다고 기억하는지 모르지만 말이죠^^. 여하튼 이 꽃은 꽃받침(Sepal)과 꽃잎(Petal)의 길이와 폭을 가지고 세 개의 종을 분류하는 예제로 사용됩니다.^^

출처 : http://mirlab.org/의 아이리스 항목

꽃받침과 꽃잎의 길이로와 폭으로 구분할 수 있는 종인 모양입니다. 뭐 꽃을 잘 모르니ㅠㅠ.

출처 : http://mirlab.org/의 아이리스 항목

구분하는 세 개의 종은 위 그림처럼 Setosa, Vericolor, Virginica입니다. 꽃에 정통하지 않은 경우라도 쉽게 저 세개의 종을 분류하기 위해서는 앞서 이야기한 네 종류의 데이터(꽃받침과 꽃잎의 가로 세로 길이)를 이용해서 분류할 수 있어야겠죠.

출처 : http://articles.concreteinteractive.com/

꽃잎의 가로 세로 길이만 가지고도 Setosa는 확실히 잘 구분이 가는 모양입니다. 그러나 Versicolor와 Virginica는 혼돈되는 데이터가 있는 것 같습니다. 좀 더 많은 자료를 찾아보면 머신러닝에서 아주 심플하게 kNN을 이용해서 분류하는 예제가 많이 보입니다. 그러나 저는 Keras도 함께 학습하는 것이 목적이니.. 오늘은 Keras를 이용해서 딥러닝으로 분류를 해볼려고 합니다.

%matplotlib inline
import seaborn as sns
import pandas as pd
import numpy as np

sns.set(style="ticks", color_codes=True)
iris = sns.load_dataset("iris")
g = sns.pairplot(iris, hue="species", palette="husl")

먼저 필요한 모듈을 먼저 읽고... Seaborn이 제공하는 Iris 데이터를 가져옵니다.

Pairplot으로 그려보면 저렇게 나오네요. Petal의 길이와 폭(제일 아랫줄 세번째)이 아까 나온 그림이구요. 확실히 녹색과 청색 , 즉, Versicolor, Virginica를 딱 구분할 방법은 없어 보입니다. 뭐 그러니까 이렇게 붓꽃의 분류가 중요한 예제로 자리 잡았겠죠...

iris.info()

데이터의 개요를 한 번 보면...

150개 데이터와 4개의 특성, 그리고 종의 이름이 있습니다.

iris['species'].unique()

종의 특성만 보면...

방금 이야기한 세 개의 종이 나타납니다.

from sklearn.preprocessing import LabelEncoder

X = iris.iloc[:,0:4].values
y = iris.iloc[:,4].values

encoder =  LabelEncoder()
y1 = encoder.fit_transform(y)
Y = pd.get_dummies(y1).values
Y

위 코드는 꽤 유용한 역할을 하는데요. 문자열로된 이름에 번호를 붙이고(LabelEncoder) 그 번호를 원핫인코딩 방식으로 펼쳐줍니다.

그럼 저렇게 결과가 나오는거죠.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, Y, 
                                                    test_size=0.2, 
                                                    random_state=1) 
X_train.shape, X_test.shape, y_train.shape, y_test.shape

이제 위 코드로 데이터를 학습용과 훈련용으로 나누고...

음.. 120개의 학습데이터와 30개의 테스트 데이터로 나누어졌네요. 이제 모델을 만들어야죠^^ 이 예제에서 사용된 Keras의 버전은 2.1.6입니다.^^

from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import Adam

model = Sequential()

model.add(Dense(64,input_shape=(4,),activation='relu'))
model.add(Dense(64,activation='relu'))
model.add(Dense(3,activation='softmax'))

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

model.summary()

심플하게~ 잡았습니다.

구조는 저렇구요^^ 대망의~

hist = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=100)

훈련... 뭐 너무나도 스몰~ 데이터라 금방 끝납니다.^^

뭐.. 한 epoch가 micro second 단위죠^^

import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(figsize=(12,8))
plt.plot(hist.history['loss'])
plt.plot(hist.history['val_loss'])
plt.plot(hist.history['acc'])
plt.plot(hist.history['val_acc'])
plt.legend(['loss','val_loss', 'acc','val_acc'])
plt.grid()
plt.show()

이제... Accuracy와 loss를 확인해보죠.

넵.. 잘 들어간듯 합니다. 100번의 epoch를 늘린다고 뭐 뽀족하게 좋을것 같진 않습니다. 테스트 데이터에 대한 결과가 있지만,

loss, accuracy = model.evaluate(X_test, y_test)
print("Accuracy = {:.2f}".format(accuracy))

를 통해 확인해보면...

이라고 나타나네요. 97%입니다. 더 좋아지기도 하고, 뭐.. 그렇더라구요.. 그냥 여기서 멈추겠습니다. 

한가지 더 재미난 결과를 확인해보기 위해 살짝 더 알아두면 좋은 것이 있는데요.

출처 : http://scikit-learn.org/

바로, Precision과 Recall, 그리고 F1-Score입니다.

출처 : http://scikit-learn.org/

이건 꽤 직관적인 예제를 scikit-learn 공식 홈페이지에서 얻을 수 있는데요.

출처 : http://scikit-learn.org/

위 예제입니다. y_true와 y_pred를 가지고 확인한 예제인데요. precision입장에서 이야기하면, 0이라고 두 번 예측했는데, 하나가 참이었던거구요. 1이라고 한 번 예측했는데 한번도 못 맞춘거죠. 그래서 precision은 예측한 결과 중에서 참인 결과를 의미하구요. recall은 0이 하나 있는데, 한 번 이상 맞췄으니 100%이구요. 2가 세 개 있는데 두 개를 맞춰서 67%인거죠. recall은 실제 데이터 중 몇 개를 예측에 성공했는지를 보는 겁니다. 그 둘을 복합적으로 점수(?)로 환산하는 장치가 F-score인거죠. 이걸 scikit-learn에서 잘 함수로 제공해 주네요^^

import numpy as np
from sklearn.metrics import classification_report,confusion_matrix

y_pred = model.predict(X_test)
y_test_class = np.argmax(y_test,axis=1)
y_pred_class = np.argmax(y_pred,axis=1)

print(classification_report(y_test_class,y_pred_class))
print(confusion_matrix(y_test_class,y_pred_class))

이렇게 사용해 주면 됩니다.

이렇네요. 특히 confusion_matrix 결과에서 보면 두 번째 종 이름을 12개는 맞고, 하나는 세번째 종으로 틀렸네요. 애초 데이터를 관찰할때도, 그걸 어느정도는 예상했죠^^

test_set = np.array([[5, 2.9, 1, 0.2]])
print("Predicted target name: {}".format(
    iris['species'].unique()[model.predict_classes(test_set)]))

이번에는 임의의 데이터를 한 번 넣어서 어떻게 판단하는지 볼까요

짠~ Setosa스러운 데이터였으니 말이죠. 또, versicolor의 특성을 보면

iris.query("species == 'versicolor'")

이 종은

각 특성별로 위와 같은 특성을 가지고 있네요. 그것과 유사하게.. 

test_set = np.array([[7, 3.0, 5, 1.4]])
print("Predicted target name: {}".format(
    iris['species'].unique()[model.predict_classes(test_set)]))

로 테스트해보니...

이런 결과가 나오네요. 뭐 150개 데이터를 사용한 것 뿐이라, 또 다른 심플하고 효율적인 방법도 많지만, 처음에 이야기했듯이, Keras를 이용해서 딥러닝(을 꼭 쓸 필요는 없지만)을 공부하는 하나의 예제로 Iris 품종 구분이라는 예제를 수행해 보았습니다.^^

반응형