具有 LSTM 网络的连体模型无法使用 tensorflow 进行训练

Posted

技术标签:

【中文标题】具有 LSTM 网络的连体模型无法使用 tensorflow 进行训练【英文标题】:Siamese Model with LSTM network fails to train using tensorflow 【发布时间】:2017-10-22 08:05:22 【问题描述】:

数据集描述

数据集包含一组问题对和一个标签,用于判断问题是否相同。例如

“如何阅读和找到我的 YouTube cmets?” ,“我怎么能看到我所有的 Youtube cmets?" , "1"

模型的目标是识别给定的问题对是相同还是不同。

接近

我创建了一个Siamese network 来确定两个问题是否相同。以下是模型:

graph = tf.Graph()

with graph.as_default():
    embedding_placeholder = tf.placeholder(tf.float32, shape=embedding_matrix.shape, name='embedding_placeholder')
    with tf.variable_scope('siamese_network') as scope:
        labels = tf.placeholder(tf.int32, [batch_size, None], name='labels')
        keep_prob = tf.placeholder(tf.float32, name='question1_keep_prob')

        with tf.name_scope('question1') as question1_scope:
            question1_inputs = tf.placeholder(tf.int32, [batch_size, seq_len], name='question1_inputs')

            question1_embedding = tf.get_variable(name='embedding', initializer=embedding_placeholder, trainable=False)
            question1_embed = tf.nn.embedding_lookup(question1_embedding, question1_inputs)

            question1_lstm = tf.contrib.rnn.BasicLSTMCell(lstm_size)
            question1_drop = tf.contrib.rnn.DropoutWrapper(question1_lstm, output_keep_prob=keep_prob)
            question1_multi_lstm = tf.contrib.rnn.MultiRNNCell([question1_drop] * lstm_layers)

            q1_initial_state = question1_multi_lstm.zero_state(batch_size, tf.float32)

            question1_outputs, question1_final_state = tf.nn.dynamic_rnn(question1_multi_lstm, question1_embed, initial_state=q1_initial_state)

        scope.reuse_variables()

        with tf.name_scope('question2') as question2_scope:
            question2_inputs = tf.placeholder(tf.int32, [batch_size, seq_len], name='question2_inputs')

            question2_embedding = question1_embedding
            question2_embed = tf.nn.embedding_lookup(question2_embedding, question2_inputs)

            question2_lstm = tf.contrib.rnn.BasicLSTMCell(lstm_size)
            question2_drop = tf.contrib.rnn.DropoutWrapper(question2_lstm, output_keep_prob=keep_prob)
            question2_multi_lstm = tf.contrib.rnn.MultiRNNCell([question2_drop] * lstm_layers)

            q2_initial_state = question2_multi_lstm.zero_state(batch_size, tf.float32)

            question2_outputs, question2_final_state = tf.nn.dynamic_rnn(question2_multi_lstm, question2_embed, initial_state=q2_initial_state)

使用 RNN 输出计算余弦距离:

with graph.as_default():
    diff = tf.sqrt(tf.reduce_sum(tf.square(tf.subtract(question1_outputs[:, -1, :], question2_outputs[:, -1, :])), reduction_indices=1))

    margin = tf.constant(1.) 
    labels = tf.to_float(labels)
    match_loss = tf.expand_dims(tf.square(diff, 'match_term'), 0)
    mismatch_loss = tf.expand_dims(tf.maximum(0., tf.subtract(margin, tf.square(diff)), 'mismatch_term'), 0)

    loss = tf.add(tf.matmul(labels, match_loss), tf.matmul((1 - labels), mismatch_loss), 'loss_add')
    distance = tf.reduce_mean(loss)

    optimizer = tf.train.AdamOptimizer(learning_rate).minimize(distance)

以下是训练模型的代码:

with graph.as_default():
    saver = tf.train.Saver()

with tf.Session(graph=graph) as sess:
    sess.run(tf.global_variables_initializer(), feed_dict=embedding_placeholder: embedding_matrix)

    iteration = 1
    for e in range(epochs):
        summary_writer = tf.summary.FileWriter('/Users/mithun/projects/kaggle/quora_question_pairs/logs', sess.graph)
        summary_writer.add_graph(sess.graph)

        for ii, (x1, x2, y) in enumerate(get_batches(question1_train, question2_train, label_train, batch_size), 1):
            feed = question1_inputs: x1,
                    question2_inputs: x2,
                    labels: y[:, None],
                    keep_prob: 0.9
                   
            loss1 = sess.run([distance], feed_dict=feed)

            if iteration%5==0:
                print("Epoch: /".format(e, epochs),
                      "Iteration: ".format(iteration),
                      "Train loss: :.3f".format(loss1))

            if iteration%50==0:
                val_acc = []
                for x1, x2, y in get_batches(question1_val, question2_val, label_val, batch_size):
                    feed = question1_inputs: x1,
                            question2_inputs: x2,
                            labels: y[:, None],
                            keep_prob: 1
                           
                    batch_acc = sess.run([accuracy], feed_dict=feed)
                    val_acc.append(batch_acc)
                print("Val acc: :.3f".format(np.mean(val_acc)))
            iteration +=1

    saver.save(sess, "checkpoints/quora_pairs.ckpt")

我已经用大约 10,000 个标记数据训练了上述模型。但是,准确度在 0.630 左右停滞不前,奇怪的是,验证准确度在所有迭代中都是相同的。

lstm_size = 64
lstm_layers = 1
batch_size = 128
learning_rate = 0.001

我创建模型的方式有什么问题吗?

【问题讨论】:

调试的良好第一步:使网络完全线性,并适合一两个微不足道的例子。一旦它适合它(令人惊讶的是它不适合的频率),慢慢地重新引入非线性。由于学习任务是微不足道的,您可以将缓慢或不存在的学习归因于死/饱和非线性。 很难说准确度如何(我不熟悉数据集或架构),但有几件事。不知道为什么你不想学习你的嵌入,但是你应该说trainable=False,而不是trainable='false',这不会有任何效果。此外,它不应该受到伤害,但我认为你不需要 scope.reuse_variables()tf.sqrtdiff 如果你以后要在两个不同的地方平方它。 我已经用简短的数据集描述和模型的目标更新了这个问题。 1)我设置了trainable=False,因为我使用的是预训练的词嵌入。 2)我在这里使用连体网络,在高层次上,它涉及使用相同权重的两个相同网络,然后我们找到两个网络输出之间的距离。如果距离小于阈值,那么它们是相同的,否则不是。因此我使用了scope.reuse_varables 我建议在单个 mini-batch 上反复训练网络,看看它是否能够完美地代表那个 mini-batch。如果没有,您很可能根本无法训练。确保您的数据没有无效条目(数字数据的 NaN 和 Infs,问题可能是空字符串?)。 【参考方案1】:

这是不平衡数据集的常见问题,例如您正在使用的最近发布的 Quora 数据集。由于 Quora 数据集是不平衡的(约 63% 的负样本和约 37% 的正样本),因此您需要正确初始化权重。如果没有权重初始化,您的解决方案将停留在局部最小值中,并且它将训练为仅预测负类。因此准确率为 63%,因为这是验证数据中“不相似”问题的百分比。如果您检查在验证集上获得的结果,您会注意到它预测的都是零。 He 等人提出的截断正态分布http://arxiv.org/abs/1502.01852 是初始化权重的一个很好的替代方案。

【讨论】:

以上是关于具有 LSTM 网络的连体模型无法使用 tensorflow 进行训练的主要内容,如果未能解决你的问题,请参考以下文章

TensorFlow 中的连体神经网络

如何在 Keras 中使用预训练的 CNN 实现连体网络?

具有交叉验证的神经网络模型的多个指标

R使用深度学习LSTM构建时间序列预测模型

具有更多功能/类的 LSTM

Python中利用LSTM模型进行时间序列预测分析 - 预测爱尔兰的电力消耗