7.4 RNN 구조
RNN이란?
은닉층 노드들이 연결되어 이전 단계 정보를 은닉층 노드에 저장할 수 있도록 구성한 신경망
RNN 구조
xt-1에서 ht-1을 얻고
다음 단계에서 ht-1과 xt를 사용
⇒과거 정보, 현재 정보를 모두 반영
RNN의 입력층, 은닉층, 출력층 + 3개의 가중치(Wxh, Whh, Why)
- 위 그림 참고
①Wxh : 입력층에서 → 은닉층으로 전달되는 가중치
② Whh : t 시점의 은닉층에서 → t+1 시점의 은닉층으로 전달되는 가중치
③ Why : 은닉층에서 → 출력층으로 전달되는 가중치
※ 3개의 가중치는 모든 시점에서 동일함(= 같은 값의 가중치를 공유)
t단계에서의 RNN 계산
1. 은닉층
- 계산을 위해 xt, ht-1이 필요 //xt :입력값, ht-1 : 이전 은닉층
- 계산 : 이전 은닉층 × 은닉층 - 은닉층 가중치 + 입력층 - 은닉층 가중치 × (현재) 입력값
- RNN의 은닉층에서 일반적으로 사용하는 활성화 함수 : 하이퍼볼릭 탄젠트 활성화 함수
2. 출력층
- 심층 신경망과 계산 방법이 동일
- 계산 : (은닉층 - 출력층 가중치 × 현재 은닉층)에 소프트맥스 함수 적용
3. RNN의 오차
- 각 단계(t)마다 오차를 측정(≠ 심층 신경망에서의 전방향 학습)
⇒ 각 단계마다 실제 값(yt)과 예측값(hat yt)으로 오차를 측정 - '평균 제곱 오차' 적용
4. RNN의 역전파
- BPTT(BackPropagation Through Time)를 이용 → 모든 단계마다 처음부터 끝까지 역전파함
//BPTT란? 각 단계(t)마다 오차를 측정하고, 이전 단계로 전달되는 것
⇒ 3에서 구한 오차를 이용 → Wxh, Whh, Why, 바이어스(bias) 업데이트
//바이어스 : 가중합에 더해주는 상수, 출력값을 조절함
- ❗ 문제점 : BPTT는 오차가 멀리 전파될 때(왼쪽으로 전파) → 계산량↑ & 전파되는 양이 점차↓(기울기 소멸 문제)
⇒ 보완 방법 : 생략된-BPTT, LSTM, GRU 사용
📝Note. 생략된-BPTT
목적 : 계산량 줄이기
방법 : 현재 단계에서 일정 시점까지만 오류를 역전파함(보통 5단계 이전까지만)
💻 실습 - RNN 계층과 셀 구현
* IMDB 데이터셋 이용 - 영화 리뷰에 대한 데이터
7.4.1 RNN 셀 구현
1️⃣필요한 값 초기화
tf.random.set_seed(22) #seed를 사용 → seed값으로 같은 값을 넣을때마다 같은 숫자가 출력
np.random.seed(22)
assert tf.__version__.startswith('2.') #텐서플로 버전이 2임을 확인 //assert : 프로그램의 내부 점검
batch_size = 128
total_words = 10000
max_review_len = 80
embedding_len = 100
2️⃣모형을 적용하기 위한 데이터셋 준비
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.imdb.load_data(num_words=total_words)
//imdb.load_data() : IMDB 데이터셋을 내려받음
- num_words : 데이터에서 등장 빈도 순위 - 몇번째에 해당하는 단어까지 사용할지를 의미
(num_words=10000 : 등장 빈도 순위가 1~10,000에 해당하는 단어만 사용하겠다는 의미)
#패딩 작업 : 모델의 입력으로 사용하려면 모든 샘플 길이를 동일하게 맞춰야함
pad_sequence(): 정해준 길이보다 길이가 긴 샘플은 값을 일부 자르고, 정해준 길이보다 길이가 짧은 샘플은 값을 0으로 채움
- 첫 번째 인자: 패딩을 진행할 데이터
- maxlen: 모든 데이터에 대해 정규화할 길이
x_train = tf.keras.preprocessing.sequence.pad_sequences(x_train, maxlen=max_review_len)
x_test = tf.keras.preprocessing.sequence.pad_sequences(x_test, maxlen=max_review_len)
#넘파이 배열을 Dataset으로 변환
from_tensor_slices : tf.data.Dataset 를 생성하는 함수, 입력된 텐서로부터 slices를 생성
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
#위에서 만들어준 데이터셋을 변형함
train_data = train_data.shuffle(10000).batch(batch_size, drop_remainder=True)
- shuffle : 데이터셋을 임의로 섞음
- batch : 데이터셋의 항목들을 하나의 배치로 묶음
* batch_size : 몇 개의 샘플로 가중치를 갱신할지 지정
- drop_reminder : 마지막 배치 크기를 무시, 지정한 배치 크기를 사용할 수 있음
#x_test, y_test 데이터에 대한 넘파이 배열을 바로 Dataset으로 변환
test_data = tf.data.Dataset.from_tensor_slices((x_test, y_test))
test_data = test_data.batch(batch_size, drop_remainder=True)
#테스트 데이터셋을 변환
- reduce_max : 지정된 차원을 따라 최대값을 선택
- reduce_min : 지정된 차원을 따라 최소값을 선택
print('x_train_shape: ', x_train.shape, tf.reduce_max(y_train), tf.reduce_min(y_train))
print('x_test_shape: ', x_test.shape)
sample = next(iter(test_data))
print(sample[0].shape)
3️⃣RNN 셀을 이용한 네트워크(신경망) 생성
#객체 지향 프로그램을 파이썬에서 구현한 것(재사용성 고려, 코드의 반복을 최소화)
class RNN_Build(tf.keras.Model):
#__init__, call : 클래스 인스턴스를 생성할 때 초기화하는 부분
#__init__ : 객체가 생성될 때 호출됨 | call : 인스턴스가 생성될 때 호출됨
def __init__(self, units):
super(RNN_Build, self).__init__() #클래스 기반의 __init__ 메서드를 호출
self.state0 = [tf.zeros([batch_size, units])] #self : 자신의 인스턴스를 의미
self.state1 = [tf.zeros([batch_size, units])] #tf.zeros를 사용 → 0값으로 채워진 텐서를 생성, state0에 저장
#임베딩층 : 텍스트 데이터에 대해 워드 임베딩을 수행(임베딩층 사용하려면 각 입력이 모두 정수로 인코딩되어 있어야함)
self.embedding = tf.keras.layers.Embedding(total_words, embedding_len, input_length=max_review_len)
- 첫번째 인자 : 텍스트 데이터의 전체 단어 집합 크기
- 두번째 인자 : 임베딩이 되고 난 후 단어의 차원 지정
- input_length : 입력 데이터의 길이
#SimpleRNN의 셀 클래스를 의미
#unit : 출력 공간의 차원
#dropout : 0과 1 사이의 부동소수점(입력 중 삭제할 유닛의 비율)
self.RNNCell0 = tf.keras.layers.SimpleRNNCell(units, dropout=0.2)
self.RNNCell1 = tf.keras.layers.SimpleRNNCell(units, dropout=0.2)
self.outlayer = tf.keras.layers.Dense(1)
def call(self, inputs, training=None):
x = inputs
x = self.embedding(x) #입력데이터에 원핫인코딩 적용
state0 = self.state0
state1 = self.state1
#unstack() : 중복된 값이 있을때 사용하면 유용(그룹으로 묶은 데이터를 행렬 형태로 전환하여 연산할때)
for word in tf.unstack(x, axis=1):
out0, state0 = self.RNNCell0(word, state0, training) #out, state 각각에 self.RNNCell에서 받아온 값을 저장
out1, state1 = self.RNNCell1(word, state0, training)
x = self.outlayer(out1) #출력층 out1을 적용한 후, 그 값을 x 변수에 저장
prob = tf.sigmoid(x) #마지막으로 x에 시그모이드 활성화 함수를 적용, prob에 저장
return prob #prob 값을 반환
📝Note. unstack() 예시
4️⃣생성된 네트워크를 활용한 모형(모델) 훈련
import time
units = 64
epochs = 4
t0 = time.time()
model = RNN_Build(units)
model.compile(optimizer = tf.keras.optimizers.Adam(0.001),
loss = tf.losses.BinaryCrossentropy(),
metrics = ['accuracy'], #훈련 모니터링 지표로 정확도 사용
experimental_run_tf_function=False) #모델을 인스턴스화하는 기능을 제공(이게 호출돼야 컴파일이 실행됨)
model.fit(train_data, epochs=epochs, validation_data=test_data, validation_freq=2)
/* - epochs : 학습 데이터 반복 횟수
- validation_data : 검증 데이터
- validation_freq : 에포크마다 무조건 검증 데이터셋에 대한 계산 수행X(적절한 간격을 두고 계산) */
5️⃣모델 평가
훈련 데이터의 정확도 : 99%
테스트 데이터의 정확도 : 80%
⇒ 결과가 나쁘지않음
7.4.2 RNN 계층 구현
네트워크 구축 - def call(..., training=None)
training mode vs inference mode
워드 임베딩 - tf.keras.layers.Embedding(...)
from_tensor_slices : https://hiseon.me/data-analytics/tensorflow/tensorflow-dataset/
tf.reduce_max, reduce_min : https://tensorflow.blog/3-%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C%EC%9A%B0-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-first-contact-with-tensorflow/
training/inference mode : https://blogs.nvidia.com/blog/2016/08/22/difference-deep-learning-training-inference-ai/
워드 임베딩 : https://davinci-ai.tistory.com/30
'Etc > Deep Learning' 카테고리의 다른 글
7장 시계열 분석(4) - 게이트 순환 신경망(GRU) (0) | 2021.08.25 |
---|---|
7장 시계열 분석(3) - LSTM (0) | 2021.08.25 |
7장 시계열 분석(1) - 시계열 데이터 분류, 시계열 분석 모델, 순환 신경망(RNN), RNN계층과 셀 (0) | 2021.08.21 |
6장 합성곱 신경망Ⅱ(5) - 이미지 분할을 위한 신경망(U-Net부터 내용추가하기) (0) | 2021.08.17 |
6장 합성곱 신경망(4) - 객체 인식을 위한 신경망(R-CNN, 공간 피라미드 풀링, Fast R-CNN, Faster R-CNN) (0) | 2021.08.12 |