NLP 자연어 처리를 위한 딥러닝 RNN 기초 (1)
RNN RNN은 Recurrent Neural Network 의 줄임말로 순환 신경망이라 불립니다. RNN 모델은 시퀀스 데이터를 효율적으로 처리하기 위해 만들어진 딥러닝 모델입니다. 여기서 시퀀스 타입의 데이터란 시간에
richdad-project.tistory.com
RNN 훈련
RNN은 BPTT 전략으로 훈련하는데, BPTT란 Backpropagation through time 즉, 타임 스텝으로 네트워크를 펼치고 기본 역전파를 사용하는 방법입니다. 입력 데이터가 정방향으로 진행되면서 가중치를 계산하고 비용함수를 통해 출력 결과를 생성합니다. 그럼 여기서 사용된 그레디언트가 다시 역방향으로 돌아와 가중치를 업데이트 하면서 모델을 최적화 시킵니다.
지금까지 이론 내용에 대해서만 소개했는데 실제 시계열 데이터를 간단하게 샘플용으로 만들고 이 데이터를 가벼운 RNN 모델에 넣어서 어떻게 학습되는지 코드로 살펴보겠습니다.
import numpy as np
# batch_size 만큼 n_steps 길이의 여러 시계열 데이터 생성
# 시계열 데이터(텍스트 문장과 같은 다른 시퀀스 데이터들도)는 입력 특성으로 3D 배열 [배치 크기, 타임 스텝 수, 차원 수]로 나타낸다
def generate_time_series(batch_size, n_steps):
# random하게 0 ~ 1 사이의 실수를 3차원으로 만들어보겠습니다.
freq1, freq2, offsets1, offsets2 = np.random.rand(4, batch_size, 1)
# 시간축 데이터 생성
time = np.linspace(0,1,n_steps)
# 사인 곡선 1
series = 0.5 * np.sin((time - offsets1) * (freq1 * 10 + 10))
# 사인 곡선 2
series += 0.2 * np.sin((time - offsets2) * (freq2 * 20 + 20))
# noise data
series += 0.1 * (np.random.rand(batch_size, n_steps) - 0.5)
return series[...,np.newaxis].astype(np.float32)
위 함수를 활용해서 훈련 데이터 세트, 검증 데이터 세트, 평가 데이터 세트를 만들어 보겠습니다
n_steps = 50
# 총 10000개의 시계열 데이터를 생성하고, 타입 스탭은 50 + 1 로 생성합니다.
series = generate_time_series(10000, n_steps+1)
# train 데이터는 7000개로 구성하고 (7000, 50, 1) shape로 구성합니다.
train_x, train_y = series[:7000, :n_steps], series[:7000, -1]
val_x, val_y = series[7000:9000, :n_steps], series[7000:9000, -1]
test_x, test_y = series[9000: , :n_steps], series[9000: , -1]
우리 모델의 성능을 비교할 기준 성능을 세팅합니다. 단순하게 각 시계열의 마지막 값을 그대로 예측해보는 성능을 기준으로 세워보겠습니다
import tensorflow as tf
pred_y = val_x[:, -1]
# 평균 제곱 오차 (MSE) 확인
print(np.mean(tf.keras.losses.mean_squared_error(val_y, pred_y)
# ==> 0.021878457
또 다른 간단한 방법으로 Fully Connected Network 를 사용해서 MSE를 구할 수 있습니다. 특별하게 이 완전연결층 네트워크는 입력마다 1차원 특성 배열을 요구하기 때문에 Flatten 층을 추가해야 합니다. 시계열 데이터를 선형 조합으로 예측할 필요가 있어, 간단한 선형 회귀 모델을 구현해보겠습니다.
# 모델 생성
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=[50,1]),
tf.keras.layers.Dense(1)
])
# 모델 컴파일
model.compile(
optimizer = tf.keras.optimizers.Adam(),
loss = tf.keras.losses.MeanSquaredError(),
metrics = [tf.keras.metrics.MeanSquaredError()]
)
# 학습
history = model.fit(
train_x,
train_y,
epochs= 20,
validation_data = (val_x, val_y)
)
#결론 (제일 마지막에 나온 MSE)
history.history['val_mean_squared_error'][-1]
# ==> 0.004973348695784807
결론을 확인해봤을 때, 위에서 세웠던 기준 성능보다 더 높은 성능을 보여줍니다(MSE가 낮을수록 높은 성능) 그럼, 기준 성능도 세웠으니깐 Simple RNN을 간단하게 구현해보겠습니다.
# Simple RNN Model
rnn_model = tf.keras.models.Sequential([
tf.keras.layers.SimpleRNN(1, input_shape=[None, 1])])
# compile
rnn_model.compile(
optimizer = tf.keras.optimizers.Adam(),
loss = tf.keras.losses.MeanSquaredError(),
metrics = [tf.keras.metrics.MeanSquaredError()]
)
# 학습
history = rnn_model.fit(
train_x,
train_y,
epochs= 20,
validation_data = (val_x, val_y)
)
#결과
history.history['val_mean_squared_error'][-1]
# ==> 0.011953050270676613
Simple RNN의 특징
- 하나의 뉴런, 하나의 층으로 구성
- RNN은 어떤 길이의 타임스텝도 처리가 가능해서 입력 시퀀스 길이 지정할 필요 없다 (input_shape =[None, 1])
- 기본적으로 하이퍼볼릭 탄젠트 활성화 함수 사용
- 초기 상태 \( h_{(hint)} \)를 0으로 설정하고 첫 번째 타임 스텝 \( x_{(0)} \)와 함께 하나의 순환 뉴런으로 전달
- 뉴런에서 이 값의 가중치 합을 계산하고 하이퍼볼릭 탄젠트 활성화 함수를 적용해서 첫번째 결과 값 \( y_{(0)} \)을 출력한다.
- 이 출력이 새로운 \( h_{0} \) 상태가 되고 새로운 상태는 다음 입력값인 \( x_{(1)} \)과 함께 순환 뉴런으로 전달
- 이 과정을 마지막 타임 스텝까지 반복
- 이 RNN은 마지막 값 \( y_{49} \)를 출력
- 모든 시계열에 대해 이 과정 동시에 수행
- keras의 RNN layer는 최종 출력만 반환. 타임 스텝마다 출력이 필요할 경우 return_sequence=True 지정
위에서 만든 모델은 앞서 만들었던 기본 선형 모델보다는 못한 성능을 보여줍니다. 아무래도 층이 1개이고, RNN의 특성상 은닉층에서 갱신되는 파라미터 수의 제약이 어느 정도 영향을 미친 것으로 예상합니다. 다음 포스팅에서 심층 RNN으로 이어서 성능을 개선시켜 보겠습니다.
NLP 자연어 처리를 위한 딥러닝 RNN 기초 (1)
RNN RNN은 Recurrent Neural Network 의 줄임말로 순환 신경망이라 불립니다. RNN 모델은 시퀀스 데이터를 효율적으로 처리하기 위해 만들어진 딥러닝 모델입니다. 여기서 시퀀스 타입의 데이터란 시간에
richdad-project.tistory.com
'AI > NLP' 카테고리의 다른 글
[엘라스틱서치] 설치 및 세팅 (0) | 2023.01.07 |
---|---|
NLP 자연어 처리를 위한 딥러닝 - LSTM (5) (1) | 2021.12.18 |
NLP 자연어처리를 위한 RNN 알고리즘 코드 기초 (4) - 심층 RNN (0) | 2021.12.17 |
NLP 자연어처리를 위한 RNN 알고리즘 코드 기초 (3) - 심층 RNN (0) | 2021.12.16 |
NLP 자연어 처리를 위한 딥러닝 RNN 기초 (1) (0) | 2021.12.14 |