이전시간에 아래의 순환 셀과 분류기(fc)에 대하여
각 가중치는 아래와 같음을 증명하였다.
- Wxh = ( sequence length(2), hidden node(3) ) → ( 2, 3 )
- Whh = ( hidden node(3), hidden node(3) ) → ( 3, 3 )
- Bh = (1, 1)
- Wy = ( hidden node(3), output feature(1) ) → ( 3, 1 )
- By = (1, 1)
순전파를 코드로 구현해 보자.
1. 데이터 전처리
38차시에서 실시했던 전처리 코드이다. 그대로 시행하면 된다.
import numpy as np
from itertools import *
np.random.seed(230907)
dataset = ["as", "soon", "as"]
datalist = list(set(permutations(dataset, 3)))
# permutations는 중복이 허용됨. 중복을 제거하기 위해 set 함수를 이용함
def parallel_prep(x):
count = 0
for sentence in x:
prep = np.array([])
for word in sentence:
if word == 'as':
word = np.array([1, 0])
elif word == 'soon':
word = np.array([0, 1])
if prep.size == 0:
prep = np.concatenate([prep, word])
else:
prep = np.vstack([prep, word])
if count == 0:
total_prep = prep.copy()
else:
total_prep = np.vstack([total_prep, prep])
count = count + 1
total_prep = np.reshape(total_prep, (-1, 3, 2)) # (batch, time steps, sequence length)
return total_prep
target = np.array([[0],
[0],
[1]])
입력 데이터는 아래와 같다.
2. 가중치들 설정
설정한 가중치를 참고하여 코드로 가중치를 정의한다. 은닉 상태의 노드는 정하기 나름인데
여기서는 3개로 정했다.
- Wxh = ( sequence length(2), hidden node(3) ) → ( 2, 3 )
- Whh = ( hidden node(3), hidden node(3) ) → ( 3, 3 )
- Bh = (1, 1)
- Wy = ( hidden node(3), output feature(1) ) → ( 3, 1 )
- By = (1, 1)
time_steps = input_RNN.shape[1] # t.s(3)
sequence_length = input_RNN.shape[2] # s.l(2)
hidden_node = 3 # 내가 설정해야 하는 것, h.n(3)
output_feature = target.shape[1] # o.f(1)
Wxh = np.random.randn(sequence_length, hidden_node) # (s.l(2), 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(1))
By = np.random.randn(1, 1) # (1, 1)
3. RNN 순전파 구현
아래의 모식도를 살펴보자. 1개의 sequence가 차례대로 time step에 따라 입력된다.
아래 예제는 "as soon as"의 예시이다.
[ [ 1, 0 ],
[ 0, 1 ],
[ 1, 0 ] ]
따라서 1개씩 접근하기 위해 for문을 이용해 볼 수 있다.
아래 for문은 input_RNN[0] 요소인 "as as soon"의 예시이다.
data는 (time steps, sequence length)로 구성되어 있다. "as as soon"의 경우
[ [ 1, 0 ]
[ 1, 0 ]
[ 0, 1 ] ]
이므로 for sequnce in data로 data를 탐색하면 sequnce 단위인
[ 1, 0 ] → [ 1, 0 ] → [ 0, 1 ] 로 진행한다.
# RNN 셀을 위한 for문을 설정해 보자. 1개씩 요소로 접근해야 한다.
def rnn_cell(data):
for sequence in data:
# 1차원 sequence를 2차원으로 변환해야 행렬곱 연산 적용이 쉽다.
x = np.reshape(sequence, (1, -1))
print(x)
rnn_cell(input_RNN[0])
출력결과
[[1. 0.]]
[[1. 0.]]
[[0. 1.]]
표기에 주의할 점이 있는데 하이퍼블릭 탄젠트(tanh)를 통과하기 전 연산을 h`으로 정의하려고 하는데 코드에서 `을 구현하기 힘들어 '를 _i로 표기하고자 한다. 즉 아래와 같다.
위 표기법을 잘 기억하여 아래의 순전파 코드를 확인하자.
# RNN 셀을 위한 for문을 설정해 보자. 1개씩 요소로 접근해야 한다.
# 1개씩 접근한 값을 rnn 연산을 해 보자.
# 마지막 출력에 linear 연산을 걸고 출력하자.
def rnn_cell(data):
h = np.zeros((1, hidden_node)) #(1, 3) h_0일 때 h_-1 상태는 없으므로 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 = 1/(1+np.exp(-pred_i))
return pred
predict = rnn_cell(input_RNN[0])
print(predict)
중요한 점을 이야기해 보자.
- h_0 상태는 존재하지 않지만 역전파 계산 시 규칙성을 적용하기 위해 필요하다. 따라서 np.zeros를 이용하여 0인 h_0상태를 정의한다.
- x → h_i, → h 를 data의 sequence에 따라 순회한다. 즉 [ 1, 0 ] → [ 1, 0 ] → [ 0, 1 ] 로 진행한다.
- sequence 진행이 끝났따면 마지막으로 fc 연산을 수행하여 pred_i → pred로 진행한다.
출력결과
[[0.57238818]]
4. 오차함수 구현
오차함수는 Binary Cross Entropy Error인 이진 교차 엔트로피 오차(BCEE)를 사용한다.
# 첫 번째 데이터에 대한 오차 계산
def BCEE_loss(y_hat, y):
loss = np.sum(-y*np.log(y_hat) - (1-y)*np.log(1-y_hat))
return loss
losses = BCEE_loss(predict, target[0])
print(losses)
출력결과
0.8495394526507959
다음 시간에는 역전파를 구현해 보도록 하겠다.
'파이썬 프로그래밍 > Numpy 딥러닝' 카테고리의 다른 글
42.[RNN기초] RNN(many to one) 역전파 구현(실습) (0) | 2023.09.20 |
---|---|
41.[RNN기초] RNN(many to one) 역전파 구현(이론) (0) | 2023.09.19 |
39.[RNN기초] RNN(many to one) 순전파 구현(이론) (0) | 2023.09.12 |
38.[RNN기초] 자연어 데이터는 어떻게 접근해야 할까? (0) | 2023.09.11 |
37. [CNN기초] 다채널(multi channel) 다루기 (0) | 2023.02.18 |