入門Python神経機械翻訳、これは非常に簡素な実戦ガイドです

18729 ワード

Medium、著者:Susan Li、機械の心コンパイルから選択します.
マシン翻訳(MT)は、コンピュータを使用してテキストまたは音声を1つの言語から別の言語に翻訳する方法を研究する非常に挑戦的なタスクです.本論文では,Kerasを用いて最も基本的なテキストロードとデータプリプロセッシングから開始し,このチュートリアルのすべてのコードがGitHubでオープンソースされた,ループニューラルネットワークとエンコーダデコーダの枠組みの下でどのように許容可能なニューラル翻訳システムを構築できるかを議論した.
従来の意味では、機械翻訳は一般的に高度に複雑な言語知識を用いて開発された大型統計モデルを用いているが、最近では深さモデルを用いて翻訳プロセスを直接モデリングし、原語データと訳文データのみを提供する場合に必要な言語知識を自動的に学習する研究が多い.この深さニューラルネットワークに基づく翻訳モデルは現在最適な効果を得ている.
プロジェクトアドレス:github.com/susanli2016…
次に,深さニューラルネットワークを用いて機械翻訳問題を解決する.英語をフランス語に翻訳するニューラルネットワーク機械翻訳モデルをどのように開発するかを示す.このモデルは、英語のテキスト入力を受信し、フランス語の訳文を返します.より正確には、次の4つのモデルを構築します.
  • 簡単なRNN;
  • 語埋め込みRNN;
  • 双方向RNN;
  • エンコーダ-デコーダモデル.

  • 深さニューラルネットワークの訓練と評価は計算密集型の任務である.著者らは、AWS EC 2インスタンスを使用して、すべてのコードを実行する.本明細書で行う場合は、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/susanli2016…
    データのロード
    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.各シーケンスが同じ長さになるようにpaddingを追加します.
    Tokensize(タグ文字列)
    KerasのTokenizer関数を使用して、各文を単語idのシーケンスに変換します.この関数を使用して、英語文とフランス語文をマークします.
    関数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は、2つのRNNからなるエンコーダ−デコーダアーキテクチャである.

  • 4つの単純な構造を試みた後,以上の4つのモデルよりも性能が優れたより深いモデルを構築する.
    idをテキストに再変換
    ニューラルネットワークは入力を単語idに変換しますが、これは私たちが最終的に望んでいる形式ではありません.私たちが望んでいるのはフランス語翻訳です.logits_to_text関数はニューラルネットワークから出力されるlogitsからフランス語翻訳へのギャップを補い,この関数を用いてニューラルネットワークの出力をよりよく理解する.
    def logits_to_text(logits, tokenizer):
     index_to_words = {id: word for word, id in tokenizer.word_index.items()}
     index_to_words[0] = ''
    return ' '.join([index_to_words[prediction] for prediction in np.argmax(logits, 1)])
    print('`logits_to_text` function loaded.')
    

    モデル1:RNN
    英語をフランス語シーケンスに翻訳するための良好な基準である基礎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:カスタム深度モデル
    単語埋め込みと双方向RNNをモデルに統合するmodel_を構築します.final.
    そこで,GPUパラメータを256に変更したり,学習率を0.005に変更したり,モデルに対して20 epochsより多く(または少ない)訓練したりするなど,いくつかの実験を行う必要がある.
    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] = ''
    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/@actsusanli…