[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🛠 기타/Data & AI

다층퍼셉트론 - 기본구조 구현

inu 2020. 8. 2. 15:30

 

 

단층 퍼셉트론 - 회귀분석 구현

데이터를 기반으로 전복의 고리수를 예측하는 단층 퍼셉트론을 구현해보자. 주어진 데이터는 'Sex', 'Length', 'Diameter', 'Height', 'Whole weight', 'Shucked weight', 'Viscera weight', 'Shell weight', 그리..

inuplace.tistory.com

 

단층 퍼셉트론 - 이진판단 구현

https://inuplace.tistory.com/460?category=912534 단층 퍼셉트론 - 회귀분석 구현 데이터를 기반으로 전복의 고리수를 예측하는 단층 퍼셉트론을 구현해보자. 주어진 데이터는 'Sex', 'Length', 'Diameter', 'He..

inuplace.tistory.com

 

단층 퍼셉트론 - 선택 분류 구현

https://inuplace.tistory.com/460?category=912534 단층 퍼셉트론 - 회귀분석 구현 데이터를 기반으로 전복의 고리수를 예측하는 단층 퍼셉트론을 구현해보자. 주어진 데이터는 'Sex', 'Length', 'Diameter', 'He..

inuplace.tistory.com

  • 이전에 단층 퍼셉트론으로 해결했던 문제들을 다층 퍼셉트론으로 해결해볼 것이다.
  • 먼저 은닉층이 하나일 경우로 구현 후, 가변적 은닉 계층에 대해 구현을 진행하겠다.
  • 수업에 사용된 코드를 분석한 자료입니다.

은닉 계층 하나를 위한 파라미터 생성 함수

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로 초기화한다.