딥러닝, 논문 리뷰

[딥러닝] RNN, LSTM, GRU Sequence - 1

cheorish 2024. 12. 27. 13:39

 

 

Sequence Data

What is Sequence Data?

출처 : 코딩애플


순서대로 정렬된 데이터의 연속이라고 생각하면 된다. 보통 데이터의 순서가 가장 중요함, 주식차트, 텍스트 데이터, 시계열 데이터 등을 표현

특징

  • 순서 의존성 : 데이터의 순서가 의미를 결정함
  • 시간적 연속성 : 시간에 따라 데이터가 연속적으로 발생

RNN

순환신경망(Recurrent Nerual Network)이란 인공신경망의 한 종류

  • 순환구조를 통해 학습이 되어 시간에 의존적, 순차적인 데이터 학습에 활용함
  • 데이터가 순환하기 때문에 끊임없는 정보 갱신 가능

기존의 MLP는 전부 은닉층에서 활성화 함수를 지난 값은 오직 출력층 방향으로만 향했음 → 피드 포워드 신경망이라고 부름

RNN의 구조

출처 : https://bigpy.oopy.io/35df4ee3-082d-44c5-8252-dc95e1ecb7ce

 

기존의 피드 포워드 신경망 방식이 아닌 은닉층의 활성화 함수를 통해 나온 임계값을 다시 다음 은닉층 노드의 다음 계산 입력으로 보내는 특징이 있음.

출처 : https://towardsdatascience.com/illustrated-guide-to-recurrent-neural-networks-79e5eb8049c9

 

Illustrated Guide to Recurrent Neural Networks

Understanding the Intuition

towardsdatascience.com

 

그림으로는 이해하기 어려울게 뻔하니 쉽게 설명해주는 GIF를 통해 이해해보자..

출처 : https://wikidocs.net/22886

 

상기 그림을 통해 더욱 더 쉽게 RNN의 매커니즘을 이해할 수 있다고 본다.

은닉층 계산식

  • \(h_t = f(W_{xh} x_t + W_{hh} h_{t-1} + b_h)\)

수식 설명

  • \(h_t:\) t 시점의 은닉상태
  • \(W_{xh}:\) 입력값을 통해 은닉층으로 진입하는 가중치
  • \(W_{hh}:\) 계산 값이 결정된 이후 다음 은닉층으로 진입하는 가중치
  • \(x_t:\) t 시점의 입력
  • \(h_{t-1}:\) 이전 은닉상태
  • \(b_h:\) 편향
  • \(f:\) 비선형 활성화 함수(예 : \(tanh\))

출력층 계산식

  • \(y_t = g(W_{hy} h_t + b_y)\)

수식 설명

  • \(y_t:\) t 시점의 출력 벡터
  • \(W_{hy}:\) 은닉 상태에서 출력으로 가는 가중치
  • \(h_t:\) t 시점의 은닉 상태
  • \(b_y:\) 출력의 편향
  • \(g:\) 출력층 활성화 함수(\(Softmax, Sigmoid\))

활성화 함수는 하이퍼볼릭 탄젠트를 사용한다

은닉층 내 활성화 함수 계산식

  • \(h_t = \tanh(W_{xh} x_t + W_{hh} h_{t-1} + b_h)\)

왜 하이퍼볼릭 탄젠트를 사용할까?

  1. 기존 활성화 함수 \(sigmoid\), \(Relu\)는 0~1까지의 비교적 작은 범위로 임계값을 업데이트 하기에 기울기 소실 가능성이 크고, 임계 범위가 -1~1까지의 넓은 범위를 가진 하이퍼볼릭 탄젠트를 선호하는 편
  2. 기존 시그모이드(\(sigmoid\)) 는 임계값이 기준치에 다다르지 못하면 0으로 바꿔버리는데, RNN에서는 0이하 즉 음수 값 또한 계산 내에서도 중요한 역할이기에 필요하다

출처 : https://wikidocs.net/22886


타입의 따른 입출력 설계

RNN은 입력과 출력의 길이를 다르게 설계할 수 있음, 하기 그림을 통해 다양하게 이용하는 RNN의 처리 방법에 대해서 정리하려고 한다.

출처 : https://wikidocs.net/22886

 

일 대 다(One-to-Many)

  • 하나의 입력 시퀀스를 받아, 여러개의 출력 시퀀스를 생성하는 구조
  • 한 개의 입력 → 여러 개 출력

사용예시

  • 이미지 캡셔닝, 음성 합성 등

 

다 대 일(Many-to-One)

  • 여러 개의 입력 시퀀스를 받아 한 개의 출력 값을 생성하는 구조
  • 여러 개의 입력 → 한 개의 출력

사용예시

  • 감정분석(긍정, 부정), 주가예측

다 대 다(Many-to-Many)

  • 여러 개의 입력 시퀀스를 받아 여러 개의 출력 시퀀스를 생성하는 구조
  • 여러 개의 입력 → 여러 개 출력

사용예시

  • 기계번역, 음성인식 등

예제코드

import torch
import torch.nn as nn
import torch.optim as optim

# ✅ 바닐라 RNN 모델 정의
class VanillaRNN(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim):
        super(VanillaRNN, self).__init__()
        self.hidden_dim = hidden_dim

        # 입력 → 은닉 상태 가중치
        self.Wxh = nn.Linear(input_dim, hidden_dim)  
        # 은닉 상태 → 은닉 상태 가중치
        self.Whh = nn.Linear(hidden_dim, hidden_dim)  
        # 은닉 상태 → 출력 가중치
        self.Why = nn.Linear(hidden_dim, output_dim)  

        self.tanh = nn.Tanh()
        self.softmax = nn.Softmax(dim=1)

    def forward(self, x, hidden):
        # x: (batch_size, input_dim)
        # hidden: (batch_size, hidden_dim)
        hidden = self.tanh(self.Wxh(x) + self.Whh(hidden))
        output = self.softmax(self.Why(hidden))
        return output, hidden

# ✅ 하이퍼파라미터 설정
input_dim = 5    # 입력 특성 차원
hidden_dim = 10  # 은닉 상태 차원
output_dim = 3   # 출력 특성 차원
seq_len = 7      # 시퀀스 길이
batch_size = 4   # 배치 크기

# ✅ 모델 초기화
model = VanillaRNN(input_dim, hidden_dim, output_dim)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# ✅ 입력 및 초기 은닉 상태 정의
inputs = torch.randn(batch_size, seq_len, input_dim)  # 입력 시퀀스
hidden = torch.zeros(batch_size, hidden_dim)         # 초기 은닉 상태

# ✅ 순전파 수행
loss = 0
for t in range(seq_len):
    input_t = inputs[:, t, :]  # 시점 t의 입력
    output, hidden = model(input_t, hidden)  # 출력 및 은닉 상태 업데이트

    # 예제용 타겟 레이블
    target = torch.randint(0, output_dim, (batch_size,))
    loss += criterion(output, target)

# ✅ 역전파 및 최적화
optimizer.zero_grad()
loss.backward()
optimizer.step()

print("Final Hidden State Shape:", hidden.shape)
print("Final Output Shape:", output.shape)
print("Loss:", loss.item())

 

근본적인 한계

  • 장기의존성 : 단방향으로 흘러가며 가중치를 업데이트하는 RNN구조상 데이터의 길이가 길어질수록 제일 먼저 학습했던 데이터를 유지하지 못하는 상황이 발생
  • 기울기 소실 : 하이퍼볼릭탄젠트를 통해 근본적으로 해결할 수 없는 구조

 

LSTM

이러한 장기의존성 문제를 해결하고자 입력 메모리셀을 구성하기 전, 선택적으로 순환이 필요한 데이터와 필요하지 않은 데이터로 구분하기 위해 제안된 방법

출처 : https://chanjun-kim.github.io/data%EB%B6%84%EC%84%9D/TimeSeries2.html

 

구조

망각 게이트

  • 어떤 정보가 필요 없는지를 판단, 중요하지 않은 정보는 버림
    • 수식 :
      • \(f_t = \sigma(W_f [h_{t-1}, x_t] + b_f)\)
    • 수식 설명:
      • \(f_t:\) 망각 게이트의 출력 (0~1 사이의 값)
      • \(W_f:\) 망각 게이트의 가중치 행렬
      • \(h_{t-1}:\) 이전 시점의 은닉 상태
      • \(x_t:\) 현재 시점의
      • \(b_f:\) 망각 게이트의 편향
      • \(\sigma:\) 시그모이드 활성화 함수

입력 게이트

  • 망각 게이트를 거쳐 들어온 입력 정보 중 중요한 정보만 선택
    • 수식 :
      • \(i_t = \sigma(W_i [h_{t-1}, x_t] + b_i)\)
    • 수식 설명 :
      • \(i_t:\) 입력 게이트의 출력(0~1사이의 값)
      • \(W_i:\) 입력 게이트의 가중치 행렬
      • \(h_{t-1}:\) 이전 시점의 은닉 상태
      • \(x_t:\) 현재 시점의 입력
      • \(b_i:\) 입력 게이트의 편향
      • \(\sigma:\) 시그모이드 활성화 함수

셀 상태(Cell State)

  • 이전에 입력된 정보와 입력 게이트를 통해 들어오는 새로운 정보를 비교하여 최종적으로 기억할 내용을 결정
  • 수식:
    • \(C_t = f_t \cdot C_{t-1} + i_t \cdot \tanh(W_C [h_{t-1}, x_t] + b_C\))
  • 수식 설명:
    • \(C_t:\) 셀 상태
    • \(f_t:\) 망각게이트
    • \(C_{t-1}:\) 이전 시점의 셀 상태
    • \(i_t:\) 입력 게이트
    • \(W_C:\) 셀 상태로 들어가는 가중치 행렬
    • \(h_{t-1}:\) 이전 시점의 은닉 상태
    • \(x_t:\) 현재 시점의 입력
    • \(b_C:\) 셀 상태의 편향
    • \(tahn:\) 하이퍼볼릭 탄젠트
  • 망각 게이트에 입력된 값에서 값을 얼마나 유지할지 버릴지를 결정 → 입력 게이트를 통해 들어온 새로운 값과 기존의 입력된 값을 비교하며 새로운 정보를 얼마나 셀 상태에 추가할지 결정

근본적 한계

  • 3가지 게이트 및 활성화 함수를 필요 이상으로 남발함(시그모이드, 하이퍼볼릭 모두 사용)
  • 긴 시퀀스 입력시 복잡성 증가(시퀀스 데이터 유지 그러니까, 장기 의존성에는 안정성이 있으나 근본적인 해결책이 아님)

GRU

https://arxiv.org/pdf/1412.3555

출처 : https://arxiv.org/pdf/1412.3555

Abstract

 

뉴욕대학교 조경현 교수님이 2014년에 발표한 내용으로 기존 헤비한 구조의 LSTM을 간소화하여, 다성 음악 및 음성신호 모델링 작업을 통해 평가를 완료한 논문이다.

LSTM의 영감을 받아, 기존 Gate의 중복성을 제거 후 보다 효율적인 방식으로 처리하기 위해 간단한 구조로 제안 Cell Gate 개념을 없애고, hidden state 단일 방식으로 사용하되, 장기 의존성 문제는 효과적으로 해결할 수 있는 방식을 제안

출처 : https://cryptosalamander.tistory.com/176

아키텍처 구조

Reset Gate

  • 첫 초기화를하며 이전 은닉층이 현재 입력층과 결합할지를 조절하는 역할
    • 수식 :
      • \(r_t=\) \(\sigma(W_rx_t + U_rh_{t-1} + b_r)\)
  • 수식 설명:
    • \(W_r:\) 입력 \(x_t\)에 대한 가중치 행렬
    • \(U_r:\) 이전 은닉층 \(h_{t-1}\)에 대한 가중치 행렬
    • \(b_r:\) 편향
    • \(\sigma:\) 시그모이드 함수

Update Gate

  • 이전 은닉층에 새로운 은닉층의 값을 얼마나 혼합할지 결정
    • 수식 :
      • \(z_t=\) \(\sigma(W_zx_t + U_zh_{t-1} + b_z)\)
      • 수식 설명:
      • \(W_z:\) 입력 \(x_t\)에 대한 가중치 행렬
      • \(U_z:\) 이전 은닉층 \(h_{t-1}\)에 대한 가중치 행렬
      • \(b_z:\) 편향
      • \(\sigma:\) 시그모이드 함수

후보 은닉층(Candidate Hidden State)

  • 현재 시점의 새로운 상태를 생성하며, Reset Gate를 사용하여 이전 은닉층을 조절함
    • 수식 :
      • \(\tilde{h}_t = \tanh(W_h x_t + U_h (r_t \odot h_{t-1}) + b_h)\)
      • \(W_h:\) 입력 \(x_t\)에 대한 가중치 행렬
      • \(u_h:\) 이전 은닉층 \(h_{t-1}:\)에 대한 가중치 행렬
      • \(b_h:\) 편향
      • \(\odot:\) 요소별 곱
      • \(tahn:\) 하이퍼볼릭 탄젠트

최종 은닉층

  • Update Gate를 사용하여 이전 은닉층과 새로운 후보 은닉층을 조합하여 최종 출력을 위한 은닉층을 계산
    • 수식: 
      • \(h_t=\) \((1 - z_t) \odot \tilde{h}_t + z_t \odot h_{t-1}\)
    • 수식 설명:
      • \(\odot:\) 요소별 곱
      • \(z_t:\) Update Gate 값
      • \(\tilde{h}_t:\) 후보 은닉층
      • \(h_{t-1}:\) 이전 은닉층 

 

리서치 결과

출처 : https://arxiv.org/pdf/1412.3555

 

출처 : https://arxiv.org/pdf/1412.3555

 

 

학습곡선이 기존 LSTM 및 하이퍼볼릭 탄젠트 유닛보다 명확한 성능 우위를 보인 결과를 보였음