Just Fighting
RNN 개념정리 본문
< RNN >
- 입력과 출력을 시퀀스 단위로 처리하는 시퀀스 모델 ( 시퀀스 : 단어가 나열된 모델)
- 가장 기본적인 인공 신경망 시퀀스 모델
- 은닉층의 노드에서 활성화 함수를 통해 나온 결과값을 출력층 방향으로도 보내고,
다시 은닉층 노드의 다음 계산의 입력으로도 보냄
- 은닉층에서 활성화 함수를 통해 결과를 내보내는 노드를 셀(cell)이라고 함.
- 셀은 이전의 값을 기억하려는 메모리 역할을 수행해 메모리 셀 또는 RNN 셀이라고 표현
- 은닉층의 메모리 셀은 각각의 시점에서 바로 이전 시점의 은닉층의 메모리 셀에서 나온 값을 자신의 입력으로 사용
>>> 재귀적 활동
- 메모리 셀이 출력층 방향 또는 다음 시점인 t+1의 자신에게 보내는 값을 은닉상태라고 함.
- RNN은 입력과 출력의 길이를 다르게 설계할 수 있음
- 일 대 다 : 하나의 입력에 대해 여러 개의 출력의 의미 >> 하나의 이미지에 사진의 제목 출력
- 다 대 일 : 단어 시퀀스에 대해서 하나의 출력을 함. >> 감성분류(입력문서의 긍부정 판별), 스팸메일 분류
- 다 대 다 : 챗봇(사용자 문장 입력 > 대답 문장 출력), 번역기 등
은닉층에서는 t시점의 입력에 가중치를 곱한 값과 t-1시점의 은닉상태에 가중치를 곱한 값과 bias 값을 더해준 뒤
하이퍼볼릭탄젠트 함수를 이용해 t시점에서의 은닉상태값을 구한다.
그리고 출력층에서는 t시점의 은닉상태와 가중치가 곱해진 값에 bias를 더해 활성화 함수를 거친 결과값을 계산한다.
< 케라스로 RNN 구현하기 >
hidden_units : 은닉상태의 크기 => 메모리 셀의 용량
input_length : timesteps, 입력 시퀀스의 길이. 시점의 수 => 문장의 길이
input_dim : 입력의 크기. 입력의 차원 => 단어 벡터의 차원
tensorflow의 SimpleRNN을 사용한다.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import SimpleRNN
model = Sequential()
model.add(SimpleRNN(3, input_shape=(2,10))) # 3*3 + 3*10 + 3 = 42
# model.add(SimpleRNN(3, input_length=2, input_dim=10))와 동일함.
model.summary()
model = Sequential()
model.add(SimpleRNN(3, batch_input_shape=(8,2,10))) # 8은 batch_size. 위에는 설정 안해줘서 None
model.summary()
model = Sequential()
model.add(SimpleRNN(3, batch_input_shape=(8,2,10), return_sequences=True)) # return_sequence=True 면 3D 텐서를 리턴, 아니면 2D 리턴
model.summary()
< 파이썬으로 RNN 구현하기 >
입력 시퀀스의 길이를 10, 입력의 크기를 4, 은닉상태의 크기를 8로 설정하고,
임의로 (10,4)의 2D텐서를 입력으로 하고, 초기 은닉상태는 영벡터로 초기화했다.
import numpy as np
timesteps = 10 # 입력 시퀀스의 길이. 시점의 수 > 문장의 길이
input_dim = 4 # 입력의 크기 > 단어 벡터의 차원
hidden_units = 8 # 은닉 상태의 크기 > 메모리 셀의 용량
# 입력에 해당되는 2D 텐서
inputs = np.random.random((timesteps, input_dim))
# 초기 은닉 상태는 0(벡터)로 초기화
hidden_state_t = np.zeros((hidden_units,))
그리고 계산에 이용할 가중치들을 생성해준다.
# 가중치
Wx = np.random.random((hidden_units, input_dim)) # (8,4) 크기의 2D 텐서 생성. 입력에 대한 가중치
Wh = np.random.random((hidden_units, hidden_units)) # (8,8) 크기의 2D 텐서 생성. 은닉 상태에 대한 가중치
b = np.random.random((hidden_units,)) # (8,) 크기의 1D 텐서 생성. 편향(bias)
이제 모든 시점의 은닉상태를 계산할 수 있다.
total_hidden_states = []
for input_t in inputs: # 10번(timesteps) 반복. totol_hidden_states 길이가 10이 될 것.
# Wx * Xt + Wh * Ht-1 + b(bias)
output_t = np.tanh(np.dot(Wx, input_t)+np.dot(Wh, hidden_state_t) + b)
total_hidden_states.append(list(output_t))
hidden_state_t = output_t # 이번 출력이 다음 셀로 가게 됨
total_hidden_states = np.stack(total_hidden_states, axis=0) # 배열을 깔끔하게 보여줌
total_hidden_states # 모든 시점의 은닉 상태
< 깊은 순환 신경망 >
RNN은 다수의 은닉층을 가질 수 있다.
아래 사진은 은닉층이 2개인 깊은 순환 신경망이다.
model = Sequential()
model.add(SimpleRNN(hidden_units, input_length=10, input_dim=5, return_sequences=True)) # return_sequences=True로 인해 모든 시점에 대해서 은닉 상태의 값을 다음 은닉층으로 보내줌
model.add(SimpleRNN(hidden_units, return_sequences=True))
< 양방향 순환 신경망 >
- 시점 t에서의 출력값을 예측할 때 '이전 시점'과 '이후 시점'의 입력 모두 예측에 기여할 수 있다는 아이디어
- 문장에 구멍을 뚫어 놨을 경우, 앞 뒤를 봐야 단어를 채울 수 있다.
- 하나의 출력값을 예측하기 위해 기본적으로 두개의 메모리 셀을 사용
- 첫번째 메모리 셀은 앞 시점의 은닉상태를 전달받아 현재의 은닉상태 계산
- 두번째 메모리 셀은 뒤 시점의 은닉상태를 전달받아 현재의 은닉상태 계산 >> 입력 시퀀스를 반대방향으로 읽는 것.
from tensorflow.keras.layers import Bidirectional
timesteps = 10
input_dim = 5
model = Sequential()
model.add(Bidirectional(SimpleRNN(hidden_units, return_sequences=True), input_shape = (timesteps, input_dim)))
# 양방향 RNN도 다수의 은닉층을 가질 수 있음
model = Sequential()
model.add(Bidirectional(SimpleRNN(hidden_units, return_sequences=True), input_shape=(timesteps, input_dim)))
model.add(Bidirectional(SimpleRNN(hidden_units, return_sequences=True)))
model.add(Bidirectional(SimpleRNN(hidden_units, return_sequences=True)))
model.add(Bidirectional(SimpleRNN(hidden_units, return_sequences=True)))
'ML & DL' 카테고리의 다른 글
LSTM 개념 정리 및 실습 (0) | 2022.07.24 |
---|---|
SimpleRNN 이해하기 (0) | 2022.07.24 |
[PySpark] 나이브 베이즈 실습 (0) | 2022.06.09 |
[개념 정리] 나이브 베이즈 (Naive Bayes) (0) | 2022.06.08 |
LSTM을 이용한 텍스트 생성 연습(2) - 모델 생성 (2) | 2022.06.02 |