[訳]RNN循環ニューラルネットワークシリーズ3:符号化、デコーダ
6227 ワード
RNN循環ニューラルネットワークシリーズ3:コード、デコーダ
本稿では、機械翻訳などのseq 2 seqタスクを処理するための基本的なエンコーダおよびデコーダについて説明する.私たちはこの文章で注意力のメカニズムを紹介するのではなく、次の文章でそれを実現します.
次の図に示すように、入力シーケンスをエンコーダに入力し、最終的な非表示状態を生成し、デコーダに入力します.すなわち、エンコーダの最後の非表示状態は、デコーダの新しい初期状態である.softmaxを用いてデコーダ出力を処理し,ターゲットと比較して損失関数を計算する.このブログから、私が原始論文でこのモデルを提出したことについてもっと紹介することができます.ここでの主な違いは、エンコーダの入力にEOS tokenを追加していないと同時に、エンコーダに文を逆読みさせていないことです.
Screen Shot 2016-11-19 at 4.48.03 PM.png
データ#データ#
(英語とスペイン語の文20個)を使用するために、非常に小さなデータセットを作成したいです.このチュートリアルの重点は、機械翻訳や他のseq 2 seq処理などのタスクに対するこのシステムの処理に注目するのではなく、符号化デコーダシステムを構築する方法を理解することです.だから私は自分でいくつかの文を書いて、それをスペイン語に翻訳しました.これが私たちのデータセットです.
まず、これらの文をtokenに分割し、これらのtokenをtoken IDに変換します.この過程で,tokenとtoken IDの間を往復変換するために,語彙辞書と逆語彙辞書を収集した.ターゲット言語(スペイン語)については、追加のEOS tokenを追加します.次に、ソースtokenとターゲットtokenを(データセットの最長文に対応する)最大長に埋めます.これは私たちのモデルの入力データです.エンコーダの場合、入力後のソースコンテンツを直接入力し、ターゲットコンテンツをさらに処理して、デコーダの入力と出力を取得します.
最後に、入力結果は次のようになります.
Screen Shot 2016-11-19 at 4.20.54 PM.png
これはあるロットのサンプルにすぎません.ここで、0は充填値、1はGO token、2はEOS tokenである.次の図は、データ変換のより一般的な表示形式です.ターゲットウェイトを無視してください.実装では使用しません.
screen-shot-2016-11-16-at-5-09-10-pm
エンコーダー
エンコーダはエンコーダの入力のみを受け入れますが、私たちが唯一関心を持っているのは最終的な非表示状態です.この非表示の状態には、入力されたすべての情報が含まれています.dynamic_rnn
のseq_len
を使用しているので、元の論文で提案したようにエンコーダの入力を反転することはありません.seq_len
に基づいて、最後の対応する非表示状態が自動的に返されます.with tf.variable_scope('encoder') as scope:
# RNN
self.encoder_stacked_cell = rnn_cell(FLAGS, self.dropout,
scope=scope)
# RNN
W_input = tf.get_variable("W_input",
[FLAGS.en_vocab_size, FLAGS.num_hidden_units])
self.embedded_encoder_inputs = rnn_inputs(FLAGS,
self.encoder_inputs, FLAGS.en_vocab_size, scope=scope)
#initial_state = encoder_stacked_cell.zero_state(FLAGS.batch_size, tf.float32)
# RNN
self.all_encoder_outputs, self.encoder_state = tf.nn.dynamic_rnn(
cell=self.encoder_stacked_cell,
inputs=self.embedded_encoder_inputs,
sequence_length=self.en_seq_lens, time_major=False,
dtype=tf.float32)
この最終的な非表示状態をデコーダの新しい初期状態として用いる.
デコーダ
この簡単なデコーダはエンコーダの最終的な隠蔽状態を自分の初期状態とする.また、デコーダの入力にアクセスし、RNNデコーダを使用して処理します.出力された結果はソフトmaxで正規化され,ターゲットと比較される.なお、デコーダ入力は、最初のターゲットtokenを予測するために使用されるGO tokenから始まる.デコーダ入力の最後の対応するtokenは、EOSターゲットtokenを予測するために使用される.with tf.variable_scope('decoder') as scope:
#
self.decoder_initial_state = self.encoder_state
# RNN
self.decoder_stacked_cell = rnn_cell(FLAGS, self.dropout,
scope=scope)
# RNN
W_input = tf.get_variable("W_input",
[FLAGS.sp_vocab_size, FLAGS.num_hidden_units])
self.embedded_decoder_inputs = rnn_inputs(FLAGS, self.decoder_inputs,
FLAGS.sp_vocab_size, scope=scope)
# RNN
self.all_decoder_outputs, self.decoder_state = tf.nn.dynamic_rnn(
cell=self.decoder_stacked_cell,
inputs=self.embedded_decoder_inputs,
sequence_length=self.sp_seq_lens, time_major=False,
initial_state=self.decoder_initial_state)
では、チャージすると何が起こりますか?出力ターゲットも予測されますが、これらの内容には関心がありませんが、それらを考慮すると、損失関数に影響します.次に、これらの損失を遮断して、ターゲット結果への影響を除去します.
ロスシールド
ターゲットをチェックし、ターゲットに埋め込まれている部分を0にマスクします.したがって、最後に関連するデコーダtokenを取得すると、EOSを表すtoken IDがターゲットとなる.次のデコーダの入力に対しては,ターゲットはPAD IDであり,これがシールド開始の場所である.# Logit
self.decoder_outputs_flat = tf.reshape(self.all_decoder_outputs,
[-1, FLAGS.num_hidden_units])
self.logits_flat = rnn_softmax(FLAGS, self.decoder_outputs_flat,
scope=scope)
#
targets_flat = tf.reshape(self.targets, [-1])
losses_flat = tf.nn.sparse_softmax_cross_entropy_with_logits(
self.logits_flat, targets_flat)
mask = tf.sign(tf.to_float(targets_flat))
masked_losses = mask * losses_flat
masked_losses = tf.reshape(masked_losses, tf.shape(self.targets))
self.loss = tf.reduce_mean(
tf.reduce_sum(masked_losses, reduction_indices=1))
PAD IDが0であるという事実を遮蔽手段として用いることができることに注意して、(1ロット中のサンプルの)各行の損失の和を計算するだけでよく、その後、すべてのサンプルの損失の平均値を取って、1ロットの損失を得る.このとき,この損失関数を最小化することによって訓練を行うことができる.
以下はトレーニング結果です.
Screen Shot 2016-11-19 at 4.56.18 PM.png
私たちはここで何のモデル推定もしませんが、次の注意力メカニズムに関する文章で見ることができます.もしあなたが本当にここでモデル推定を実現したいなら、同じモデルを使えばいいですが、予測目標の結果を入力として次のRNNデコーダユニットにアクセスしなければなりません.また、同じウェイトセットをデコーダに埋め込み、RNNの別の入力として使用します.これは、初期のGO tokenに対して、偽造tokenを埋め込んで入力しなければならないことを意味します.
結論
この符号化デコーダモデルは非常に簡単であるが,seq 2 seq実装を理解する前に必要な基礎である.次のRNNチュートリアルでは、Attentionモデルと符号化デコーダモデル構造における利点について説明します.
コード#コード#
GitHub倉庫(更新中ですので、お楽しみに!)
掘金翻訳計画は良質なインターネット技術の文章を翻訳するコミュニティであり、文章の出所は掘金上の英語で文章を共有することである.内容はAndroid、iOS、React、フロントエンド、バックエンド、製品、デザインなどの分野をカバーしています.より多くの良質な訳文を表示するには、掘金翻訳計画、公式微博、知乎コラムに引き続き注目してください.
with tf.variable_scope('encoder') as scope:
# RNN
self.encoder_stacked_cell = rnn_cell(FLAGS, self.dropout,
scope=scope)
# RNN
W_input = tf.get_variable("W_input",
[FLAGS.en_vocab_size, FLAGS.num_hidden_units])
self.embedded_encoder_inputs = rnn_inputs(FLAGS,
self.encoder_inputs, FLAGS.en_vocab_size, scope=scope)
#initial_state = encoder_stacked_cell.zero_state(FLAGS.batch_size, tf.float32)
# RNN
self.all_encoder_outputs, self.encoder_state = tf.nn.dynamic_rnn(
cell=self.encoder_stacked_cell,
inputs=self.embedded_encoder_inputs,
sequence_length=self.en_seq_lens, time_major=False,
dtype=tf.float32)
with tf.variable_scope('decoder') as scope:
#
self.decoder_initial_state = self.encoder_state
# RNN
self.decoder_stacked_cell = rnn_cell(FLAGS, self.dropout,
scope=scope)
# RNN
W_input = tf.get_variable("W_input",
[FLAGS.sp_vocab_size, FLAGS.num_hidden_units])
self.embedded_decoder_inputs = rnn_inputs(FLAGS, self.decoder_inputs,
FLAGS.sp_vocab_size, scope=scope)
# RNN
self.all_decoder_outputs, self.decoder_state = tf.nn.dynamic_rnn(
cell=self.decoder_stacked_cell,
inputs=self.embedded_decoder_inputs,
sequence_length=self.sp_seq_lens, time_major=False,
initial_state=self.decoder_initial_state)
# Logit
self.decoder_outputs_flat = tf.reshape(self.all_decoder_outputs,
[-1, FLAGS.num_hidden_units])
self.logits_flat = rnn_softmax(FLAGS, self.decoder_outputs_flat,
scope=scope)
#
targets_flat = tf.reshape(self.targets, [-1])
losses_flat = tf.nn.sparse_softmax_cross_entropy_with_logits(
self.logits_flat, targets_flat)
mask = tf.sign(tf.to_float(targets_flat))
masked_losses = mask * losses_flat
masked_losses = tf.reshape(masked_losses, tf.shape(self.targets))
self.loss = tf.reduce_mean(
tf.reduce_sum(masked_losses, reduction_indices=1))