11줄의 파이썬 코드로 뉴럴 네트워크를 만들어보자.

이 글은 머신러닝 블로그 iamtrask 저자 Trask의 허락을 받아 A Neural Network in 11 lines of Python (Part 1)의 듀토리얼 글을 번역한 것입니다. 원문도 꼭 읽어보셨으면 합니다. 그리고 part2 13lines 번역도 거의 마쳐가고 있습니다. 1주일 내로 업로드 하도록 하겠습니다.

이 글의 jupyter notebook은 곧 제 깃헙에 올리도록 하겠습니다. * 9월 30일 수정 - 링크 ***

요약: 저는 가지고 놀 수 있는 코드로 배우는 것이 가장 좋았습니다. 이 듀토리얼은 매우 단순한 toy 예시를 간단하게 파이썬으로 향상시켜보면서 역전파법(backpropagation)에 대해 알아봅니다.

Edit: 몇몇 사람들이 이어지는 글에 대해 물어보시는 데 하나 더 쓸 생각입니다. @iamtrask 트윗을 통해서 완성되면 알려드리도록 하겠습니다. 재미있게 읽었다면 팔로우 부담없이 하시고, 모든 피드백에 대해 감사드립니다.

코드부터 보면:


import numpy as np

X = np.array([ [0,0,1],
               [0,1,1],
               [1,0,1],
               [1,1,1,] ])
y = np.array([[0,1,1,0]]).T
syn0 = 2*np.random.random((3,4)) -1 
syn1 = 2*np.random.random((4,1)) -1
for j in range(60000):
    l1 = 1 / (1+np.exp(-(np.dot(X, syn0))))
    l2 = 1 / (1+np.exp(-(np.dot(l1, syn1))))
    l2_delta = (y - l2)*(l2*(1-l2))
    l1_delta = l2_delta.dot(syn1.T) * (l1*(1-l1))
    
    syn1 += l1.T.dot(l2_delta)
    syn0 += X.T.dot(l1_delta)
    

그러나, 너무 간결합니다. 하나하나 몇몇 간단한 부분으로 쪼개봅시다.

단계 1 : 작은 토이 네트워크

역전파법을 이용한 뉴럴네트워크는 입력값을 활용해서 결과값을 예측합니다.

입력값들 결과값
0 0 1 0
1 1 1 1
1 0 1 1
0 1 1 0

3열들의 입력값들을 활용해서 결과값 열을 예측합니다. 간단하게 입력값들과 결과값들의 통계를 측정함으로써 이 문제를 해결 할 수 있습니다. 그렇게 본다면, 가장 왼쪽에 있는 입력값 열이 결과값 열과 완벽히 연관되어 있음을 알 수 있습니다. 역전파법은 이런 모델을 만들기 위해 통계를 측정할 수 있는 가장 간단한 폼입니다. 바로 넘어가서 사용해보록 하겠습니다.


import numpy as np # 1번째 줄

#sigmoid function

def nonlin(x, deriv=False): # 4번째 줄
	if (deriv==True): # 5번쨰 줄
		return x*(1-x)
	return 1/(1+np.exp(-x))

#input dataset

X = np.array([ [0,0,1], #10번째 줄 
                [0,1,1],
                [1,0,1], 
                [1,1,1]])


# 결과 데이터값
y = np.array([[0,0,1,1]]).T #16번째 줄 

# 계산을 위한 시드 설정
# 실험의 편의를 위해 항상 같은 값이 나오게 한다.
np.random.seed(1) #20번째 줄 

# weights를 랜덤적으로 mean of 0으로 초기화하자.
syn0 = 2*np.random.random((3,1)) - 1 # 23번째 줄

for iter in range(10000): #25번째줄 

    # forwad propagation
    l0 = X # 25번째 줄 # 28번째 줄
    l1 = nonlin(np.dot(l0, syn0)) # 29번째 줄

    # 우리가 얼마나 놓쳤는가?
    l1_error = y- l1 # 32번째 줄 

    # 우리가 놓친 것들과 
    # 11 의 시그모이드 경사와 곱하기.
    l1_delta = l1_error * nonlin(l1, True) # 36번째 줄

    # weight 업뎃
    syn0 += np.dot(l0.T, l1_delta)

print ("Output After Traing")
print (l1)

표로 변수들을 조금 더 정리해보자면

변수 변수에 관한 설명
X 각각의 행들이 트레이닝 샘플인 입력 데이터 행
y 각각의 행들이 트레이닝 샘플인 결과 데이터 행
l0 네트워크의 첫 번째 층. 입력 데이터값들을 특징화한다.
l1 네트워크의 두 번째 층. 보통 히든 레이어으로 알려져 있다.
syn0 weight들의 첫번째 레이어인 시냅스 0로 l0과 l1를 연결시킨다.
* 벡터의 원소별 곱셈(Elementwise multiplication). 같은 크기의 두 벡터의 상응하는 값들을 대응시켜 곱한 같은 크기의 벡터를 반환한다.
- 벡터의 원소별 뺄셈(Elementwise subtraction). 같은 크기의 두 벡터의 상응하는 값들을 대응시켜 뺀 벡터를 반환한다.
x.dot(y) x, y가 벡터라면, 벡터의 내적(dot product)이다. 둘 다 행라면, 행의 곱이고, 만약 오직 하나만이 행라면, 벡터-행 곱이다.

“트레이닝 후 결과값”에서 보듯이 성공했습니다!! 제가 과정을 설명하기 전에 코드를 가지고 놀며 어떻게 돌아가는 지 직관적인 느낌을 한 번 느껴보길 바랍니다. ipython notebook에서 실행해 볼 수 있습니다. (스크립트에서도 돌아가지만, notebook에서 해보기를 강력하게 권한다.) 이 코드안에서 확인 해 볼만한 좋은 부분들을 보자면,

  • 첫번째 반복을 하고 난 후와 마지막 반복을 하고 난 후의 l1을 비교해보라
  • “nonlin” 함수가 무엇인지 확인해보라. 결과값의 확률을 보여준다.
  • l1_error이 반복 하는 동안 얼마나 변하는 지 확인해보라.
  • 36번째 줄을 확인해보라. 이 코드의 특제 소스가 이 부분이다.
  • 39번째 줄도 확인해보라. 이 부분에서 네트워크 안의 모든 것들이 준비된다.

자, 이제 코드를 한 줄 한 줄 확인 해보죠.

추천: 창 두 개를 띄워놓고 이 글을 읽으면서 코드를 볼 수 있으면 좋습니다. 이 글을 쓸 때 했던 방법이기도 합니다. :)

1번째 줄: 선형대수 라이브러리인 numpy를 임포트할 것입니다. 우리가 사용하는 모듈은 이것 하나 뿐입니다.

4번째 줄: 여기가 바로 “비선형성” 부분입니다. 비선형적인 함수에는 몇 가지 함수들이 있지만, 우리가 매핑한 함수는 “시그모이드”라는 함수입니다. 시그모이드 함수는 0과 1사이에 모든 값들을 매핑 시킵니다. 숫자들을 확률로 바꾸기 위해 사용합니다. 뉴런 네트워크을 트레이닝하기에도 다른 괜찮은 속성들을 가지고 있습니다.

시그모이드 함수

5번째 줄: 이 함수는 (deriv=True 일 때) 시그모이드의 미분된 값들을 만듭니다. 시그모이드 함수의 또 다른 좋은 점은 이 함수의 결과값을 미분할 때에도 사용한다는 점입니다. 만약 시그모이드 함수의 결과값이 “out”이라면, 미분 값은 단순히 out*(1-out)입니다. 정말 효율적인 점이죠.

미분에 익숙하지 않으시다면, 특정 지점에서 시그모이드함수의 경사를 계산한다고 생각해보시면 됩니다.(위에서 봤듯, 서로 다른 지점에서는 서로 다른 경사를 가진다). 미분에 대해 좀 더 알고 싶다면, 칸 아카데미의 미분 듀토리얼을 확인 해보시면 도움이 될 것입니다.

10번째 줄: numpy 행로 입력 데이터값을 설정합니다. 각각의 줄은 ‘트레이닝 예시’ 각각 입니다. 각각의 열들은 입력 노드 중 하나이구요. 그러므로 우리는 네트워크에 3개의 입력노드와 4개의 트레이닝 예시들을 가지고 있는 것입니다.

16번째 줄: 결과 데이터값들을 설정합니다. 여기서는 공간사정 상 데이터셋을 수평으로 (4행 1열짜리를) 만들었습니다. “.T”는 전치행렬로 만들어줍니다. 전치행렬 이후에는 1열과 4개의 행이 됩니다. 입력값과 형태는 같으면서 각각의 행들은 트레이닝 예시이고, 각각의 열들은 결과 노드입니다. 그러면 우리 네트워크는 입력 3개와 결과 1개를 가지고 있습니다.

20번째 줄: 난수(random number)에 씨드(seed)를 설정 해주는 것은 매우 좋은 습관입니다. 숫자들은 아직 랜덤적으로 분포되어 있지만(역자주 : 랜덤을 돌릴때마다 숫자가 다르게 나온다는 것), 씨드는 트레이닝을 할 때마다 정확히 같은 방식으로 난수를 생성 해 줍니다. (역자주 : 언제나 같은 숫자로 실험하게 된다.) 그러면 변화를 줄 때에 네트워크에 어떤 영향을 미치는 지 쉽게 확인 할 수 있게 해 주죠.

23번째 줄: 이 뉴럴네트워크의 weight 행입니다. “synapse zero”를 암시하는 “syn0”로 이름을 지었습니다. 우리가 단 2개의 레이어만 가지고 있기 때문에(입력과 결과) 이를 연결시킬 단 하나의 weights 행만 있으면 됩니다. 이 행의 차원은 우리가 3개의 입력값들과 1개의 아웃풋값을 가지고 있기 때문에 (3,1)입니다. 그래서, 또 하나 보아야할 것은 l0의 크기는 3이고 l1의 크기는 1입니다. l0의 모든 노드를 l1의 모든 노드로 연결시키려 하기 때문에 (3,1) 차원의 행렬이 필요합니다. :)

또한 랜덤적으로 mean of zero으로 초기 설정을 해주어야합니다. weight를 초기 설장하는 이론이 몇 있지만 현재까지는 mean of zero로 weight를 초기화시켜주는 것이 최선이고 좋은 방법입니다.

한 가지 더 알아야할 것은 “뉴럴 네트워크”는 진짜 단순히 행일 뿐이라는 것입니다. “레이어” l0와 l1 들을 가지고 있지만, 단지 데이터 셋를 기초로 한 일시적인 값일 뿐입니다. 데이터 셋들을 저장하지는 않습니다. 배우는 모든 것들은 syn0 행에 저장됩니다.

(역주: mean of zero를 설명하는 부분이 애매모호해서, 찾아봤더니, numpy.random.random에서 (-1, 0]의 범위 내에서 값을 세팅해 주는 것 같습니다. 혹시 추가 설명해주실 분들은 환영합니다!)

2016.08.01 댓글을 보고 추가합니다.

ybbarnng님 정말 감사합니다. ‘ mean of 0는 ‘평균이 0이 되게 한다’는 뜻입니다. numpy.random.random()의 api 문서를 보면 [0.0, 1.0) 범위의 실수를 반환하는 것을 알 수 있습니다. 이것에 2배를 하고 1을 빼면, [-1.0, 1.0) 사이의 임의의 실수를 반환하게 됩니다. 이렇게 되면 평균값이 0이 되게 되는데, 인공신경망에서 초기화를 할 때 평균값이 0인 것이 더 도움이 되나봅니다.’

25번째 줄: 우리의 네트워크 트레이닝 코드가 실제로 시작되는 부분입니다. 이 for 루프는 데이터셋에 최적화시키기 위해서 트레이닝코드를 계속 “반복”시킵니다.

28번째 줄: 첫번째 레이어 l0 이후로, l0은 단순히 데이터입니다. 어떤 특정 지점에서 명시적으로 만들어놓습니다. X가 4개 트레이닝 예시(행)들을 가지고 있었던 것 기억하세요. 향상되는 동시에 모든 과정을 같이 실행시킬 것입니다. 이는 “full batch” 트레이닝으로 알려져있습니다. 그래서 네 개의 서로 다른 10개의 행들을 가지고 있지만, 원한다면 하나의 트레이닝 예시만 가지고 있다고 생각해도 무방합니다. 지금 시점에서는 다른 점이 별로 없습니다. (코드 한 줄 바꾸지도 않고 1000 이나 10000으로 로딩시킬 수 있습니다.)

29번째 줄: 이 부분이 예측단계입니다. 먼저, 네트워크로 주어진 입력값으로 결과값 예측을 “시도”하도록 해봅시다. 그리고 어떻게 동작하는지 배우고 난다면, 조정하면서 반복되는 동안 향상시킬 수 있을 것입니다.

이 줄은 2가지 단계를 내포하고 있습니다. 첫번째로 l0와 syn0를 매트릭스 곱합니다. 두 번째로 결과값을 시그모이드 함수를 통해 통과시킵니다. 각각의 차원을 살펴보자면,

(4 x 3) dot (3 x 1) = (4 x 1)

매트릭스의 곱은 순차적으로 이루어지고, 방정식의 중간 안의 차원은 반드시 같아야합니다.(역주: 위의 경우 3 = 3으로 같다) 만들어지는 행렬은 첫번째 행의 행의 개수와 두번째 행의 열의 갯수로 만들어집니다.

우리가 4개의 트레이닝 예시를 집어넣었기 때문에, 알맞은 정답을 위해 4개의 추측값이 나옵니다. (4*1) 매트릭스이죠. 각각의 결과값은 주어진 입력값의 네트워크의 추측로 나온 값입니다. 왜 우리가 트레이닝 예시에 임의의 숫자를 “안에 넣었는지” 직관적으로 좀 아셨을 것이라 생각합니다. 매트릭스의 곱은 여전히 계속됩니다. :)

32번째 줄: l1은 각각의 입력값에 대해 추측한 값들을 가지고 있습니다. 이제 정답(y)값에서 추측(l1)값을 빼보면 얼마나 잘 되었는지 비교해볼 수 있습니다. l1_error은 얼마나 네트워크가 놓쳤는지 반영하는 단순히 음양의 벡터 값입니다.

36번째 줄: 이제, 괜찮은 결과물을 얻었습니다! 이 부분이 특제 소스 부분입니다! 이 줄은 많은 것들을 설명하고 있기 때문에 두 부분으로 나누어서 확인 해봅시다.

부분1: 미분

nonlin(l1, True)

만약 l1이 아래 세 점들을 표현한 것이라면, 위 코드는 아래와 같은 기울기선을 만듭니다. x=2.0(초록색 점)같은 매우 높은 값들과 x=-1.0(보라색 점)같은 매우 작은 값들은 경사가 작습니다. 가장 높은 기울기를 가지고 있는 선은 x=0(파란색 점)입니다. 이것이 매우 중요한 역할을 합니다. 그리고 또 하나 알아둘 것은 이 모든 미분값들은 0과 1사이에 존재합니다.

시그모이드 미분

전체 상태: 미분에 의해 가중치가 계산된 에러

“미분에 의해 가중치가 계산된 에러” 보다 더 “수학적으로 정확한” 방법들이 많지만 직관적으로 이해하는 데는 이 방법이 좋다고 생각합니다. l1error 은 (4,1) 행입니다. nonlin(l1, True)는 (4,1) 행를 반환합니다. “요소별 곱셈 elementwise_” 을 이용해 곱합니다. 이는 두 값들을 곱한 (4,1) 매트릭스 l1_delta를 반환합니다.

에러와 “경사”를 곱할 때, high confidence prediction의 에러를 줄일 수 있습니다. 시그모이드 함수를 다시 한 번 봅시다! 만약 경사도가 매우 작다면(0에 가깝다면 - 평평하다면) 네트워크는 매우 높은 값을 같거나, 매우 낮은 값을 가집니다. 이는 네트워크는 한 방면이든 혹은 다른 쪽이든 신뢰관계에 있음을(confident) 보여줍니다. 그러나, 네트워크가 어떤 것을 (x=0, y=0.5) 에 가깝게 추측한다면, 신뢰관계가 높다고 확신하기는 매우 쉽지않습니다. 이러한 “wishy-wishy” 예측을 가능한한 업데이트하고, 0에 가까운 숫자를 곱함으로써 확실한 것 하나를 버릴 수 있습니다.

39번째 줄 이제 네트워크를 개선시킬 준비가 다 되었습니다! 그럼 트레이닝 예시 하나를 살펴봅시다.

트레이닝 예시

이 트레이닝 예시에서는, weight들를 전부 업데이트한 상황입니다. 그럼, 가장 왼 쪽에 있는 웨이트(9.5)를 업데이트 시켜봅시다.

weight_update = input_value*l1_delta

가장 왼쪽에 있는 weight부터 살펴보면, 1.0과 l1_delta를 곱할 것입니다. 아마도 9.5보다 아주 조금 높은 값을 반환할 것입니다. 왜 매우 작은 숫자일까요? 예측은 이미 매우 확실한데다 전방위적으로 맞습니다. 작은 에러값과 작은 경사도는 매우매우 작은 향상을 의미합니다. weight들을 전부 고려해보세요. 세 개 모두 아주 조금 올라갈 것입니다.

network batch

하지만, “full batch” configuration를 사용하기 때문에, 우리는 모든 트레이싱 예시 4개들을 위의 단계로 해 볼 것입니다. 그래서 위 이미지처럼 많아 보입니다. 그렇다면 39번째 줄이 의미하는 바는 무엇일까요? 웨이트가 각각의 트레이닝 예시들로 각각의 웨이트들을 업데이트하고, 그것들을 다 합하고, 그리고 또 웨이트를 업데이트 시키고, 이 모든 것들이 단 한 줄에 들어가 있는 것입니다. 행렬의 곱과 함께 놀다보면, 이 모든 것들을 확인할 수 있을 것입니다!

꼭 기억해야할 것들:

이때까지 어떻게 네트워크가 업데이트 되는지 보았습니다. 우리의 트레이닝 데이터와 반영된 것으로 돌아가 보겠습니다. 입력괎과 결과값이 1이라면, 그 사이의 웨이트들을 증가시켜줍니다. 인풋이 1이고 아웃풋이 0이라면, 그 사이의 웨이트들을 감소시켜주면 됩니다.

입력값들 결과값
0 0 1 0
1 1 1 1
1 0 1 1
0 1 1 0

그러므로, 위에 있는 트레이닝 예시안에서는 웨이트는 첫 번째 입력값부터 결과값까지 끊임없이 증가되거나 변하지 않고 유지될 것이고, 반면에 다른 두 웨이트들은 자체에서 트레이닝 예시들을 증가시키고 감소시키면서 통과하며(진행상황을 취소시키면서) 값들을 찾을 것입니다. 입력값과 아웃풋 값과의 관계를 바탕으로 무언가가 네트워크에게 배울 수 있도록 하게 하는 현상입니다.

단계 2 : 약간 어려운 문제

입력값들 결과값
0 0 1 0
0 1 1 1
1 0 1 1
1 1 1 0

주어진 입력값 열 두 개를 가지고 결과값 열을 에측한다고 생각 해봅시다. 중요한 것은 두 열 모두 결과값과 아무런 관련이 없다는 것입니다. 각각의 칼럼은 50%의 확률로 1을 예측하고, 50%의 확률로 0을 예측할 것입니다.

그래서, 여기서는 어떤 패턴을 가지고 있을까요? 모든 값들이 1인 세 번째 열은 완전히 관계가 없어 보입니다. 그러나, 첫 번째 열과 두 번째 열은 좀 더 명료하게 있어 보입니다. 첫 번째 열 혹은 두 번째 열 중 하나가 1이 나온다면,(그렇지만 둘 다 1이 나와서는 안됩니다!) 결과값은 1이 될 것입니다. 바로 우리가 가지고 있는 패턴은 이것입니다.

인풋과 아웃풋이 직접적으로 연결된 1대1 관계가 아니기 때문에, “선형적이지않은(직선이지않은)” 것으로 보입니다. 대신 입력값들의 복합적인 1대1관게 입니다. 즉, 1열과 2열을 말 하는 것이죠.



믿거나 말거나, 이미지 인식 또한 비슷한 문제입니다. 만약 완전히 동일한 크기의 파이프사진과 자전거사진이 있다면, 자전거와 파이프가 실재하는 부분에 직접적으로 관계가 있는 각각의 픽셀은 없을 것입니다. 픽셀들은 순수한 통계적인 관점에서는 아마 랜덤적으로 보일 것입니다. 그러나, 특정 부분의 픽셀 조합은 랜덤적이지 않습니다. 즉 자전거와 사람으로 구성된 이미지 형태 일 수도 있다는 것입니다.

우리의 목표.

첫번째 결합된 픽셀을 결과값에 1-to-1 관계를 가지는 어떤 것에 넣기 위해서는 또 다른 레이어가 필요합니다. 첫 번째 레이어는 입력값들을 결합시키고, 두번째 레이어는 첫번째 레이어들의 결과값들을 입력값으로 사용해서 결과값쪽으로 매핑시키는 것입니다. 본격적으로 향상에 들어가기 전에, 아래의 표부터 살펴봅시다.

입력값들(l0) 숨겨진 웨이트들(l1) 결과값(l2)
0 0 1 0.1 0.2 0.5 0.2 0
0 1 1 0.2 0.6 0.7 0.1 1
1 0 1 0.3 0.2 0.3 0.9 1
1 1 1 0.2 0.1 0.3 0.8 0

만약 웨이트들을 랜덤적으로 설정한다면, 첫 번째 레이어의 숨겨진 상태값들을 갖게 될 것입니다. 뭔가 기억나지 않나요? 두번째 열은(두번째 숨겨진 노드) 결과값과 약간의 관계를 이미 가지고 있습니다! 완벽하지는 않지만, 있긴 있어요. 믿거나 말거나, 이 부분은 뉴럴 네트워크를 트레이닝하는 것에 많은 부분을 차지합니다. (사실은, 정말로 뉴럴 네트워크를 트레이닝 시키는 유일한 방법입니다.) 아래의 트레이닝 과정은 이 관계를 증폭시켜줍니다. 결과값들에 매핑시키는 syn1와 좀 더 나은 입력값들을 만드는 sync0 둘 다 업데이트 시켜줍니다.

Note: 모델에 복합적인 조합들의 관계들로 레이어들을 더 추가시키는 분야를 “deep learning”이라 익히 알려져있습니다. 모델링된 딥 레이어들을 추가해주기 때문이죠.

3 층의 뉴럴 네트워크:


import numpy as np

def nonlin(x,deriv=False):
    if(deriv==True):
        return x*(1-x)
    return 1/(1+np.exp(-x))
    
X = np.array([[0,0,1],
            [0,1,1],
            [1,0,1],
            [1,1,1]])
                
y = np.array([[0],
            [1],
            [1],
            [0]])

np.random.seed(1)

# weights를 랜덤적으로 mean of 0으로 초기화하기
syn0 = 2*np.random.random((3,4)) - 1
syn1 = 2*np.random.random((4,1)) - 1

for j in range(60000):

    #  레이어 0, 1, 2 로 키우기
    l0 = X
    l1 = nonlin(np.dot(l0,syn0))
    l2 = nonlin(np.dot(l1,syn1))

    # 목표로 한 값에 비해 얼마나 놓쳤을까?
    l2_error = y - l2
    
    if (j% 10000) == 0:
        print ("에러: " + str(np.mean(np.abs(l2_error))))
        
    # 목표 값들의 방향은 무엇인가?
    # 정말 확신할 수 있나요? 그렇다면 많이 바꾸면 안된다. 
    l2_delta = l2_error*nonlin(l2,deriv=True)

    # (웨이트에 따르면) 얼마나 각각의 l1 값들은 l2에러에 기여했을까? 
    l1_error = l2_delta.dot(syn1.T) # 43번째 줄
        
    # l1 목표의 방향은 무엇인가요?
    # 정말 확신할 수 있나요? 그렇다면 많이 바꾸면 안된다. 
    l1_delta = l1_error * nonlin(l1,deriv=True)

    syn1 += l1.T.dot(l2_delta)
    syn0 += l0.T.dot(l1_delta)
변수 변수에 관한 설명
X 각각의 행들이 트레이닝 샘플인 입력 데이터 행
y 각각의 행들이 트레이닝 샘플인 결과 데이터 행
l0 네트워크의 첫 번째 층. 입력 데이터값들을 특징화한다.
l1 네트워크의 두 번째 층. 보통 히든 레이어으로 알려져 있다.
l2 네트워크의 마지막 층. 우리의 가정에 의하면, 트레이닝 후 정답과 가깝게 나와야한다.
syn0 weight들의 첫번째 레이어인 시냅스 0로 l0과 l1를 연결시킨다.
syn0 웨이트들의 두번째 레이어인 시냅스 1 로 l1과 l2를 연결시킨다.
l2_error 뉴럴 네트워크가 “놓친” 값들
l2_delta 높은 예측에 의한 크기가 결정된 네트워크의 에러. 변환된 매우 confident한 에러를 제외하고는 거의 비슷하다.
l1_error syn1 안에서 l2_delta를 가충치 한 것, 중간 /히든 레이어들의 에러를 계산할 수 있다.
l1_delta 높은 예측에 의한 크기가 결정된 l1 에러. 다시 말하지만, l1_error와 거의 비슷하며, 제거된 높은 에러는 제외한 것.

추천: 창 두 개를 띄워놓고 이 글을 읽으면서 코드를 볼 수 있으면 좋습니다. 이 글을 쓸 때 했던 방법이기도 합니다. :)

모든 것들이 매우 비슷하게 보입니다! 이전의 향상기법에서 각각 맨 윗 줄에 2개를 쌓기만 한 것입니다. 첫 번째 레이어(l1)의 결과값은 두 번째 레이어의 입력값입니다. 43번째 줄만, 새롭게 추가된 것입니다.

43번째 줄: l2의 “confidence weighted error”을 사용해서 l1을 위한 에러를 만듭니다. 이 에러를 만들기 위해, l2에서 l1으로 에러를 웨이트로 단순히 통과만 시켜줍니다. l1의 얼마나 많은 각각의 노드 값들이 l2의 에러에 “기여”했는지 배웠는 데, 이는 “contribution weighted error” 이라는 것을 만듭니다. 이것은 “backpropagating”이라 불리는데, 알고리즘을 만든 사람 이름에서 따온 것입니다. 그리고 이미 했던 2 레이어 향상법과 같은 방법으로 syn0을 업데이트 할 것입니다.

단계 3 : 다음에 해야할 일들과 결론.

제 추천.

뉴럴 네트워크에 대해 진지하게 관심이 있다면, 추천할 것이 있습니다. 기억으로만 네트워크를 다시 만들어보세요, 이 말이 약간 바보처럼 들릴 수도 있겠지만 정말 도움이 될 것입니다. 새로운 논문에 나온 임의적인 아키텍처들을 만들고 싶거나 서로 다른 아키텍쳐들로 만들어진 샘플코드를 읽고 이해하고 싶다면 필시 최고의 방법인 것 같습니다. Torch, Caffe, Theano 같은 프레임워크를 사용한다해도 유용할 것입니다. 이런 연습을 하기 전에도 몇 년동안 뉴럴네트워크를 사용해왔는데, 이 연습에 대한 시간 투자가 제가 이 분야에 들어오고 가장 잘했던 투자였다고 생각합니다. (그리고 오래 걸리지도 않습니다.)

다음에 해야할 일

여태까지 쉽게 만들어 본 예시는 state-of-the-art 아키텍처를 만들기에는 아직 몇몇 것들이 더 필요합니다. 여기 네트워크를 향상시키고 싶다면 봐야할 몇 가지 것들을 소개합니다. (아마 다음 포스트에서 다룰 게 될 것입니다. )