RNN, LSTM 등은 공부해 본 적이 없는데, 이번 재직자 교육 때 할 수 있게 되었다.(LSTM 몇 주 뒤에 배운다.) 현재 회사에서의 업무는 전통적인 통계학
(회귀, 로지스틱, Lasso, PCA, 잘해봤다 decision tree)에 매우 가깝지만,
인생이라는게 어떤 식으로든 준비를 해두면 언젠가는 빛을 발하는 것 같더라.

01. RNN이란?

  • RNN(Recurrent Neural Network)은 순차적(Sequential) 데이터를 처리하는 데 특화된 신경망 구조
  • MLP, CNN 등은 입력 데이터가 서로 독립이라고 가정하지만, RNN에서는
    이전 상태가 다음 상태에 영향을 주는 구조를 가정함.(순서 혹은 시계열)
  • 대표적인 사용처
    • 음성 인식
    • 자연어처리(NLP)
    • 시계열 예측
    • 음악 생성

02. 기본 구조

  • RNN은 시간 축에 따라 같은 구조의 셀(cell)을 반복적으로 사용
  • 위의 그림은 ChatGPT에게 그려달라고 한 그림이다. 그림 내용을
    설명하자면 하기와 같다.
    • 입력 xtx_{t} : 현재 시점 입력
    • 은닉 상태 hth_{t} : 이전 정보를 압축한 벡터
    • 출력 yty_{t} : 필요 시 사용하는 출력

03. 수식 정리 (기본 RNN)

  • RNN의 수식은 아래와 같이 구성되어 있음
    ht=tanh(Wxhxt+Whhht1+bh)h_{t} = tanh(W_{xh} x_{t}+ W_{hh} h_{t-1} + b_{h})
    • xtx_{t} : 현재 입력
    • ht1h_{t-1} : 이전 은닉 상태
    • WxhW_{xh} : 입력 --> 은닉 가중치
    • bhb_{h} : bias
    • tanhtanh : 비선형 활성화 함수(hyperbolic-tangent)

04. RNN의 순환적 특성

  • 공통 가중치를 공유하며 학습 효율을 높임 (모든 시점에서 동일한 W 사용)
  • 은닉 상태 hth_{t}는 시계열의 문맥(Context)을 반영함

05. Backpropagation Through Time (BPTT)

  • RNN 학습은 시계열 전체에 걸친 오차를 통해 역전파를 수행
    • 일반 신경망은 1~2층만 거꾸로 가면 되지만, RNN은 시간에 따라
      펼친 후 모든 시점에 대해 역전파를 함(시간을 통한 역전파)
    • 단점: 이때 기울기가 너무 커지거나(Exploding) 작아지는(Vanishing) 문제가 생김

06. RNN의 한계 및 대안

  • 한계점
    • 장기 의존성 문제 : 시차가 커질수록 잊어버림
    • 기울기 소실(폭발) 문제 : 기울기가 0이 되거나 발산함
    • 계산량 증가 : 시간축이 길수록 계산량이 증가하고 메모리 사용도 많아짐
  • 대안
    • LSTM (Long Short-Term Memory) : 장기 기억 가능(게이트 구조)
    • GRU (Gated Recurrent Unit) : LSTM보다 단순, 비슷한 성능
    • Bidirectional RNN : 과거와 미래 문맥을 동시에 활용
    • Attention / Transformer : 순차성 제거, 병렬 처리 가능 (현대 NLP의 표준)

07. PyTorch 예제 (문자 예측)

import torch
import torch.nn as nn
import numpy as np

# 문자셋
char_data = 'hihello'
char_set = list(set(char_data))
char2idx = {c: i for i, c in enumerate(char_set)}
idx2char = {i: c for i, c in enumerate(char_set)}

# 입력/출력 시퀀스
x_data = [char2idx[c] for c in 'hihell']
y_data = [char2idx[c] for c in 'ihello']

# One-hot encoding
x_one_hot = [np.eye(len(char_set))[x] for x in x_data]
inputs = torch.Tensor([x_one_hot])  # shape: (1, seq_len, input_size)
labels = torch.LongTensor([y_data])  # shape: (1, seq_len)

# 모델 정의
class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super().__init__()
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)
    
    def forward(self, x):
        out, _ = self.rnn(x)           # out: (batch, seq, hidden)
        out = self.fc(out)            # out: (batch, seq, output)
        return out

model = RNN(input_size=len(char_set), hidden_size=8, output_size=len(char_set))
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)

# 학습 루프
for epoch in range(100):
    optimizer.zero_grad()
    outputs = model(inputs)
    loss = criterion(outputs.view(-1, len(char_set)), labels.view(-1))
    loss.backward()
    optimizer.step()

    result = outputs.argmax(dim=2)
    result_str = ''.join([idx2char[c.item()] for c in result[0]])
    if epoch % 10 == 0:
        print(f"[{epoch}] loss: {loss.item():.4f}, prediction: {result_str}")
profile
2025화이팅!

0개의 댓글