이전 내용
긴 시퀀스를 다룰 때 두 문제
긴 시퀀스로 RNN을 훈련하려면 많은 타임 스텝에 걸쳐 실행해야 하므로 펼친 RNN이 매우 깊은 네트워트가 된다. 다른 심층 신경망과 마찬가지로 그레이디언트 소실과 폭주 문제를 겪을 수 있는데, 이는 훈련하는 데 아주 오랜 시간이 걸리거나 훈련이 불안정할 수 있음을 의미한다. 또한 RNN이 긴 시퀀스를 처리할 때 입력의 첫 부분을 조금씩 잊게 된다.
- 불안정한 그레이디언트: 그레이디언트 소실, 폭주 문제
- 단기 기억 문제: 입력의 첫 부분을 조금씩 잊게 됨.
※ 그레이디언트 소실과 폭주 문제
- 그레이디언트 소실(Gradient Vanishing): 역전파 과정에서 입력층으로 갈수록 기울기가 점차적으로 작아지는 현상. 경사 하강법이 하위 층의 연결 가중치들이 제대로 업데이트 되지 않아 최적의 값을 찾지 못하게 된다. 기울기 소실문제는 신경망의 활성화 함수의 미분 결과 값(도함수 값)이 계속 곱해지면서 가중치에 따른 결과값의 기울기가 0이 되어 경사하강법을 이용할 수 없게 된다.
- 그레이디언트 폭주(Gradient Exploding): 기울기가 점차 커져 가중치들이 비정상적으로 큰 값이 되어 발산되어 최적의 값을 찾지 못하는 현상. 순환 신경망(RNN)에서 주로 나타난다.
불안정한 그레이디언트 개선하기
불안정한 그레이디언트 문제를 완화하기 위해 심층 신경망에 사용했던 많은 기법(좋은 가중치 초기화, 빠른 옵티마이저, 드롭아웃 등) 을 RNN에서도 사용할 수 있으나, 수렴하지 않는 ReLU 같은 활성화 함수는 큰 도움되지 못하며, 오히려 훈련하는 동안 RNN을 더 불안하게 만들 수 있다.
- 작은 학습률 사용, tahn 함수 같은 수렴하는 활성화 함수 사용
- 훈련이 불안정하다 느껴지면 그레이디언트의 크기를 모니터링(텐서보드 활용)하고 그레이디언트 클리핑 사용
- 배치 정규화는 타임 스텝 사이에 사용할 수 없으며 순환 층 사이에서만 사용할 수 있음.
- 층 정규화는 RNN에서 잘 맞는 정규화로, 배치 정규화와 매우 비슷하지만 배치 차원에 대해 정규화하는 대신 특성 차원에 대해 정규화한다. (샘플에 독립적으로 타임 스텝마다 동적으로 필요한 통계 계산 가능)
케라스를 사용해 간단한 메모리 셀 안에 층 정규화를 구현하기 위해서는 사용자 정의 메모리 셀을 정의해야 한다. 이 층은 call() 메서드가 두 개의 매개변수를 받는 것을 제외하고는 일반적인 층으로, 현재 타임 스텝의 inputs과 이전 타임 스텝의 은닉 states이다.
states 매개변수는 하나 이상의 텐서를 담은 리스트로, 간단한 RNN 셀의 경우 이전 타임 스텝의 출력과 동일한 하나의 텐서를 담고 있다. 셀은 state_size 속성과 output_size 속성을 가져야 하며, 간단한 RNN에서는 둘 다 유닛 개수와 동일하다.
아래 코드는 각 타임 스텝마다 층 정규화를 적용하는 SimpleRNNCell 처럼 작동하는 사용자 정의 메모리 셀을 구현한다.
class LNSimpleRNNCell(tf.keras.layers.Layer):
def __init__(self, units, activation='tanh', **kwargs):
super().__init__(**kwargs)
self.state_size = units
self.output_size = units
self.simple_rnn_cell = tf.keras.layers.SimpleRNNCell(units, activation=None)
self.layer_norm = tf.keras.layers.LayerNormalization()
self.activation = tf.keras.activations.get(activation)
def call(self, inputs, states):
outputs, new_states = self.simple_rnn_cell(inputs, states)
norm_outputs = self.activation(self.layer_norm(outputs))
return norm_outputs, [norm_outputs]
[코드 설명]
- LNSimpleRNNCell 클래스: tf.keras.layers.Layer 클래스 상속
- 생성자는 선형 연산 후와 활성화 함수 전에 층 정규화를 수행하기 위해 유닛 개수와 활성화 함수를 매개변수로 받고 state_size와 output_size 속성을 설정한 다음 활성화 함수 없이 SimpleRNNCell 만듦.
- 생성자는 LayerNormalization 층 생성하고 원하는 활성화 함수 선택
- call() 매서드: 간단한 RNN 셀을 적용하여 현재 입력과 이전 은닉 상태의 선형 조합 계산
- call() 메서드는 층 정규화와 활성화 함수를 차례대로 적용하고, 출력을 두 번 반환하는 데, 하나는 출력이 되고 다른 하나는 새로운 은닉 상태가 됨.
이 사용자 정의 셀을 사용하려면 tf.keras.layers.RNN 층을 만들어 이 셀의 객체를 전달하면 된다.
tf.random.set_seed(42)
custom_ln_model = tf.keras.Sequential([
tf.keras.layers.RNN(LNSimpleRNNCell(32), return_sequences=True,
input_shape=[None, 5]),
tf.keras.layers.Dense(14)
])
fit_and_evaluate(custom_ln_model, seq2seq_train, seq2seq_valid, learning_rate=0.02)
단기 기억 문제 해결하기
RNN을 거치면서 데이터가 변환되므로 일부 정보는 매 훈련 스텝 후 사라지고 어느정도 시간이 지나면 RNN의 상태는 사실상 첫 번째 입력의 흔적을 가지고 있지 않다. 이는 심각한 문제가 될 수 있기 때문에 이런 문제를 해결하기 위해 장기 메모리를 가진 여러 종류의 셀이 연구됐다.
◆ LSTM 셀
LSTM은 순환 신경망(RNN)의 일종으로, 장기 의존성 문제를 해결하기 위해 설계된 신경망 구조이다. RNN은 시계열 데이터나 자연어 처리(NLP) 등 시퀀스 데이터를 다루는 데 강력하지만, 긴 시퀀스를 처리할 때 '기울기 소실(Vanishing Gradient)' 문제로 인해 초기에 입력된 정보를 잃어버리는 한계가 있다. LSTM은 이러한 문제를 극복하기 위해 고안되었다.
1) LSTM 셀 구조
LSTM 셀은 기본 RNN 셀보다 복잡한 구조를 가지고 있으며, 다음과 같은 주요 구성 요소로 이루어져 있다:
- 셀 상태(Cell State): 셀 상태는 LSTM 셀의 핵심이다. 일종의 컨베이어 벨트처럼 정보가 그대로 흘러가도록 하여 필요할 때만 정보를 추가하거나 제거한다.
- 입력 게이트(Input Gate): 새로운 입력을 얼마나 받아들일지 결정한다. 입력 값과 이전의 은닉 상태를 통해 결정되며, 활성화 함수로 시그모이드 함수가 사용된다.
- 포겟 게이트(Forget Gate): 현재 셀 상태에서 정보를 얼마나 버릴지 결정한다. 이전 셀 상태와 새로운 입력 값을 통해 결정되며, 시그모이드 함수를 사용한다.
- 출력 게이트(Output Gate): 새로운 은닉 상태를 얼마나 출력할지를 결정한다. 이전 은닉 상태와 새로운 입력 값을 통해 결정되며, 시그모이드 함수를 사용한다.
- 임시 셀 상태 후보(Temporary Cell State Candidate): 입력 게이트와 함께 사용되어 새로운 정보로 셀 상태를 업데이트한다. 하이퍼볼릭 탄젠트 함수(tanh)가 주로 사용된다.
2) LSTM의 장점
- 장기 의존성 문제 해결: LSTM은 기울기 소실 문제를 완화하여 긴 시퀀스로부터 중요한 정보를 기억할 수 있다.
- 성능: 자연어 처리, 음성 인식, 시계열 예측 등 다양한 분야에서 매우 우수한 성능을 보인다.
케라스에서는 간단하게 SimpleRNN 층 대신 LSTM 층을 사용하면 된다.
tf.random.set_seed(42)
lstm_model = tf.keras.Sequential([
tf.keras.layers.LSTM(32, return_sequences=True, input_shape=[None, 5]),
tf.keras.layers.Dense(14)
])
fit_and_evaluate(lstm_model, seq2seq_train, seq2seq_valid, learning_rate=0.02)
GRU 셀
GRU는 순환 신경망(RNN) 계열의 한 종류로, LSTM(Long Short-Term Memory) 셀과 유사하지만 구조가 더 간단한 장점을 가진 신경망이다. GRU는 RNN이 가지고 있는 장기 의존성 문제를 해결하면서도 계산 효율성을 높이기 위해 고안되었다.
- GRU 셀 구조
GRU 셀은 LSTM 셀보다 간단한 두 개의 게이트로 구성되어 있다.
- 업데이트 게이트(Update Gate): 현재 입력과 이전 은닉 상태를 바탕으로 현재 은닉 상태를 업데이트할지 말지를 결정한다.이 게이트는 이전 은닉 상태가 얼마나 새로운 상태에 반영될지 결정한다.
- 리셋 게이트(Reset Gate): 현재 입력과 이전 은닉 상태를 바탕으로 이전 정보를 얼마나 무시할지를 결정한다. 이 게이트는 새로운 입력을 얼마나 반영할지 결정한다.
- GRU의 장점
- 단순성: GRU는 LSTM보다 구조가 단순하여 구현이 용이하고, 계산 비용이 더 적다.
- 효율성: 적은 수의 매개변수를 사용하면서도 LSTM과 비슷한 성능을 보인다.
- 성능: 다양한 시퀀스 데이터 처리 문제에서 우수한 성능을 보인다.
케라스는 tf.keras.layers.GRU 층을 제공하는 데, 이 층을 사용하려면 SimpleRNN이나 LSTM을 GRU로 바꾸면 된다.
tf.random.set_seed(42)
gru_model = tf.keras.Sequential([
tf.keras.layers.GRU(32, return_sequences=True, input_shape=[None, 5]),
tf.keras.layers.Dense(14)
])
fit_and_evaluate(gru_model, seq2seq_train, seq2seq_valid, learning_rate=0.02)
1D 합성곱 층으로 시퀀스 처리하기
1D 합성곱 층은 시퀀스 전체에 대해 몇 개의 커널을 슬라이딩하여 커널마다 1D 특성 맵을 출력한다. 각 커널은 매우 짧은 하나의 순차 패턴을 감지하도록 학습되며, 커널을 사용하면 이 층의 출력은 모두 길이가 같은 10개의 1D 시퀀스로 구성되거나, 이 출력을 10D 시퀀스 하나로 볼 수 있다.
스트라이드 1과 'same' 패딩을 가진 1D 합성곱 층을 사용하면 출력 시퀀스의 길이는 입력 시퀀스와 같으나, 'valid' 패딩과 1보다 큰 스트라이드를 사용하면 출력 시퀀스는 입력 시퀀스보다 짧아지므로 그에 맞게 타깃을 조정해야 한다.
아래의 모델은 앞의 모델과 같으나, 스트라이드 2를 사용해 입력 시퀀스를 두 배로 다운샘플링하는 1D 합성곱 층으로 시작하는 점이 다르다. 커널 크기가 스트라이드보다 크므로 모든 입력을 사용하여 이 층의 출력을 계산한다. 따라서 모델이 중요하지 않은 일부 사항은 버리고 유용한 정보를 보존하도록 학습할 수 있다. 합성곱 층으로 시퀀스 길이를 줄이면 GRU 층이 더 긴 패턴을 감지하는 데 도움이 되므로, 입력 시퀀스 길이를 두 배인 112일로 늘릴 수 있다.
실제로 커널의 크기가 4이므로 합성곱 층의 첫 번째 출력은 입력 타임 스텝 0~3을 기반으로 하고 첫 번재 예측은 타임 스텝 4~17을 기반으로 한다. 또한 스트라이드 때문에 타깃을 두 배로 다운샘플링해야 한다.
tf.random.set_seed(42)
conv_rnn_model = tf.keras.Sequential([
tf.keras.layers.Conv1D(filters=32, kernel_size=4, strides=2,
activation="relu", input_shape=[None, 5]),
tf.keras.layers.GRU(32, return_sequences=True),
tf.keras.layers.Dense(14)
])
longer_train = to_seq2seq_dataset(mulvar_train_np, seq_length=112,
shuffle=True, seed=42)
longer_valid = to_seq2seq_dataset(mulvar_valid_np, seq_length=112)
downsampled_train = longer_train.map(lambda X, Y: (X, Y[:, 3::2]))
downsampled_valid = longer_valid.map(lambda X, Y: (X, Y[:, 3::2]))
fit_and_evaluate(conv_rnn_model, downsampled_train, downsampled_valid, learning_rate=0.01)
WaveNet
WaveNet은 딥러닝 기반의 신경망 모델로, Google DeepMind에서 개발한 음성 합성 모델이다. WaveNet은 합성 음성을 매우 자연스럽게 생성할 수 있으며, 이외에도 음악 생성, 음성 인식, 시계열 예측 등 다양한 응용 분야에서 사용된다.
- WaveNet의 주요 특징
- 딜레이드 신경망(Causal Convolutional Neural Networks): WaveNet은 순방향(시간 전진) 방향으로만 정보를 전달하는 딜레이드 신경망 구조를 사용한다. 이는 미래 정보가 과거에 영향을 미치지 않도록 보장한다.
- 다이레이션(Dilation): 다이레이션을 통해 신경망의 수용 영역(Receptive Field)을 효과적으로 늘려 더 넓은 시퀀스의 문맥을 이해할 수 있게 한다.
- 확률적 생성: PixelCNN과 유사하게 각 샘플을 확률적으로 생성하여 매우 사람처럼 들리는 음성을 합성한다.
- WaveNet의 구조
WaveNet의 구조는 여러 개의 딥러닝 레이어와 게이트 방식, 다이레이션으로 구성되어 있다.
- 딜레이드 합성(Causal Convolutions): 시간 순서대로 과거 데이터를 활용하여 현재 데이터를 예측한다. 이는 순차적인 데이터의 미래 정보를 사용하지 않도록 보장한다.
- 게이트 구조(Gated Activation Units): WaveNet은 게이트 구조를 사용하여 신경망을 활성화한다. 이는 (\tanh)와 (\sigma) 함수를 사용하여 게이트 양식을 적용한다.
- 다이레이트(Dilated Convolutions): 다이레이트 합성은 입력 신호 사이의 간격을 조절하여 넓은 범위의 문맥 정보를 효율적으로 처리할 수 있게 한다. 다이레이션 지수는 주로 1, 2, 4, 8, ... 이렇게 증가하면서 적용된다. 수용 영역(Receptive Field)을 넓힐 수 있으며, 이를 통해 장기 의존성(Long-term dependency)을 잘 모델링할 수 있다.
- WaveNet의 작동 방식
WaveNet은 음성 합성의 경우 매우 세밀한 단위(샘플 수준)에서 데이터를 모델링한다. 이는 시간 축을 따라 각 샘플을 예측하고, 이러한 예측을 바탕으로 다음 샘플을 생성한다.
- 조건부 생성(Conditional Generation): 입력된 시퀀스를 기반으로 각 다음 샘플의 확률 분포를 추정한다.
대개 잠재벡터(Latent Vector)를 사용하여 조건부 확률을 계산하고, 이를 바탕으로 실제 값을 생성한다. - 확률적 샘플링(Probabilistic Sampling): 각 샘플의 확률 분포를 바탕으로 실제 샘플 값을 확률적으로 생성한다. 이는 매우 자연스러운 음성 합성을 가능하게 한다.
tf.random.set_seed(42)
wavenet_model = tf.keras.Sequential()
wavenet_model.add(tf.keras.layers.Input(shape=[None, 5]))
for rate in (1, 2, 4, 8) * 2:
wavenet_model.add(tf.keras.layers.Conv1D(
filters=32, kernel_size=2, padding='causal', activation="relu", dilation_rate=rate))
wavenet_model.add(tf.keras.layers.Conv1D(filters=14, kernel_size=1))
fit_and_evaluate(wavenet_model, longer_train, longer_valid, learning_rate=0.02)
이 Sequential 모델은 명시적인 입력 층으로 시작하고, 이어서 'causal' 패딩을 사용한 1D 합성곱 층을 추가한다. 이렇게 하면 합성곱 층이 예측을 만들 때 미래의 시퀀스를 훔쳐보게 되지 않는다. 이때 동일하게 팽창 비율이 늘어나는 일련의 층을 반복한다. (팽창 비율 1, 2, 4, 8의 층을 추가 하고 다시 팽창 비율 1, 2, 4, 8의 층 추가)마지막으로 출력 층을 추가한다. 이 층은 크기가 1인 필터 14개를 사용하고 홣성화 함수가 없는 합성곱 층이다. 따라서 훈련하는 동안 112일의 전체 시퀀스를 잘라내거나 다운샘플링할 필요 없이 타깃으로 사용할 수 있다.
다음 내용
[출처]
핸즈 온 머신러닝
https://github.com/restful3/ds4th_study/blob/main/source/%ED%95%B8%EC%A6%88%EC%98%A8_%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D/ch15_RNN%EA%B3%BC%20CNN%EC%9D%84%20%EC%82%AC%EC%9A%A9%ED%95%9C%20%EC%8B%9C%ED%80%80%EC%8A%A4%20%EC%B2%98%EB%A6%AC_jeong.ipynb
한땀한땀 딥러닝 컴퓨터 비전 백과사전
'[파이썬 Projects] > <파이썬 딥러닝, 신경망>' 카테고리의 다른 글
[딥러닝] 비지도 학습: 오토인코더, GAN, 확산 모델 (0) | 2024.12.03 |
---|---|
[딥러닝] RNN을 사용한 자연어 처리 (1) | 2024.12.02 |
[딥러닝] RNN & CNN(feat. 시카고 교통국 데이터셋) - 2 (1) | 2024.12.01 |
[딥러닝] RNN & CNN(feat. 시카고 교통국 데이터셋) - 1 (0) | 2024.11.30 |
[딥러닝] 객체 탐지, 객체 추적 (0) | 2024.11.29 |