Начало работы с Python Neural Machine Translation, очень краткое практическое руководство

искусственный интеллект Python Keras Нейронные сети

Выбрано из Medium, автор: Susan Li, составлено сердцем машины.

Машинный перевод (МП) — чрезвычайно сложная задача, изучающая, как переводить текст или речь с одного языка на другой с помощью компьютера. Эта статья начинается с самой простой загрузки текста и предварительной обработки данных с помощью Keras, а также обсуждает, как создать приемлемую систему нейронного перевода в рамках рекуррентной нейронной сети и кодировщика-декодера, Весь код этого руководства был открыт на GitHub.

Традиционно машинный перевод обычно использует большие статистические модели, разработанные с очень сложными языковыми знаниями, но многие недавние исследования используют глубокие модели для прямого моделирования процесса перевода и автоматического обучения, когда предоставляются только данные исходного языка и данные перевода.Необходимые языковые знания. Эта модель перевода, основанная на глубокой нейронной сети, показала наилучшие результаты.

адрес проекта:GitHub.com/Сьюзан с 2016 года…

Далее мы будем использовать глубокие нейронные сети для решения задач машинного перевода. Мы покажем, как разработать модель машинного перевода нейронной сети, которая переводит с английского на французский язык. Модель будет получать ввод текста на английском языке, возвращая переводы на французский язык. Точнее, мы построим 4 модели, а именно:

  • простой РНН;
  • RNN с встраиванием слов;
  • двунаправленный RNN;
  • Модель кодер-декодер.

Обучение и оценка глубоких нейронных сетей — задача, требующая больших вычислительных ресурсов. Автор использует экземпляр AWS EC2 для запуска всего кода. Если вы планируете следовать этой статье, вам потребуется доступ к экземпляру GPU.


загрузить библиотеку

import collections
import helper
import numpy as np
import project_tests as tests
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.models import Model
from keras.layers import GRU, Input, Dense, TimeDistributed, Activation, RepeatVector, Bidirectional
from keras.layers.embeddings import Embedding
from keras.optimizers import Adam
from keras.losses import sparse_categorical_crossentropy

Автор использует help.py для загрузки данных и project_test.py для тестирования функций.


данные

Набор данных содержит относительно небольшой словарь, где файл small_vocab_en содержит английские предложения, а small_vocab_fr содержит соответствующие французские переводы.

Адрес загрузки набора данных:GitHub.com/Сьюзан с 2016 года…


Скачать данные

english_sentences = helper.load_data('data/small_vocab_en')
french_sentences = helper.load_data('data/small_vocab_fr')
print('Dataset Loaded')


  • образец предложения

Каждая строка в small_vocab_en содержит английское предложение с его французским переводом в соответствующей строке в small_vocab_fr.

for sample_i in range(2):
 print('small_vocab_en Line {}: {}'.format(sample_i + 1, english_sentences[sample_i]))
 print('small_vocab_fr Line {}: {}'.format(sample_i + 1, french_sentences[sample_i]))


  • Глоссарий

Сложность задачи зависит от сложности лексики. Более сложная лексика означает более сложную проблему. Для набора данных, который будет обрабатываться, давайте посмотрим на его сложность.

english_words_counter = collections.Counter([word for sentence in english_sentences for word in sentence.split()])
french_words_counter = collections.Counter([word for sentence in french_sentences for word in sentence.split()])
print('{} English words.'.format(len([word for sentence in english_sentences for word in sentence.split()])))
print('{} unique English words.'.format(len(english_words_counter)))
print('10 Most common words in the English dataset:')
print('"' + '" "'.join(list(zip(*english_words_counter.most_common(10)))[0]) + '"')
print()
print('{} French words.'.format(len([word for sentence in french_sentences for word in sentence.split()])))
print('{} unique French words.'.format(len(french_words_counter)))
print('10 Most common words in the French dataset:')
print('"' + '" "'.join(list(zip(*french_words_counter.most_common(10)))[0]) + '"')


предварительная обработка

Мы преобразуем текст в последовательность целых чисел, используя следующие методы предварительной обработки:

1. Преобразование слов в id-выражения;

2. Добавьте заполнение, чтобы каждая последовательность была одинаковой длины.


Tokensize (строка токена)

Используйте функцию Tokenizer Keras, чтобы преобразовать каждое предложение в последовательность идентификаторов слов. Используйте эту функцию для токенизации английских и французских предложений.

Функция tokenize возвращает токенизированный ввод и класс.

def tokenize(x):
 x_tk = Tokenizer(char_level = False)
 x_tk.fit_on_texts(x)
 return x_tk.texts_to_sequences(x), x_tk
text_sentences = [
 'The quick brown fox jumps over the lazy dog .',
 'By Jove , my quick study of lexicography won a prize .',
 'This is a short sentence .']
text_tokenized, text_tokenizer = tokenize(text_sentences)
print(text_tokenizer.word_index)
print()
for sample_i, (sent, token_sent) in enumerate(zip(text_sentences, text_tokenized)):
 print('Sequence {} in x'.format(sample_i + 1))
 print(' Input: {}'.format(sent))
 print(' Output: {}'.format(token_sent))


Padding

Все английские последовательности имеют одинаковую длину, и все французские последовательности имеют одинаковую длину за счет добавления нулей в конце каждой последовательности с помощью функции Keras pad_sequences.

def pad(x, length=None):
 if length is None:
 length = max([len(sentence) for sentence in x])
 return pad_sequences(x, maxlen = length, padding = 'post')
tests.test_pad(pad)
# Pad Tokenized output
test_pad = pad(text_tokenized)
for sample_i, (token_sent, pad_sent) in enumerate(zip(text_tokenized, test_pad)):
 print('Sequence {} in x'.format(sample_i + 1))
 print(' Input: {}'.format(np.array(token_sent)))
 print(' Output: {}'.format(pad_sent))

Процесс предварительной обработки

Реализуйте функцию предварительной обработки:

def preprocess(x, y):
 preprocess_x, x_tk = tokenize(x)
 preprocess_y, y_tk = tokenize(y)
preprocess_x = pad(preprocess_x)
 preprocess_y = pad(preprocess_y)
# Keras's sparse_categorical_crossentropy function requires the labels to be in 3 dimensions
 preprocess_y = preprocess_y.reshape(*preprocess_y.shape, 1)
return preprocess_x, preprocess_y, x_tk, y_tk
preproc_english_sentences, preproc_french_sentences, english_tokenizer, french_tokenizer =\
 preprocess(english_sentences, french_sentences)

max_english_sequence_length = preproc_english_sentences.shape[1]
max_french_sequence_length = preproc_french_sentences.shape[1]
english_vocab_size = len(english_tokenizer.word_index)
french_vocab_size = len(french_tokenizer.word_index)
print('Data Preprocessed')
print("Max English sentence length:", max_english_sequence_length)
print("Max French sentence length:", max_french_sequence_length)
print("English vocabulary size:", english_vocab_size)
print("French vocabulary size:", french_vocab_size)

Модель

В этом разделе мы попробуем различные архитектуры нейронных сетей. Мы начнем с обучения 4 относительно простых структур:

  • Модель 1 — это простая RNN;
  • Модель 2 — это RNN с встраиванием слов;
  • Модель 3 — двунаправленная RNN;
  • Модель 4 представляет собой архитектуру кодер-декодер, состоящую из двух RNN.

Попробовав 4 простые структуры, мы построим более глубокую модель, которая превзойдет 4 модели выше.


id преобразуется обратно в текст

Нейронная сеть превращает входные данные в идентификаторы слов, но это не та форма, которую мы в конечном итоге хотим, нам нужен французский перевод. Функция logits_to_text устраняет разрыв между выводом логитов нейронной сети и французским переводом, и мы будем использовать эту функцию, чтобы лучше понять вывод нейронной сети.

def logits_to_text(logits, tokenizer):
 index_to_words = {id: word for word, id in tokenizer.word_index.items()}
 index_to_words[0] = '<PAD>'
return ' '.join([index_to_words[prediction] for prediction in np.argmax(logits, 1)])
print('`logits_to_text` function loaded.')


Модель 1: РНН

Мы строим базовую модель RNN, которая является хорошим эталоном для перевода последовательностей с английского на французский.

def simple_model(input_shape, output_sequence_length, english_vocab_size, french_vocab_size):
 learning_rate = 1e-3
 input_seq = Input(input_shape[1:])
 rnn = GRU(64, return_sequences = True)(input_seq)
 logits = TimeDistributed(Dense(french_vocab_size))(rnn)
 model = Model(input_seq, Activation('softmax')(logits))
 model.compile(loss = sparse_categorical_crossentropy, 
 optimizer = Adam(learning_rate), 
 metrics = ['accuracy'])

 return model
tests.test_simple_model(simple_model)
tmp_x = pad(preproc_english_sentences, max_french_sequence_length)
tmp_x = tmp_x.reshape((-1, preproc_french_sentences.shape[-2], 1))
# Train the neural network
simple_rnn_model = simple_model(
 tmp_x.shape,
 max_french_sequence_length,
 english_vocab_size,
 french_vocab_size)
simple_rnn_model.fit(tmp_x, preproc_french_sentences, batch_size=1024, epochs=10, validation_split=0.2)
# Print prediction(s)
print(logits_to_text(simple_rnn_model.predict(tmp_x[:1])[0], french_tokenizer))

Точность проверочного набора базовой модели RNN составляет 0,6039.


Модель 2: встраивание слов

Вложение слов — это векторное представление близких синонимов в n-мерном пространстве, где n — размер вектора вложения. Мы будем использовать вложения слов для построения модели RNN.

from keras.models import Sequential
def embed_model(input_shape, output_sequence_length, english_vocab_size, french_vocab_size):
 learning_rate = 1e-3
 rnn = GRU(64, return_sequences=True, activation="tanh")

 embedding = Embedding(french_vocab_size, 64, input_length=input_shape[1]) 
 logits = TimeDistributed(Dense(french_vocab_size, activation="softmax"))

 model = Sequential()
 #em can only be used in first layer --> Keras Documentation
 model.add(embedding)
 model.add(rnn)
 model.add(logits)
 model.compile(loss=sparse_categorical_crossentropy,
 optimizer=Adam(learning_rate),
 metrics=['accuracy'])

 return model
tests.test_embed_model(embed_model)
tmp_x = pad(preproc_english_sentences, max_french_sequence_length)
tmp_x = tmp_x.reshape((-1, preproc_french_sentences.shape[-2]))
embeded_model = embed_model(
 tmp_x.shape,
 max_french_sequence_length,
 english_vocab_size,
 french_vocab_size)
embeded_model.fit(tmp_x, preproc_french_sentences, batch_size=1024, epochs=10, validation_split=0.2)
print(logits_to_text(embeded_model.predict(tmp_x[:1])[0], french_tokenizer))

Точность проверочного набора встроенной модели составляет 0,8401.


Модель 3: двунаправленная RNN

def bd_model(input_shape, output_sequence_length, english_vocab_size, french_vocab_size):

 learning_rate = 1e-3
 model = Sequential()
 model.add(Bidirectional(GRU(128, return_sequences = True, dropout = 0.1), 
 input_shape = input_shape[1:]))
 model.add(TimeDistributed(Dense(french_vocab_size, activation = 'softmax')))
 model.compile(loss = sparse_categorical_crossentropy, 
 optimizer = Adam(learning_rate), 
 metrics = ['accuracy'])
 return model
tests.test_bd_model(bd_model)
tmp_x = pad(preproc_english_sentences, preproc_french_sentences.shape[1])
tmp_x = tmp_x.reshape((-1, preproc_french_sentences.shape[-2], 1))
bidi_model = bd_model(
 tmp_x.shape,
 preproc_french_sentences.shape[1],
 len(english_tokenizer.word_index)+1,
 len(french_tokenizer.word_index)+1)
bidi_model.fit(tmp_x, preproc_french_sentences, batch_size=1024, epochs=20, validation_split=0.2)
# Print prediction(s)
print(logits_to_text(bidi_model.predict(tmp_x[:1])[0], french_tokenizer))

Точность проверочного набора для двунаправленной модели RNN составляет 0,5992.


Модель 4: структура кодировщик-декодер

Кодер строит матричное представление предложения, а декодер берет эту матрицу в качестве входных данных и выводит предсказанный перевод.

def encdec_model(input_shape, output_sequence_length, english_vocab_size, french_vocab_size):

 learning_rate = 1e-3
 model = Sequential()
 model.add(GRU(128, input_shape = input_shape[1:], return_sequences = False))
 model.add(RepeatVector(output_sequence_length))
 model.add(GRU(128, return_sequences = True))
 model.add(TimeDistributed(Dense(french_vocab_size, activation = 'softmax')))

 model.compile(loss = sparse_categorical_crossentropy, 
 optimizer = Adam(learning_rate), 
 metrics = ['accuracy'])
 return model
tests.test_encdec_model(encdec_model)
tmp_x = pad(preproc_english_sentences)
tmp_x = tmp_x.reshape((-1, preproc_english_sentences.shape[1], 1))
encodeco_model = encdec_model(
 tmp_x.shape,
 preproc_french_sentences.shape[1],
 len(english_tokenizer.word_index)+1,
 len(french_tokenizer.word_index)+1)
encodeco_model.fit(tmp_x, preproc_french_sentences, batch_size=1024, epochs=20, validation_split=0.2)
print(logits_to_text(encodeco_model.predict(tmp_x[:1])[0], french_tokenizer))

Точность проверочного набора модели кодировщик-декодер составляет 0,6406.


Модель 5: Пользовательская глубокая модель

Создайте model_final, который объединяет встраивание слов и двунаправленные RNN в одну модель.

На этом этапе нам нужно провести некоторые эксперименты, такие как изменение параметров графического процессора до 256, изменение скорости обучения до 0,005, обучение модели более (или менее) 20 эпохам и т. д.

def model_final(input_shape, output_sequence_length, english_vocab_size, french_vocab_size):

 model = Sequential()
 model.add(Embedding(input_dim=english_vocab_size,output_dim=128,input_length=input_shape[1]))
 model.add(Bidirectional(GRU(256,return_sequences=False)))
 model.add(RepeatVector(output_sequence_length))
 model.add(Bidirectional(GRU(256,return_sequences=True)))
 model.add(TimeDistributed(Dense(french_vocab_size,activation='softmax')))
 learning_rate = 0.005

 model.compile(loss = sparse_categorical_crossentropy, 
 optimizer = Adam(learning_rate), 
 metrics = ['accuracy'])

 return model
tests.test_model_final(model_final)
print('Final Model Loaded')


предсказывать

def final_predictions(x, y, x_tk, y_tk):
 tmp_X = pad(preproc_english_sentences)
 model = model_final(tmp_X.shape,
 preproc_french_sentences.shape[1],
 len(english_tokenizer.word_index)+1,
 len(french_tokenizer.word_index)+1)

 model.fit(tmp_X, preproc_french_sentences, batch_size = 1024, epochs = 17, validation_split = 0.2)

 y_id_to_word = {value: key for key, value in y_tk.word_index.items()}
 y_id_to_word[0] = '<PAD>'
sentence = 'he saw a old yellow truck'
 sentence = [x_tk.word_index[word] for word in sentence.split()]
 sentence = pad_sequences([sentence], maxlen=x.shape[-1], padding='post')
 sentences = np.array([sentence[0], x[0]])
 predictions = model.predict(sentences, len(sentences))
print('Sample 1:')
 print(' '.join([y_id_to_word[np.argmax(x)] for x in predictions[0]]))
 print('Il a vu un vieux camion jaune')
 print('Sample 2:')
 print(' '.join([y_id_to_word[np.argmax(x)] for x in predictions[1]]))
 print(' '.join([y_id_to_word[np.max(x)] for x in y[0]]))
final_predictions(preproc_english_sentences, preproc_french_sentences, english_tokenizer, french_tokenizer)

Мы получили безупречные переводы и точность валидации 0,9776!

Оригинальная ссылка:medium.com/@act Сьюзен далеко…