Выбрано из 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 Сьюзен далеко…