繁体   English   中英

理解为什么 tensorflow RNN 不学习玩具数据

[英]Understanding why tensorflow RNN is not learning toy data

我正在尝试在玩具分类问题上使用 Tensorflow(r0.10,python 3.5)训练递归神经网络,但结果令人困惑。

我想将一系列 0 和 1 输入 RNN,并让序列中给定元素的目标类成为由序列的当前值和先前值表示的数字,将其视为二进制数。 例如:

input sequence: [0,     0,     1,     0,     1,     1]
binary digits : [-, [0,0], [0,1], [1,0], [0,1], [1,1]]
target class  : [-,     0,     1,     2,     1,     3]

看起来这是 RNN 应该能够很容易地学习的东西,但是我的模型只能区分 [0,2] 和 [1,3] 类。 换句话说,它能够区分当前数字为 0 的类别和当前数字为 1 的类别。这让我相信 RNN 模型没有正确地学习查看序列的先前值.

有几个教程和示例([ 1 ]、[ 2 ]、[ 3 ])演示了如何在 tensorflow 中构建和使用递归神经网络(RNN),但是在研究它们之后我仍然没有看到我的问题(它没有帮助所有示例都使用文本作为源数据)。

我将我的数据作为长度T的列表输入到tf.nn.rnn() ,其元素是[batch_size x input_size]序列。 由于我的序列是一维的, input_size等于一,所以基本上我相信我正在输入一个长度为batch_size的序列列表( 文档不清楚我不清楚哪个维度被视为时间维度)。 这种理解正确吗? 如果是这样的话,那么我不明白为什么 RNN 模型没有正确学习。

很难得到一小组代码可以通过我的完整 RNN 运行,这是我能做的最好的(它主要改编自这里的 PTB 模型和这里的 char-rnn 模型):

import tensorflow as tf
import numpy as np

input_size = 1
batch_size = 50
T = 2
lstm_size = 5
lstm_layers = 2
num_classes = 4
learning_rate = 0.1

lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_size, state_is_tuple=True)
lstm = tf.nn.rnn_cell.MultiRNNCell([lstm] * lstm_layers, state_is_tuple=True)

x = tf.placeholder(tf.float32, [T, batch_size, input_size])
y = tf.placeholder(tf.int32, [T * batch_size * input_size])

init_state = lstm.zero_state(batch_size, tf.float32)

inputs = [tf.squeeze(input_, [0]) for input_ in tf.split(0,T,x)]
outputs, final_state = tf.nn.rnn(lstm, inputs, initial_state=init_state)

w = tf.Variable(tf.truncated_normal([lstm_size, num_classes]), name='softmax_w')
b = tf.Variable(tf.truncated_normal([num_classes]), name='softmax_b')

output = tf.concat(0, outputs)

logits = tf.matmul(output, w) + b

probs = tf.nn.softmax(logits)

cost = tf.reduce_mean(tf.nn.seq2seq.sequence_loss_by_example(
    [logits], [y], [tf.ones_like(y, dtype=tf.float32)]
))

optimizer = tf.train.GradientDescentOptimizer(learning_rate)
tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(cost, tvars),
                                  10.0)
train_op = optimizer.apply_gradients(zip(grads, tvars))

init = tf.initialize_all_variables()

with tf.Session() as sess:
    sess.run(init)
    curr_state = sess.run(init_state)
    for i in range(3000):
        # Create toy data where the true class is the value represented
        # by the current and previous value treated as binary, i.e.
        train_x = np.random.randint(0,2,(T * batch_size * input_size))
        train_y = train_x + np.concatenate(([0], (train_x[:-1] * 2)))

        # Reshape into T x batch_size x input_size
        train_x = np.reshape(train_x, (T, batch_size, input_size))

        feed_dict = {
            x: train_x, y: train_y
        }
        for j, (c, h) in enumerate(init_state):
            feed_dict[c] = curr_state[j].c
            feed_dict[h] = curr_state[j].h

        fetch_dict = {
            'cost': cost, 'final_state': final_state, 'train_op': train_op
        }

        # Evaluate the graph
        fetches = sess.run(fetch_dict, feed_dict=feed_dict)

        curr_state = fetches['final_state']

        if i % 300 == 0:
            print('step {}, train cost: {}'.format(i, fetches['cost']))

    # Test
    test_x = np.array([[0],[0],[1],[0],[1],[1]]*(T*batch_size*input_size))
    test_x = test_x[:(T*batch_size*input_size),:]
    probs_out = sess.run(probs, feed_dict={
            x: np.reshape(test_x, [T, batch_size, input_size]),
            init_state: curr_state
        })
    # Get the softmax outputs for the points in the sequence
    # that have [0, 0], [0, 1], [1, 0], [1, 1] as their
    # last two values.
    for i in [1, 2, 3, 5]:
        print('{}: [{:.4f} {:.4f} {:.4f} {:.4f}]'.format(
                [1, 2, 3, 5].index(i), *list(probs_out[i,:]))
             )

这里的最终输出是

0: [0.4899 0.0007 0.5080 0.0014]
1: [0.0003 0.5155 0.0009 0.4833]
2: [0.5078 0.0011 0.4889 0.0021]
3: [0.0003 0.5052 0.0009 0.4936]

这表明它只是在学习区分 [0,2] 和 [1,3]。 为什么这个模型不学习使用序列中的前一个值?

这篇博文的帮助下想通了(它有输入张量的精彩图表)。 事实证明,我没有正确理解tf.nn.rnn()输入的形状:

假设您有batch_size个序列。 每个序列都有input_size维度和长度T (选择这些名称是为了匹配tf.nn.rnn() 文档)。 然后,您需要将输入拆分为T长度列表,其中每个元素的形状为batch_size x input_size 这意味着您的连续序列将分布在列表的元素中 我认为连续的序列将保持在一起,以便列表inputs的每个元素都是一个序列的示例。

回想起来,这是有道理的,因为我们希望通过序列并行化每个步骤,所以我们希望运行每个序列的第一步(列表中的第一个元素),然后是每个序列的第二步(列表中的第二个元素),等等.

代码的工作版本:

import tensorflow as tf
import numpy as np

sequence_size = 50
batch_size = 7
num_features = 1
lstm_size = 5
lstm_layers = 2
num_classes = 4
learning_rate = 0.1

lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_size, state_is_tuple=True)
lstm = tf.nn.rnn_cell.MultiRNNCell([lstm] * lstm_layers, state_is_tuple=True)

x = tf.placeholder(tf.float32, [batch_size, sequence_size, num_features])
y = tf.placeholder(tf.int32, [batch_size * sequence_size * num_features])

init_state = lstm.zero_state(batch_size, tf.float32)

inputs = [tf.squeeze(input_, [1]) for input_ in tf.split(1,sequence_size,x)]
outputs, final_state = tf.nn.rnn(lstm, inputs, initial_state=init_state)

w = tf.Variable(tf.truncated_normal([lstm_size, num_classes]), name='softmax_w')
b = tf.Variable(tf.truncated_normal([num_classes]), name='softmax_b')

output = tf.reshape(tf.concat(1, outputs), [-1, lstm_size])

logits = tf.matmul(output, w) + b

probs = tf.nn.softmax(logits)

cost = tf.reduce_mean(tf.nn.seq2seq.sequence_loss_by_example(
    [logits], [y], [tf.ones_like(y, dtype=tf.float32)]
))

# Now optimize on that cost
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(cost, tvars),
                                  10.0)
train_op = optimizer.apply_gradients(zip(grads, tvars))

init = tf.initialize_all_variables()

with tf.Session() as sess:
    sess.run(init)
    curr_state = sess.run(init_state)
    for i in range(3000):
        # Create toy data where the true class is the value represented
        # by the current and previous value treated as binary, i.e.
        
        train_x = np.random.randint(0,2,(batch_size * sequence_size * num_features))
        train_y = train_x + np.concatenate(([0], (train_x[:-1] * 2)))
        
        # Reshape into T x batch_size x sequence_size
        train_x = np.reshape(train_x, [batch_size, sequence_size, num_features])
        
        feed_dict = {
            x: train_x, y: train_y
        }
        for j, (c, h) in enumerate(init_state):
            feed_dict[c] = curr_state[j].c
            feed_dict[h] = curr_state[j].h
        
        fetch_dict = {
            'cost': cost, 'final_state': final_state, 'train_op': train_op
        }
        
        # Evaluate the graph
        fetches = sess.run(fetch_dict, feed_dict=feed_dict)
        
        curr_state = fetches['final_state']
        
        if i % 300 == 0:
            print('step {}, train cost: {}'.format(i, fetches['cost']))
    
    # Test
    test_x = np.array([[0],[0],[1],[0],[1],[1]]*(batch_size * sequence_size * num_features))
    test_x = test_x[:(batch_size * sequence_size * num_features),:]
    probs_out = sess.run(probs, feed_dict={
            x: np.reshape(test_x, [batch_size, sequence_size, num_features]),
            init_state: curr_state
        })
    # Get the softmax outputs for the points in the sequence
    # that have [0, 0], [0, 1], [1, 0], [1, 1] as their
    # last two values.
    for i in [1, 2, 3, 5]:
        print('{}: [{:.4f} {:.4f} {:.4f} {:.4f}]'.format(
                [1, 2, 3, 5].index(i), *list(probs_out[i,:]))
             )

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM