C.W.K.
Stream
Lesson 01 of 10 · published

트랜스포머 이전의 시퀀스 모델링: RNN과 LSTM

~22 min · history, rnn, lstm, background

Level 0Token
0 XP0/94 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

2017년 전까지는 RNN(순환 신경망)과 그 게이트 버전인 LSTM이 시퀀스 데이터의 기본 무기였어. 텍스트, 음성, 시계열, 음악까지 — 거의 다 RNN/LSTM이 깔았지. 트랜스포머가 왜 큰 사건이었는지 이해하려면, RNN이 잘하던 게 뭐고 어디서 막혔는지부터 봐야 돼.

RNN은 시퀀스를 한 칸씩 읽어들이면서 hidden state를 계속 업데이트해. 이 벡터 하나가 "지금까지 본 거 다 압축한 거"야. 매 스텝마다 현재 입력 + 직전 hidden state를 섞어서 다음 hidden state를 만들지.

LSTM은 여기에 게이트를 붙였어. forget gate / input gate / output gate가 "뭘 잊을지 / 뭘 새로 쓸지 / 뭘 내보낼지"를 명시적으로 학습해. 그래서 장기 의존성을 그나마 잡을 수 있게 됐는데 — 본질적인 "한 칸씩"은 그대로야.

진짜 문제 세 가지

전부 "한 칸씩 처리"에서 파생되는 문제들이야:

  • 시퀀스 안에서 병렬화 불가능. 위치 100의 hidden state를 구하려면 99가 끝나야 해. 99는 98이 있어야 하고. GPU는 행렬곱을 병렬로 돌리고 싶어 죽겠는데, RNN은 그걸 시켜주질 않아.
  • gradient vanishing / exploding. LSTM 게이트로도 수백 스텝 넘어가면 gradient가 0에 수렴하거나 폭발해. 실전에서 200토큰 넘는 의존성은 거의 못 잡아.
  • 정보 병목. 현재 스텝보다 앞에 있던 모든 정보가 hidden state 벡터 하나를 통과해야 해. 멀어지면 흐려져.

이게 옛날 얘기 같지만, RNN/LSTM은 2017년까지 구글 번역(2016), 초기 Siri 음성 인식, 대부분의 production NLP를 굴리던 현역이었어. 망가진 걸 트랜스포머가 갈아치운 게 아니야 — 잘 굴러가지만 본질적으로 막혀 있던 걸 갈아치운 거지.

Code

RNN forward pass — sequential by construction·python
# RNN: each hidden state depends on the previous one
import numpy as np

def rnn_forward(x, W_h, W_x, b):
    # x: (seq_len, d_in), h: (d_h,)
    seq_len = x.shape[0]
    h = np.zeros(W_h.shape[0])
    outputs = []
    for t in range(seq_len):
        h = np.tanh(W_h @ h + W_x @ x[t] + b)
        outputs.append(h.copy())
    return np.stack(outputs)

# Notice: cannot start step t until step t-1 finishes.
# This is the bottleneck the Transformer eliminates.
LSTM cell — gates around the same recurrence·python
# LSTM cell (simplified): same sequential structure, more parameters
def lstm_step(x_t, h_prev, c_prev, W):
    # f: forget, i: input, o: output, g: candidate
    f = sigmoid(W['f'] @ np.concatenate([x_t, h_prev]))
    i = sigmoid(W['i'] @ np.concatenate([x_t, h_prev]))
    o = sigmoid(W['o'] @ np.concatenate([x_t, h_prev]))
    g = np.tanh(W['g'] @ np.concatenate([x_t, h_prev]))
    c = f * c_prev + i * g       # update cell state
    h = o * np.tanh(c)            # produce hidden state
    return h, c
# Gates help long dependencies, but t still waits for t-1.

External links

Exercise

PyTorch로 2-layer LSTM 만들어서 Tiny Shakespeare 데이터로 character-level 생성기 학습시켜 봐. CPU에서 5 epoch만 돌리고 세 가지 기록해 — (a) epoch당 wall-clock 시간, (b) perplexity 어떻게 떨어지는지, (c) 생성 텍스트가 언제부터 문장 같아지는지. 이 숫자 챙겨놔, Track 4에서 작은 트랜스포머랑 비교할 거니까.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.