[회고] 신입 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로 초기화한다.
반응형