web-dev-qa-db-ja.com

RNN出力をテンソルフローの入力にフィードバックする方法

トレーニング済みのRNN(言語モデルなど)があり、それ自体で何が生成されるかを確認したい場合、出力を入力にフィードバックするにはどうすればよいですか?

私は次の関連する質問を読みました:

理論的には、テンソルフローでは切り捨てられたバックプロパゲーションを使用することは明らかです。したがって、「トレース」する最大ステップを定義する必要があります。また、バッチ用の次元を予約しているため、正弦波をトレーニングする場合は、[None, num_step, 1]入力をフィードする必要があります。

次のコードが機能します。

tf.reset_default_graph()
n_samples=100

state_size=5

lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(state_size, forget_bias=1.)
def_x = np.sin(np.linspace(0, 10, n_samples))[None, :, None]
zero_x = np.zeros(n_samples)[None, :, None]
X = tf.placeholder_with_default(zero_x, [None, n_samples, 1])
output, last_states = tf.nn.dynamic_rnn(inputs=X, cell=lstm_cell, dtype=tf.float64)

pred = tf.contrib.layers.fully_connected(output, 1, activation_fn=tf.tanh)

Y = np.roll(def_x, 1)
loss = tf.reduce_sum(tf.pow(pred-Y, 2))/(2*n_samples)


opt = tf.train.AdamOptimizer().minimize(loss)
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()

# Initial state run
plt.show(plt.plot(output.eval()[0]))
plt.plot(def_x.squeeze())
plt.show(plt.plot(pred.eval().squeeze()))

steps = 1001
for i in range(steps):
    p, l, _= sess.run([pred, loss, opt])

LSTMの状態サイズは変更できます。また、正弦波をネットワークとゼロに供給することを実験しました。どちらの場合も、約500回の反復で収束しました。これまでのところ、この場合、グラフはパラメータを共有するn_samples個のLSTMセルで構成されていることを理解しており、入力を時系列としてフィードするのは私次第です。ただし、generateサンプルの場合、ネットワークは以前の出力に明示的に依存しています。つまり、展開されたモデルに一度にフィードすることはできません。私はすべてのステップで状態と出力を計算しようとしました:

with tf.variable_scope('sine', reuse=True):
    X_test = tf.placeholder(tf.float64)
    X_reshaped = tf.reshape(X_test, [1, -1, 1])
    output, last_states = tf.nn.dynamic_rnn(lstm_cell, X_reshaped, dtype=tf.float64)
    pred = tf.contrib.layers.fully_connected(output, 1, activation_fn=tf.tanh)


    test_vals = [0.]
    for i in range(1000):
        val = pred.eval({X_test:np.array(test_vals)[None, :, None]})
        test_vals.append(val)

ただし、このモデルでは、LSTMセル間に連続性がないようです。ここで何が起こっているのですか?

ゼロ配列を100タイムステップで初期化し、各実行の結果を配列に割り当てる必要がありますか?これをネットワークに供給するように:

実行0:input_feed = [0, 0, 0 ... 0]; res1 = result

実行1:input_feed = [res1, 0, 0 ... 0]; res2 = result

実行1:input_feed = [res1, res2, 0 ... 0]; res3 = result

等...

このトレーニング済みネットワークを使用して、次のタイムステップで入力として独自の出力を使用する場合はどうすればよいですか?

8
botcs

私があなたを正しく理解していれば、タイムステップtの出力をタイムステップt+1への入力としてフィードする方法を見つけたいと思いませんか?そうするために、テスト時間で使用できる比較的簡単な回避策があります:

  1. 入力プレースホルダーが動的なシーケンスの長さを受け入れることができることを確認してください。つまり、時間ディメンションのサイズはNoneです。
  2. tf.nn.dynamic_rnnを使用していることを確認してください(投稿された例で使用しています)。
  3. 初期状態をdynamic_rnnに渡します。
  4. 次に、テスト時に、シーケンスをループして、各タイムステップを個別にフィードできます(つまり、最大シーケンス長は1です)。さらに、RNNの内部状態を引き継ぐ必要があります。以下の擬似コードを参照してください(変数名はコードスニペットを参照します)。

つまり、モデルの定義を次のように変更します。

lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(state_size, forget_bias=1.)
X = tf.placeholder_with_default(zero_x, [None, None, 1])  # [batch_size, seq_length, dimension of input]
batch_size = tf.shape(self.input_)[0]
initial_state = lstm_cell.zero_state(batch_size, dtype=tf.float32)
def_x = np.sin(np.linspace(0, 10, n_samples))[None, :, None]
zero_x = np.zeros(n_samples)[None, :, None]
output, last_states = tf.nn.dynamic_rnn(inputs=X, cell=lstm_cell, dtype=tf.float64,
    initial_state=initial_state)
pred = tf.contrib.layers.fully_connected(output, 1, activation_fn=tf.tanh)

次に、次のように推論を実行できます。

fetches = {'final_state': last_state,
           'prediction': pred}

toy_initial_input = np.array([[[1]]])  # put suitable data here
seq_length = 20  # put whatever is reasonable here for you

# get the output for the first time step
feed_dict = {X: toy_initial_input}
eval_out = sess.run(fetches, feed_dict)
outputs = [eval_out['prediction']]
next_state = eval_out['final_state']

for i in range(1, seq_length):
    feed_dict = {X: outputs[-1],
                 initial_state: next_state}
    eval_out = sess.run(fetches, feed_dict)
    outputs.append(eval_out['prediction'])
    next_state = eval_out['final_state']

# outputs now contains the sequence you want

これはバッチでも機能する可能性があることに注意してください。ただし、同じバッチで異なる長さのシーケンスを作成する場合は、少し複雑になる可能性があります。

この種の予測をテスト時だけでなくトレーニング時にも実行したい場合は、実行することもできますが、実装が少し複雑になります。

6
kafman

独自の出力(最後の状態)を次のステップの入力(初期状態)として使用できます。これを行う1つの方法は、次のとおりです。

  1. すべてのタイムステップで、ゼロで初期化された変数を入力状態として使用します
  2. 切り捨てられたシーケンスを完了して出力状態を取得するたびに、取得したばかりのこの出力状態で状態変数を更新します。

2番目は、次のいずれかで実行できます。

  1. tensorflow/modelsのptbの例 で行われているように、状態をpythonにフェッチし、次回にフィードバックします。
  2. tensorpackのptbの例 のように、グラフに更新操作を作成し、依存関係を追加します。
1
ppwwyyxx