본문 바로가기
파이썬 프로그래밍/Numpy 딥러닝

45.[RNN기초] RNN(many to many) 역전파 구현(이론, 실습)

by Majestyblue 2023. 9. 24.

저번시간에 'hello'라는 단어를 이용하여 아래와 같이 순서대로 출력하는 many to many  RNN 신경망을 어떻게 순전파 할 수 있는지 탐구하였다.

1. h 입력 → e 출력
2. e 입력 → l 출력
3. l 입력 → l 출력
4. l 입력 → o 출력

 

 

이 때 hyper parameters는 아래와 같이 설정하였다.

time_steps = input_RNN.shape[1] # t.s(4)
sequence_length = input_RNN.shape[2] # s.l(4)
hidden_node = 3 # 내가 설정해야 하는 것, h.n(3)
output_feature = targets.shape[1] # o.f(4)

 

 

 

1. many to many 역전파

조금만 생각하면 역전파는 어렵지 않다. 아래와 같이 분할해서 생각해 보면 many to one 역전파를 time step 역순으로 여러 번 실시하는 것과 같다. 

 

 

위의 그림에 따라, 역전파 순서를 글로 표현하면 다음과 같아야 한다.

 

<time step = 3 에서 역전파 실시>

3 번째 fc 분류기 역전파 실시

이전 상태인 fc분류기 기울기를 받고 현재 t= 3 rnn셀 역전파 실시

이전 상태인 rnn셀의 기울기를 받고 현재 t= 2 rnn셀 역전파 실시

이전 상태인 rnn셀의 기울기를 받고 현재 t= 1 rnn셀 역전파 실시

이전 상태인 rnn셀의 기울기를 받고 현재 t= 0 rnn셀 역전파 실시

 

<time step = 2 에서 역전파 실시>

2 번째 fc 분류기 역전파 실시

이전 상태인 fc분류기 기울기를 받고 현재 t= 2 rnn셀 역전파 실시

이전 상태인 rnn셀의 기울기를 받고 현재 t= 1 rnn셀 역전파 실시

이전 상태인 rnn셀의 기울기를 받고 현재 t= 0 rnn셀 역전파 실시

 

<time step = 1 에서 역전파 실시>

1 번째 fc 분류기 역전파 실시

이전 상태인 fc분류기 기울기를 받고 현재 t= 1 rnn셀 역전파 실시

이전 상태인 rnn셀의 기울기를 받고 현재 t= 0 rnn셀 역전파 실시

 

<time step = 0 에서 역전파 실시>

0 번째 fc 분류기 역전파 실시

이전 상태인 fc분류기 기울기를 받고 현재 t= 0 rnn셀 역전파 실시

 

 

위를 다중 for문으로 표현하면 아래와 같다.

for t_s in reversed(range(time_steps)):
  print(" ")
  print("<time step = ", t_s, "에서 역전파 실시>")
  print(t_s,"번째 fc 분류기 역전파 실시")
  for t in reversed(range(t_s+1)) :
    if t == t_s:
      print("이전 상태인 fc분류기 기울기를 받고 현재 t=",t, "rnn셀 역전파 실시")
    else:
      print("이전 상태인 rnn셀의 기울기를 받고 현재 t=",t, "rnn셀 역전파 실시")

many to one과 다른 점은, fc 분류기도 for문으로 순회를 돈다는 것이다. 그외의 코드는 거의 같다.

 

 

 

아래는 역전파 코드이다 many to one과 차이점은 다음과 같다.

  • Wy, By의 기울기를 누적할 변수(list) 추가
  • 첫 번째 for문 for t_s in reversed(range(time_steps)): 에서는 fc 분류기에 대한 역전파를 실시
    fc 분류기 역전파 실시 후 기울기 변수에 추가
  • 두 번째 for문 for t in reversed(range(t_s+1)): 에서는 rnn 셀에 대한 역전파를 실시
    rnn 셀 역전파 실시 후 기울기 변수에 추가
def loss_gradient(X, Y):

  dE_dWxh_list = np.zeros_like(Wxh) # (s.l(4), h.n(3)) 기울기 누적 변수
  dE_dWhh_list = np.zeros_like(Whh) # (h.n(3), h.n(3)) 기울기 누적 변수
  dE_dBh_list = np.zeros_like(Bh) # (1, 1) 기울기 누적 변수
  dE_dWy_list = np.zeros_like(Wy) # (h.n(3), o.f(4)) 기울기 누적 변수
  dE_dBy_list = np.zeros_like(By) # (1, 1) 기울기 누적 변수

  #순전파
  predics, pred_list, pred_i_list, ht_list, ht_i_list  = rnn_cell(X)
  # hi_i_list는 [h0_i, h1_i, h2_i, h3_i] 순서
  # ht_list는 [h-1, h0, h1, h2, h3] 순서
  loss = CEE_loss(predics, Y)
  count = 0
  for t_s in reversed(range(time_steps)):
    # 역전파 실시
    dE_dpred_i = pred_list[t_s] - Y[t_s]

    #fc 분류기에 대한 기울기 구하기 실시
    dpred_i_dWy = np.transpose(ht_list[t_s+1], (1, 0))
    dE_dWy = np.dot(dpred_i_dWy, dE_dpred_i) 
    dE_dBy = np.sum(dE_dpred_i, keepdims=True)
    dE_dWy_list += dE_dWy
    dE_dBy_list += dE_dBy

    for i in reversed(range(t_s+1)) :
      if i == t_s:
        dht_plus1_i_dht = np.transpose(Wy, (1, 0)) 
        dE_dth_plus1_i = dE_dpred_i
      else:
        dht_plus1_i_dht = np.transpose(Whh, (1, 0)) 
        dE_dth_plus1_i = dE_dht_i

      dE_dht = np.dot(dE_dth_plus1_i, dht_plus1_i_dht) 
      dht_dht_i = (1-np.power(np.tanh(ht_i_list[i]), 2)) 
      dE_dht_i = dE_dht * dht_dht_i 

      # dE_dWxh 구하기
      dht_i_dWxh = np.transpose(np.reshape(X[i], (1, -1)), (1, 0)) 
      # X를 그냥 가지고 오면 1차원 배열이여서 2차원으로 바꾸어주어야 함
      dE_dWxh = np.dot(dht_i_dWxh, dE_dht_i) 

      # dE_dWhh 구하기
      dht_i_dWhh = np.transpose(ht_list[i], (1, 0)) 
      dE_dWhh = np.dot(dht_i_dWhh, dE_dht_i) 

      # dE_dBh 구하기
      dE_dBh = np.sum(dE_dht_i, keepdims=True) 

      # 기울기 누적
      dE_dWxh_list += dE_dWxh
      dE_dWhh_list += dE_dWhh
      dE_dBh_list += dE_dBh

  return dE_dWy_list, dE_dBy_list, dE_dWxh_list, dE_dWhh_list, dE_dBh_list

print(loss_gradient(input_RNN[0], targets[0]))

 

잘 될까? 아래 경사하강법으로 훈련하고 결과를 확인해 보자

# 훈련 전 예측값
pred, _, _, _, _ = rnn_cell(input_RNN[0])
print('before pred', pred)

learning_rate = 0.005
for i in range(2000):
  dL_dWy, dL_dBy, dL_dWxh, dL_dWhh, dL_dBh = loss_gradient(input_RNN[0], target[0])
  Wy = Wy + -1*learning_rate * dL_dWy
  By = By + -1*learning_rate * dL_dBy
  Wxh = Wxh + -1*learning_rate * dL_dWxh
  Whh = Whh + -1*learning_rate * dL_dWhh
  Bh = Bh + -1*learning_rate * dL_dBh
  if i % 100 == 0:
    pred, _, _, _, _ = rnn_cell(input_RNN[0])
    print(i, CEE_loss(pred, targets[0]))

출력결과

before pred

[ [0.06397614 0.03819114 0.85299781 0.0448349 ]

  [0.0403228 0.76902459 0.06926133 0.12139129]

  [0.27148641 0.47031804 0.03033868 0.22785687]

  [0.19165547 0.02808298 0.7083062 0.07195535] ]

0 11.995305385065343

100 8.542236164743558

200 7.253592358973341

300 6.544396677982483

400 5.995739152192827

500 5.742292420310077

600 5.6522130480782815

700 5.612021495038995

800 5.58417699010521

900 5.565104086398525

1000 5.5549817621692865

1100 5.5502348707000575

1200 5.547996947460872

1300 5.546874182259792

1400 5.546265877072143

1500 5.545911904998333

1600 5.545693327490806

1700 5.545551769519191

1800 5.545456506378257

1900 5.545390345216081

 

# 훈련 후 예측값
pred, _, _, _, _ = rnn_cell(input_RNN[0])
print('after pred', pred)

출력 결과

after pred

[ [0.24983103 0.2500011 0.24998167 0.2501862 ]

  [0.24993175 0.24999342 0.24999242 0.25008242]

  [0.25008197 0.25001243 0.2500092 0.2498964 ]

  [0.25005301 0.24998581 0.25000541 0.24995576] ]

 

 

2. RNN 역전파 문제점과 Truncated

결과를 보면 전부 0.25다. 직관력을 사용하여 왜 이렇게 된 건지 설명할 수 있다.

softmax 공식을 생각할 때 softmax를 통과하는 모든 값이 0이라면, 1/4이 출력된다.

a = np.array([0, 0, 0, 0])
softmax = np.exp(a) / np.sum(np.exp(a))
print(softmax)

출력결과

[0.25 0.25 0.25 0.25]

 

softmax값을 통과하고자 하는 값들이 0이 되었다는 것인데 아래와 같이 fc 분류기와 rnn 셀 연산을 생각할 때 모두 0이 나오는 경우는 가중치의 값들이 0이 되었다는 것이다!

가중치의 값들이 0이 되었다는 것은 바로 vanishing gradient, 기울기 소실이 발생했다는 것이다.

(16. 다층 퍼셉트론(MPL)의 등장-2.비선형 회귀식(기초이론) (tistory.com) 이 포스팅 참고, sigmoid 함수의 vanishing gradient 문제로 relu를 사용했다는 사실을 다시 한번 기억해 보자)

 

잘 작동되는 것처럼 보이는 RNN 역전파를 시행할 때 여러가지 문제점이 있다.

  •  아래와 같이 tanh의 기울기는 크기가 0보다 크고 1보다 작다. 따라서 tanh 미분값이 역전파 시 계속해서 곱해진다면 기울기가 작아져서 소멸하는 vanishing gradient 문제를 발생시킨다.

http://ronny.rest/blog/post_2017_08_16_tanh/

 

  • 위의 역전파 과정을 보면 알겠지만 x오차에따른 rnn셀의 가중치 기울기 계산의 총 횟수를 따지면 time step = 0은 4번, time step = 1은 3번 ... time step = 3은 1번 밖에 시행하지 않는다. 이는 편향의 문제를 발생시킬 수 있다.

  • 이러한 복잡적인 문제들로 인해 기울기를 무작정 누적시킨다면 기울기값이 0에 수렴하거나 발산할 수 있다.

이를 해결하기 위해 아래와 같이 경사하강법을 끉어서 적용하는 Truncated 방법(TBPTT)이 있다.

https://calvinfeng.gitbook.io/machine-learning-notebook/supervised-learning/recurrent-neural-network/recurrent_neural_networks

 

 

아래와 같이, 경사하강법을 time step별로 실시하고 적용할 것이다.

 

 

 

 

Truncated를 적용한 코드를 살펴보자. loss_gradient 함수에서 바로 경사하강법을 적용할 것이다. 이를 위해 몇 가지 변경점을 살펴보자.

  • 경사하강법을 함수 내에서 적용하기 위해, 가중치 변수들을 지역 변수(global)로 선언해야 한다.
  • Wy, By를 곧바로 경사하강법을 적용시킬 것이므로 Wy, By에 대한 기울기 누적 함수는 필요 없다.
  • time step 단위로 경사강법을 실시한다.
def loss_gradient(X, Y, lr):
  global Wy, By, Wxh, Whh, Bh # 전역 변수 선언 

  dE_dWxh_list = np.zeros_like(Wxh) 
  dE_dWhh_list = np.zeros_like(Whh)
  dE_dBh_list = np.zeros_like(Bh)
  # Wy, By 누적 변수 삭제

  #순전파
  predics, pred_list, pred_i_list, ht_list, ht_i_list  = rnn_cell(X)
  # hi_i_list는 [h0_i, h1_i, h2_i, h3_i] 순서
  # ht_list는 [h-1, h0, h1, h2, h3] 순서
  loss = CEE_loss(predics, Y)
  count = 0
  for t_s in reversed(range(time_steps)):
    # 역전파 실시
    dE_dpred_i = pred_list[t_s] - Y[t_s] 

    #fc 분류기에 대한 기울기 구하기 실시
    dpred_i_dWy = np.transpose(ht_list[t_s+1], (1, 0)) 
    dE_dWy = np.dot(dpred_i_dWy, dE_dpred_i) 
    dE_dBy = np.sum(dE_dpred_i, keepdims=True)

    for i in reversed(range(t_s+1)) :
      if i == t_s:
        dht_plus1_i_dht = np.transpose(Wy, (1, 0))
        dE_dth_plus1_i = dE_dpred_i
      else:
        dht_plus1_i_dht = np.transpose(Whh, (1, 0)) 
        dE_dth_plus1_i = dE_dht_i

      dE_dht = np.dot(dE_dth_plus1_i, dht_plus1_i_dht) 
      dht_dht_i = (1-np.power(np.tanh(ht_i_list[i]), 2))
      dE_dht_i = dE_dht * dht_dht_i 

      # dE_dWxh 구하기
      dht_i_dWxh = np.transpose(np.reshape(X[i], (1, -1)), (1, 0))
      # X를 그냥 가지고 오면 1차원 배열이여서 2차원으로 바꾸어주어야 함
      dE_dWxh = np.dot(dht_i_dWxh, dE_dht_i) 

      # dE_dWhh 구하기
      dht_i_dWhh = np.transpose(ht_list[i], (1, 0)) 
      dE_dWhh = np.dot(dht_i_dWhh, dE_dht_i) 

      # dE_dBh 구하기
      dE_dBh = np.sum(dE_dht_i, keepdims=True) 

      # 기울기 누적
      dE_dWxh_list += dE_dWxh
      dE_dWhh_list += dE_dWhh
      dE_dBh_list += dE_dBh

  # time step 단위로 경사하강법 적용
  Wy = Wy + -1*lr * dE_dWy
  By = By + -1*lr * dE_dBy
  Wxh = Wxh + -1*lr * dE_dWxh_list
  Whh = Whh + -1*lr * dE_dWhh_list
  Bh = Bh + -1*lr * dE_dBh_list

print(loss_gradient(input_RNN[0], targets[0], lr=0.001))

 

loss_gradient를 여러번 호출하여 경사하강법을 실시한다.

# 훈련 전 예측값
pred, _, _, _, _ = rnn_cell(input_RNN[0])
print('before pred', pred)

for i in range(2000):
  loss_gradient(input_RNN[0], targets[0], lr=0.005)
  if i % 100 == 0:
    pred, _, _, _, _ = rnn_cell(input_RNN[0])
    print(i, CEE_loss(pred, targets[0]))

출력결과

before pred

[ [0.06421967 0.03826087 0.85256996 0.0449495 ]

  [0.04060981 0.76764176 0.07027615 0.12147228]

  [0.26839292 0.47363898 0.03059547 0.22737263]

  [0.19292056 0.02834739 0.70545919 0.07327286] ]

0 11.79515658524334

100 4.959578421445732

200 3.8864902061821684

300 3.3365350067287416

400 3.0671210403292317

500 2.97612582400557

600 2.9461502059859317

700 2.9154979716596223

800 2.8759475780457127

900 2.840092041101867

1000 2.8161915388443486

1100 2.8019042952954507

1200 2.7896491419146354

1300 2.7656251067003956

1400 2.699229231984696

1500 2.5950109611261034

1600 2.4972545725645796

1700 2.417265166209865

1800 2.3579062697518554

1900 2.316414169055823

 

# 훈련 후 예측값
pred, _, _, _, _ = rnn_cell(input_RNN[0])
print('after pred', pred)

출력 결과

after pred

[ [0.01002641 0.96251158 0.02394973 0.00351228]

  [0.04563556 0.35968796 0.36162614 0.23305034]

  [0.09699241 0.10147225 0.41357891 0.38795642]

  [0.16231759 0.00431234 0.12814991 0.70522016] ]

 

 

약 2.3의 오차로 수렴하였고 출력 결과는 깔끔하지 않다. 이건 RNN 셀의 은닉 노드 수가 부족하다는 뜻이다.아래와 같이 hidden node수를 3에서 6으로 늘리고 수행해 보면 아래와 같다.

time_steps = input_RNN.shape[1] # t.s(4)
sequence_length = input_RNN.shape[2] # s.l(4)
hidden_node = 6 # 내가 설정해야 하는 것, 3에서 6으로 증가
output_feature = targets.shape[1] # o.f(4)

 

출력 결과

before pred

[ [3.24394454e-01 3.89065541e-01 6.23646205e-02 2.24175385e-01]

  [1.69186579e-01 2.30567092e-01 3.42921900e-03 5.96817110e-01]

  [6.55115321e-03 9.26344723e-01 2.39628192e-04 6.68644959e-02]

  [6.20281475e-04 6.23833255e-01 7.67802601e-04 3.74778661e-01] ]

0 15.867816298322511

100 1.7177823033832111

200 1.111257945581254

300 0.8524019952247719

400 0.6141454089919963

500 0.4375735437356167

600 0.35056229976798425

700 0.2952008157918694

800 0.25682387009542507

900 0.22856920030817837

1000 0.20684935044625707

1100 0.1896041743061186

1200 0.17556253753538015

1300 0.16389573067107027

1400 0.15403950705062275

1500 0.14559595572188555

1600 0.13827620932799925

1700 0.1318653873817163

1800 0.12620028791289195

1900 0.1211547117310094

 

 

after pred

[ [0.00253563 0.98030863 0.00392367 0.01323207]

  [0.00124215 0.00208411 0.98927185 0.00740189]

  [0.01060847 0.00131486 0.97464874 0.01342794]

  [0.01737727 0.03434559 0.00681429 0.94146284] ]

 

 

아주 깔끔하고 올바른 결과가 도출되었다! 이러한 vanishing gradient 문제를 해결하기 위해 LSTM, GRU 방법이 거론되었지만 만족스럽지 못하였고, 2017년 google의 Attention all you need 라는 논문을 통해 Attention 기법이 크게 떠올랐고 아예 rnn 셀이 제거된 transformer 구조가 탄생하여 자연어 인공지능의 새로운 장을 열었다.

 

RNN은 현재 쓸모없지 않을까? 싶지만 자연어 처리 인공지능의 기본인 만큼 이를 잘 이해한다면 Attention과 Transformer 구조를 잘 이해할 수 있을 것이다.

 

아래는 전체 코드이다.

import numpy as np
np.random.seed(230912)
dict_heel = {'h':np.array([1, 0, 0, 0]), \
             'e':np.array([0, 1, 0, 0]), \
             'l':np.array([0, 0, 1, 0]), \
             'o':np.array([0, 0, 0, 1]), \
             }

# input 값을 설정
inputs = np.array([])
for text in "hell":
  for key, value in dict_heel.items():
    if key == text:
     if inputs.size == 0:
       inputs = np.concatenate([inputs, value])
     else:
       inputs = np.vstack([inputs, value])

input_RNN = np.reshape(inputs, (1, 4 ,-1 ))

# target 값을 설정
target = np.array([])
for text in "ello":
  for key, value in dict_heel.items():
    if key == text:
     if target.size == 0:
       target = np.concatenate([target, value])
     else:
       target = np.vstack([target, value])

targets = np.reshape(target, (1, 4, -1))

time_steps = input_RNN.shape[1] # t.s(4)
sequence_length = input_RNN.shape[2] # s.l(4)
hidden_node = 6 # 내가 설정해야 하는 것, 3에서 6으로 증가
output_feature = targets.shape[1] # o.f(4)

Wxh = np.random.randn(sequence_length, hidden_node) # (s.l(4), h.n(3))
Whh = np.random.randn(hidden_node, hidden_node) # (h.n(3), h.n(3))
Bh = np.random.randn(1, 1) # (1, 1)

Wy = np.random.randn(hidden_node, output_feature) # (h.n(3), o.f(4))
By = np.random.randn(1, 1) # (1, 1)

def rnn_cell(data):
  h = np.zeros((1, hidden_node)) #(1, 3) h_-1 상태이며 사실 없으므로 0이다. 반복문을 위해 넣어줌.
  # 역전파 계산을 위해 만든 list들
  ht_i_list = []
  ht_list = []
  pred_i_list = []
  pred_list = []
  ht_list.append(h) # 규칙적인 역전파를 위해 h_-1 상태를 입력함.
  count = 0
  for sequence in data:
    x = np.reshape(sequence, (1, -1))
    h_i = np.dot(x, Wxh) + np.dot(h, Whh) + Bh
    h = np.tanh(h_i)
    pred_i = np.dot(h, Wy) + By
    pred = np.exp(pred_i)/np.sum(np.exp(pred_i))

    ht_i_list.append(h_i)
    ht_list.append(h)
    pred_i_list.append(pred_i)
    pred_list.append(pred)
    if count == 0:
      total_pred = pred.copy()
    else:
      total_pred = np.concatenate([total_pred, pred], axis=0)
    count = count + 1
  return total_pred, pred_list, pred_i_list, ht_list, ht_i_list

# 오차 계산
def CEE_loss(y_hat, y):
  loss =  np.sum(-y*np.log(y_hat))
  return loss

def loss_gradient(X, Y, lr):
  global Wy, By, Wxh, Whh, Bh

  dE_dWxh_list = np.zeros_like(Wxh) 
  dE_dWhh_list = np.zeros_like(Whh) 
  dE_dBh_list = np.zeros_like(Bh) 

  #순전파
  predics, pred_list, pred_i_list, ht_list, ht_i_list  = rnn_cell(X)
  # hi_i_list는 [h0_i, h1_i, h2_i, h3_i] 순서
  # ht_list는 [h-1, h0, h1, h2, h3] 순서
  loss = CEE_loss(predics, Y)
  count = 0
  for t_s in reversed(range(time_steps)):
    # 역전파 실시
    dE_dpred_i = pred_list[t_s] - Y[t_s] 

    #fc 분류기에 대한 기울기 구하기 실시
    dpred_i_dWy = np.transpose(ht_list[t_s+1], (1, 0)) 
    dE_dWy = np.dot(dpred_i_dWy, dE_dpred_i) 
    dE_dBy = np.sum(dE_dpred_i, keepdims=True)

    for i in reversed(range(t_s+1)) :
      if i == t_s:
        dht_plus1_i_dht = np.transpose(Wy, (1, 0))
        dE_dth_plus1_i = dE_dpred_i
      else:
        dht_plus1_i_dht = np.transpose(Whh, (1, 0))
        dE_dth_plus1_i = dE_dht_i

      dE_dht = np.dot(dE_dth_plus1_i, dht_plus1_i_dht)
      dht_dht_i = (1-np.power(np.tanh(ht_i_list[i]), 2)) 
      dE_dht_i = dE_dht * dht_dht_i 

      # dE_dWxh 구하기
      dht_i_dWxh = np.transpose(np.reshape(X[i], (1, -1)), (1, 0)) # (s.l(2), 1)
      # X를 그냥 가지고 오면 1차원 배열이여서 2차원으로 바꾸어주어야 함
      dE_dWxh = np.dot(dht_i_dWxh, dE_dht_i) # (s.l(2), 1) × (1, h.n(3)) = (s.l(2), h.n(3))

      # dE_dWhh 구하기
      dht_i_dWhh = np.transpose(ht_list[i], (1, 0)) # (h.n(3), 1)
      dE_dWhh = np.dot(dht_i_dWhh, dE_dht_i) # (h.n(3), 1) × (1, h.n(3)) = (h.n(3), h.n(3))

      # dE_dBh 구하기
      dE_dBh = np.sum(dE_dht_i, keepdims=True) # (1, h.n(3)) -> (1, 1)

      # 기울기 누적
      dE_dWxh_list += dE_dWxh
      dE_dWhh_list += dE_dWhh
      dE_dBh_list += dE_dBh

  Wy = Wy + -1*lr * dE_dWy
  By = By + -1*lr * dE_dBy
  Wxh = Wxh + -1*lr * dE_dWxh_list
  Whh = Whh + -1*lr * dE_dWhh_list
  Bh = Bh + -1*lr * dE_dBh_list
  
  # 훈련 전 예측값
pred, _, _, _, _ = rnn_cell(input_RNN[0])
print('before pred', pred)

for i in range(2000):
  loss_gradient(input_RNN[0], targets[0], lr=0.005)
  if i % 100 == 0:
    pred, _, _, _, _ = rnn_cell(input_RNN[0])
    print(i, CEE_loss(pred, targets[0]))


# 훈련 후 예측값
pred, _, _, _, _ = rnn_cell(input_RNN[0])
print('after pred', pred)

 

 

3. 마치며

Numpy 딥러닝 시리즈는 당분간 여기에서 멈출 것 같다.

2022년 1월 3일부터 시작하여 23년 9월 24일 RNN을 끝으로 종료하게 되었다. 사실 본업 + 육아로 인해 퇴근하고 나서 밤 10시마다 졸면서(...) 공부하고 작성한 내 자신에게 뿌듯하면서도 성취감을 느낄 수 있었다.(속 시원하다!) 취미로 공부해서 그런지 본업인 물리보다 더 재미있었다는건 안비밀 ㅋㅋㅋㅋ 

 

여기서 다루지 않은 dropout 기법, L1, L2 regularization, 주성분 분석 등 다루어야 할 것은 산더미지만, 딥러닝의 기초를 익히기에는 이 정도면 충분하지 않을까(...) 생각한다. 왜냐면 주 독자를 고등학생, 고등학교를 졸업한지 얼마 안된 1~2학년 대학생 또는 학사 이상의 일반인으로 설정하였기 때문이다. 즉, 이 내용은 개발자들이 공부하는 딥러닝 이론의 '마이너' 이다. 이 글의 목적은 개발자들이 공부하는 딥러닝 이론, 교재가 너무 어렵기 때문에 쉽게 풀어서 설명하기 위해서였다. 

 

그리고 중구난방인 Numpy 딥러닝 시리즈를 정리하여 위키독스(wikidocs)에 다시 작성할 계획이다. 

 

앞으로의 목적은 diffusion 모델과 transformer 구조를 탐구하고 고등학생을 대상으로 수학으로 쉽게 설명하는것이다. 그 때가지 Numpy 딥러닝 시리즈를 탐구한 여러분들께 많은 도움이 되었으면 하는 바램이다. (사실 이걸로 교재도, 책도 쓰고 싶어요... ㅎㅎ)