수달이네 기술 블로그

5. RNN(Recurrent Neural Network)기초 본문

AI공부/자연어처리

5. RNN(Recurrent Neural Network)기초

슬픈 수달이 2026. 2. 22. 18:37

시퀀스 데이터

시간적, 순서적 관계가 중요한 데이터.

  • 자연어 문장(단어 순서), 시계열 데이터(주가, 온도 변화), 영상(연속된 이미지), 음성(시간에 따른 파형 변화)

RNN(Recurrent Neural Network)

시계열데이터, 연속적인 데이터를 다룰 때 사용되는 인공신경망

  • CNN, MLP등: 현재의 입력만 계산에 반영
  • RNN, LSTM등: 과거 정보를 계산에 반영

  • RNN의 구조는 위와 같다.
  • 하나하나의 계산처리 단위를 셀(cell)이라 하고 셀 하나의 출력을 은닉상태(hidden state)라 한다.
  • 위 그림을 보면 이전의 출력을 계산에 집어 넣는 것을 확인할 수 있다.

은닉 상태의 계산식

$$ h_t = tanh(W_hh_{t-1}+W_xx_t+b) $$

  • t = 시계열
  • h = 은닉상태
  • x = 입력값
  • W = 가중치
  • b = 편향
  • tanh = 하이퍼볼릭 탄젠트 활성화 함수

계산식을 보면 이전 t-1시계열의 h를 반영해 현재 t를 계산한다.

최종 출력값은

$$ y_t = W_hh_t + b_y $$

  • y = 최종 출력

위의 계산식으로 계산해 보면

$$ 입력값: x_t = 2 \\ 이전\ 은닉\ 상태: h_{t-1}=0.5\\ 가중치\ 값: W_h = 0.8, W_x = 0.6, b = 0.1 $$

일때

$$ h_t = tanh((0.8×0.5)+(0.6×2)+0.1)\\ h_t = tanh(0.4+1.2+0.1)=tanh(1.7)\\ tanh(1.7)=0.935... $$

위와 같이 출력되어 hidden state = 0.935가 된다.

  • 위에서 한 tanh함수는 -1~1범위의 값으로 반환하는 함수이다.

RNN모델을 이미지에 사용해보기

import torch
import torch.nn
import torchvision.transforms as transforms
import torchvision.datasets as datasets
train_dataset = datasets.MNIST(root='.',
                            train=True,  
                            transform=transforms.ToTensor(),
                            download=True)
test_dataset = datasets.MNIST(root='.',
                            train=False,  
                            transform=transforms.ToTensor())

MNIST데이터셋을 불러와서 이미지를 분류할 것이다.

  • root = ‘.’: 현재 디렉토리
  • train: 학습용
  • ToTensor: 텐서형으로 바꿔주고 0~1로 정규화
batch_size = 100

train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                            batch_size=batch_size, 
                                            shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                            batch_size=batch_size, 
                                            shuffle=False)
  • 데이터 로더를 만들어 준다.

모델 구현

class RNNModel(nn.Module):
    def __init__(self, input_dim, hidden_dim, layer_dim, output_dim):
        super(RNNModel, self).__init__()
        self.hidden_dim = hidden_dim
        self.layer_dim = layer_dim
        self.rnn = nn.RNN(input_dim, hidden_dim, layer_dim, batch_first=True)

batch_first란?

  • 우리가 데이터를 가져올때 (seq_len, input_size, batch_size)로 가져온다.
  • 그러나 해당 옵션을 true로 둘 경우 (batch_size, seq_len, input_size)순서대로 가져온다.

layer_dim = RNN의 층수를 나타냄

hidden_dim = hidden state의 크기

    def forward(self, x):
        h0 = torch.zeros(self.layer_dim, x.size(0), self.hidden_dim).to(x.device)
        out, hn = self.rnn(x, h0)
        out = self.fc(out[:, -1, :])
        return out

x값에 shape가 존재하는데 이걸 받아서 처리할 것임.

  • x.size(0)을 통해 0번째에 있는 batch_size를 가져옴

hn: n번째 히든레이어, h0를 받아 hn을 생성

out = self.fc(out[:, -1, :])

  • 시계열 전체를 보고 최종 결과 하나만 가져감.
input_dim = 28
hidden_dim = 100
layer_dim = 1
output_dim = 10

#가상으로 형태를 만들어봄
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')  

model = RNNModel(input_dim, hidden_dim, layer_dim, output_dim).to(device)
  • 형태를 만들어줌
criterion = nn.CrossEntropyLoss()
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
  • 손실함수 = CELoss를 사용
  • 학습률 = 0.01
  • 옵티마이저 = SGD

학습 사이클

num_epochs = 20
for epoch in range(num_epochs):
    model.train()
    for images, labels in train_loader:
        images = images.to(device)
        labels = labels.to(device)
        images = images.view(-1, input_dim, input_dim)
        optimizer.zero_grad()  
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

    model.eval()
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.to(device)
        labels = labels.to(device)
        images = images.view(-1, 28, input_dim)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum()
    accuracy = 100 * correct / total
    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}, Accuracy: {accuracy:.2f}%')

# Epoch [19/20], Loss: 0.1973, Accuracy: 95.63%
# Epoch [20/20], Loss: 0.0572, Accuracy: 96.03%
  • RNN으로 이미지를 처리했는데 성능이 96으로 상당히 잘 나오는 것을 알 수 있다.
  • 이것으로 RNN을 굳이 자연어에서만 사용하는것이 아님을 알 수 있었다.

'AI공부 > 자연어처리' 카테고리의 다른 글

7. LSTM(Long Short Term Memory)  (0) 2026.02.27
6. RNN활용 KOSPI예측(시퀀스데이터)  (0) 2026.02.23
4. FastText  (0) 2026.02.21
3. NSMC를 활용한 단어 분류  (1) 2026.02.20
2. 자연어 처리(벡터화)  (1) 2026.02.16