순환 신경망(Recurrent Neural Network, RNN)
- RNN은 입력과 출력을 시퀀스 단위로 처리하는 시퀀스 모델임
1. 순환 신경망(RNN)의 특징
- RNN은 은닉층의 노드에서 활성화 함수를 통해 나온 결과값을 출력층 방향으로도 보내 다시 은닉층 노드의 다음 계산의 입력으로 보냄
- 셀(cell)은 RNN의 은닉층에서 활성화 함수를 통해 결과를 내보내는 역할을 하는 노드, 이전의 값을 기억하려고 하는 일종의 메모리 역할을 수행하므로 메모리 셀 또는 RNN 셀이라 표현
- 은닉층의 셀은 각각의 시점(time step)에서 바로 이전 시점의 셀에서 나온 값을 자신의 입력으로 사용
- 은닉 상태(hidden state)는 셀이 출력층 방향 또는 다음 시점의 자신에게 보내는 값
- RNN은 입력과 출력의 길이를 다르게 설계할 수 있으므로 다양한 용도로 사용 가능
하나의 입력 여러개 출력 | 예) 하나의 이미지 입력에 대해 사진의 제목을 출력하는 이미지 캡셔닝 등 |
여러개 입력 하나의 출력 | 예) 문장의 긍부정 판별(감성 분류), 스펨 매일 판별 등 |
여러개 입력 여러개 출력 | 예) 입력문장으로부터 대답 문장 출력, 챗봇, 번역, 개체명 인식, 품사 태깅 등 |
2. RNN 구현
# 2D 텐서를 통한 예시
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(벡터)로 초기화
# 초기 은닉상태 출력
print(hidden_state_t) # 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)) # (은닉 상태의 크기 x 입력의 차원)
print(np.shape(Wh)) # (은닉 상태의 크기 x 은닉 상태의 크기)
print(np.shape(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 텐서를 출력.
3. 파이토치의 nn.RNN()
# 도구 임포트
import torch
import torch.nn as nn
# 입력과 은닉 상태 크기 정의
input_size = 5 # 입력의 크기
hidden_size = 8 # 은닉 상태의 크기
# 입력 텐서 정의 / 배치 크기 1, 10번의 시점동안 5차원의 입력 벡터가 들어가도록 텐서 정의
# (batch_size, time_steps, input_size)
inputs = torch.Tensor(1, 10, 5)
# nn.RNN()을 사용해 RNN 셀 생성. 인자로 입력 크기, 은닉 상태의 크기 정의 및 batch_fist = True를 통해 입력 텐서의 첫번째 차원이 배치 크기임을 정의
cell = nn.RNN(input_size, hidden_size, batch_first=True)
# 입력 텐서를 RNN 층에 입력하여 출력 확인
outputs, _status = cell(inputs)
# RNN 셀은 두 개의 입력을 리턴하는데 첫번째 리턴값은 모든 시점(timesteps)의 은닉상태들이며, 두번째 리턴값은 마지막 시점(timesteps)의 은닉상태
# 첫번째 리턴값 출력
print(outputs.shape) # 모든 time-step의 hidden_state, 10번의 시점동안 8차원의 은닉상태가 출력되었음
# 두번째 리턴값 출력
print(_status.shape) # 최종 time-step의 hidden_state
4. 깊은 순환 신경망(Deep Recurrent Nueral Network, DRNN)
- RNN 층에 은닉층을 N개 더 추가
- 파이토치로 DRNN을 구현할 때는 nn.RNN()의 인자인 num_layers에 값을 전달해 층을 쌓는다.
# 층이 2개인 깊은 순환 신경망
# (batch_size, time_steps, input_size)
inputs = torch.Tensor(1, 10, 5)
cell = nn.RNN(input_size = 5, hidden_size = 8, num_layers = 2, batch_first=True)
outputs, _status = cell(inputs)
print(outputs.shape) # 모든 time-step의 hidden_state
# 두번째 리턴값 출력
print(_status.shape) # (층의 개수, 배치 크기, 은닉 상태의 크기)
5. 양방향 순환 신경망(Bidirectional Recurrent Neural Network, BRNN)
- 양방향 순환 신경망은 과거 시점의 데이터와 향후 시점의 데이터를 힌트로 사용하기 위해 고안
- 시점 t에서의 출력값을 예측할 때 이전 시점의 데이터 뿐 아니라 이후 데이터로도 출력값을 예측할 수 있다는 아이디어에 기반
- BRNN은 하나의 출력값을 예측하기 위해 두 개의 메모리 셀을 사용(Forward States, Backword States)
- BRNN을 구현할 때는 nn.RNN()의 인자인 bidirectional의 값을 True로 전달
# 층이 2개인 깊은 순환먕이면서 양방향인 경우 임의의 입력에 대해 출력의 변화 확인
# (batch_size, time_steps, input_size)
inputs = torch.Tensor(1, 10, 5)
cell = nn.RNN(input_size = 5, hidden_size = 8, num_layers = 2, batch_first=True, bidirectional = True)
outputs, _status = cell(inputs)
print(outputs.shape) # (배치 크기, 시퀀스 길이, 은닉 상태의 크기 x 2), 은닉상태의 크기 값이 두배가 됨, 이는 양방향의 은닉 상태들의 값이 연결되었기 때문
print(_status.shape) # (층의 개수 x 2, 배치 크기, 은닉 상태의 크기), 정방향 기준으로는 마지막 시점에 해당되면서, 역방향 기준에서는 첫번째 시점에 해당되는 시점의 출력값을 층의 개수만큼 쌓아올린 값
장단기 메모리(Long Short-Term Memory, LSTM)
- 가장 기본적 형태의 RNN을 바닐라 RNN이라고 부름
- 바닐라 RNN의 한계를 극복하기 위한 변형 중 하나
1. 바닐라 RNN의 한계
- 바닐라 RNN은 출력 겨로가가 이전의 계산 겨로가에 의존, time step이 길어질 수록 앞의 정보가 뒤로 충분히 전달 되지 않음
- 이때문에 중요한 정보가 시점의 앞에 있을 때 충분한 기억력을 갖지 못하므로 답의 정확도가 떨어짐
2. LSTM
- LSTM은 은닉층의 메모리 셀에 입력 게이트, 망각 게이트, 출력 게이트를 추가해 불필요한 기억을 지우고, 기억해야 할 것을 정함
- LSTM은 RNN과 비교할 때 시퀀스의 입력을 처리하는데 탁월한 성능을 보임
3. 파이토치의 nn.LSTM()
# 도구 임포트
import torch
import torch.nn as nn
# 입력과 은닉 상태 크기 정의
input_size = 5 # 입력의 크기
hidden_size = 8 # 은닉 상태의 크기
# 입력 텐서 정의 / 배치 크기 1, 10번의 시점동안 5차원의 입력 벡터가 들어가도록 텐서 정의
# (batch_size, time_steps, input_size)
inputs = torch.Tensor(1, 10, 5)
# nn.LSTM()을 사용해 LSTM 셀 생성. 인자로 입력 크기, 은닉 상태의 크기 정의 및 batch_fist = True를 통해 입력 텐서의 첫번째 차원이 배치 크기임을 정의
cell = nn.LSTM(input_size, hidden_size, batch_first=True)
# 첫번째 리턴값 출력
print(outputs.shape) # 모든 time-step의 hidden_state, 10번의 시점동안 8차원의 은닉상태가 출력되었음
# 두번째 리턴값 출력
print(_status.shape) # 최종 time-step의 hidden_state
참고:
https://wikidocs.net/book/2788
PyTorch로 시작하는 딥 러닝 입문
이 책은 딥 러닝 프레임워크 PyTorch를 사용하여 딥 러닝에 입문하는 것을 목표로 합니다. 이 책은 2019년에 작성된 책으로 비영리적 목적으로 작성되어 출판 ...
wikidocs.net
'AI > PyTorch' 카테고리의 다른 글
PyTorch #다대일 RNN (0) | 2022.04.07 |
---|---|
PyTorch #다대다 RNN (0) | 2022.04.06 |
PyTorch #원-핫 인코딩 #워드 임베딩 (0) | 2022.04.05 |
PyTorch #자연어 데이터 전처리 (0) | 2022.04.04 |
PyTorch #합성곱 신경망(CNN) (0) | 2022.04.01 |