직접 Numpy로 RNN 층을 구현해보겠습니다. 앞서 메모리 셀에서 은닉 상태를 계산하는 식을 다음과 같이 정의하였습니다.
h{t} = tanh(W{x}X{t} + W{h}h_{t−1} + b)
#의사 코드(pseudocode)로 실제 동작하는 코드가 아님.
hidden_state_t = 0 # 초기 은닉 상태를 0(벡터)로 초기화
for input_t in input_length: # 각 시점마다 입력을 받는다.
output_t = tanh(input_t, hidden_state_t) # 각 시점에 대해서 입력과 은닉 상태를 가지고 연산
hidden_state_t = output_t # 계산 결과는 현재 시점의 은닉 상태가 된다.
우선 t 시점의 은닉 상태를 hidden_state_t라는 변수로 선언하였고, 입력 데이터의 길이를 input_length로 선언하였습니다. 이 경우, 입력 데이터의 길이는 곧 총 시점의 수(timesteps)가 됩니다. 그리고 t 시점의 입력값을 input_t로 선언하였습니다. 각 메모리 셀은 각 시점마다 input_t와 hidden_sate_t(이전 상태의 은닉 상태)를 입력으로 활성화 함수인 하이퍼볼릭탄젠트 함수를 통해 현 시점의 hidden_state_t를 계산합니다.
의사 코드를 통해 간단히 개념 정립을 해보았습니다. 이제 RNN 층을 실제 동작되는 코드로 구현해보겠습니다. 아래의 코드는 이해를 돕기 위해 (timesteps, input_size) 크기의 2D 텐서를 입력으로 받았다고 가정하였으나, 실제로 파이토치에서는 (batch_size, timesteps, input_size)의 크기의 3D 텐서를 입력으로 받는 것을 기억합시다.
import numpy as np
timesteps = 10 # 시점의 수. NLP에서는 보통 문장의 길이가 된다.
input_size = 4 # 입력의 차원. NLP에서는 보통 단어 벡터의 차원이 된다.
hidden_size = 8 # 은닉 상태의 크기. 메모리 셀의 용량이다.
inputs = np.random.random((timesteps, input_size)) # 입력에 해당되는 2D 텐서
hidden_state_t = np.zeros((hidden_size,)) # 초기 은닉 상태는 0(벡터)로 초기화
# 은닉 상태의 크기 hidden_size로 은닉 상태를 만듬.
우선 시점, 입력의 차원, 은닉 상태의 크기, 그리고 초기 은닉 상태를 정의하였습니다. 현재 초기 은닉 상태는 0의 값을 가지는 벡터로 초기화가 된 상태입니다. 초기 은닉 상태를 출력해보겠습니다.
print(hidden_state_t) # 8의 크기를 가지는 은닉 상태. 현재는 초기 은닉 상태로 모든 차원이 0의 값을 가짐.
[0. 0. 0. 0. 0. 0. 0. 0.]
은닉 상태의 크기를 8로 정의하였으므로 8의 차원을 가지는 0의 값으로 구성된 벡터가 출력됩니다. 이제 가중치와 편향을 정의합니다.
Wx = np.random.random((hidden_size, input_size)) # (8, 4)크기의 2D 텐서 생성. 입력에 대한 가중치.
Wh = np.random.random((hidden_size, hidden_size)) # (8, 8)크기의 2D 텐서 생성. 은닉 상태에 대한 가중치.
b = np.random.random((hidden_size,)) # (8,)크기의 1D 텐서 생성. 이 값은 편향(bias).
가중치와 편향을 각 크기에 맞게 정의하였습니다. 가중치와 편향의 크기를 출력해보겠습니다.
print(np.shape(Wx))
print(np.shape(Wh))
print(np.shape(b))
(8, 4)
(8, 8)
(8,)
각 가중치와 편향의 크기는 다음과 같습니다. Wx는 (은닉 상태의 크기 × 입력의 차원), Wh는 (은닉 상태의 크기 × 은닉 상태의 크기), b는 (은닉 상태의 크기)의 크기를 가집니다. 이제 모든 시점의 은닉 상태를 출력한다고 가정하고, RNN 층을 동작시켜봅시다.
total_hidden_states = []
# 메모리 셀 동작
for input_t in inputs: # 각 시점에 따라서 입력값이 입력됨.
output_t = np.tanh(np.dot(Wx,input_t) + np.dot(Wh,hidden_state_t) + b) # Wx * Xt + Wh * Ht-1 + b(bias)
total_hidden_states.append(list(output_t)) # 각 시점의 은닉 상태의 값을 계속해서 축적
print(np.shape(total_hidden_states)) # 각 시점 t별 메모리 셀의 출력의 크기는 (timestep, output_dim)
hidden_state_t = output_t
total_hidden_states = np.stack(total_hidden_states, axis = 0)
# 출력 시 값을 깔끔하게 해준다.
print(total_hidden_states) # (timesteps, output_dim)의 크기. 이 경우 (10, 8)의 크기를 가지는 메모리 셀의 2D 텐서를 출력.
(1, 8)
(2, 8)
(3, 8)
(4, 8)
(5, 8)
(6, 8)
(7, 8)
(8, 8)
(9, 8)
(10, 8)
[[0.90142205 0.90233105 0.86505535 0.9192613 0.92869835 0.63196008
0.91863417 0.83898806]
[0.99964962 0.99992201 0.99990636 0.99999223 0.99983392 0.99856956
0.99985278 0.99995263]
[0.99983638 0.9999857 0.99997938 0.99999745 0.99996956 0.9997384
0.99998289 0.99999282]
[0.99978567 0.99998037 0.9999776 0.99999509 0.99997606 0.9997665
0.99998853 0.99999201]
[0.99931767 0.9999286 0.99993998 0.99998973 0.99991017 0.99962329
0.99996701 0.99998561]
[0.99966482 0.99996424 0.9999506 0.99999377 0.99995004 0.99962537
0.99997232 0.99998558]
[0.99959928 0.99997308 0.99996019 0.99999097 0.99997053 0.99975769
0.99998738 0.99998745]
[0.99935617 0.99995146 0.99992358 0.99998638 0.99994997 0.99966389
0.99997771 0.99997947]
[0.99983393 0.99998834 0.99998857 0.99999708 0.99998027 0.99983651
0.99999232 0.99999567]
[0.99914543 0.99987321 0.99984848 0.99998028 0.9998981 0.99938457
0.99994531 0.99996519]]
'파이썬 > Tensorflow,Pytorch' 카테고리의 다른 글
장단기 메모리(Long Short-Term Memory, LSTM) (0) | 2022.12.14 |
---|---|
파이토치로 RNN구현 (0) | 2022.12.14 |
순환 신경망(Recurrent Neural Network, RNN) (1) | 2022.12.14 |
Tensorflow 소프트맥스 회귀(IRIS데이터 분류 시각화) (0) | 2022.12.13 |
Error Could not locate zlibwapi.dll. Please make sure it is in your library path (0) | 2022.12.13 |