08. RNN ๊ธฐ์ด (Recurrent Neural Networks)
08. RNN ๊ธฐ์ด (Recurrent Neural Networks)¶
ํ์ต ๋ชฉํ¶
- ์ํ ์ ๊ฒฝ๋ง์ ๊ฐ๋ ๊ณผ ๊ตฌ์กฐ
- ์ํ์ค ๋ฐ์ดํฐ ์ฒ๋ฆฌ
- PyTorch nn.RNN ์ฌ์ฉ๋ฒ
- ๊ธฐ์ธ๊ธฐ ์์ค ๋ฌธ์ ์ดํด
1. RNN์ด๋?¶
์์ฐจ ๋ฐ์ดํฐ์ ํน์ฑ¶
์๊ณ์ด: [1, 2, 3, 4, 5, ...] - ์ด์ ๊ฐ์ด ๋ค์ ๊ฐ์ ์ํฅ
ํ
์คํธ: "๋๋ ํ๊ต์ ๊ฐ๋ค" - ์ด์ ๋จ์ด๊ฐ ๋ค์ ๋จ์ด์ ์ํฅ
MLP์ ํ๊ณ¶
- ๊ณ ์ ๋ ์ ๋ ฅ ํฌ๊ธฐ
- ์์ ์ ๋ณด ๋ฌด์
- ๊ฐ๋ณ ๊ธธ์ด ์ํ์ค ์ฒ๋ฆฌ ๋ถ๊ฐ
RNN์ ํด๊ฒฐ¶
h(t) = tanh(W_xh ร x(t) + W_hh ร h(t-1) + b)
h(t): ํ์ฌ ์๋ ์ํ
x(t): ํ์ฌ ์
๋ ฅ
h(t-1): ์ด์ ์๋ ์ํ
2. RNN ๊ตฌ์กฐ¶
์๊ฐ ํผ์นจ (Unrolling)¶
x1 x2 x3 x4
โ โ โ โ
โโโโโ โโโโโ โโโโโ โโโโโ
โ h โโโโบโ h โโโโบโ h โโโโบโ h โโโโบ ์ถ๋ ฅ
โโโโโ โโโโโ โโโโโ โโโโโ
h0 h1 h2 h3
ํ๋ผ๋ฏธํฐ ๊ณต์ ¶
- ๋ชจ๋ ์๊ฐ ๋จ๊ณ์์ ๋์ผํ W_xh, W_hh ์ฌ์ฉ
- ๊ฐ๋ณ ๊ธธ์ด ์ํ์ค ์ฒ๋ฆฌ ๊ฐ๋ฅ
3. PyTorch RNN¶
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ¶
import torch
import torch.nn as nn
# RNN ์์ฑ
rnn = nn.RNN(
input_size=10, # ์
๋ ฅ ์ฐจ์
hidden_size=20, # ์๋ ์ํ ์ฐจ์
num_layers=2, # RNN ์ธต ์
batch_first=True # ์
๋ ฅ: (batch, seq, feature)
)
# ์
๋ ฅ ํํ: (batch_size, seq_len, input_size)
x = torch.randn(32, 15, 10) # ๋ฐฐ์น 32, ์ํ์ค 15, ํน์ฑ 10
# ์์ ํ
# output: ๋ชจ๋ ์๊ฐ์ ์๋ ์ํ (batch, seq, hidden)
# h_n: ๋ง์ง๋ง ์๋ ์ํ (layers, batch, hidden)
output, h_n = rnn(x)
print(f"output: {output.shape}") # (32, 15, 20)
print(f"h_n: {h_n.shape}") # (2, 32, 20)
์๋ฐฉํฅ RNN¶
rnn_bi = nn.RNN(
input_size=10,
hidden_size=20,
num_layers=1,
batch_first=True,
bidirectional=True # ์๋ฐฉํฅ
)
output, h_n = rnn_bi(x)
print(f"output: {output.shape}") # (32, 15, 40) - ์ ๋ฐฉํฅ+์ญ๋ฐฉํฅ
print(f"h_n: {h_n.shape}") # (2, 32, 20) - ๋ฐฉํฅ๋ณ ๋ง์ง๋ง ์ํ
4. RNN ๋ถ๋ฅ๊ธฐ ๊ตฌํ¶
์ํ์ค ๋ถ๋ฅ ๋ชจ๋ธ¶
class RNNClassifier(nn.Module):
def __init__(self, input_size, hidden_size, num_classes, num_layers=1):
super().__init__()
self.rnn = nn.RNN(
input_size, hidden_size,
num_layers=num_layers,
batch_first=True
)
self.fc = nn.Linear(hidden_size, num_classes)
def forward(self, x):
# x: (batch, seq, features)
output, h_n = self.rnn(x)
# ๋ง์ง๋ง ์๊ฐ์ ์๋ ์ํ ์ฌ์ฉ
# h_n[-1]: ๋ง์ง๋ง ์ธต์ ์๋ ์ํ
out = self.fc(h_n[-1])
return out
Many-to-Many ๊ตฌ์กฐ¶
class RNNSeq2Seq(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):
output, _ = self.rnn(x)
# ๋ชจ๋ ์๊ฐ ๋จ๊ณ์ FC ์ ์ฉ
out = self.fc(output) # (batch, seq, output_size)
return out
5. ๊ธฐ์ธ๊ธฐ ์์ค ๋ฌธ์ ¶
๋ฌธ์ ¶
๊ธด ์ํ์ค์์:
h100 โ W_hh ร W_hh ร ... ร W_hh ร h1
โ
100๋ฒ ๊ณฑ์
โ ๊ธฐ์ธ๊ธฐ ํญ๋ฐ ๋๋ ์์ค
์์ธ¶
- |W_hh| > 1: ๊ธฐ์ธ๊ธฐ ํญ๋ฐ
- |W_hh| < 1: ๊ธฐ์ธ๊ธฐ ์์ค
ํด๊ฒฐ์ฑ ¶
- LSTM/GRU ์ฌ์ฉ (๋ค์ ๋ ์จ)
- Gradient Clipping
# ๊ธฐ์ธ๊ธฐ ํด๋ฆฌํ
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
6. ์๊ณ์ด ์์ธก ์์ ¶
์ฌ์ธํ ์์ธก¶
import numpy as np
# ๋ฐ์ดํฐ ์์ฑ
def generate_sin_data(seq_len=50, n_samples=1000):
X = []
y = []
for _ in range(n_samples):
start = np.random.uniform(0, 2*np.pi)
seq = np.sin(np.linspace(start, start + 4*np.pi, seq_len + 1))
X.append(seq[:-1].reshape(-1, 1))
y.append(seq[-1])
return np.array(X), np.array(y)
X, y = generate_sin_data()
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32)
# ๋ชจ๋ธ
class SinPredictor(nn.Module):
def __init__(self):
super().__init__()
self.rnn = nn.RNN(1, 32, batch_first=True)
self.fc = nn.Linear(32, 1)
def forward(self, x):
_, h_n = self.rnn(x)
return self.fc(h_n[-1]).squeeze()
7. ํ ์คํธ ๋ถ๋ฅ ์์ ¶
๋ฌธ์ ์์ค RNN¶
class CharRNN(nn.Module):
def __init__(self, vocab_size, embed_size, hidden_size, num_classes):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = nn.RNN(embed_size, hidden_size, batch_first=True)
self.fc = nn.Linear(hidden_size, num_classes)
def forward(self, x):
# x: (batch, seq) - ๋ฌธ์ ์ธ๋ฑ์ค
embedded = self.embedding(x) # (batch, seq, embed)
output, h_n = self.rnn(embedded)
out = self.fc(h_n[-1])
return out
# ์์
vocab_size = 27 # a-z + ๊ณต๋ฐฑ
model = CharRNN(vocab_size, embed_size=32, hidden_size=64, num_classes=5)
8. ์ฃผ์์ฌํญ¶
์ ๋ ฅ ํํ¶
# batch_first=True โ (batch, seq, feature)
# batch_first=False โ (seq, batch, feature) # ๊ธฐ๋ณธ๊ฐ
๊ฐ๋ณ ๊ธธ์ด ์ํ์ค¶
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
# ํจ๋ฉ๋ ์ํ์ค์ ์ค์ ๊ธธ์ด
padded_seqs = ... # (batch, max_len, features)
lengths = ... # ๊ฐ ์ํ์ค์ ์ค์ ๊ธธ์ด
# ํจํน (ํจ๋ฉ ๋ฌด์)
packed = pack_padded_sequence(padded_seqs, lengths,
batch_first=True, enforce_sorted=False)
output, h_n = rnn(packed)
# ์ธํจํน
output_padded, _ = pad_packed_sequence(output, batch_first=True)
9. RNN ๋ณํ ๋น๊ต¶
| ๋ชจ๋ธ | ์ฅ์ | ๋จ์ |
|---|---|---|
| Simple RNN | ๋จ์, ๋น ๋ฆ | ๊ธด ์ํ์ค ํ์ต ์ด๋ ค์ |
| LSTM | ์ฅ๊ธฐ ์์กด์ฑ ํ์ต | ๋ณต์ก, ๋๋ฆผ |
| GRU | LSTM๊ณผ ์ ์ฌ, ๋ ๋จ์ | - |
์ ๋ฆฌ¶
ํต์ฌ ๊ฐ๋ ¶
- ์ํ ๊ตฌ์กฐ: ์ด์ ์ํ๊ฐ ๋ค์ ๊ณ์ฐ์ ์ํฅ
- ํ๋ผ๋ฏธํฐ ๊ณต์ : ์๊ฐ ๋ ๋ฆฝ์ ๊ฐ์ค์น
- ๊ธฐ์ธ๊ธฐ ๋ฌธ์ : ๊ธด ์ํ์ค์์ ํ์ต ์ด๋ ค์
ํต์ฌ ์ฝ๋¶
rnn = nn.RNN(input_size, hidden_size, batch_first=True)
output, h_n = rnn(x) # output: ์ ์ฒด, h_n: ๋ง์ง๋ง
๋ค์ ๋จ๊ณ¶
14_LSTM_GRU.md์์ LSTM๊ณผ GRU๋ฅผ ํ์ตํฉ๋๋ค.