Китайско-английский перевод с использованием Seq2Seq

машинное обучение

1. Введение

1.1 Deep NLP

Обработка естественного языка (NLP) — это область пересечения информатики, искусственного интеллекта и лингвистики, которая в основном позволяет компьютерам обрабатывать или понимать естественный язык, такой как машинный перевод, системы ответов на вопросы и т. д. Но НЛП часто считается трудным из-за его сложности в представлении, изучении и использовании языка. В последние годы, с появлением глубокого обучения (ГО), люди пытались применить ГО к НЛП, называемому Глубоким НЛП, и добились многих прорывов. Среди них модель Seq2Seq.

1.2 Происхождение

Модель Seq2Seq — это аббревиатура модели Sequence to Sequence (Sequence to Sequence), также известной как модель кодер-декодер (Encoder-Decoder), основанная на двух статьях, опубликованных в 2014 году:

  • Последовательность к последовательному обучению с помощью нейронных сетей Sutskever et al.,
  • Изучение представлений фраз с использованием кодировщика-декодера RNN для статистического машинного перевода Чо и др.,

Автор Суцкевер проанализировал, что глубокие нейронные сети (ГНС) не могут обрабатывать последовательности неизвестной длины и неопределенной длины из-за ограничения длины входных и выходных последовательностей, и многие важные проблемы представлены последовательностями неизвестной длины. Следовательно, необходимо предложить новое решение проблемы последовательности неизвестной длины. Поэтому модель Seq2Seq была предложена инновационно. Давайте посмотрим, что представляет собой эта модель на самом деле.

2. Непрерывное изучение модели Seq2Seq

В чем его инновационность?Потому что автор Суцкевер провел три демонстрации моделирования, прежде чем окончательно определился с моделью Seq2Seq. И дизайн модели очень умный. Давайте сначала рассмотрим исследовательский опыт автора.
Языковая модель (LM) использует условную вероятность для вычисления следующего слова из заданного слова. Это основа предсказания модели Seq2Seq. Поскольку между последовательностями существует контекстуальная связь, похожие предложения связаны между собой, а также характеристики языковой модели (условная вероятность), автор сначала выбрал RNN-LM (языковая модель рекуррентной нейронной сети, языковая модель рекуррентной нейронной сети).

rnn.png

Выше показан простой блок RNN. RNN зацикливается и возвращает результат вычисления предыдущего шага в качестве условия и помещает его в текущий ввод.
Подходит для моделирования контекстных зависимостей в последовательностях произвольной длины. Но есть проблема в том, что нам нужно заранее выровнять входные и выходные последовательности, и непонятно, как применять RNN к последовательностям разной длины со сложными неравномерными отношениями. Для решения проблемы выравнивания авторы предлагают теоретически возможное решение: использовать две RNN. Одна RNN сопоставляет входные данные с вектором фиксированной длины, а другая RNN предсказывает выходную последовательность из этого вектора.
double RNN.png

Почему это теоретически возможно? Автор докторской диссертации Суцкевера TRAINING RECURRENT NEURAL NETWORKS (обучение рекуррентных нейронных сетей) предполагает, что обучение RNN затруднено. Поскольку из-за сетевой структуры самой RNN вывод текущего момента должен учитывать ввод всех предыдущих моментов, поэтому при использовании обучения обратного распространения, когда входная последовательность очень длинная, очень легко возникает проблема исчезновения градиентов. (Градиенты исчезают). Чтобы решить сложную задачу обучения RNN, автор использует сеть LSTM (Long Short-Term Memory, долговременная кратковременная память).
lstm0.png

На приведенном выше рисунке показана внутренняя структура модуля LSTM. LSTM предлагается решить проблему исчезновения градиента RNN, Он инновационно добавляет вентиль забывания, так что LSTM может забыть предыдущие входные нерелевантные последовательности, не рассматривая все входные последовательности. После трех попыток и, наконец, добавления LSTM строится простая модель Seq2Seq.
seq2seq1.png

Выше простая модель Seq2Seq состоит из 3 частей: Encoder-LSTM, Decoder-LSTM и Context. Входная последовательность — ABC, Encoder-LSTM обработает входную последовательность и вернет скрытое состояние (hidden state) всей входной последовательности на последнем нейроне, также известное как контекст (Context, C). Затем Decoder-LSTM шаг за шагом предсказывает следующий символ целевой последовательности в соответствии со скрытым состоянием. Окончательная выходная последовательность wxyz. Стоит отметить, что автор Суцкевер специально разработал конкретную модель Seq2Seq под свою конкретную задачу. Входная последовательность обрабатывается в обратном порядке, что позволяет модели обрабатывать длинные предложения и повышает точность.
seq2seq1.png

На картинке выше реальная модель, разработанная автором Суцкевером, и гордится тремя баллами. Первый использует два LSTM, один для кодирования и один для декодирования. Это также результат исследования и демонстрации автора. Второй использует глубокий LSTM (4 слоя), который снижает сложность каждого слоя на 10% по сравнению с мелкой сетью. Третья пара входных последовательностей использует операцию обратного порядка, что улучшает способность LSTM обрабатывать длинные последовательности.

3. Китайский и английский перевод

Пришло время сделать это.После понимания приведенной выше модели Seq2Seq, давайте построим простую модель китайско-английского перевода.

3.1 Набор данных

Мы используем набор данных на китайском и английском языках с веб-сайта manythings, который теперь загружен на платформу Mo.Нажмите, чтобы просмотреть. Формат набора данныханглийский+вкладка+китайский.

image.png

3.2 Обработка данных

from keras.models import Model
from keras.layers import Input, LSTM, Dense
import numpy as np

batch_size = 64  # Batch size for training.
epochs = 100  # Number of epochs to train for.
latent_dim = 256  # Latent dimensionality of the encoding space.
num_samples = 10000  # Number of samples to train on.
# Path to the data txt file on disk.
data_path = 'cmn.txt'

# Vectorize the data.
input_texts = []
target_texts = []
input_characters = set()
target_characters = set()
with open(data_path, 'r', encoding='utf-8') as f:
    lines = f.read().split('\n')
for line in lines[: min(num_samples, len(lines) - 1)]:
    input_text, target_text = line.split('\t')
    # We use "tab" as the "start sequence" character
    # for the targets, and "\n" as "end sequence" character.
    target_text = '\t' + target_text + '\n'
    input_texts.append(input_text)
    target_texts.append(target_text)
    for char in input_text:
        if char not in input_characters:
            input_characters.add(char)
    for char in target_text:
        if char not in target_characters:
            target_characters.add(char)

input_characters = sorted(list(input_characters))
target_characters = sorted(list(target_characters))
num_encoder_tokens = len(input_characters)
num_decoder_tokens = len(target_characters)
max_encoder_seq_length = max([len(txt) for txt in input_texts])
max_decoder_seq_length = max([len(txt) for txt in target_texts])

print('Number of samples:', len(input_texts))
print('Number of unique input tokens:', num_encoder_tokens)
print('Number of unique output tokens:', num_decoder_tokens)
print('Max sequence length for inputs:', max_encoder_seq_length)
print('Max sequence length for outputs:', max_decoder_seq_length)

3.3 Encoder-LSTM

# mapping token to index, easily to vectors
input_token_index = dict([(char, i) for i, char in enumerate(input_characters)])
target_token_index = dict([(char, i) for i, char in enumerate(target_characters)])

# np.zeros(shape, dtype, order)
# shape is an tuple, in here 3D
encoder_input_data = np.zeros(
    (len(input_texts), max_encoder_seq_length, num_encoder_tokens),
    dtype='float32')
decoder_input_data = np.zeros(
    (len(input_texts), max_decoder_seq_length, num_decoder_tokens),
    dtype='float32')
decoder_target_data = np.zeros(
    (len(input_texts), max_decoder_seq_length, num_decoder_tokens),
    dtype='float32')

# input_texts contain all english sentences
# output_texts contain all chinese sentences
# zip('ABC','xyz') ==> Ax By Cz, looks like that
# the aim is: vectorilize text, 3D
for i, (input_text, target_text) in enumerate(zip(input_texts, target_texts)):
    for t, char in enumerate(input_text):
        # 3D vector only z-index has char its value equals 1.0
        encoder_input_data[i, t, input_token_index[char]] = 1.
    for t, char in enumerate(target_text):
        # decoder_target_data is ahead of decoder_input_data by one timestep
        decoder_input_data[i, t, target_token_index[char]] = 1.
        if t > 0:
            # decoder_target_data will be ahead by one timestep
            # and will not include the start character.
            # igone t=0 and start t=1, means 
            decoder_target_data[i, t - 1, target_token_index[char]] = 1.

3.4 Контекст (скрытое состояние)

# Define an input sequence and process it.
# input prodocts keras tensor, to fit keras model!
# 1x73 vector 
# encoder_inputs is a 1x73 tensor!
encoder_inputs = Input(shape=(None, num_encoder_tokens))

# units=256, return the last state in addition to the output
encoder_lstm = LSTM((latent_dim), return_state=True)

# LSTM(tensor) return output, state-history, state-current
encoder_outputs, state_h, state_c = encoder_lstm(encoder_inputs)

# We discard `encoder_outputs` and only keep the states.
encoder_states = [state_h, state_c]

3.5 Decoder-LSTM

# 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)

# obtain output
decoder_outputs, _, _ = decoder_lstm(decoder_inputs,initial_state=encoder_states)

# dense 2580x1 units full connented layer
decoder_dense = Dense(num_decoder_tokens, activation='softmax')

# why let decoder_outputs go through dense ?
decoder_outputs = decoder_dense(decoder_outputs)

# Define the model that will turn, groups layers into an object 
# with training and inference features
# `encoder_input_data` & `decoder_input_data` into `decoder_target_data`
# model(input, output)
model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

# Run training
# compile -> configure model for training
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
# model optimizsm
model.fit([encoder_input_data, decoder_input_data], 
          decoder_target_data,
          batch_size=batch_size,
          epochs=epochs,
          validation_split=0.2)
# Save model
model.save('seq2seq.h5')

3.6 Последовательность декодирования

# Define sampling models
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)

# Reverse-lookup token index to decode sequences back to
# something readable.
reverse_input_char_index = dict(
    (i, char) for char, i in input_token_index.items())
reverse_target_char_index = dict(
    (i, char) for char, i in target_token_index.items())

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.
    # this target_seq you can treat as initial state

    # 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
        # argmax: Returns the indices of the maximum values along an axis
        # just like find the most possible char
        sampled_token_index = np.argmax(output_tokens[0, -1, :])
        # find char using index
        sampled_char = reverse_target_char_index[sampled_token_index]
        # and append sentence
        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).
        # append then ?
        # creating another new target_seq
        # and this time assume sampled_token_index to 1.0
        target_seq = np.zeros((1, 1, num_decoder_tokens))
        target_seq[0, 0, sampled_token_index] = 1.

        # Update states
        # update states, frome the front parts
        states_value = [h, c]

    return decoded_sentence

3.7 Прогноз

for seq_index in range(100,200):
    # Take one sequence (part of the training set)
    # for trying out decoding.
    input_seq = encoder_input_data[seq_index: seq_index + 1]
    decoded_sentence = decode_sequence(input_seq)
    print('Input sentence:', input_texts[seq_index])
    print('Decoded sentence:', decoded_sentence)

Проект был обнародован на платформе Mo,Seq2Seq перевод на китайский и английский языки, рекомендуется обучение GPU.
Представьте очень интимную и практичную функцию платформы Mo: API Doc (вторая колонка в правой части интерфейса разработки).

推广1.png


При написании кода на платформе Mo можно легко добиться многооконного отображения, пока перетаскивается строка заголовка окна, столбец можно разделить.
推广2.png

4. Резюме и перспективы

Удивительно, что мы придумали классическую модель Seq2Seq, которая решает множество важных и трудных проблем, которые НЛП не может решить в таких областях, как машинный перевод и распознавание речи. Это также знаковое событие в применении глубокого обучения к НЛП. Впоследствии на основе этой модели было предложено множество улучшений и оптимизаций, таких как механизм Attention. Я верю, что в ближайшем будущем нас ждут новые и крупные открытия, поживем-увидим.
Адрес исходного кода проекта (можно открыть компьютер для форка):Тихо потяните. Сможет ли 3 из /explore/5...

5. Цитаты

бумага:Sequence to Sequence Learning with Neural Networks
Блог:Understanding LSTM Networks
Код:A ten-minute introduction to sequence-to-sequence learning in Keras