통계, IT, AI

딥러닝: 화풍을 모방하기 (5) - 연습: 이진 분류 문제 본문

머신러닝

딥러닝: 화풍을 모방하기 (5) - 연습: 이진 분류 문제

Harold_Finch 2017. 2. 1. 20:54

1. 개요

- 변수가 2개이며 종속변수가 이진형인 경우를 생각해보자. 신경망은 그림 1과 같다.

그림 1. 두개의 입력을 갖는 단층 신경망

- 훈련데이터의 수는 200으로 한다. 

- 출력이 이진형이므로 활성함수는 sigmoid로 한다. 

- 즉, \(u_1=w_{11}x_1+w_{12}x_2+b_1\)이며 \(z_1=f(u_1)=1/(1+exp(-u_1))\)이다.


- \(x_1\)과 \(x_2\)는 \(U(-1,1)\)에서 각각 독립적으로 생성한다.

- 출력 \(d\)는 아래와 같이 정의한다. 단, \(\varepsilon\)은 \(N(0, 0.3^2)\)를 따른다.

\begin{eqnarray}d =  \begin{cases}    1 & \text{if } x_2 > x_1 + 1 + \varepsilon \\    0 & \text{otherwise} \end{cases}\end{eqnarray}


- 데이터의 형태는 그림 2와 같다. 붉은 선은 분류의 기준이며 추정해야 할 파라미터이다. 붉은 점은 \(d=1\)이며 파란 점은 \(d=0\)이다.

그림 2. 훈련데이터의 분포

- 미니배치를 사용하며 1개의 배치는 20개의 훈련데이터로 하고 총 반복횟수는 20000으로 한다.

- 테스트 데이터는 훈련데이터에서 임의로 20개를 골라 사용한다.


- \(n\)번째 데이터의 오차함수는 아래와 같다. 

$$E_n(\boldsymbol{w})=-\left[ d_n log\left\{ y(x_n;\boldsymbol{w})\right\} + (1-d_n)log\left\{ 1-y(x_n;\boldsymbol{w})\right\}  \right]$$


- \(w_{1i}\)에 대한 미분은 다음과 같다.

$$\begin{eqnarray} \frac{\partial E_n(\boldsymbol{w})}{\partial w_{1i}}&=&-\left(d_n(1-y(x_n;\boldsymbol{w}))x_{n1}-(1-d_n)y(x_n;\boldsymbol{w})x_{n1} \right ) =(y(x_i;\boldsymbol{w})-d_n)x_{n1} \end{eqnarray}$$


- 따라서 파라미터에 대한 미분이 아래와 같다.

$$\begin{eqnarray}\frac{\partial E_n(\boldsymbol{w})}{\partial \boldsymbol{w}}&=&\begin{bmatrix} \frac{\partial E_n(\boldsymbol{w})}{\partial w_{11}} \\ \frac{\partial E_n(\boldsymbol{w})}{\partial w_{12}} \end{bmatrix} =\begin{bmatrix}(y(x_i;\boldsymbol{w})-d_n)x_{n1} \\ (y(x_i;\boldsymbol{w})-d_n)x_{n2} \end{bmatrix} \\ \frac{\partial E_n(\boldsymbol{w})}{\partial \boldsymbol{b}}&=&\begin{bmatrix} \frac{\partial E_n(\boldsymbol{w})}{\partial b_{1}} \end{bmatrix} =\begin{bmatrix}y(x_i;\boldsymbol{w})-d_n \end{bmatrix}\end{eqnarray}$$


2. 구현

python으로 위의 신경망을 구현한다.

# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import datetime as dt

""" GENERAL SETTING """
np.random.seed(0)
enable_visualization = True


""" DATA """
N = 200
X = np.column_stack((np.random.uniform(-1, 1, N), np.random.uniform(-1, 1, N)))  # x_1. x_2
d = np.asmatrix(X[:,1] > X[:,0] + 1 + np.random.normal(0, 0.3, N), dtype=float).reshape((N,1))
p = X.shape[1]

if enable_visualization:
    plt.scatter(X[:,0], X[:,1], color=[c==1 and 'red' or 'blue' for c in d.A1])
    plt.plot([-1,0],[0,1],color='red')
    plt.show()


""" FUNCTIONS """
def activate(u):
    return 1/(1+np.exp(-u))

def feedforward(X, w, b, activate_function=activate):
    X_ = np.column_stack(([1]*X.shape[0], X))
    w_ = np.vstack((b, w))
    return activate_function(X_.dot(w_))

def error(X, w, b, d, feedforward_function=feedforward):
    y = feedforward_function(X, w, b)
    return -np.sum(np.multiply(d, np.log(y)) + np.multiply(1-d, 1-np.log(y)))


""" PARAMETER & OPTIMIZATION SETTING """
w, b = np.zeros((p, 1)), np.zeros((1, 1))
epsilon = 0.001
batch_size, epoch_size = 20, 20000

index_list = np.arange(N)
np.random.shuffle(index_list)

test_x, test_d = X[index_list[0:20],], d[index_list[0:20],]
error_history = [error(test_x, w, b, test_d)]


""" OPTIMIZATION """
for epoch in range(epoch_size):
    np.random.shuffle(index_list)

    if epoch % 5000 == 0:
        print('epoch: ', epoch, ', ', dt.datetime.now())

    for i in range(N//batch_size):
        selected_index_list = index_list[(i*batch_size):((i+1)*batch_size)]
        training_x, training_d = X[selected_index_list,], d[selected_index_list,]

        # calculate nabla E
        der = feedforward(training_x, w, b) - training_d
        der_w, der_b = np.mean(np.diag(der.A1).dot(training_x), axis=0), np.mean(der, axis=0)
        der_w, der_b = der_w.reshape((p, 1)), der_b.reshape((1, 1))

        w -= epsilon*der_w
        b -= epsilon*der_b

    error_history.append(error(test_x, w, b, test_d))


""" RESULT """
print('b: ', b)
print('w: ', w)

if enable_visualization:
    plt.scatter(X[:,0], X[:,1], color=[c==1 and 'red' or 'blue' for c in d.A1])
    plt.plot([-1,0],[0,1],color='red')
    plt.plot([-b[0,0]/w[0,0],0], [0,-b[0,0]/w[1,0]], color='green')
    plt.show()

    plt.plot(error_history)
    plt.show()


3. 결과

- \(w_{11}\), \(w_{12}\) 그리고 \(b_{1}\)의 추정치는 각각 -2.883, 3.842 그리고 -3.86이다.

- 하지만 경계선을 결정하는 파라미터는 무한하게 많으므로 값보다는 값의 비에 의미가 있다. 

- 추정치로 구한 경계선은 그림 3의 초록색 선과 같고 테스트 오차의 경향은 그림 4와 같다.

그림 3. 추정된 경계선

그림 4. 테스트 오차의 경향

- 이전 포스팅과는 다르게 오차가 계속 감소함을 볼 수 있다. 파라미터의 추정치는 무한하게 많기 때문에 테스트 오차 또한 계속 변할 수 있기 때문이다. 하지만 경계선은 변하지 않는다.

- 반복 횟수를 20,000으로 설정한 것은 trial and error로 구한 것으로 다분히 임의적인 결정이다. 차후에는 어떠한 기준으로 계산을 멈춰야 하는지에 대하여 고민해야 한다.



Comments