from tensorflow.keras.layers import SimpleRNN
import random
EMBEDDING_DIM = 128
EPOCHS = 10
BATCH_SIZE = 32
random.seed(42)
np.random.seed(42)
tf.random.set_seed(42)
rnn_model = tf.keras.Sequential([
tf.keras.Input(shape=(MAX_LEN,)),
tf.keras.layers.Embedding(
input_dim=VOCAB_SIZE,
output_dim=EMBEDDING_DIM,
input_length=MAX_LEN
),
SimpleRNN(units=50), #RNN 사용
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid'),
])
rnn_model.summary()
rnn_model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
위의 코드로 예시를 들자면,
시점 t에서의 RNN 계산:
h_t = tanh(W_xh · x_t + W_hh · h_{t-1} + b_h)
y_t = W_hy · h_t + b_y
xt: 현재 시점의 입력 (embedding 벡터)
h_t: 현재 시점의 hidden state
h{t-1}: 이전 시점의 hidden state (메모리 역할)
W_xh: 입력-은닉층 가중치
W_hh: 은닉-은닉층 가중치 (재귀 연결)
W_hy: 은닉-출력층 가중치
입력 시퀀스: [단어1, 단어2, 단어3, 단어4]
t=1: h_1 = tanh(W_xh·x_1 + W_hh·h_0 + b)
→ 단어1 정보 저장
t=2: h_2 = tanh(W_xh·x_2 + W_hh·h_1 + b)
→ 단어1+단어2 정보 누적
t=3: h_3 = tanh(W_xh·x_3 + W_hh·h_2 + b)
→ 단어1+단어2+단어3 정보 누적
t=4: h_4 = tanh(W_xh·x_4 + W_hh·h_3 + b)
→ 전체 시퀀스 정보 포함
# 1. Embedding Layer
# [배치, MAX_LEN] → [배치, MAX_LEN, 128]
# 각 단어가 128차원 벡터로 변환
# 2. SimpleRNN(units=50)
# 내부 동작:
for t in range(MAX_LEN):
h_t = tanh(W_xh @ embedding[t] + W_hh @ h_{t-1} + b)
# h_{t-1}: 이전까지의 문맥 정보
# embedding[t]: 현재 단어 정보
# → 결합하여 h_t에 순서 정보 누적
# 3. 최종 출력
# 마지막 h_{MAX_LEN}만 Dense층으로 전달
# → 전체 시퀀스의 순서 정보가 압축된 50차원 벡터
# LSTM: 이중 경로 (Cell State + Hidden State)
1. Cell State (C_t): 장기 기억 (컨베이어 벨트)
- 정보가 거의 변형 없이 흐름
- 필요한 정보만 선택적으로 추가/제거
2. Hidden State (h_t): 단기 기억 (작업 메모리)
- 현재 시점의 출력
3. Gates (게이트): 정보 흐름 제어
- Forget Gate: 버릴 정보 결정
- Input Gate: 추가할 정보 결정
- Output Gate: 출력할 정보 결정
측면 | RNN | LSTM
긴 문장 처리 | 초기 정보 손실 | 끝까지 보존
확신 정도 | 애매함 (35.93%) | 명확함 (0.09%)
정보 누적 | 덮어쓰기 | 선택적 추가
복잡한 감정 | 약함 | 강함
gradient 문제 | 소실 | 해결