이글은 www.analyticsvidhya.com의 내용임. (링크)
Essentials of Deep Learning – Sequence to Sequence modelling with Attention (using python)
Introduction
규모에서 딥러닝은 챗봇과 이전에 보지 못한 봇을 생성하여 많은 산업을 와해시키고 있다. 반면 방금 딥러닝을 시작한 사람은 기본적인 신경망과 CNN과 RNN같은 다양한 구조를 읽을 것이다.
하지만, 단순한 개념에서 딥러닝의 산업적 응용까지 큰 도약처럼 보인다. 배치정규화, 드롭아웃 그리고 어뎉션 같은 개념은 딥러닝 어플리케이션 구축에서 거의 필수적으로 알아야 하는 것이다.
이 글에서는 음성인식과 자연어 처리 분야에서 현재 최신 기술에서 사용되는 중요한 두가지 개념즉, Seq2Seq 모델링과 어텐션 모델을 다룬다.
이들 두 기술의 잠재적인 응용을 훔쳐볼 수 있도록, Baidu의 AI 시스템은 단지 3초간의 훈련으로 목소리를 이해하여 사람의 목소리를 복제하는 clone your voice에 이 기술을 사용했다. 원본과 합성된 목소리로 구성된 바이두 연구팀이 제공하는 audio samples로 점거해 볼 수도 있다.
Table of Contents
- Problem Formulation for Sequence to Sequence modelling
- A glance of Sequence to Sequence modelling technique
- Improving the performance of seq2seq – Beam Search and Attention models
- Hands-on view of Sequence to Sequence modelling
Problem Formulation for Sequence to Sequence modelling
시퀀스 모델링 문제를 해결하기 위해서는 RCNN이 찾는 구조임을 안다. 시퀀스 모델링 문제가 무엇처럼 보이는지 이해하기 위해 질문-응답 시스템의 예를 살펴보자.
다음과 같은 일련의 진술이 있다고 해보자.
- Joe went to the kitchen. Fred went to the kitchen. Joe picked up the milk.
- Joe travelled to the office. Joe left the milk. Joe went to the bathroom.
그리고 아래와 같은 질문을 한다.
- Where was Joe before the office?
적절한 응답은 "kitchen"이다. 얼핏 보면 간단한 문제처럼 보이지만 복잡성을 이해하기 위해 시스템이 이해해야만 하는 두개의 차원이 있다.
- 영어 기반이고 문장을 완성하는 문자/단어의 시퀀스
- 진술에서 언급된 사람 주변을 맴도는 일련의 사건
이는 시퀀스를 이해하는 것은 주변에서 어떠한 예측을 만드는 것이 중요하기 때문에 시퀀스 모델링 문제로 고려될 수 있다.
시퀀스 모델링 문제의 시나리오는 많으며 아래 그림으로 요약할 수 있다. 위의 예시는 많은 입력 - 하나의 출력 문제이다.(하나의 출력으로써 단어를 고려한다면)
이런 문제의 특별한 경우는 Seq2Seq 모델링 문제로 불린다. 여기서 출력처럼 입력도 시퀀스이다. Seq2Seq 문제의 예는 다음과 같을 수 있다.
- 기계 번역(Machine Translation)
: 다른 언어로 문장을 번역하는 인공적인 시스템 - 비디오 캡셔닝(Video Caption)
: 소리신호의 설명을 포함하여 각 프레임에 대해 비디오의 자막을 자동적으로 생성하는 것.(기계 시작, 배경으로 사람 웃음소리 등과 같이)
A glance of Sequence to Sequence modelling technique
대표적인 Seq2Se1 모델은 인코더와 디코더 두 부분으로 구성된다. 두 부분 모두 실제로 두개의 다른 신경망 모델을 하나의 커다란 네트워크로 묶은 것이다.
넓게, 인코더 네트워크의 작업은 입력 시퀀스를 이해하고 이를 더 작은 차원적 표현으로 만드는 것이다. 이 표현은 그리고 출력을 나타내는 자체의 시퀀스를 생성하는 디코더 네트워크로 전달된다. 이 개념을 이해하기 위해 대화 에이전트의 예를 살펴보자.
위 이미지에서 입력 시퀀스는 "How are you"이다. 따라서 그같은 입력 시퀀스가 LSTM 블록으로 구성된 인코더-디코더 네트워크를 통해 전달 되면, 디코더는 디코더의 반복 각 단계에서 하나씩 단어를 생성한다. 전체 반복 후 생성된 출력 시퀀스는 "I am fine"이다.
Improving the performance of seq2seq – Beam Search and Attention models
Seq2Seq 모델링 네트워크는 바로 사용할 수 없다. 이느 여전히 기대치에 부응하기 위해 최고의 성능을 내도록 튜닝이 필요하다. 아래는 Seq2Seq 모델링 응용분야에서 과거에 유용함이 증명된 두가지 기술이다.
- Beam Search
- Attention mechanism
Beam Search
이전에 살펴본 것과 같이 디코더 네트워크는 시퀀스에서 단어의 발생 활률을 생성한다. 각 단계에서 디코더는 시퀀스에서 다음 단어가 무엇인지에 관해 결정해야만 한다.
결정을 하는 한가지 방법은 각 단계에서 가장 가능성있는 단어를 탐욕적으로(greedily) 찾는 것이다.
예를 들면, 입력 시퀀스가 "Who does John like?"라고 하면, 단일 디코더 네트워크에서 여러 번 반복하여 생성 할 수있는 많은 문장이있을 수 있으며, 위에 표시된 것처럼 트리와 같은 문장 구조를 만들 수 있다. 탐욕적 방법은 각 단계에서 가장 가능성이 큰 단어를 선택할 것이다.
만약 "likes Mary John?"이 나온다면? 이것은 반드시 가장 높은 확률을 가진 문장을 제공하지 않는다. 이를 위해 문장에 적합한 순서를 지능적으로 정렬해야 할 것이다.
Bean Search는 아래 그림에서 보여지는 것과 같이 시퀀스에서 다음 k 단어들의 확률을 고려하고 최고 확률을 선택한다.
Attention mechanism
사람이 그림을 이해하려고 할때, 전체적인 그림의 본질을 얻기 위해 그림의 특정 부분에 집중한다. 동일한 방법으로 인공적인 시스템이 전체 "그림"을 얻기 위해 이미지의 특정 요소에 집중하도록 훈련할 수 있다. 이것이 근본적으로 어텐션 메커니즘이 동작하는 방법이다.
이미지 캡셔닝 문제의 예를 살펴보자. 여기서 시스템은 이미지에 대한 적합한 캡션을 생성해야 한다. 이 시나리오에서 캡션을 생성하기 위해서는 어텐션 메터니즘이 모델이 특정 인스턴스에서 가장 중요한 개별부분을 파악하는데 도움이 된다.
어텐션 메커니즘을 구현하기 위해 인코더의 각 단계에서 입력을 가지고 온다. - 하지만 timestep에 가중치를 준다. 가중치는 아래 그림과 같이 시퀀스에서 다음 단어를 최적으로 생성하기 위한 디코더에 대해 해당 단계의 중요도에 따라 달라진다.
Source – https://github.com/google/seq2seq
Hands-on view of Sequence to Sequence modelling
케라스로 간단한 Seq2Seq 모델링 구현을 살펴보자. 이 작업은 짧은 영어문장을 프랑스어 문장으로 Seq2Seq 모델을 사용하여 문자별로 변환한다.
이글의 코드 소스는 여기에서 찾을 수 있고, 원작자의 소스코드는 여기에 있다.
구현을 위한 영어와 프랑스어 번역 쌍의 데이터셋은 여기 - frg-eng.zip에서 다운로드 할 수 있다.
문장을 3개의 넘파이(Numpy) 배열로 바꾼다. encoder_input_data, decoder_input_data, decoder_target_data
- encoder_input_data는 영어 문장의 원-핫 벡터화를 포함하는 (num_pairs, max_english_sentence_length, num_english_characters) 모양의 3차원 배열이다.
- decoder_input_data는 프랑스어 문장의 원핫 벡터화를 포함하는 (num_pairs, max_french_sentence_length, num_french_characters)모양의 3차원 배열이다.
- decoder_target_data는 decoder_input_data와 같지만 한 timestep으로 offset 된다. decoder_target_data[:, t, :]는 decoder_input_data[:, t+1, :]와 같을 것이다.
encoder_input_data와 decoder_input_data가 주어지면 decoder_target_data를 예측하기 위해 기본적인 LSTM기반 Seq2Seq 모델을 훈련한다.
모델 동작을 점검하기 위해 문장을 디코딩한다.(즉, encoder_input_data의 샘플을 decoder_target_data의 일치하는 샘플로 변환.)
이제 파이썬 코드를 살펴보자.
# import modules from keras.models import Model from keras.layers import Input, LSTM, Dense # Define an input sequence and process it. encoder_inputs = Input(shape=(None, num_encoder_tokens)) encoder = LSTM(latent_dim, return_state=True) encoder_outputs, state_h, state_c = encoder(encoder_inputs) # We discard `encoder_outputs` and only keep the states. encoder_states = [state_h, state_c] # Set up the decoder, using `encoder_states` as initial state. decoder_inputs = Input(shape=(None, num_decoder_tokens)) # We set up our decoder to return full output sequences, # and to return internal states as well. We don't use the # return states in the training model, but we will use them in inference. decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True) decoder_outputs, _, _ = decoder_lstm(decoder_inputs, initial_state=encoder_states) decoder_dense = Dense(num_decoder_tokens, activation='softmax') decoder_outputs = decoder_dense(decoder_outputs) # Define the model that will turn # `encoder_input_data` & `decoder_input_data` into `decoder_target_data` model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
# Run training model.compile(optimizer='rmsprop', loss='categorical_crossentropy') model.fit([encoder_input_data, decoder_input_data], decoder_target_data, batch_size=batch_size, epochs=epochs, validation_split=0.2)
encoder_model = Model(encoder_inputs, encoder_states) decoder_state_input_h = Input(shape=(latent_dim,)) decoder_state_input_c = Input(shape=(latent_dim,)) decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c] decoder_outputs, state_h, state_c = decoder_lstm( decoder_inputs, initial_state=decoder_states_inputs) decoder_states = [state_h, state_c] decoder_outputs = decoder_dense(decoder_outputs) decoder_model = Model( [decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states)
def decode_sequence(input_seq): # Encode the input as state vectors. states_value = encoder_model.predict(input_seq) # Generate empty target sequence of length 1. target_seq = np.zeros((1, 1, num_decoder_tokens)) # Populate the first character of target sequence with the start character. target_seq[0, 0, target_token_index['\t']] = 1. # Sampling loop for a batch of sequences # (to simplify, here we assume a batch of size 1). stop_condition = False decoded_sentence = '' while not stop_condition: output_tokens, h, c = decoder_model.predict( [target_seq] + states_value) # Sample a token sampled_token_index = np.argmax(output_tokens[0, -1, :]) sampled_char = reverse_target_char_index[sampled_token_index] decoded_sentence += sampled_char # Exit condition: either hit max length # or find stop character. if (sampled_char == '\n' or len(decoded_sentence) > max_decoder_seq_length): stop_condition = True # Update the target sequence (of length 1). target_seq = np.zeros((1, 1, num_decoder_tokens)) target_seq[0, 0, sampled_token_index] = 1. # Update states states_value = [h, c] return decoded_sentence
아래는 생성된 출력의 예이다.
Input sentence: Be nice.
Decoded sentence: Soyez gentil !
Input sentence: Drop it!
Decoded sentence: Laissez tomber !
Input sentence: Get out!
Decoded sentence: Sortez !