-
[PyTorch] RNN 모듈 알아보기AI-ML 2023. 12. 29. 06:31728x90반응형
- 목차
들어가며.
PyTorch 에는 내장된 RNN 모듈이 존재합니다.
torch.nn.RNN 클래스를 사용하여 손쉽게 RNN 모듈을 사용 가능합니다.
다만 PyTorch 의 내장 RNN 모듈이 사용법이 간단히 와닿지 않는 경향이 있어서 이번 글을 통해 그 사용법에 대해서 명확히 하려고 합니다.
기본적인 사용법 알아보기.
RNN 모듈은 기본적으로 아래와 같이 생성합니다.
RNN 모듈은 필수적으로 input_size 와 hidden_size 값을 입력으로 받아야합니다.
import torch.nn as nn rnn = nn.RNN(input_size=5, hidden_size=10)
input_size 는 입력 텐서의 차원의 크기가 됩니다.
예를 들어서, NLP 모델에서 단어 하나하나가 입력으로 사용된다고 가정해보겠습니다.
그리고 사용된 모든 단어의 수가 10 일 때에 One Hot Encoding 을 적용한다면 input_size 의 값은 10이 됩니다.
또한 주가를 예측하는 모델에서 "시가", "종가" 2개의 Feature 를 사용한다고 한다면, 이는 input_size 가 2가 됩니다.
일반적으로 아래와 같은 방식으로 사용될 수 있는데요.
sequence, batch, feature, hidden 등의 헷갈리는 용어들을 정리하는 시간을 가져보도록 하겠습니다.
import torch import torch.nn as nn input_size = 5 batch_size = 1 sequence_length = 4 hidden_size = 32 rnn = nn.RNN(input_size=input_size, hidden_size=hidden_size) input_tensor = torch.ones(sequence_length, batch_size, input_size) hidden_tensor = torch.zeros(1, batch_size, hidden_size) output, hidden = rnn(input_tensor, hidden_tensor)
hidden_size.
RNN 은 내부적인 Memory Cell 을 구현하기 위해서 hidden state 를 사용합니다.
아래의 이미지와 같은 방식으로 hidden state 는 업데이트되어 갑니다.
업데이트의 과정에서 X0, X1, ..., Xn 에 해당하는 모든 Input Data 에 영향을 받게 되는되요.
이러한 과정을 거치면서 hidden state 는 Memory Cell 로써 역할을 수행할 수 있게 됩니다.
$$ H_{1} = tanh( W_{xh} \times X_{1} + W_{hh} \times H_{0} )$$
hidden_size 는 RNN 모듈을 생성할 때에 명시적으로 그리고 필수적으로 선언해야하는 파라미터입니다.
아래와 같은 형식으로 hidden_size 가 필요하며, hidden_size 와 input_size 를 설정한 RNN 모듈은 내부적인 weight 와 bias 를 확인할 수 있습니다.
import torch.nn as nn hidden_size = 32 input_size = 5 rnn = nn.RNN(input_size=input_size, hidden_size=hidden_size) print(f'rnn.weight_hh_l0: {rnn.weight_hh_l0.shape}') print(f'rnn.weight_ih_l0: {rnn.weight_ih_l0.shape}') print(f'rnn.bias_ih_l0: {rnn.bias_ih_l0.shape}') print(f'rnn.bias_ih_l0: {rnn.bias_ih_l0.shape}')
rnn.weight_hh_l0: torch.Size([32, 32]) rnn.weight_ih_l0: torch.Size([32, 5]) rnn.bias_ih_l0: torch.Size([32]) rnn.bias_ih_l0: torch.Size([32])
input_size.
input_size 또한 hidden_size 와 같이 PyTorch RNN 모듈이 필수적으로 입력받는 파라미터입니다.
input_size 와 hidden_size 를 통해서 RNN 에서 사용하는 Weight 를 생성할 수 있습니다.
생성된 RNN 모델은 내부적으로 weight_ih_l0 이라는 Weight Tensor 를 가집니다.
이름이 의미하는 바는 0번째 Layer 중 Input -> Hidden 으로 변형하기 위한 Weight Tensor 를 뜻합니다.
$$ H_{1} = tanh( W_{xh} \times X_{1} + W_{hh} \times H_{0} )$$
RNN 는 새로운 Output Tensor 와 Hidden State 를 생성하기 위해서 Input 과 Hidden State 의 연산을 반복적으로 수행합니다.
Input 과 Hidden State 는 서로 Shape 가 동일하지 않기 때문에 Input Size 를 Hidden Size 로 변경하는 Matrix 연산이 필요하며,
RNN 은 input_size 와 hidden_size 를 인자로 받아서 Weight 를 생성합니다.
sequence length.
RNN 이 여타 Neural Network 와의 차별점은 순환 신경망이라는 점입니다.
즉, 신경망 내부적으로 Loop 가 존재하고 Hidden State 를 계속 업데이트하게 됩니다.
그럼 이러한 순환 연산은 몇 차례가 수행될까요 ?
무한히 수행되는 것은 아닐겁니다.
순환 횟수는 바로 Sequence Length 에 의해서 결정되는데요.
이 Sequence 가 의미하는 바는
1. 문장을 구성하는 단어의 갯수
2. 시계열 데이터를 구성하는 Window 의 크기
와 같습니다.
예를 들어, 문장의 Sentiment Analysis 를 위해서 10개의 단어들로 구성된 문장은 Sequence Length 가 10이 됩니다.
그리고 주가 예측을 위해서 5일치의 주가 정보를 통해서 다음 날의 주가를 예측한다면, 이때의 Sequence Length 는 5가 됩니다.
이처럼 Sequence Length 만큼 RNN 는 순환하며 Hidden State 를 업데이트합니다.
이러한 Sequence Length 는 PyTorch RNN 에 사용되는 명시적인 파라미터는 아닙니다.
대신 입력 텐서가 Sequence Length 만큼의 데이터를 포함하고 있어야합니다.
예를 들어, 아래와 같이 Sequence Length 가 5 인 입력 텐서는 RNN 모듈에 의해서 torch.Size([5, 1, 32]) 인 텐서를 출력합니다.
또한 Sequence Length 가 10인 입력 텐서는 RNN 모듈에 의해서 orch.Size([10, 1, 32]) 인 텐서를 출력합니다.
input_size = 5 batch_size = 1 sequence_length = 4 import torch.nn as nn hidden_size = 32 input_size = 5 rnn = nn.RNN(input_size=input_size, hidden_size=hidden_size) sequence_length = 5 input_tensor = torch.ones(sequence_length, batch_size, input_size) hidden_tensor = torch.zeros(1, batch_size, hidden_size) output, hidden = rnn(input_tensor, hidden_tensor) print(output.shape) # torch.Size([5, 1, 32]) sequence_length = 10 input_tensor = torch.ones(sequence_length, batch_size, input_size) hidden_tensor = torch.zeros(1, batch_size, hidden_size) output, hidden = rnn(input_tensor, hidden_tensor) print(output.shape) # torch.Size([10, 1, 32])
즉, 입력 텐서가 100인 Sequence Length 를 가진다면 RNN 는 100 회의 순환 연산을 수행합니다.
Sequence Length 는 입력 텐서에 영향을 받는 요소이며,
RNN 은 입력 텐서의 Sequence Length 에 따라서 순환하게 됩니다.
Many to One RNN Model.
만약 Many-to-One RNN Model 을 사용하게 된다면,
RNN 의 출력 텐서에서 가장 마지막 Index 의 Tensor 를 사용하면 됩니다.
이게 무슨의미냐하면,
보통 입력 텐서는 Seq_num x Batch Size x Input Size 에 해당하는 3차원 텐서로 구성됩니다.
그리고 출력 텐서는 Seq_num x Batch Size x Hidden Size 에 해당하는 3차원 텐서로 구성되는데요.
Sequence 의 N 번째 입력 텐서의 출력 텐서는 출력 텐서의 N 번째 텐서에 해당합니다.
예를 들어, "I Love You" 라는 문장을 Input Tensor 로 표현하면
3개의 단어로 구성된 문장으므로 Sequence Length 는 3이며,
1개의 문장이기에 Batch Size 는 1 입니다.
그리고 Input Size 는 편의상 OneHot Encoding 한다는 가정하에 3 이라고 하겠습니다.
이때에 "I" 에 해당하는 출력은 Output Tensor 의 1번째 요소가 되며,
"Love" 에 해당하는 출력은 Output Tensor 의 2번째 요소가 됩니다.
batch size.
batch size 또한 Sequence Length 와 마찬가지로 RNN 을 구성하는 명시적인 요소는 아닙니다.
Input Tensor 에 의해서 결정되는 요소이구요.
일반적으로 입력 텐서의 Shape 는 (Sequence Size x Batch Size x Input Size) 와 같은 3차원으로 구성됩니다.
그 중에서 Batch Size 는 일반적인 신경망에서 사용하는 그 Batch Size 의 개념과 동일합니다.
한번의 Forward Propagation Step 에서 처리할 수 있는 데이터의 양을 의미하죠.
RNN 에서 중요한 Batch Size 의 포인트는 Initial Hidden State 를 구성할 때에도 Batch Size 가 사용된다는 점일 것입니다.
만약에 Input Tensor 의 Shape 가 5 x 3 x 9 라고 한다면,
이는 Sequence Size 가 5 이고, Batch Size 가 3 그리고 Input Feature 의 Size 가 9 일 것입니다.
이 때의 Initial Hidden State 는 1 x Batch Size x Hidden Size 가 됩니다.
즉, Hidden State 에도 Batch Size 가 적용된다는 점에 유의하시면 좋을 것 같네요.
num_layers.
PyTorch 의 RNN 은 num_layers 라는 파라미터를 가집니다.
이는 Hidden Layer 의 크기가 됩니다.
아래의 이미지는 num_layers 의 값이 2인 상태의 RNN 구조입니다.
Hidden Layer 의 크기가 2가 되고, 첫번째 Layer 의 결과가 두번째 Layer 의 입력값으로 사용됩니다.
PyTorch RNN 사용해보기.
아래의 예시는 아주 간단한 Sentiment Analysis 를 위한 RNN 예시입니다.
I Love You 와 I Hate You 에 대한 Sentiment Analysis 을 수행하는 간단한 예시입니다.
Batch Size 는 1, Hidden Size 는 32 으로 설정하였고
문장을 구성하는 단어의 수가 3이므로 자연히 Sequence Length 는 3이 됩니다.
import numpy as np import torch import torch.nn as nn class EmotionRNN(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(EmotionRNN, self).__init__() self.rnn = nn.RNN(input_size, hidden_size) self.dense = nn.Linear(hidden_size, output_size) self.softmax = nn.Softmax(dim=1) def forward(self, input_tensor, hidden_tensor): rnn_output, _ = self.rnn(input_tensor, hidden_tensor) last_one = rnn_output[-1] output = self.dense(last_one) logit = self.softmax(output) return logit dataset = [ ["I Love You", "Good"], ["I Hate You", "Bad"], ] word_to_index_map = { "I": 0, "Love": 1, "Hate": 2, "You": 3, } label_to_index_map = { "Good": 0, "Bad": 1 } n_epoch = 10 input_size = 4 output_size = 2 hidden_size = 32 model = EmotionRNN(input_size, hidden_size, output_size) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) for epoch in range(n_epoch): for data in dataset: sentence = data[0] target = data[1] sentence_to_index = [word_to_index_map[word] for word in sentence.split()] sentence_to_one_hot_vector = [np.eye(4)[index] for index in sentence_to_index] input_tensor = torch.tensor(sentence_to_one_hot_vector, dtype=torch.float) input_tensor = input_tensor.reshape(3, 1, 4) target_to_index = label_to_index_map[target] target_to_one_hot_vector = np.eye(2)[target_to_index] target_tensor = torch.tensor(target_to_one_hot_vector, dtype=torch.float) target_tensor = target_tensor.reshape(1, -1) hidden_state = torch.zeros(1, 1, hidden_size, dtype=torch.float) prediction = model(input_tensor, hidden_state) loss = criterion(prediction, target_tensor) loss.backward() optimizer.step() print(loss.item())
반응형'AI-ML' 카테고리의 다른 글
[pytorch] NLLLoss 알아보기 (Negative Log Likelihood) (0) 2024.02.22 Sigmoid Activation Saturation 알아보기 (0) 2024.01.10 Covariate Shift 알아보기 (0) 2023.12.24 [pytorch] nn.BatchNorm 알아보기 (0) 2023.12.24 [pytorch] optim.SGD 알아보기 (0) 2023.12.13