[AI ์คํฐ๋] Section 9 : RNN
RNN ํน์ง
- Sequence Data ์ ํนํ : ์์๊ฐ ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ํนํ๋ ๋ชจ๋ธ
- ‘๊ธฐ์ต’ ๋ฅ๋ ฅ์ ๊ฐ๊ณ ์์
- ์์๋๋ก ๋ค์ด์ค๋ ๊ฒ์ ๊ธฐ์ตํ๋ค๊ฐ ๋ค์ ์์๋ก ๋ค์ด์จ ๊ฒ์ ๊ฒฐ๊ณผ๋ ๋ฌด์์ผ์ง ์์ธก
⇒ ๋งฅ๋ฝ์ ๋ง๊ฒ ๊ธฐ์ต์ด ์ด์ด์ง๊ณ , ์์ธก์ด ์งํ - ๋คํธ์ํฌ์ ๊ธฐ์ต : ์ง๊ธ๊น์ง์ ์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์์ฝํ ์ ๋ณด
⇒ ๋งฅ๋ฝ์ ๋ง์ง ์์ ๋ด์ฉ์ด ๋ค์ด์จ ๊ฒฝ์ฐ (์๋ก์ด ์ ๋ ฅ์ด ๋ค์ด์ด) : ๊ทธ๋๋ง๋ค ๋คํธ์ํฌ๋ ๊ธฐ์ต์ ์กฐ๊ธ์ฉ ์์
- ์์๋๋ก ๋ค์ด์ค๋ ๊ฒ์ ๊ธฐ์ตํ๋ค๊ฐ ๋ค์ ์์๋ก ๋ค์ด์จ ๊ฒ์ ๊ฒฐ๊ณผ๋ ๋ฌด์์ผ์ง ์์ธก
- ์
๋ ฅ์ ๋ชจ๋ ์ฒ๋ฆฌํ๊ณ ๋ ํ ๋คํธ์ํฌ์๊ฒ ๋จ๊ฒจ์ง ๊ธฐ์ต์ ์ํ์ค ์ ์ฒด๋ฅผ ์์ฝํ๋ ์ ๋ณด๋ก ์์ถ
⇒ ์ฌ๋์ ์ํ์ค ์ ๋ณด ์ฒ๋ฆฌ ๋ฐฉ์๊ณผ ๋น์ท, ๊ธฐ์ต์ ๋ฐํ์ผ๋ก ์๋ก์ด ๋จ์ด ์ดํด - ์๋ก์ด ๋จ์ด๋ง๋ค ๊ณผ์ ๋ฐ๋ณต ⇒ Recurrent (์ํ์ )
RNN ๊ตฌ์กฐ
: ์์๋๋ก ํผ์ณ ๋์ผ๋ฉด ๊ฐ์ค์น๋ฅผ ๊ณต์ ํ๋ ๋งค์ฐ ๋ฅํ ๋ด๋ด๋คํธ์ํฌ

⇒ Internal State : \( h_t = tanh(W_hh_{t-1} + W_xx_t) \) → ์๋ก์ด ๊ธฐ์ต์ ๋ง๋๋ ๊ฒ (tanh์ด๋ฏ๋ก (-1, 1))
Output : \( O_t =softmax(W_oh_t) \) → ๋ค์ค ๋ถ๋ฅ ๋ฌธ์ ์ธ ๊ฒฝ์ฐ
- ๊ณผ์
- ํ์์คํ ์ด ์ ๋ถ ํ๋ฒ์ ์ ๋ ฅ
- ์ฒซ๋ฒ์จฐ ํ์ ์คํ \( X_1 \) : ์ ๋ฐธ๋ฅ๋ฅผ ์ด์ฉํ ์๋ก์ด ๊ธฐ์ต์ ๋ง๋ฌ
- ๋๋ฒ์งธ ํ์ ์คํ : \( X_1 \) ์ ๊ธฐ์ต๊ณผ \( X_2 \) ๊ฐ ํฉ์ณ์ ธ ์๋ก์ด ๊ธฐ์ต์ด ๋ง๋ค์ด์ง
- ๋ง์ง๋ง ํ์์คํ : ์์์ ๊ณ์ ์์ถ๋ ๊ธฐ์ต๊ณผ ๋ง์ง๋ง ๋ฐธ๋ฅ๊ฐ ํฉ์ณ์ ธ ๊ฒฐ๊ณผ๋ฅผ ์์ฑ
- ์์ : ์ค๋ ๊ฐ๊ฒฉ์ด 60์, ๋ด์ผ 70์์ด๋ฉด, ๋ชจ๋ ๊ฐ๊ฒฉ์ ?
- ํน์ง
- ํ์์คํ ๋ณ ์์ํ๋ ์ถ๋ ฅ ๊ฐ๋ฅ
- ํญ์ ๊ฐ์ ๊ฐ์ค์น๋ฅผ ๊ฐ์ง๋ค.
- ํ์ต : BPTT (Backpropagation Through Time)๋ก ํ๋ผ๋ฏธํฐ ํ์ต
- ํ์์คํ
์ผ๋ก ๊ณ์ ์ฐ๊ฒฐ๋๋ฏ๋ก ๋ณํ๋ Backpropagation์ผ๋ก ํ๋ จ๋จ
⇒ ํ์ ์คํ ๋งํผ ๊ณ์ ์ฐ๊ฒฐ๋๋ ๋ฅํ ๋ด๋ด ๋คํธ์ํฌ์ ํํ๋ฅผ ๋๊ธฐ ๋๋ฌธ
- ํ์์คํ
์ผ๋ก ๊ณ์ ์ฐ๊ฒฐ๋๋ฏ๋ก ๋ณํ๋ Backpropagation์ผ๋ก ํ๋ จ๋จ
- Simple (Vanilla) RNN ์ ๊ตฌ์กฐ
→ ๋จ๊ธฐ๊ธฐ์ต์ ํนํ๋ ๋ชจ๋ธ๋ก, ์๋ฆฌ๋ฅผ ์ดํดํ๋ ์ฉ๋๋ก ์ฌ์ฉ๋๊ณ , ์ค์ ์์ ์ ์ฌ์ฉํ์ง ์์
- Hidden State Update : \( H_t = \phi(H_{t-1}W_{hh} + X_tW_{xh} + b_h) \)
- Observation Update : \( o_t = H_tW_{hq} + b_q \)
- weight ๊ฐ ํ์ต๋์ด์ง
\( W_{hh} \) : state ๊ฐ์ค์น ๋งคํธ๋ฆญ์ค
\( W_{hq} \) : ์ถ๋ ฅ ๊ฐ์ค์น ๋งคํธ๋ฆญ์ค
\( W_{xh} \) : ์ ๋ ฅ ๊ฐ์ค์น ๋งคํธ๋ฆญ์ค, - ์
๋ ฅ : \( X_t \) ํ์ ์ํ์ค ๋ฐ์ดํฐ
→ ์๋์ฐ ์ฌ์ด์ฆ ๋งํผ์ ๊ฐ์๊ฐ ๋ค์ด๊ฐ
( ์๋์ฐ์ฌ์ด์ฆ = 3 ⇒ ์ธํ : ํ์์คํ 1 2 3 (3๊ฐ) ) - ๊ธฐ์ต ์์ฑ : ์
๋ ฅ → tanh ํต๊ณผ
(ํ์ ์คํ ๋ง๋ค ๊ณผ์ ์ ๋ฐ๋ณตํด์, ๋ง์ง๋ง ํ์์คํ ์ด ๋ ๋ ๊น์ง) - ์ถ๋ ฅ : ๋ง์ง๋ง ํ์ ์คํ ๊น์ง์ ์ต์ข ๊ธฐ์ต
LSTM
: ์๋ ์๋ ๋จ๊ธฐ ๊ธฐ์ต์ ์ฅ๊ธฐ๊ธฐ์ต์ ํ๋ ์ถ๊ฐํด, ์ฅ๊ธฐ๊ธฐ์ต์ด ์ญ ์ฐ๊ฒฐ๋ ํํ
⇒ ํ์์คํ ์ด ๊ธธ์ด์ ธ๋, ์๋ ๊ธฐ์ต์ ๋ ์๋๋ก


๋ด๋ถ ๊ตฌ์กฐ
→ ๊ฒ์ดํธ์ ๊ฐ์ ์ญ์ ํ๋ก ์ต์ข ์์ธก์ ๋์์ด ๋๋๋ก ์ค์ค๋ก ์กฐ์ ๋จ
→ weight๋ฅผ simple RNN๋ณด๋ค 3๊ฐ ์ถ๊ฐํ์ฌ ํ์ตํ๋๋ฐ ์์ด์ ๋ ์ ๊ตํ ํ์ต์ด ๊ฐ๋ฅ
- Input : ์ด์ step์ hidden + ์๋ก์ด ๋ฐ์ดํฐ → ์๋ก์ด cell ์ํ ํ๋ณด
$$\tilde{C}^t = \tanh(W_c[a^{t-1}, x^t] + b_c)$$ - Update Gate : Input์ ์ด๋ ์ ๋ ๋ฐ์๋ค์ผ์ง ๊ฒฐ์ (0-๋ฌด์, 1-์ ์ฒด )
→ Input์ด ์์ํ ์์ธก์ ๋์์ด ๋๋์ง์ ๋ฐ๋ผ ์ฅ๊ธฐ๊ธฐ์ต์ผ๋ก ๋ณด๋ผ์ง ๋ง์ง ๊ฒฐ์
$$\Gamma_u = \sigma(W_u[a^{t-1}, x^t] + b_u)$$ - Forget Gate : ์ด์ cell state๋ฅผ ์ด๋ ์ ๋ ๊ธฐ์ตํ ์ง ๊ฒฐ์ (0-forget, 1-์ ์ฒด ๊ธฐ์ต)
→ ํ์ฌ ํ๊ณ ์๋ ๊ธฐ์ต์ด ์์ํ ์์ธก์ ๋์์ด ๋๋์ง์ ๋ฐ๋ผ ๊ธฐ์ตํ ์ง ๊ฒฐ์
$$\Gamma_f = \sigma(W_f[a^{t-1}, x^t] + b_f)$$ - Output Gate : Input์ ์ด๋ ์ ๋ ๋ค์ step์ผ๋ก ๋ณด๋ผ์ง ๊ฒฐ์
$$\Gamma_o = \sigma(W_o[a^{t-1}, x^t] + b_o)$$ - Cell State
$$C^t = \Gamma_u \ast \tilde{C}^t + \Gamma_f \ast C^{t-1}$$ - Hidden State
$$a^t = \Gamma_o \ast \tanh(C^t)$$
Recurrent Neural Network I/O Overview
→ simple RNN, LSTM, GRU ์ธํ, ์์ํ์ ๋์ผ

- Input shape : 3 ์ฐจ์
- ์ฒซ๋ฒ์งธ ์ฐจ์ : Batch size
- ๋๋ฒ์งธ ์ฐจ์ : Time step → ์๋์ฐ ์ฌ์ด์ฆ
- ์ธ๋ฒ์งธ ์ฐจ์ : Input features → dims • Univariate – one • Multivariate - many
- EXAMPLE
- ๋ด๊ฐ ๋ฐฐ์น๋ฅผ ํ๋ฒ์ 64๊ฐ์ฉ ๋ฐ์ดํฐ๋ฅผ ๋ฌถ์ด์ ๋ณด๋ธ๋ค → batch_size = 64
- ์๋์ฐ ์ฌ์ด์ฆ๋ฅผ 7๋กํ์ฌ, ๊ณผ๊ฑฐ 7์ผ์ ์ฃผ๊ฐ๋ฅผ ๋ณด๊ณ ์ค๋ ์ฃผ๊ฐ๋ฅผ ์์ธก ํ๊ฒ ๋ค. → time step = 7 / Input features = 1
- ์ฃผ์๊ฐ๊ฒฉ, ํ์จ, ๊ธ๋ฆฌ, ๊ฑฐ๋๋์ ์ฌ์ฉํ์ฌ ์์ธกํ๊ฒ ๋ค. → Input features = 4
์ค์ต - LSTM์ ์ด์ฉํ ์์ด ํจํด ์ธ์
๋ฐ์ดํฐ ์๊ฐ ๋ฐ ์์ธก
- input ์ 0 ~ 99 ๊น์ง์ ์ฐ์๋ ์ซ์
- target ์ (1 ~ 101) * 2
๋ชจ๋ธ ๊ตฌ์กฐ
: ์ฐ์๋ 5 ๊ฐ์ ์ซ์๋ฅผ ๋ณด๊ณ ๋ค์ ์ซ์๋ฅผ ์์๋ง์ถ๋๋ก LSTM์ ์ด์ฉํ ๋ชจ๋ธ ⇒ ex) [[5], [6], [7], [8], [9]] → [20]
[[35], [36], [37], [38], [39]]. → [80]
sequential data ์์ฑ
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM
numbers = [[i] for i in range(105)]
numbers[:5] # [[0], [1], [2], [3], [4]]
# ์์๊ฐ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด์ฃผ๋ ๊ฒ
# window size = 5
# ์ฐ์๋ ์ซ์๊ฐ ์์๋, ๋ง์ง๋ง ์ซ์์ 2๋ฐฐ๊ฐ ๋๋ ์ซ์๋ฅผ ๋ง์ถฐ๋ผ
data = []
target = []
for i in range(5, len(numbers)):
data.append(numbers[i-5: i]) # 5๋ถํฐ ์์ํด์ i-5
target.append(numbers[i][0] * 2)
print(data[5]) # [[5], [6], [7], [8], [9]]
print(target[5]) # 20
# ํ์ด์ฌ List๋ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก -> numpy ndarray๋ก ๋ณํ
data = np.array(data, dtype="float32")
target = np.array(target, dtype="float32")
data.shape, target.shape # ((100, 5, 1), (100,)) : 5 -> time step / 1 -> feature
LSTM ๋ชจ๋ธ : ์ค์ผ์ผ๋ง x
model = Sequential()
model.add(LSTM(16, input_shape=(5, 1)))
model.add(Dense(1)) # ์ฐ์๋ ์ซ์ ์์๋ง์ถ๊ธฐ ์ด๋ฏ๋ก 1
# ๋ชจ๋ธ ์ปดํ์ผ
model.compile(optimizer='adam', loss='mae', metrics=['mae'])
# ๋ชจ๋ธ ํ๋ จ
history = model.fit(data, target, epochs=500, validation_split=0.2)
# ๊ฐ๋จํด์ test data๋ฅผ ๋ง๋ฌ
test_data = [[35], [36], [37], [38], [39]]
x = np.array(test_data, dtype="float32").reshape(1, 5, 1)
# ์์ธก : [[2950.1875]] => ๊ตฌ๋ฆผ
model.predict(x.reshape(1, 5, 1)) * 100
LSTM ๋ชจ๋ธ : ์ค์ผ์ผ๋ง o
data = []
target = []
for i in range(5, len(numbers)):
data.append(numbers[i-5: i]) # 5๋ถํฐ ์์ํด์ i-5
target.append(numbers[i][0] * 2)
print(data[5]) # [[5], [6], [7], [8], [9]]
print(target[5]) # 20
# ํ์ด์ฌ List๋ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก -> numpy ndarray๋ก ๋ณํ
data = np.array(data, dtype="float32")
target = np.array(target, dtype="float32")
# Normalize
data = data / 100.
target = target / 100.
data.shape, target.shape # ((100, 5, 1), (100,)) : 5 -> time step / 1 -> feature
model = Sequential()
model.add(LSTM(16, input_shape=(5, 1)))
model.add(Dense(1)) # ์ฐ์๋ ์ซ์ ์์๋ง์ถ๊ธฐ ์ด๋ฏ๋ก 1
# ๋ชจ๋ธ ์ปดํ์ผ
model.compile(optimizer='adam', loss='mae', metrics=['mae'])
# ๋ชจ๋ธ ํ๋ จ
history = model.fit(data, target, epochs=500, validation_split=0.2)
# ๊ฐ๋จํด์ test data๋ฅผ ๋ง๋ฌ
test_data = [[35], [36], [37], [38], [39]]
x = np.array(test_data, dtype="float32").reshape(1, 5, 1) /100
# ์์ธก : [[[80.26072] => ์ข๋ค
model.predict(x.reshape(1, 5, 1)) * 100
์ค์ต - LSTM์ ์ด์ฉํ ์ฃผ์ ๊ฐ๊ฒฉ ์์ธก
๋ฐ์ดํฐ : Yahoo finance data
๋ชจ๋ธ : Apple ์ฃผ์์ ๊ฐ๊ฒฉ ์ถ์ธ ์์ธก (์ง๋ window-size ์ผ์ ์ญ์ฌ์ ๊ฐ๊ฒฉ์ ์ฌ์ฉํ์ฌ ์๊ฐ t์ ๊ฐ๊ฒฉ์ ์์ธก)
→ ์ถ์ธ๋ฅผ ํ์
๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ
!pip install yfinance
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
aapl = yf.download('AAPL', start='2018-01-01', end='2022-12-31', progress=False)
# ์ ํ ์ฃผ๊ฐ ๊ทธ๋ํ ๊ทธ๋ฆฌ๊ธฐ
aapl.Close.plot()

๋ฐ์ดํฐ์ ๋ถ๋ฆฌ ๋ฐ ์ค์ผ์ผ๋ง
hist = []
target = []
window = 3
close = aapl['Close'].values # ์ข
๊ฐ
for i in range(len(close) - window): # ์๋์ฐ ๊ฐ์๋งํผ ๋จ๊ธฐ๋๋ก
x = close[i:i+window]
y = close[i+window]
hist.append(x)
target.append(y)
close[:10]
# array([[27.33250046],
# [26.5625 ],
# [26.56500053],
# [26.9375 ],
# [27.97249985],
# [28.00250053],
# [27.3125 ],
# [27.55500031],
# [27.45000076],
# [26.70499992]])
hist[:5] # ํ๋์ฉ ์์ง์ด๋ฉฐ ์๋์ฐ ๊ฐ์๋งํผ ๊ฐ์ง
[array([[27.33250046],
[26.5625 ],
[26.56500053]]),
array([[26.5625 ],
[26.56500053],
[26.9375 ]]),
array([[26.56500053],
[26.9375 ],
[27.97249985]]),
array([[26.9375 ],
[27.97249985],
[28.00250053]]),
array([[27.97249985],
[28.00250053],
[27.3125 ]])]
# hist"์ ๊ฐ ์์๋ window๊ฐ timestep์ list์
๋๋ค.
# => 1์ฉ ์ฆ๊ฐํ๊ธฐ ๋๋ฌธ์ "hist"์ ๋ ๋ฒ์งธ ์์์ ๋ง์ง๋ง ํญ๋ชฉ์ "target"์ ์ฒซ ๋ฒ์งธ ์์์ ๊ฐ์์ผ ํฉ๋๋ค.
# ๋ํ ๋ง์ง๋ง ์ซ์๊ฐ ๊ฐ์์ผ ํฉ๋๋ค.
# print(close[-1])
# print(i+length)
# print(target[-1])
hist[1][-1] == target[0] # True
hist = np.array(hist)
target = np.array(target)
target = target.reshape(-1, 1)
print(hist.shape) # 1835, 3 : 3์ผ์น์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์
print(target.shape) # 1835, 1 : ๋ค์๋ ํ๋ฃจ์ ์ข
๊ฐ๋ฅผ ์์๋ง์ถ๋๋ก
# train/test split : ๋
๋ฆฝ์ ์ด์ง ์์ผ๋ฏ๋ก shuffle ํ๋ฉด ์๋๋ฏ๋ก ํจํค์ง ์ฌ์ฉ ๋ถ๊ฐ
# 1098์ผ์ ๋ฐ์ดํฐ๋ก ๋ชจ๋ธ์ ํ์ต์ํค๊ณ ๋ค์ 100์ผ์ ๋ฐ์ดํฐ๋ก ํ
์คํธํ๋ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ถํ
split = len(hist) - 100 # 1735
X_train = hist[:split] # 1735,3
X_test = hist[split:] # 100,3
y_train = target[:split] # 1735,1
y_test = target[split:] # 100, 1
# ์ค์ผ์ผ๋ง
sc1 = MinMaxScaler()
X_train_scaled = sc1.fit_transform(X_train)
X_test_scaled = sc1.transfrom(X_test)
sc2 = MinMaxScaler()
y_train_scaled = sc2.fit_transform(y_train)
y_test_scaled = sc2.transfrom(y_test)
# time sequence๊ฐ ์์ผ๋ฏ๋ก ์ฐจ์์ด ํ๋ ๋ ๋์ด์ผํ๋ฏ๋ก reshape -> 3D๊ฐ ๋์ด์ผํจ
X_train = X_train.reshape(-1, window, 1)
X_tes = X_test.reshape(-1, window, 1)
X_train.shape, X_test.shape # ((1735, 3, 1), (100, 3, 1)) : ๋ฐฐ์น ์ฌ์ด์ฆ, ํ์ ์ํ์ค, ํผ์ฒ ๊ฐ์
๋ชจ๋ธ ์์ฑ
model = tf.keras.Sequential()
# ์ฒซ๋ฒ์งธ LSTM๊ณผ ๋๋ฒ์งธ LSTM์ด ์ฐ๊ฒฐ๋์ด์ผํ๋ฏ๋ก return_sequences๋ฅผ ์ฃผ์ด์ผํจ
# 3๊ฐ์ LSTM ์
์ ์์
model.add(LSTM(units=64, return_sequences=True, input_shape=(window, 1), dropout=0.2))
model.add(LSTM(units=32, return_sequences=True, dropout=0.2))
model.add(LSTM(units=16, dropout=0.2))
# Dense ๋ ์ด์ด์ ์ฐ๊ฒฐ
model.add(Dense(units=1))
model.add(Lambda(lambda x: x * 100))
# ์ปดํ์ผ
model.compile(optimizer='adam', loss='mean_squared_error')
# ์์ธก
history = model.fit(X_train, y_train, epochs=100, batch_size=32)
# ํ๋ จ ๋์์ loss ๋ณํ ์๊ฐํ
plt.plot(history.history['loss'])
plt.legend(['Training Loss'])
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show();

์ถ์ธ ์์ธก
# ์์ผ๋ก 100์ผ๋์์ ์ฃผ๊ฐ ์ถ์ด ์์ธก
pred = model.predict(X_test)
# ์์ธก๊ณผ ์ค์ ๋น๊ตํ ์๊ฐํ
plt.figure(figsize=(12,6))
plt.plot(np.concatenate((y_train, y_test)), label='True')
plt.plot(np.concatenate((y_train, pred)), label='Predicted')
plt.title('Apple Stock Price Prediction')
plt.legend()
plt.show()
plt.figure(figsize=(8,4))
plt.plot(y_test, label='True')
plt.plot( pred, label='Predicted')
plt.title('Apple Stock Price Prediction')
plt.legend()
plt.show()

