티스토리 뷰
2. Neural Networks / L2. Implementing Gradient Descent - Backpropagation
chrisysl 2018. 7. 11. 21:07Backpropagation
- multiple layers와 gradient descent를 이용한 네트워크 학습에대해 더 알아보자.
- 우리가 만든 네트워크를 이용하여 최초의 시도로 output을 뽑아낸 뒤,
- 얼만큼 이 값이 잘못된지를 반영하여 weights를 다시 설정하여야 하지 않겠는가?
- 따라서 input → hidden → output 으로 이어지는 네트워크 flow에서
- weights를 다시 바로잡기위해 역행하며 정보를 전달하는 과정을 Backpropagation이라 함.
- gradient descent를 사용하여 hidden layer의 weights를 업데이트하려면
- 각 hidden unit이 최종 output에 얼마나 많은 오차를 남겼는지를 알아야한다.
- 각 layer의 output은 layer간의 weights에 영향을 받으므로,
- 이 오차를 알아내야 weights를 수정하여 최종 목표로 나아갈 수 있다.
- error는 output을 통해 알 수 있기 때문에, 우리는 역방향으로 작업을 해 나가야 하는것임.
- 예를들어, output layer에서 각 output unit인 k에 기여한 좌측 델타의 error가 있다.
- hidden unit인 j에 기여한 output error는
- output과 hidden layers 사이의 weights와 그래디언트 값에 의한것임.
- 그렇게되면, 그래디언트 스텝은 전과 동일하지만 새로운 에러만 반영됨.
- 여기서 Wij는 input layer와 hidden layer사이의 weight이고, Xi는 input unit의 값이다.
- 이 양식은 아무리 layer들이 많아도 모두 적용됨
- weight의 step들은 위와같이 구할 수 있다.
- (step size) * (the output error of the layer) * (the values of the inputs to that layer)
- 여기서 상위 계층의 error를 역으로 전파(propagating)하여 output error인 델타를 얻고,
- input values인 V는 input layer에 있어서는 input value가 되고,
- hidden layer에 있어서는 activation된 즉, sigmoid function에 의한 output이 됨(output layer의 input value).
Working through an example
- 예제를 통해 이해해보자.
- 최하단에 input layers, 그다음 중간에 hidden layer, 마지막에 output layer가 있고,
- 각 layer들을 이어주는 weights 값이 표시되어있다.
- 또한 hidden layer 와 output layer에는 sigmoid function이 들어간다.
· Calculating the input to the hidden unit
- binary data(0 또는 1)를 찾아가려 하고있고, 이 때의 target은 y = 1일 경우,
- 우리는 먼저 순서대로 hidden unit에 대한 input을 먼저 계산한다.
· Output of the hidden unit
- hidden layer에 대한 input값에 sigmoid function을 돌려서 hidden unit의 output을 구해낸다.
· Using this(output of the hidden unit) as the input to the output unit
- 즉, 최종 output of the network는 0.512가 나오게 된다.
· Backwards pass to calculate the weight updates(output unit → hidden unit)
· Backwards pass to calculate the weight updates(hidden unit → input unit)
- 이제 backpropagation을 이용해서 hidden unit의 error term을 계산해보자.
- output unit으로 부터의 error term을 weight인 W를 hidden unit에 연결하여 조정한다.
- hidden unit의 error term은 위와같이 구하지만,
- 예시에서는 하나의 hidden unit과 하나의 output unit뿐이라 훨씬 간단하다.
· Gradient descent steps
- error를 얻었으므로, gradient descent step을 진행할 수 있다.
- hidden 에서 output weight step은 learning rate에 해당하고
- 거기에 output unit의 error를 곱하고
- 그리고 hidden unit의 activation value(sigmoid function의 결과값)까지 곱해준다.
- The hidden to output weight = learning rate * output unit error * hidden unit activation value 로 구함
· Update input to hidden weights
- 그 다음으로는 input에서 hidden unit으로 가는 weight들을 업데이트 해주면 된다.
- learning rate * hidden unit error * input values 로 구함.
- 이 예제에서 sigmoid function을 activation function으로 사용했을때의 효과를 알아볼 수 있다.
- sigmoid function의 최대 미분값은 0.25이므로, output layer의 error가 75%이상 감소하고
- hidden layer의 error는 최소 93.75%로 축소된다.
- 만약 layer들이 많을 경우(multi-layers) sigmoid function을 이용하면
- 매우 빠르게 가중치 단계(weight steps)들을 줄일 수 있다.
- 이를 Vanishing gradient problem 이라 칭함.
- 물론 나중에 또 다른 activation function에 대해 더 알아볼것임.
Implementing in NumPy
- Backpropagation을 수행하는데에 있어 NumPy는 매우 강력한 도움을 준다.
- 이전에는 하나의 unit에 대한 error term만을 다뤘다면,
- 앞으로는 weight 업데이트에 있어서 hidden layer에 있는 각각의 unit에 대한 error를 고려해야한다.
- 위와같이 다루게 되는데, input과 hidden unit의 개수가 다를 수 있다.
- 따라서 이전과 같이 error와 input들을 곱해버리면 다음과같은 에러를 던진다.
- 각각의 shape이 다르기 때문.
- 또한 Wij(가중치)는 이제 행렬이므로, 곱해질 좌 우 의 행과 열을 즉 shape를 고려해서 곱해야한다.
- NumPy는 이를 적절하게 처리해 줄 수 있는데 다음과 같이 처리해주면 됨.
- 이 경우 (len(column_vector), len(row_vector)) 꼴의 shape를 가진 matrix를 리턴한다.
- 이 방법이 정확하게 우리가 weight를 업데이트하는데에 있어 원하는 결과값을 도출해내고
- 전에 언급하였듯, 2차원 배열이지만 하나의 행을 가진경우 transpose (inputs.T 와 같이)를 사용할 수 있다.
- 1차원의 경우 transpose는 불가능.
Backpropagation exercise
import numpy as np def sigmoid(x): """ Calculate sigmoid """ return 1 / (1 + np.exp(-x)) x = np.array([0.5, 0.1, -0.2]) target = 0.6 learnrate = 0.5 weights_input_hidden = np.array([[0.5, -0.6], [0.1, -0.2], [0.1, 0.7]]) weights_hidden_output = np.array([0.1, -0.3]) ## Forward pass hidden_layer_input = np.dot(x, weights_input_hidden) hidden_layer_output = sigmoid(hidden_layer_input) output_layer_in = np.dot(hidden_layer_output, weights_hidden_output) output = sigmoid(output_layer_in) ## Backwards pass ## TODO: Calculate output error error = target - output # TODO: Calculate error term for output layer output_error_term = error * output * (1 - output) # TODO: Calculate error term for hidden layer hidden_error_term = np.dot(output_error_term, weights_hidden_output) * \ hidden_layer_output * (1 - hidden_layer_output) # TODO: Calculate change in weights for hidden layer to output layer delta_w_h_o = learnrate * output_error_term * hidden_layer_output # TODO: Calculate change in weights for input layer to hidden layer delta_w_i_h = learnrate * hidden_error_term * x[:, None] print('Change in weights for hidden layer to output layer:') print(delta_w_h_o) print('Change in weights for input layer to hidden layer:') print(delta_w_i_h)