인코더 디코더 네트워크
인코더 디코더 네트워크 구조
* 위 그림은 영어 단어를 입력하면 프랑스어로 번역해주는 인코더-디코더 네트워크를 뜻한다.
* 인코더 부분의 input은 I drink milk를 뒤집어 놓은 문장인 것을 확인할 수 있다. 이는 문장의 시작 단어가 인코더의 마지막에 가도록 하기 위해서이며, 디코더가 번역할 첫 단어가 문장의 시작 단어( ' I ' ) 이기 때문이다.
* 각 단어는 초기에 1차원 값(ex - milk : 288) 으로 표현되어 있지만, Embedding layer를 지나면서 단어 임베딩으로 변환되어 인코더, 디코더의 input으로 들어가게 된다.
* 각 time step마다 디코더는 vocabulary dictionary에 있는 단어에 대한 점수를 출력하고 이 output은 다시 softmax 층으로 입력되어 확률을 출력하게 된다. 예를 들어 디코더의 첫 번째 time step의 Je는 0.2의 확률 값을 가지고, Tu는 0.01의 확률 값을 가지게 된다면 가장 높은 확률 값을 띠는 것은 Je이기 때문에 Je가 출력된다.
* 이러한 작업은 소프트맥스를 사용한 일반적인 분류 작업과 매우 유사하다. 손실함수 또한 'sparse_categorical_crossentropy'를 사용한다.
*추론 과정에서는 이전 time step의 output을 다음 step의 입력으로 집어넣는 방식을 사용하게 된다.
인코더 디코더 네트워크 구현
* 인코더 디코더 네트워크를 구현하기 위해서는 몇 가지를 더 살펴봐야 한다.
* 먼저 앞서 구조에서 살펴본 바에 의하면 인코더와 디코더의 입력 sequence의 길이가 동일하다고 가정하였다. 하지만, 실제로 문장의 길이는 다르다. 이를 해결하기 위해 문장을 비슷한 길이의 bucket으로 그루핑 해야 한다(1
6길이 버킷, 7
12길이 버킷 등으로 길이마다 그루핑). 또한 버킷에 담긴 문장이 모두 동일한 길이가 되도록 padding을 추가해야 한다('I drink milk'가 1~6 길이 버킷에 들어가게 되면 ', , , milk, drink, I' 가 된다.).
* EOS 토큰 이후의 출력은 모두 무시해야 한다. 예를 들어 'Je bois du lait oui'를 출력하면 마지막 단어에 대한 손실은 무시한다(마스킹 처리를 통해).
* 출력 어휘 사전의 크기가 50000개와 같이 매우 크다면 출력 벡터가 50000차원이 될 것이고 방대한 양의 연산이 필요해진다. 이를 방지하기 위하여 tf.nn.sampled_softmax_loss()를 사용해 sampling softmax를 적용한다. sampling softmax는 타깃 단어에 대한 로짓이 아닌 무작위로 sampling한 단어의 로짓만을 고려하기 때문에 연산량을 줄일 수 있다. 하지만, sampling softmax는 타깃 값을 알고 있어야 하기 때문에 추론 시에는 사용 불가능하다.
import tensorflow_addons as tfa
encoder_inputs = keras.layers.Input(shape=[None], dtype=np.int32)
decoder_inputs = keras.layers.Input(shape=[None], dtype=np.int32)
sequence_lengths = keras.layers.Input(shape=[], dtype=np.int32)
embeddings = keras.layers.Embedding(vocab_size, embed_size)
encoder_embeddings = embeddings(encoder_inputs)
decoder_embeddings = embeddings(decoder_inputs)
encoder = keras.layers.LSTM(512, return_state=True) #return hidden state
encoder_outputs, state_h, state_c = encoder(encoder_embeddings)
encoder_state = [state_h, state_c]
sampler = tfa.seq2seq.sampler.TrainingSampler()
decoder_cell = keras.layers.LSTMCell(512)
output_layer = keras.layers.Dense(vocab_size)
decoder = tfa.seq2seq.basic_decoder.BasicDecoder(decoder_cell, sampler,
output_layer=output_layer)
final_outputs, final_state, final_sequence_lengths = decoder(
decoder_embeddings, initial_state=encoder_state,
sequence_length=sequence_lengths)
Y_proba = tf.nn.softmax(final_outputs.rnn_output)
model = keras.models.Model(
inputs=[encoder_inputs, decoder_inputs, sequence_lengths],
outputs=[Y_proba])
* TrainingSampler는 tensorflow addons에서 지원하는 샘플러 중 하나이다. 각 step에서 디코더에게 이전 step의 출력이 무엇인지를 알려주는 역할을 한다. 추론 시에는 실제로 출력되는 토큰의 임베딩이 된다. 훈련 시에는 이전 타깃 토큰의 임베딩이 된다.
양방향 RNN
* 일반적인 순환층은 이전 time step의 정보와 현재의 입력만 출력에 사용하기 때문에 과거 및 현재의 정보만을 활용한다고 할 수 있다.
* 하지만 번역과 같은 NLP 문제에서 이렇게 과거의 정보만을 입력으로 받는 것은 충분하지 않다. 번역의 경우 과거의 문장 흐름 만으로 다음 단어가 어떤 것이 올지 예측하는 것은 매우 어렵다. 그렇기 때문에 두 개의 순환층을 사용해 하나는 앞에서부터, 하나는 뒤에서부터 단어를 읽어 각 time step마다 두 출력을 연결해 prediction을 출력하는 방법을 사용한다.
* 이러한 양방향 순환층을 keras로 구현하기 위해서는 아래와 같은 코드를 사용한다.
keras.layers.Bidirectional(keras.layers.GRU(10, return_sequence=True)
빔 검색(beam search)
* 인코더 - 디코더 모델을 활용하여 문장을 번역할 때 step마다 가장 확률이 높은 단어만을 선택하는 것은 잘못된 번역으로 이어지는 경우가 많다. 예를 들어 훈련 세트에 'Comment vas-tu jouer?' 라는 'How will you play?'를 뜻하는 프랑스어 문장이 다수 들어있다고 해보자. 하지만, 인코더에 입력으로 'Comment vas-tu?'라는 'How are you?'를 뜻하는 문장이 들어왔다고 가정해보자. 이 경우 How are you?가 target임에도 불구하고 How will you?로 번역할 가능성이 높아진다. Comment vas-tu가 입력되었을 때 How 다음에 가장 확률이 높은 단어는 will이기 때문이다.
* 이러한 문제를 방지하기 위하여 가장 확률이 높은 단어가 아닌 k개의 가능성 있는 문장 리스트를 생성하여 k개의 가능성 있는 문장을 만드는 방법을 사용한다. 이 방법이 beam search이다. 여기서 k는 beam width라고 부른다.
* 빔 검색으로 'Comment vas-tu jouer?'를 번역한다고 해보자. beam width는 3이다. 디코더의 첫 번째 time step에서는 3개의 가장 가능성 높은 단어 리스트와 추정확률을 생성한다. How(75%), What(3%), You(1%)가 가장 가능성이 높다. 이제 3개의 단어를 각 모델이라고 생각하고 각 모델별로 다음 단어로 가능성이 높은 3개의 확률을 출력할 것이다. How로 시작한 모델의 경우를 보면 다음 단어로 가능성이 높은 3개는 will(36%), are(32%), do(16%)이다. 이 확률은 How가 주어졌다는 조건이 붙은 조건부 확률이다. 이 조건부 확률을 계산하는 과정에서 각 모델은 어휘사전에 있는 단어 개수, 예를 들어 10000개의 단어가 있다고 했을 때 10000개에 대한 조건부 확률을 일일히 계산하게 된다. 그리고 이 최초 단어의 추정 확률과 조건부 확률을 곱한다. 예를 들어 How will은 75% x 36% = 27%이다. 그리고 모든 모델 중 가장 확률이 높은 3개의 문장을 추린다. 예를 들자면 전체 문장 중 How will(27%), How are(24%), How do(12%)와 같이 3개의 문장을 추리는 것과 같다. 그리고 동일한 과정으로 3개의 문장을 계속해서 추려 나간다. 그러면 최종적으로 How do you do(7%), How are you (6%), How are you doing(3%) 와 같은 문장을 얻을 수 있다. 이 중 가장 확률이 높은 문장을 선택한다.
빔 검색 구현
* 이를 tensorflow addons를 활용하면 쉽게 구현할 수 있다.
* 이 빔 검색은 짧은 문장에서는 잘 작동하지만 긴 문장에서는 기억 문제 때문에 잘 작동하지 않는다. 이를 해결하기 위해서는 어텐션 메커니즘을 사용할 수 있다.
model = keras.models.Sequential([
keras.layers.GRU(10, return_sequences=True, input_shape=[None, 10]),
keras.layers.Bidirectional(keras.layers.GRU(10, return_sequences=True))
])
model.summary()
'Deep Learning > Hands On Machine Learning' 카테고리의 다른 글
17.2 Undercomplete linear AutoEncoder로 PCA 수행하기 (0) | 2021.12.23 |
---|---|
17.1 효율적인 데이터 표현 (0) | 2021.12.23 |
16.2 감성 분석 (0) | 2021.11.23 |
16.1 Char-RNN을 통해 셰익스피어 문체 생성하기 (0) | 2021.11.23 |
15.4 긴 시퀀스 다루기 (0) | 2021.11.21 |