통계, IT, AI

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

머신러닝

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

Harold_Finch 2017. 2. 1. 20:54

1. 개요

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

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

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

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

- 즉, u1=w11x1+w12x2+b1이며 z1=f(u1)=1/(1+exp(u1))이다.


- x1x2U(1,1)에서 각각 독립적으로 생성한다.

- 출력 d는 아래와 같이 정의한다. 단, εN(0,0.32)를 따른다.

d={1if x2>x1+1+ε0otherwise


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

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

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

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


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

En(w)=[dnlog{y(xn;w)}+(1dn)log{1y(xn;w)}]


- w1i에 대한 미분은 다음과 같다.

En(w)w1i=(dn(1y(xn;w))xn1(1dn)y(xn;w)xn1)=(y(xi;w)dn)xn1


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

En(w)w=[En(w)w11En(w)w12]=[(y(xi;w)dn)xn1(y(xi;w)dn)xn2]En(w)b=[En(w)b1]=[y(xi;w)dn]


2. 구현

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# -*- 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. 결과

- w11, w12 그리고 b1의 추정치는 각각 -2.883, 3.842 그리고 -3.86이다.

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

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

그림 3. 추정된 경계선

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

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

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