С английского на маратхи

Обучение от последовательности к последовательности (Seq2Seq) предназначено для обучения моделей преобразованию последовательностей из одной области (например, предложений на английском языке) в последовательности из другой области (например, те же самые предложения, переведенные на маратхи). Это может быть используется для машинного перевода или для ответов на вопросы без ответа (генерация ответа на естественном языке на вопрос на естественном языке) — в общем, он применим в любое время, когда вам нужно сгенерировать текст.

"how are you" -> [Seq2Seq model] -> "तू कसा आहेस"

Как базовая модель кодировщика-декодера решает проблему обучения Seq2Seq:

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

  • Экодер кодирует входную последовательность в новое представление.
  • Это представление называется Context/Toughth Vector.
  • Декодер декодирует Context/Toughth Vector в выходную последовательность.

  • Обучение. Во время обучения мы обучаем кодировщик и декодер таким образом, чтобы они работали вместе для создания контекста (представления) между вводом и выводом.
  • Вывод (прогнозирование). Узнав, как создавать контекст (представление), они могут работать вместе, чтобы прогнозировать результат.
  • Кодировать все — декодировать по одному. Обычно кодировщик считывает всю входную последовательность и создает вектор контекста (представления). Декодер использует этот вектор контекста (представления) и ранее декодированный результат для пошагового создания нового вывода.

ЛСТМ

LSTM имеет 3 важных параметра (на данный момент!)

  • единицы: положительное целое число, размерность выходного пространства.
  • return_sequences: логическое значение, возвращать ли последний вывод. в выходной последовательности или полной последовательности. По умолчанию: Ложь.
  • return_state: логическое значение, возвращающее ли последнее состояние в дополнение к выходным данным. По умолчанию: Ложь.

Первый параметр (единицы) указывает размерность выходного вектора/матрицы.

Последние 2 параметра (return_sequences и return_state) определяют, что выводит уровень LSTM.

  1. По умолчанию: последнее скрытое состояние (скрытое состояние последнего временного шага).

2.return_sequences=True : Все скрытые состояния (скрытое состояние ВСЕХ временных шагов)

3. return_state=True : Последнее скрытое состояние + Последнее скрытое состояние (снова!) + Последнее состояние ячейки (состояние ячейки последнего временного шага)

4. return_sequences=True + return_state=True: Все скрытые состояния (скрытые состояния ВСЕХ временных шагов) + последнее скрытое состояние + последнее состояние ячейки (состояние ячейки последнего временного шага)

Используя эти 4 разных результата/состояния, мы можем складывать слои LSTM различными способами.

Окончательная архитектура

Пошаговое руководство по коду

"источник данных"

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

lines= pd.read_table('mar.txt', names=['eng','mar', 'del'])
lines=lines.drop(columns='del')
# Lowercase all characters
lines.eng=lines.eng.apply(lambda x: x.lower())
lines.mar=lines.mar.apply(lambda x: x.lower())
# Remove quotes
lines.eng=lines.eng.apply(lambda x: re.sub("'", '', x))
lines.mar=lines.mar.apply(lambda x: re.sub("'", '', x))
exclude = set(string.punctuation) # Set of all special characters
# Remove all the special characters
lines.eng=lines.eng.apply(lambda x: ''.join(ch for ch in x if ch not in exclude))
lines.mar=lines.mar.apply(lambda x: ''.join(ch for ch in x if ch not in exclude))
remove_digits = str.maketrans('', '', digits)
lines.eng=lines.eng.apply(lambda x: x.translate(remove_digits))
lines.mar = lines.mar.apply(lambda x: re.sub("[२३०८१५७९४६]", "", x))
lines.eng=lines.eng.apply(lambda x: x.strip())
lines.mar=lines.mar.apply(lambda x: x.strip())
lines.eng=lines.eng.apply(lambda x: re.sub(" +", " ", x))
lines.mar=lines.mar.apply(lambda x: re.sub(" +", " ", x))
# Add start and end tokens to target sequences
lines.mar = lines.mar.apply(lambda x : 'START_ '+ x + ' _END')

Пополнение словарного запаса:

# Vocabulary of English
all_eng_words=set()
for eng in lines.eng:
    for word in eng.split():
        if word not in all_eng_words:
            all_eng_words.add(word)

# Vocabulary of Marathi 
all_marathi_words=set()
for mar in lines.mar:
    for word in mar.split():
        if word not in all_marathi_words:
            all_marathi_words.add(word)

вычисление максимальной длины исходной и целевой последовательности, построение input_token и target_token.

lenght_list=[]
for l in lines.eng:
    lenght_list.append(len(l.split(' ')))
max_length_src = np.max(lenght_list)
lenght_list=[]
for l in lines.mar:
    lenght_list.append(len(l.split(' ')))
max_length_tar = np.max(lenght_list)
input_words = sorted(list(all_eng_words))
target_words = sorted(list(all_marathi_words))
num_encoder_tokens = len(all_eng_words)
num_decoder_tokens = len(all_marathi_words)
num_decoder_tokens += 1 # For zero padding
input_token_index = dict([(word, i+1) for i, word in enumerate(input_words)])
target_token_index = dict([(word, i+1) for i, word in enumerate(target_words)])
reverse_input_char_index = dict((i, word) for word, i in input_token_index.items())
reverse_target_char_index = dict((i, word) for word, i in target_token_index.items())

Сплит тестового поезда:

X, y = lines.eng, lines.mar
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.1)

пакетный генератор

def generate_batch(X = X_train, y = y_train, batch_size = 128):
    ''' Generate a batch of data '''
    while True:
        for j in range(0, len(X), batch_size):
            encoder_input_data = np.zeros((batch_size, max_length_src),dtype='float32')
            decoder_input_data = np.zeros((batch_size, max_length_tar),dtype='float32')
            decoder_target_data = np.zeros((batch_size, max_length_tar, num_decoder_tokens),dtype='float32')
            for i, (input_text, target_text) in enumerate(zip(X[j:j+batch_size], y[j:j+batch_size])):
                for t, word in enumerate(input_text.split()):
                    encoder_input_data[i, t] = input_token_index[word] # encoder input seq
                for t, word in enumerate(target_text.split()):
                    if t<len(target_text.split())-1:
                        decoder_input_data[i, t] = target_token_index[word] # decoder input seq
                    if t>0:
                        # decoder target sequence (one hot encoded)
                        # does not include the START_ token
                        # Offset by one timestep
                        decoder_target_data[i, t - 1, target_token_index[word]] = 1.
            yield([encoder_input_data, decoder_input_data], decoder_target_data)

Кодировщик — Архитектура модели декодера:

# Encoder
encoder_inputs = Input(shape=(None,))
enc_emb =  Embedding(num_encoder_tokens, latent_dim, mask_zero = True)(encoder_inputs)
encoder_lstm = LSTM(latent_dim, return_state=True)
encoder_outputs, state_h, state_c = encoder_lstm(enc_emb)
# 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,))
dec_emb_layer = Embedding(num_decoder_tokens, latent_dim, mask_zero = True)
dec_emb = dec_emb_layer(decoder_inputs)
# 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(dec_emb,
                                     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)
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['acc'])

Модель сортировки:

model.fit_generator(generator = generate_batch(X_train, y_train, batch_size = batch_size),steps_per_epoch = train_samples//batch_size,epochs=epochs,validation_data = generate_batch(X_test, y_test, batch_size = batch_size),validation_steps = val_samples//batch_size)

В режиме вывода, т. е. когда мы хотим декодировать неизвестные входные последовательности, мы проходим немного другой процесс:

  • 1) Закодируйте входную последовательность в векторы состояния.
  • 2) Начните с целевой последовательности размера 1 (просто символ начала последовательности).
  • 3) Подайте векторы состояния и целевую последовательность из 1 символа в декодер, чтобы произвести предсказания для следующего символа.
  • 4) Образец следующего символа, используя эти предсказания (мы просто используем argmax).
  • 5) Добавить выбранный символ в целевую последовательность
  • 6) Повторяйте, пока мы не сгенерируем символ конца последовательности или не достигнем предела символов.
# Encode the input sequence to get the "thought vectors"
encoder_model = Model(encoder_inputs, encoder_states)

# Decoder setup
# Below tensors will hold the states of the previous time step
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]

dec_emb2= dec_emb_layer(decoder_inputs) # Get the embeddings of the decoder sequence

# To predict the next word in the sequence, set the initial states to the states from the previous time step
decoder_outputs2, state_h2, state_c2 = decoder_lstm(dec_emb2, initial_state=decoder_states_inputs)
decoder_states2 = [state_h2, state_c2]
decoder_outputs2 = decoder_dense(decoder_outputs2) # A dense softmax layer to generate prob dist. over the target vocabulary

# Final decoder model
decoder_model = Model(
    [decoder_inputs] + decoder_states_inputs,
    [decoder_outputs2] + decoder_states2)

Декодируйте примерную последовательность:

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))
    # Populate the first character of target sequence with the start character.
    target_seq[0, 0] = target_token_index['START_']

    # 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 == '_END' or
           len(decoded_sentence) > 50):
            stop_condition = True

        # Update the target sequence (of length 1).
        target_seq = np.zeros((1,1))
        target_seq[0, 0] = sampled_token_index

        # Update states
        states_value = [h, c]

    return decoded_sentence

Оценка случайных данных:

def Test(test,y):
  testt={'new':[test]}
  testt=pd.DataFrame(testt)
  y_orignal={'new':[y]}
  y_orignal=pd.DataFrame(y_orignal)
  testt=testt.new.tolist()
  y_orignal=y_orignal.new.tolist()
  train_gen = generate_batch(testt, y_orignal, batch_size = 1)
  (input_seq, actual_output), _ = next(train_gen)
  decoded_sentence = decode_sequence(input_seq)
  print('Predicted Marathi Translation:', decoded_sentence[:-4])
  print('Orignal Marathi Translation:', y)
Test(test='how are you',y='तू कसा आहेस')

Демонстрация веб-приложения Flask:

Некоторые результаты:

Полный код на моем GitHub

Заключение:

Модель seq-seq плохо работает с длинными предложениями. также требуется больше данных для небольших предложений. модель внимания - хороший вариант для этого.

Использованная литература:

1] https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html

2] https://towardsdatascience.com/word-level-english-to-marathi-neural-machine-translation-using-seq2seq-encoder-decoder-lstm-model-1a913f2dc4a7

3]https://www.youtube.com/watch?v=iHJkfsV9cqY&list=LL&index=3

4] https://medium.com/@dev.elect.iitd/neural-machine-translation-using-word-level-seq2seq-model-47538cba8cd7