티스토리 뷰
2. Neural Networks / L2. Implementing Gradient Descent - Multilayer Perceptrons
chrisysl 2018. 7. 10. 21:14Implementing the hidden layer
- 이전에 XOR 퍼셉트론을 사용해서 추가적으로 units의 layer를 추가하면
- 우리의 네트워크 모델이 선형적으로 해결할 수 없는 문제점에 대한 해결이 가능했다.
- 위 사진에서 보듯, multi-layer 퍼셉트론의 개념은
- Input unit → Hidden unit → Output unit 으로 이어지는 구조를 가지고있다.
- hidden layer에 대한 activation이 output layer의 input으로 사용된다.
- 이와같은 구조로 이어지게 된다.
- 이 hidden layer의 수가 많을수록 정교한 네트워크를 형성하게되고
- 이 개념이 딥러닝의 핵심이 된다.
Implementing the hidden layer
- 신경망 네트워크에서 다층 퍼셉트론에 사용되는 수학을 먼저 훑어보자.
- 벡터 복습
- 행렬 복습
- 이전까지 우리는 하나의 output node만을 다뤘었는데,
- 지금부터는 다중 input units와 다중 hidden units을 통해 연산을 진행하므로,
- weights간의 인덱스가 두개가 필요하다 (ex. Wij)
- 여기서 인덱스 i 는 input units을, j는 hidden units을 의미한다.
- 네트워크를 표현한것인데, input unit으로는 X1, X2, X3 그리고 hidden unit으로는 h1과 h2가 있다
- 각각의 input unit들에서 h1으로 가는 선이 주황색으로 표시되어있는데, 이 선이 의미하는 바가 weights이다.
- weight의 인덱스에 있어 input unit의 번호를 i로, hidden unit의 번호를 j로 지정하였으므로
- W11의 경우 X1 → h1 으로 가는 weight에 해당한다.
- 마찬가지로 W12의 경우 X1 → h2 과 같은 방식이다.
- 따라서 weights는 행렬로 저장되어야하고,
- 각 행은 input unit 중심의 weights를
- 각 열은 hidden unit 중심의 weights와 대응된다고 볼 수 있다.
- NumPy에서 이렇게 weights를 초기화하려면 행렬로 값을 할당해줘야 한다.
- 만약 최초 데이터 features가 input data를 포함한 2D array일 경우
- 위와같이 설정해줘서 얻어낼 수 있다.
- 위의 결과 weights_input_to_hidden이라는 2D array 또는 행렬을 얻고, 이 때의 차원은 n_inputs 또는 n_hidden 이다.
- 각 hidden layer unit인 Hj에 대해 위와같이 weight * input 의 합으로 처리해준다.
- 다른 layer에서와 동일하다.
- 이때 행렬 곱셈을 사용하여야 함.
- 행벡터인 inputs와 weights를 곱해줘야하는데, 이를위해 dot product를 사용한다.
- 예를들어 첫번째 hidden unit(j = 1) 에 대해 계산하려면 위와같이 input과 weight을 계산해준다.
- 그냥 일반 행렬 곱셈과 같이 처리해주면 된다. 기억해야할 점은 dot product를 사용한다는것.
- 위와같은 방식으로 hidden layer 에 대한 연산이 가능하다.
- 넘파이에선 이러한 dot product 연산을 한번에 처리할 수 있음. 위와같이.
- 당연하게 위의 연산은 각각 transpose 시켜 연산을 처리할 수 있음.
- 당연한 얘기지만 행렬의 shape의 쌍이 맞지않으면 위와같은 에러를 출력하며 연산이 처리되지않음.
Making a column vector
- NumPy 배열이 행벡터로 작동하더라도, 열벡터가 필요할 때가 있다.
- 이럴때 Transpose를 적절히 이용해주면 됨. <array name>.T 와 같이 해주면 됨.
- 대신 1차원 배열의 경우 transpose를 취해도 동일하게 행벡터를 리턴하기 때문에
- 이렇게 처리해주면 됨
- 또는 위와같이 2차원으로 만들어서 transpose 해주면 열벡터를 얻을 수 있다.
- 하지만 벡터의경우 1차원으로 가지고 있는것이 직관적으로 계산하기 더 수월하다.
- 코드로 보자면
import numpy as np def sigmoid(x): """ Calculate sigmoid """ return 1/(1+np.exp(-x)) # Network size N_input = 4 N_hidden = 3 N_output = 2 np.random.seed(42) # Make some fake data X = np.random.randn(4) weights_input_to_hidden = np.random.normal(0, scale=0.1, size=(N_input, N_hidden)) weights_hidden_to_output = np.random.normal(0, scale=0.1, size=(N_hidden, N_output)) # TODO: Make a forward pass through the network hidden_layer_in = np.dot(X, weights_input_to_hidden) hidden_layer_out = sigmoid(hidden_layer_in) print('Hidden-layer Output:') print(hidden_layer_out) output_layer_in = np.dot(hidden_layer_out, weights_hidden_to_output) output_layer_out = sigmoid(output_layer_in) print('Output-layer Output:') print(output_layer_out)
- 이런식으로 처리해준다.