반응형
- 이전에 단층 퍼셉트론으로 해결했던 문제들을 다층 퍼셉트론으로 해결해볼 것이다.
- 먼저 은닉층이 하나일 경우로 구현 후, 가변적 은닉 계층에 대해 구현을 진행하겠다.
- 수업에 사용된 코드를 분석한 자료입니다.
은닉 계층 하나를 위한 파라미터 생성 함수
def init_model_hidden1():
global pm_output, pm_hidden, input_cnt, output_cnt, hidden_cnt
pm_hidden = alloc_param_pair([input_cnt, hidden_cnt])
pm_output = alloc_param_pair([hidden_cnt, output_cnt])
- 신경망 각 계층은 가중치 행렬과 편향 벡터로 구성되는 파라미터 쌍이 필요하다.
- 은닉 계층 하나를 추가로 가지는 다층 퍼셉트론구조에서는 출력 계층과 은닉 계층을 하나씩 가지기 때문에 두 쌍의 파라미터 쌍이 필요하다.
- alloc_param_pair에 각 계층의 크기를 넣어 적절한 파라미터를 리턴받는다.
def alloc_param_pair(shape):
weight = np.random.normal(RND_MEAN, RND_STD, shape)
bias = np.zeros(shape[-1])
return {'w':weight, 'b':bias}
- 계층의 크기를 받아 그에 맞는 가중치와 편향을 리턴하는 함수이다.
- 예를 들어 shape로 4,3이 입력된다면 가중치는 (4,3) 사이즈 행렬, 편향은 3 사이즈 벡터가 생성될 것이다.
은닉 계층 하나를 위한 순전파 함수
def relu(x):
return np.maximum(x, 0)
def forward_neuralnet_hidden1(x):
global pm_output, pm_hidden
hidden = relu(np.matmul(x, pm_hidden['w']) + pm_hidden['b'])
output = np.matmul(hidden, pm_output['w']) + pm_output['b']
return output, [x, hidden]
- ReLU는 정의가 간단하기 때문에 쉽게 작성할 수 있다. (np.maximum함수 활용)
- hidden은 은닉 계층의 아웃풋이다. 두 행렬의 곱에 편향을 더한 뒤 이를 ReLU(비선형 활성화함수)에 적용한다.
- output은 출력 계층의 아웃풋이다. 두 행렬의 곱에 편향을 더한다.
- 역전파에 활용하기 위해 output뿐만 아니라 x, hidden값도 리턴해준다.
은닉 계층 하나를 위한 역전파 함수
def relu_derv(y):
return np.sign(y)
- relu함수의 미분값은 y로 간단하게 파악할 수 있다.
- 해당 부호를 체크하고 음수면 0, 양수면 1을 리턴한다.
def backprop_neuralnet_hidden1(G_output, aux):
global pm_output, pm_hidden
x, hidden = aux
g_output_w_out = hidden.transpose()
G_w_out = np.matmul(g_output_w_out, G_output)
G_b_out = np.sum(G_output, axis=0)
g_output_hidden = pm_output['w'].transpose()
G_hidden = np.matmul(G_output, g_output_hidden)
pm_output['w'] -= LEARNING_RATE * G_w_out
pm_output['b'] -= LEARNING_RATE * G_b_out
G_hidden = G_hidden * relu_derv(hidden)
g_hidden_w_hid = x.transpose()
G_w_hid = np.matmul(g_hidden_w_hid, G_hidden)
G_b_hid = np.sum(G_hidden, axis=0)
pm_hidden['w'] -= LEARNING_RATE * G_w_hid
pm_hidden['b'] -= LEARNING_RATE * G_b_hid
- 파라미터 행렬들이 들어가는 pm_output, pm_hidden에 학습한 파라미터쌍을 담는다.
- aux로부터 x값과 hidden층의 값을 받는다.
- 출력 계층부터 역전파를 진행한다. 위의 초기 코드는 복잡해보이지만 결국 기존의 방법을 사용한 것이다. hidden은 은닉계층의 출력값이자 출력 계층의 입력값이다. 따라서 이를 G_output(부분기울기)에 행렬곱해서 기울기에 대한 손실함수값을 정의한다. 그리고 편향값은 부분 기울기를 더한 값으로 정의한다.
- 최종적으로 구했던 부분 기울기를 기존의 가중치와 곱해 역전파한다.(체인룰 적용을 위함) 이는 은닉계층의 학습에 활용될 것이다.
- 출력 계층에 학습을 적용한다.
- 위에서 구한 역전파 받은 G_hidden에 relu의 미분값을 곱한다. (체인룰)
- 이를 활용해 기존에 한 것과 마찬가지의 원리를 적용하고 학습을 진행한다.
- 여기까지가 은닉층 하나를 가정한 구현이었다.
- 다음은 가변적 은닉 계층에 대해 구현을 진행하겠다.
가변적 은닉 계층 구성을 위한 파라미터 생성 함수
def init_model_hiddens():
global pm_output, pm_hiddens, input_cnt, output_cnt, hidden_config
pm_hiddens = []
prev_cnt = input_cnt
for hidden_cnt in hidden_config:
pm_hiddens.append(alloc_param_pair([prev_cnt, hidden_cnt]))
prev_cnt = hidden_cnt
pm_output = alloc_param_pair([prev_cnt, output_cnt])
- hidden_config에는 은닉 계층의 수와 폭이 저장되어 있다.
- 생성된 파라미터쌍은 pm_hiddens에 저장된다.
- hidden_config를 돌면서 그 곳에서 얻어온 폭(hidden_cnt)과 이전 출력벡터 크기(prev_cnt)를 기반으로 가중치 행렬과 편향 벡터를 생성한다.
- 그렇게 모든 은닉 계층의 파라미터를 정의한다.
가변적 은닉 계층 구성을 위한 순전파 함수 정의
def forward_neuralnet_hiddens(x):
global pm_output, pm_hiddens
hidden = x
hiddens = [x]
for pm_hidden in pm_hiddens:
hidden = relu(np.matmul(hidden, pm_hidden['w']) + pm_hidden['b'])
hiddens.append(hidden)
output = np.matmul(hidden, pm_output['w']) + pm_output['b']
return output, hiddens
- 파라미터 값을 기반으로 순전파를 수행한다.
- x부터 시작해서 은닉계층을 돌며 작업을 수행한다. 해당 정보는 hiddens 리스트에 계속해서 저장한다.
- 최종 아웃풋까지 계산이 완료되면 output과 hiddens를 함께 리턴한다.
가변적 은닉 계층 구성을 위한 역전파 함수 정의
def backprop_neuralnet_hiddens(G_output, aux):
global pm_output, pm_hiddens
hiddens = aux
g_output_w_out = hiddens[-1].transpose()
G_w_out = np.matmul(g_output_w_out, G_output)
G_b_out = np.sum(G_output, axis=0)
g_output_hidden = pm_output['w'].transpose()
G_hidden = np.matmul(G_output, g_output_hidden)
pm_output['w'] -= LEARNING_RATE * G_w_out
pm_output['b'] -= LEARNING_RATE * G_b_out
for n in reversed(range(len(pm_hiddens))):
G_hidden = G_hidden * relu_derv(hiddens[n+1])
g_hidden_w_hid = hiddens[n].transpose()
G_w_hid = np.matmul(g_hidden_w_hid, G_hidden)
G_b_hid = np.sum(G_hidden, axis=0)
g_hidden_hidden = pm_hiddens[n]['w'].transpose()
G_hidden = np.matmul(G_hidden, g_hidden_hidden)
pm_hiddens[n]['w'] -= LEARNING_RATE * G_w_hid
pm_hiddens[n]['b'] -= LEARNING_RATE * G_b_hid
- aux에서 앞서 처리한 hiddens의 정보가 들어있다. 이는 각 은닉계층의 출력값들이다.
- 원리는 은닉층이 1개일때와 동일하고, 단지 반복해서 적용해줘야한다는 점이 다르다.
- 반복문을 돌면서 역전파 및 각 가중치와 편향에 대한 학습이 진행된다.
스위치 함수 정의
global hidden_config
def init_model():
if hidden_config is not None:
print('은닉 계층 {}개를 갖는 다층 퍼셉트론이 작동되었습니다.'. \
format(len(hidden_config)))
init_model_hiddens()
else:
print('은닉 계층 하나를 갖는 다층 퍼셉트론이 작동되었습니다.')
init_model_hidden1()
def forward_neuralnet(x):
if hidden_config is not None:
return forward_neuralnet_hiddens(x)
else:
return forward_neuralnet_hidden1(x)
def backprop_neuralnet(G_output, hiddens):
if hidden_config is not None:
backprop_neuralnet_hiddens(G_output, hiddens)
else:
backprop_neuralnet_hidden1(G_output, hiddens)
- hidden_config를 미리 정의해놨다면 은닉층 다수의 다층 퍼셉트론이, 그렇지 않다면 은닉층 하나의 다층 퍼셉트론이 작동된다.
- 그리고 순전파와 역전파에서도 그에 따라 실행함수를 다르도록 설정했다.
은닉 계층 구조 지정 함수
def set_hidden(info):
global hidden_cnt, hidden_config
if isinstance(info, int):
hidden_cnt = info
hidden_config = None
else:
hidden_config = info
- info가 어떤값인지에 따라 hidden_config을 None으로 초기화하거나 정의한다.
- 정수형일 경우 하나의 은닉층만을 활용할 것이기 때문에 hidden_cnt만 정의하고 hidden_config를 None으로 초기화한다.
- 그렇지 않을 경우 hideen_config을 info로 초기화한다.
반응형
'🛠 기타 > Data & AI' 카테고리의 다른 글
LSTM을 활용한 스팸분류 (0) | 2020.08.05 |
---|---|
합성곱 신경망(CNN) - 기본구조 이론 (0) | 2020.08.03 |
다층 퍼셉트론 - 기본구조 이론 (0) | 2020.08.02 |
Keras Embedding (워드 임베딩) (0) | 2020.07.31 |
Keras Tokenizer (단어 인덱스 부여 및 원-핫 인코딩) (0) | 2020.07.31 |