RNN #1
_2025.03.21_
Chapter 09. RNN
- 55_RNN - simple rnn
- 56_RNN - LSTM
Simple RNN
RNN?
순환 신경망( Recurrent Neural Network )이라 한다.
- 활성화 신호가 입력에서 출력으로 한 방향으로 흐르는 피드포워드 신경망
- 순환 신경망은 뒤쪽으로 연결하는 순환 연결이 있음
- 순서가 있는 데이터를 입력으로 받고 변화하는 입력에 대한 출력을 얻는다.
RNN(순환신경망)은 이전 시간의 정보를 기억하면서 현재 입력을 처리하는 신경망 구조. 기존의 신경망과 달리, 은닉 상태(hidden state)를 통해 과거 데이터를 순차적으로 반영할 수 있어서 시계열 데이터나 순차적 데이터 처리에 적합
Simple RNN은 자연어 처리(Natural Language Processing, NLP), 시계열 예측(Time Series Forecasting), 음성 인식(Speech Recognition)과 같이 입력 데이터가 시간적 순서를 가지고 있는 문제를 해결하는 데 사용
Sequence-to-Sequence
Sequence-to-Vector 형태
인코더-디코더
RNN의 셀 모양
RNN 코드실습
# RNN 실습
import tensorflow as tf
import numpy as np
# time stamp 데이터 만들기
X = []
Y = []
for i in range(6):
lst = list(range(i, i+4))
X.append(list(map(lambda c: [c/10], lst)))
Y.append((i+4)/10)
print(f'lst : {lst}')
print(f'X : {X}')
print(f'Y : {Y}')
X = np.array(X)
Y = np.array(Y)
for i in range(len(X)):
print(X[i], Y[i])
<output>
lst : [0, 1, 2, 3]
X : [[[0.0], [0.1], [0.2], [0.3]]]
Y : [0.4]
lst : [1, 2, 3, 4]
X : [[[0.0], [0.1], [0.2], [0.3]], [[0.1], [0.2], [0.3], [0.4]]]
Y : [0.4, 0.5]
lst : [2, 3, 4, 5]
X : [[[0.0], [0.1], [0.2], [0.3]], [[0.1], [0.2], [0.3], [0.4]], [[0.2], [0.3], [0.4], [0.5]]]
Y : [0.4, 0.5, 0.6]
lst : [3, 4, 5, 6]
X : [[[0.0], [0.1], [0.2], [0.3]], [[0.1], [0.2], [0.3], [0.4]], [[0.2], [0.3], [0.4], [0.5]], [[0.3], [0.4], [0.5], [0.6]]]
Y : [0.4, 0.5, 0.6, 0.7]
lst : [4, 5, 6, 7]
X : [[[0.0], [0.1], [0.2], [0.3]], [[0.1], [0.2], [0.3], [0.4]], [[0.2], [0.3], [0.4], [0.5]], [[0.3], [0.4], [0.5], [0.6]], [[0.4], [0.5], [0.6], [0.7]]]
Y : [0.4, 0.5, 0.6, 0.7, 0.8]
lst : [5, 6, 7, 8]
X : [[[0.0], [0.1], [0.2], [0.3]], [[0.1], [0.2], [0.3], [0.4]], [[0.2], [0.3], [0.4], [0.5]], [[0.3], [0.4], [0.5], [0.6]], [[0.4], [0.5], [0.6], [0.7]], [[0.5], [0.6], [0.7], [0.8]]]
Y : [0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
[[0. ]
[0.1]
[0.2]
[0.3]] 0.4
[[0.1]
[0.2]
[0.3]
[0.4]] 0.5
[[0.2]
[0.3]
[0.4]
[0.5]] 0.6
[[0.3]
[0.4]
[0.5]
[0.6]] 0.7
[[0.4]
[0.5]
[0.6]
[0.7]] 0.8
[[0.5]
[0.6]
[0.7]
[0.8]] 0.9
Simple RNN 모델 구성 진행
# Simple RNN 구성
model = tf.keras.Sequential([
tf.keras.layers.SimpleRNN(units=10, return_sequences=False, input_shape=[4,1]),
tf.keras.layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.summary()
input_shape이 4,1이라는 것. (timesteps가 4,input_dim이 1)
units : simple RNN 레이어에 존재하는 뉴런의 수
return_sequences는 출력으로 시퀀스 전체를 출력할지 여부
구조를 보면...
Fit(학습 진행)
Predict 진행 : 결과는 상당히 허접하다.
위에서 설정한 X, Y값을 보면 유추할 수 있듯이 결과는 [1.0]이 나와야 하지만 조금 부족한 모습을 확인할 수 있다.
그래서 아래처럼 조금 더 복잡하게 모델을 구성해본다.
조금 더 복잡한 SimpleRNN
## 모델 재구성
model = tf.keras.Sequential([
tf.keras.layers.SimpleRNN(units=10, return_sequences=True, input_shape=[4,1]),
tf.keras.layers.SimpleRNN(units=10, return_sequences=True, input_shape=[4,1]),
tf.keras.layers.SimpleRNN(units=10, return_sequences=True, input_shape=[4,1]),
tf.keras.layers.SimpleRNN(units=10, return_sequences=False, input_shape=[4,1]),
tf.keras.layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.summary()
그 다음 위와 같이 학습(FIT) 및 예측 (Predict)을 진행해서 결과를 확인하였다. 거기서 거기다. 조금 더 많은 데이터를 주고 layer를 더 복잡하게 하면 가능성은 있어 보인다..
단,
Simple RNN의 단점
- 입력 데이터가 길어지면 학습 능력이 저하된다. Long-Term Dependency 문제
- 현재의 답을 얻기 위해 과거의 정보에 의존해야 하는 RNN이지만, 과거 시점이 현재와 너무 멀어지면 문제를 풀기 어렵다.
LSTM
Simple RNN을 보완하기 위해 나온 모델이다.
LSTM의 핵심 아이디어는 cell state 이다. 장기적인 기억을 저장한다.
첫번째 X에서는 이전 셀의 기억을 얼마나 보존할 것인가를 결정한다. 그 이후 학습결과에 대한 정보를 얼만큼 더할 것인가를 결정하게 된다 (+). 그 후 다시 그 결과를 옆 셀로 보내준다.
구현코드는 굉장히 복잡하다.
LSTM 예제 하나
랜덤하게 변수 및 정답값 생성
Y = []
for i in range(3000):
lst = np.random.rand(100)
idx = np.random.choice(100, 2, replace=False)
zeros = np.zeros(100)
zeros[idx] = 1
X.append(np.array(list(zip(zeros, lst))))
Y.append(np.prod(lst[idx]))
print('result : ', X[0], Y[0])
우선 RNN을 먼저 적용해보고, LSTM과 비교해보자..
### RNN TEST
model = tf.keras.Sequential([
tf.keras.layers.SimpleRNN(units=30, return_sequences=True, input_shape=[100, 2]),
tf.keras.layers.SimpleRNN(units=30),
tf.keras.layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.summary()
학습 진행
X = np.array(X)
Y = np.array(Y)
history = model.fit(X[:2560], Y[:2560], epochs=50, validation_split=0.2)
------------------
Epoch 50/50
64/64 [==============================] - 6s 88ms/step - loss: 0.0447 - val_loss: 0.0560
결과를 보면...
plt.plot(history.history['loss'], '-b', label='loss')
plt.plot(history.history['val_loss'], '-r', label='val_loss')
plt.xlabel('Epoch')
plt.legend()
plt.show()
Epoch가 반복되더라도 Loss가 줄지 않는것 같다. 별로 학습의 효과가 좋다고 볼수 없고 학습 데이터와 Validation 데이터의 Loss 차이도 들쑥날쑥하다.
다음으로는 LSTM 방법으로 진행해보자.
## LSTM
model = tf.keras.Sequential([
tf.keras.layers.LSTM(units=30, return_sequences=True, input_shape=[100, 2]),
tf.keras.layers.LSTM(units=30),
tf.keras.layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.summary()
history = model.fit(X[:2560], Y[:2560], epochs=50, validation_split=0.2)
----------------
Epoch 50/50
64/64 [==============================] - 1s 17ms/step - loss: 0.0013 - val_loss: 0.0013
epoch가 반복될 수록 모두 loss가 0.01 이하로 낮아지는 것을 확인할 수 있다.
SimpleRNN에서는 학습이 잘 되지 않는 단점 (학습 데이터가 길어지거나, 깊어질수록)을 보완한 것을 확인할 수 있다.