-이에 대한 해결책이 비선형 활성화 함수를 동반한 다층 퍼셉트론
def init_model():
global weight, bias, input_cnt, output_cnt
weight = np.random.normal(RND_MEAN, RND_STD,[input_cnt, output_cnt])
bias = np.zeros([output_cnt])
def forward_neuralnet(x):
global weight, bias
output = np.matmul(x, weight) + bias
return output, x
def backprop_neuralnet(G_output, x):
global weight, bias
g_output_w = x.transpose()
G_w = np.matmul(g_output_w, G_output)
G_b = np.sum(G_output, axis=0)
weight -= LEARNING_RATE * G_w
bias -= LEARNING_RATE * G_b
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])
def alloc_param_pair(shape):
#계층 하나를 위한 파라미터 쌍 생성
weight = np.random.normal(RND_MEAN, RND_STD, shape)
#2차원 형태를 넘는 것을 대비해 편향은 shape의 마지막 순서로 할당
bias = np.zeros(shape[-1])
return {'w':weight, 'b':bias}
def forward_neuralnet_hidden1(x):
# init_model_hidden1 쌍의 파라미터에 접근
global pm_output, pm_hidden
#입력 x와 pm_hidden을 통해 은닉계층 출력 계산 Relu함수 사용
hidden = relu(np.matmul(x, pm_hidden['w']) + pm_hidden['b'])
#hidden과 pm_output을 통해 출력계층 출력이자 최종출력 output 계산
output = np.matmul(hidden, pm_output['w']) + pm_output['b']
#출력 계층의 역전파 처리 때 가중치에 대한 편미분 정보로 hidden이 필요함
return output, [x, hidden]
def relu(x):
return np.maximum(x, 0)
다층 퍼셉트론은 (Hidden input -> Y -> L), (X -> Hidden output -> Hidden input -> Y) 두 단계에서 손실 기울기를 구해야 함
ReLU함수는 기울기 수정을 할 필요가 없기 때문에 두번의 기울기에 대한 미분을 구하면 됨
def backprop_neuralnet_hidden1(G_output, aux):
# G_output은 역전파 후처리 후만들어지는 파라미터
global pm_output, pm_hidden
# 순전파에 사용됐던 input
x, hidden = aux
# 출력층에 대한 역전파
# 출력층에서 사용하는 input은 hidden
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으로부터 G_hidden을 구해내는 과정
g_output_hidden = pm_output['w'].transpose()
# pm_output['w']은 변환이 되는 계수이기 때문에 미리 기록한 것 위에 명시된 식에서 W_2
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
# 은닉층 -> relu -> 출력층 순서
# 역전파는 그 반대로 가야하기 때문에 Relu 부분을 처리해줘야 함 위 식에서 편미분값
G_hidden = G_hidden * relu_derv(hidden)
# 은닉층에 대한 역전파
# 은닉층에서 사용하는 input은 x
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
def relu_derv(y):
return np.sign(y)
def init_model_hiddens():
# 은닉 계층의 수와 폭은 hidden_config리스트를 통해 지정
global pm_output, pm_hiddens, input_cnt, output_cnt, hidden_config
pm_hiddens = []
prev_cnt = input_cnt
# 리스트 성분 개수 = 계층의 수 | 리스트 성분 = 폭
# 반복문을 통해 prev_cnt를 갱신해서 은닉층의 벡터 크기를 맞물리게 갱신
for hidden_cnt in hidden_config:
pm_hiddens.append(alloc_param_pair([prev_cnt, hidden_cnt]))
prev_cnt = hidden_cnt
#은닉층은 반복문이었지만 출력층은 하나이기 때문에 먼저 제시한 1개짜리 함수와 동일
pm_output = alloc_param_pair([prev_cnt, output_cnt])
def forward_neuralnet_hiddens(x):
# pm_hiddens는 앞에서 생성한 파라미터들
global pm_output, pm_hiddens
# 처음에 x로 input되어 첫번째 은닉층의 입력으로 이용됨
hidden = x
# input들이 정리된 리스트
hiddens = [x]
for pm_hidden in pm_hiddens:
# hidden이 다음 층의 입력으로 초기화 됨
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
def backprop_neuralnet_hiddens(G_output, aux):
global pm_output, pm_hiddens
#foward_neuralnet_hiddens에서 파생된 hiddens가 aux로 input됨
hiddens = aux
# 출력층에서부터 역전파 시작 [-1]은 마지막 층을 출력을 가져오기 위해
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)
# 기울기 수정 전 Relu역전파를 위한 기울기 저장
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
# 반복문을 통해 은닉층 역전파, reversed()를 통해 뒤에서부터 가져옴
# hiddens의 길이가 아닌 pm_hiddens의 길이를 반영
# 처리해야하는 횟수는 은닉층의 개수 만큼 (pm_hiddens)이기 때문
for n in reversed(range(len(pm_hiddens))):
# Relu의 역전파 처리 부분
# 입력벡터 pm_hiddens[n]를 relu함수의 역전파 처리를 위해 필요한 출력 벡터 내용에는 hiddens[n+1]로 접근
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
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)
def set_hidden(info):
global hidden_cnt, hidden_config
if isinstance(info, int):
hidden_cnt = info
hidden_config = None
else:
hidden_config = info