2016년 4월 12일 화요일

Neural Network - Python

신경망(neural network)에서 backpropagation을 이용해서 학습 하는것은 input을 이용해서 output을 예측하려는 것이다.

InputsOutput
0010
1111
1011
0110

3개열의 input을 이용해서 output을 예측하는 것을 이용해 보자.

2 Layer Neural Network:

import numpy as np

def sigmo(x, deriv=False):
    if(deriv == True):
        return x*(1-x)
    return 1/(1+np.exp(-x))


X = np.array([[0,0,1],
             [1,1,1],
             [1,0,1],
             [0,1,1]])


y = np.array([[0,1,1,0]]).T

np.random.seed(1)
syn0 = np.random.random_sample((3,1))
for itr in xrange(10000): l0 = X l1 = sigmo(np.dot(l0, syn0)) l1_error = y - l1 l1_delta = l1_error * sigmo(l1,True) syn0 += np.dot(l0.T,l1_delta) print "Output after training :"
print l1


Output after training :
[[ 0.00966449]
 [ 0.99211957]
 [ 0.99358898]
 [ 0.00786506]]


VariableDefinition
Xinput dataset 행렬
yOutput dataset 행렬
l0첫번째 Layer, input data에 해당
l1두번째 Layer, hidden layer라고 부르는 것
syn0시냅스0, weights값의 첫번째 Layer, l0과 l1을 연결시켜 줌
*두 벡터 사이즈가 동일하다면, 벡터의 원소들 간에서 1대1 연산을 하여 결과값도 동일한 벡터 사이즈를 갖는 곱셈 연산
-두 벡터 사이즈가 동일하다면, 벡터의 원소들 간에서 1대1 연산을 하여 결과값도 동일한 벡터 사이즈를 갖는 뺄셈 연산
x.dot(y)x와 y가 벡터라면, 벡터의 곱연산. 만약 둘다 행렬이라면, 행렬의 곱. 만약 하나만 행렬이라면, 벡터와 행렬의 곱

위의 결과 "Output after training"을 보면, 뭔가 작동하여 결과가 나왔다. 아래의 설명을 보기 전에 직관적으로 코드가 어떻게 구성되어 있는지, 사이사이의 값들은 어떻게 변하는지 직접 실행해 보길 바란다.

예를 들어,
print y 
print syn0
print l1
print l1_error 

등의 값을 확인해 보기 바란다. xrange(1), xrange(10) 등의 값으로 반복 횟수를 줄여서도 확인해 보기 바란다.

code 설명 : 
def sigmo(x, deriv=False):  
--> sigmoid 함수를 정의한다. Sigmoid 함수는 0과 1사이의 값이다. 이 값은 확률값을 구하는데 사용된다. (누적확률분포(여기서는 Sigmoid 함수)와 uniform 분표(여기서는 y 값(0~1사이)을 이용하면 확률 분포를 컴퓨터를 이용하여 구현할 수 있다. )

if(deriv == True):
    return x*(1-x)
--> sigmoid 를 미분하면 나오는 값 (deviv == True 일때)
return 1/(1+np.exp(-x))
--> sigmoid 함수 값

X = np.array([[0,0,1],  
                 [1,1,1],     
                 [1,0,1],
                 [0,1,1]]) 
--> 3개의 input nodes가 있는, 4개의 training 샘플을 초기화, 행렬의 형태로 보면, 4 X 3 행렬
y = np.array([[0,1,1,0]]).T  
--> output. numpy의 transpose(행렬 변환)를 이용하여, 1 X 4 행렬(벡터)를 4 X 1 행렬(벡터)로 변환 시킴.
input과 output을 연결시켜줄 스냅시스는 3 X 4 행렬이어야 하는 것을 알 수 있다.

np.random.seed(1)
--> Rnadom number 생성을 위한 seed를 1로 고정 시킴. seed값을 변경하지 않으면, 항상 동일한 Random number가 성생됨. (컴퓨터의 Random number는 일정한 수식에 의해서 생성된다. 이 수식에 입력되는 초기 값을 seed라고 한다. 즉 seed 값이 같으면, 동일한 Random number가 생성되는 것이다.)

syn0 = np.random.random_sample((3,1))
--> random 함수는 [0.0, 1.0)의 범위에서 생성되는 uniform 분포이다. 그 값을 3 X 1 형태의 array 에 넣어 준다. 이것은 weight matrix이다. 우리의 간단한 예제는 3개의 input과 1개의 output이 있다. 또한 4X3(input)행렬과 4X1(output)행렬의 연결시켜 주기 위해서는 3X1(syn0)이 필요한 것이다. 우리는 이것을 시냅스0라고 부를 것이다.
이 간단한 예제에서 neural network은 syn0이다. l0(layer0), l1(layer1)은 input과 output값이고, syn0만 hidden layer로써, 모든 learning이 저장된다. (참고, backpropagation)

 l1 = sigmo(np.dot(l0, syn0)) 
--> for 반복문 안에서, 먼저 l0와 syn0 를 행렬 dot 연산을 한다. 위에서 살펴본 것처럼, l0 = X이므로, np.dot(l0, syn0)의 결과는  (4X3) X (3X1) 형태의 형렬 연상이다. 결과는 4X1 형태의 행렬이 된다. 즉 4X1형태의 연산 결과가 sigmo 함수로 전달되고, 그 결과가 l1에 저장된다. sigmo함수 선언에서 deriv==False로 줘놨기 때문에 기본적으로 False값이 전달된다.

l1_error = y - l1
--> y(4X1)와 l1(4X1)을 동일한 형식을 갖고 있으므로, 행렬의 뺄셈을 할 수 있다. 그 차이값(y와 l1의 차이)을 l1_error로 저장하였고, 그 값다 4X1 행렬 형태이다. learning(예측)결과와 output(실제) 값을 비교하여 error값을 계산한다.

l1_delta = l1_error * sigmo(l1,True)
syn0 += np.dot(l0.T,l1_delta)

--> 아래 결과를 보면, for 반복문이 진행되면서, l1, l1_error, sigmo(l1, True), l1_delta 값이 어떻게 변해 가는지 확인할 수 있다.

0 -l1 : 예측값 (for문의 0번째 iteration)
[[ 0.50002859]
 [ 0.75721315]
 [ 0.60279781]
 [ 0.67270365]]
l1_error : (y - l1)의 결과, 실제값과 예측값의 차이
[[-0.50002859]
 [ 0.24278685]
 [ 0.39720219]
 [-0.67270365]]
sigmo(l1,True) :  위의 l1에서의 sigmoid 함수의 기울기
[[ 0.25      ]
 [ 0.1838414 ]
 [ 0.23943261]
 [ 0.22017345]]
l1_delta : l1_error에  sigmo(l1,True) 를 곱한 값
[[-0.12500715]
 [ 0.04463427]
 [ 0.09510316]
 [-0.14811148]]
syn0 :
[[ 0.55675944]
 [ 0.61684728]
 [-0.13326682]]
1 -l1 : (for문의 1번째 iteration)
[[ 0.46673252]
 [ 0.73891558]
 [ 0.6043187 ]
 [ 0.61859299]]
l1_error
[[-0.46673252]
 [ 0.26108442]
 [ 0.3956813 ]
 [-0.61859299]]
sigmo(l1,True) :
[[ 0.24889327]
 [ 0.19291934]
 [ 0.23911761]
 [ 0.2359357 ]]
l1_delta
[[-0.11616658]
 [ 0.05036823]
 [ 0.09461437]
 [-0.14594817]]
syn0
[[ 0.70174204]
 [ 0.52126735]
 [-0.25039898]]
2 -l1
[[ 0.4377253 ]
 [ 0.7256395 ]
 [ 0.61095851]
 [ 0.56730608]]
l1_error
[[-0.4377253 ]
 [ 0.2743605 ]
 [ 0.38904149]
 [-0.56730608]]
sigmo(l1,True) :
[[ 0.24612186]
 [ 0.19908682]
 [ 0.23768821]
 [ 0.24546989]]
l1_delta
[[-0.10773377]
 [ 0.05462156]
 [ 0.09247057]
 [-0.13925656]]
syn0
[[ 0.84883417]
 [ 0.43663234]
 [-0.35029717]]
3 -l1
[[ 0.41331036]
 [ 0.71812285]
 [ 0.62211546]
 [ 0.5215704 ]]
l1_error
[[-0.41331036]
 [ 0.28187715]
 [ 0.37788454]
 [-0.5215704 ]]
sigmo(l1,True) :
[[ 0.24248491]
 [ 0.20242242]
 [ 0.23508781]
 [ 0.24953472]]
l1_delta
[[-0.10022152]
 [ 0.05705825]
 [ 0.08883605]
 [-0.13014992]]
syn0
[[ 0.99472848]
 [ 0.36354068]
 [-0.43477431]]


작성중,
참고 : https://iamtrask.github.io/2015/07/12/basic-python-network/



















댓글 없음:

댓글 쓰기