1. "주가는 정말 예측이 안 되나?"
머신러닝 공부를 시작하고 나서 제일 먼저 해보고 싶었던 건 당연히 '주가 예측'이었습니다. "과거 데이터를 보고 미래를 예측한다." 머신러닝의 정의와 딱 맞아떨어지는 문제 같았거든요.
처음엔 CNN(Convolutional Neural Network)을 썼습니다. 이미지 처리용 모델이지만, 차트 그림을 학습시키면 되지 않을까 싶었죠. 결과는 처참했습니다. 정확도가 50%도 안 됐습니다. 동전 던지기보다 못했죠.
선배 개발자에게 물었더니 한심하다는 듯 쳐다보며 말했습니다. "야, 주가는 순서가 중요하잖아. 어제 오른 게 오늘 영향을 주는 건데, CNN은 그런 걸 몰라. RNN(Recurrent Neural Network)을 써야지."
그때 처음 알았습니다. 데이터에도 '시간의 흐름'이 있다는 것을요.
2. 처음엔 뭐가 이해가 안 갔나
혼란 1 - "순환(Recurrent)? 뱅글뱅글 돈다고?"
책을 펴보니 RNN 구조도가 있었는데, 화살표가 자기 자신에게 돌아오는 모양이었습니다. "아니, 함수는 입력을 받고 출력을 뱉으면 끝이지, 왜 다시 들어가?" 직관적으로 이해가 안 됐습니다.
혼란 2 - "LSTM은 왜 이렇게 복잡해?"
RNN을 좀 이해하나 싶었더니, 바로 LSTM이 튀어나왔습니다. Input Gate, Forget Gate, Output Gate... 수식만 한 페이지가 넘더군요. "그냥 RNN 쓰면 안 되나? 왜 굳이 이걸 써야 해?"
혼란 3 - "과거를 잊는다(Vanishing Gradient)?"
RNN은 긴 문장을 기억 못 한다는데, 도대체 왜 못 한다는 건지 수학적으로 와닿지가 않았습니다.
3. 어떤 포인트에서 이해가 됐나
이해하는 데 결정적이었던 비유는 "일기 쓰기"였습니다.
RNN = 어제 일기를 보고 오늘 일기 쓰기
RNN은 매일 밤 일기를 쓰는 사람입니다. 그런데 규칙이 있습니다. "오늘 일기를 쓸 때, 반드시 어제 쓴 일기 내용을 참고해야 한다."
- Day 1: 날씨가 맑았다. (입력) -> 맑음 (기억)
- Day 2: 비가 왔다. (입력) + 어제 맑았음 (기억) -> 비 옴, 우울함 (오늘 기억)
- Day 3: 무지개가 떴다. (입력) + 어제 우울했음 (기억) -> 기분 좋아짐 (오늘 기억)
이렇게 '오늘의 기억(Hidden State)'을 다음 날로 넘겨주는 것(Recurrent), 이게 바로 RNN의 핵심이었습니다.
하지만 1년 전 일기는요? 매일매일 전날 일기만 참고하니, 365일 전 일기 내용은 희미해지고 사라집니다. 이게 바로 기울기 소실(Vanishing Gradient) 문제입니다.
LSTM = 중요한 것만 적어두는 포스트잇
LSTM은 단순히 일기를 이어 쓰는 게 아니라, "중요한 정보는 포스트잇(Cell State)에 따로 적어서 책상에 붙여두는" 사람입니다.
- Forget Gate: "어제 점심 메뉴 같은 건 잊어도 돼." (쓰레기통)
- Input Gate: "부장님이 내일까지 보고서 내라고 한 건 중요해. 포스트잇에 적자." (입력)
- Output Gate: "지금 상황에서 필요한 정보는 이거야." (출력)
이 비유를 통해 LSTM이 왜 장기 기억(Long-Term Memory)이 가능한지 깨달았습니다. 중요한 정보는 고속도로(Cell State)를 타고 계속 전달되니까요.
4. RNN (Recurrent Neural Network): 기본기
구조:
입력(x_t) → [RNN 셀] → 출력(h_t)
↑ ↓
이전 기억(h_t-1)
동작 원리:
# 가장 간단한 RNN 로직
def rnn_cell(current_input, prev_memory):
# 현재 입력과 이전 기억을 합쳐서(Weighted Sum)
combined = W_x * current_input + W_h * prev_memory + bias
# tanh로 압축 (-1 ~ 1 사이 값)
current_memory = tanh(combined)
return current_memory
이 current_memory가 바로 출력(Output)이자, 다음 단계로 넘겨줄 기억(Hidden State)이 됩니다.
치명적 단점: 기울기 소실 (Vanishing Gradient)
역전파(Backpropagation) 과정에서 기울기를 계속 곱하는데, tanh 미분값은 최대 1이고 보통 1보다 작습니다.
0.9를 100번 곱하면 0.000026이 되듯이, 먼 과거의 정보는 학습에 영향을 미치지 못하게 됩니다.
결국 "초반의 입력이 나중에 아무런 영향을 주지 못하는 치매 현상"이 발생합니다.
5. LSTM (Long Short-Term Memory): 기억의 예술
이 문제를 해결하기 위해 1997년 제프 힌튼 교수가 아닌 호크라이터와 슈미트후버가 고안한 게 LSTM입니다. 핵심은 "기억을 더하는(+) 구조"입니다. 곱하기(*)가 아니라 더하기를 하면 값이 작아지지 않고 보존되니까요.
LSTM의 3대 게이트
LSTM 셀 안에는 3명의 문지기(Gate)가 있습니다. 시그모이드(Sigmoid, 0~1) 함수로 문을 얼마나 열지 결정합니다.
- Forget Gate (망각 게이트): "과거 기억 중 쓸모없는 건 지우자."
f_t = sigmoid(...)-> 0이면 삭제, 1이면 유지.
- Input Gate (입력 게이트): "새로운 정보 중 중요한 것만 기억하자."
i_t = sigmoid(...)-> 얼마나 저장할지 결정.
- Output Gate (출력 게이트): "현재 기억 중 지금 당장 필요한 것만 내보내자."
o_t = sigmoid(...)-> 다음 층이나 다음 시점으로 보낼 값 결정.
핵심 - Cell State (장기 기억의 고속도로)
LSTM에는 Hidden State(h_t) 외에 Cell State(C_t)라는 게 하나 더 흐릅니다.
이건 일종의 컨닝 페이퍼입니다. 게이트들의 간섭을 최소화하며 시간의 흐름을 관통합니다.
# LSTM의 핵심 수식 (직관적 해석)
Cell_State = (과거_기억 * 잊을_비율) + (새_기억 * 기억할_비율)
여기서 더하기(+) 연산이 들어가기 때문에, 기울기가 사라지지 않고 멀리까지 전달됩니다.
6. 실제 예시 - 코드로 구현하기
PyTorch로 주가 예측 모델을 짜보겠습니다. 다들 이걸로 부자가 되길 꿈꾸지만, 현실은 냉혹하죠. (저도 그랬습니다...)
주가 예측 모델 (LSTM)
import torch
import torch.nn as nn
class StockPredictor(nn.Module):
def __init__(self, input_size, hidden_size, num_layers=2):
super(StockPredictor, self).__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
# LSTM 레이어 정의
# batch_first=True: 입력 데이터가 (batch, seq, feature) 순서임
self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
# 마지막에 주가(1개 값)를 예측하기 위한 Linear 레이어
self.fc = nn.Linear(hidden_size, 1)
def forward(self, x):
# x shape: (batch_size, sequence_length, input_size)
# 초기 Hidden State와 Cell State를 0으로 초기화
h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
# LSTM 통과
# out shape: (batch_size, seq_len, hidden_size)
out, _ = self.lstm(x, (h0, c0))
# 우리는 '마지막 날'의 정보만 필요하므로 마지막 시퀀스만 가져옴
out = out[:, -1, :]
# 최종 예측
out = self.fc(out)
return out
# 모델 생성
# input_size=5 (시가, 고가, 저가, 종가, 거래량)
model = StockPredictor(input_size=5, hidden_size=64)
이 코드를 짜면서 batch_first=True 옵션을 몰라서 차원 오류(Dimention Error)로 3시간을 삽질했던 기억이 납니다. PyTorch 기본값은 (seq, batch, feature)라서 우리가 흔히 쓰는 데이터 형태랑 다르거든요.
7. RNN/LSTM의 몰락 vs 트랜스포머(Transformer)
한때는 번역, 음성 인식, 시계열 분석의 왕이었지만, 2017년 구글의 "Attention Is All You Need" 논문 이후 판도가 뒤집혔습니다. 지금(2025년) 자연어 처리(NLP) 분야에서 RNN/LSTM을 쓴다고 하면 "왜요?" 소리를 듣습니다.
LSTM의 한계:
- 순차 처리(Sequential): 입력
t를 처리하려면 반드시t-1계산이 끝나야 합니다. 즉, 병렬 처리가 불가능해서 GPU를 100개 꽂아도 학습 속도가 느립니다. - 고정된 컨텍스트: 아무리 LSTM이라도 문장이 엄청나게 길어지면 정보가 희석되거나 왜곡됩니다.
트랜스포머(Transformer):
- 병렬 처리(Parallel): 문장 전체를 한 번에 행렬 연산으로 때려 넣습니다. 속도가 엄청 빠릅니다.
- Attention: 문장 내의 모든 단어 간의 관계를 직접 계산합니다. 1번째 단어와 100번째 단어를 거리 1로 연결해버립니다.
결국 "기억하는(Memory) 방식보다, 필요할 때마다 찾아보는(Attention) 방식이 더 우월하다"는 것이 증명되었습니다.
하지만 시계열 데이터(Time Series)나 센서 데이터 분석 등, 데이터가 실시간 스트림으로 들어오는 가벼운 엣지(Edge) 환경에서는 여전히 LSTM이나 GRU(LSTM 경량화 버전)가 현역으로 뛰고 있습니다. 가볍고 빠르니까요.
8. 한 줄 요약
RNN은 이전 상태를 순차적으로 기억하는 신경망이고, LSTM은 중요한 정보만 장기 기억(Cell State)에 남겨 기울기 소실 문제를 해결한 진화형입니다. 이제 NLP의 왕좌는 트랜스포머에게 넘겨주었지만, 시계열 예측과 경량화 모델에서는 여전히 강력한 도구입니다.