Binary classification using Multilayer Perceptron

종속변수 \(y\)가 categorical variable이고 라벨이 0과 1 두 개인 이진분류(binary classification)문제를 Multilayer perceptron(MLP)를 이용해서 해결해보자. 이진분류 문제이기 때문에 final layer의 activation 함수는 sigmoid를 사용한다(cost 함수는 cross entropy를 사용). 다음과 같이 주어진 데이터를 분류해보자.

\(X\)는 2차원이고 \(y\)는 0 또는 1을 갖는 데이터로 같은 색은 같은 클래스를 나타낸다. 두 데이터셋 모두 로지스틱회귀분석으로 해결하기 어려운 구조이다(로지스틱회귀분석으로 분류를 하는 것은 주어진 데이터를 직선으로 분류(linear separation)하는 것이라고 생각하면 된다). 그래서 다음과 같이 Hidden layer가 2개 있는 MLP(Miltilayer Perceptron)로 분류 모델을 만들 것이다.

첫 번째 layer의 노드 수는 20, 두 번째 layer의 노드 수는 10으로 설정하였고 activation으로 모두 sigmoid를 사용하였다.

  • 입력 \(X=(x_1,x_2)\), Weight \(W^0 = \left(\begin{array}{cc} w^0_{1\, 1}\, \cdots\, w^0_{1\,20} \\ w^0_{2\, 1} \, \cdots\, w^0_{2\,20} \end{array}\right)\), bias \(b^0=(b^0_{1} \cdots b^0_{20})\)으로 첫 번째 hidden layer \(H^0\)을 다음과 같이 구성하였다.
\[H^0 = H^0(X)=f(XW^0+b^0)\]

여기서 \(f\)는 sigmoid이다. 즉, 첫 번째 hidden layer \(H^0=(h^0_1, \cdots, h^0_{20})\)은 20차원의 벡터다.

  • 두 번째 hidden layer \(H^1\)은 \(H^0\)을 입력으로하고 Weight \(W^1 = \left(\begin{array}{cc} w^1_{1\, 1}\, \cdots\, w^1_{1\,10} \\ \cdots \\ w^1_{20\, 10} \, \cdots\, w^1_{20\,10} \end{array}\right)\), bias \(b^1=(b^1_{1}, \cdots ,b^1_{10})\)으로 구성되고 식은 다음과 같다.
\[H^1 = H^1(X)=f(H^0W^1+b^1)\]

위와 같은 이유로 \(H^1=(h^1_1, \cdots,h^1_{10})\)은 10차원 벡터가 되고 마지막 \(H\)는 다음과 같다.

\[\begin{align*} H = H(X) &= f(H^1 W^2 + b^2) \\ &=f\left(\sum_{i=1}^{10} h^1_i \cdot w^2_i + b^2 \right) \end{align*}\]

여기서 \(W^2 = \left(\begin{array}{cc} w^2_{1}\, \\ \vdots \\ w^2_{10} \end{array}\right)\), bias \(b^2=b^2\).

학습데이터와 테스트데이터의 비율은 7:3으로 설정하여 학습을 진행하였다.

결과

다음은 학습에 사용하지 않은 테스트데이터의 산포도를 나타내며 민트색은 파란색(Class 0) 그룹이라고 판단한 점이고 녹색은 오렌지색(Class 1) 그룹이라고 판단한 점들이다. 빨간색 점은 실제 라벨 \(y\)를 잘못 예측한 점들을 나타낸다.


TensorFlow code

XOR data

# 라이브러리 불러오기
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split

%matplotlib inline

# XOR 데이터셋 구성
np.random.seed(20180324)

x_data = np.random.uniform(-1,1, [1000,2])
y_data = np.array([0 if x_data[i][0]*x_data[i][1] >= 0.
else 1 for i in range(len(x_data))])

plt.suptitle("Scatter Plot", fontsize=20)
plt.scatter(x_data[y_data == 0, 0], x_data[y_data == 0, 1], label="Class 0", alpha=0.7)
plt.scatter(x_data[y_data == 1, 0], x_data[y_data == 1, 1], label="Class 1", alpha=0.7)
plt.legend()

# 학습데이터와 테스트데이터 구분(비율 7:3)
x_train, x_test, y_train, y_test = \
train_test_split(x_data, y_data, test_size=0.3, random_state=777)

# MLP 모델 구성
X = tf.placeholder(tf.float32, shape=[None,2])
Y = tf.placeholder(tf.float32, shape=[None,1])

# Variables
W0 = tf.Variable(tf.random_normal([2,20]), dtype=tf.float32)
b0 = tf.Variable(tf.random_normal([20]), dtype=tf.float32)

W1 = tf.Variable(tf.random_normal([20,10]), dtype=tf.float32)
b1 = tf.Variable(tf.random_normal([10]), dtype=tf.float32)

W2 = tf.Variable(tf.random_normal([10,1]), dtype=tf.float32)
b2 = tf.Variable(tf.random_normal([1]), dtype=tf.float32)


def model(inputs):
    H0 = tf.sigmoid(tf.matmul(inputs,W0)+b0)
    H1 = tf.sigmoid(tf.matmul(H0,W1)+b1)
    H = tf.sigmoid(tf.matmul(H1,W2)+b2)

    return H

H = model(X)

loss = - tf.reduce_mean(Y * tf.log(H) + (1 - Y) * tf.log(1 - H))
train = tf.train.GradientDescentOptimizer(learning_rate=0.05).minimize(loss)

predicted = tf.cast(H > 0.5, dtype=tf.float32)
accuracy = tf.reduce_mean(tf.cast(tf.equal(predicted, Y), dtype=tf.float32))

sess = tf.Session()
sess.run(tf.global_variables_initializer())

cost_list = []
acc_list = []

iteration = 10000
for step in range(iteration):
    acc, cost, _, = sess.run([accuracy, loss,  train], feed_dict={X: x_train, Y: np.reshape(y_train, [-1,1])})
    cost_list.append(cost)
    acc_list.append(acc)
    if (step+1) % (iteration//10) ==0:
        print("Step : %i, Cost : %s  Accuracy : %s" %(step+1, cost, acc))

plt.figure(figsize=(12,10))
plt.subplot(221)
plt.xlabel("Steps")
plt.title("Cost", fontsize=20)
_ = plt.plot(cost_list, "c")

plt.subplot(222)
plt.xlabel("Steps")
plt.title("Accuracy", fontsize=20)
_ = plt.plot(acc_list, "k--")

# 학습한 모델을 테스트 데이터에 적용
Hypothesis = sess.run(H, feed_dict={X: x_test})

plt.subplot(223)
plt.title("Result", fontsize=20)
for i, j in enumerate(Hypothesis):
    if j > 0.5 :
        _ = plt.plot(x_test[i][0], x_test[i][1], "yo", alpha=0.7)
    else :
        _ = plt.plot(x_test[i][0], x_test[i][1], "co", alpha=0.7)
for i in range(len(Hypothesis)):
    if np.around(Hypothesis)[i] != y_test[i]:
        _ = plt.plot(x_test[i][0], x_test[i][1], "ro")

Moon data

sklearn을 이용하여 Moon 데이터셋을 만들 수 있고 동일한 MLP 모델에 적용 할 수 있다.

# Moon data
x_moon, y_moon = make_moons(n_samples=1000, shuffle=True, noise=0.15, random_state=10)

plt.suptitle("Scatter Plot", fontsize=20)
plt.scatter(x_moon[y_moon == 0, 0], x_moon[y_moon == 0, 1], label="Class 0", alpha=0.7)
plt.scatter(x_moon[y_moon == 1, 0], x_moon[y_moon == 1, 1], label="Class 1", alpha=0.7)
plt.legend(fontsize=15)

x_train, x_test, y_train, y_test = \
train_test_split(x_moon, y_moon, test_size=0.3, random_state=777)