我正在尝试使用LSTM单元和TensorFlow创建一个文本生成神经网络。我在时间优先格式[time_steps, batch_size, input_size]上训练网络,并且希望每个时间步能够预测序列中的下一个词。序列在时间步长内用空值填充,并且一个单独的占位符包含批次中每个序列的长度。
关于时间反向传播的概念有很多信息,但是我找不到关于在TensorFlow中实现变长序列成本计算的任何资料。由于序列的末尾是填充的,我假设我不希望在填充部分计算成本。因此,我需要一种方法来截取从第一个输出到序列结束的输出。
这是我目前的代码:
outputs = [] states = [] cost = 0 for i in range(time_steps+1): output, state = cell(X[i], state) z1 = tf.matmul(output, dec_W1) + dec_b1 a1 = tf.nn.sigmoid(z1) z2 = tf.matmul(a1, dec_W2) + dec_b2 a2 = tf.nn.softmax(z2) outputs.append(a2) states.append(state) #== 计算成本 cost = cost + tf.nn.softmax_cross_entropy_with_logits(logits=z2, labels=y[i]) optimizer = tf.train.AdamOptimizer(0.001).minimize(cost)
这段代码在没有变长序列的情况下可以正常工作。然而,如果在末尾添加了填充值,那么它也会计算填充部分的成本,这没有多大意义。
我如何只计算序列长度限制前的输出的成本呢?
回答:
解决了!
在研究了大量的例子后(大多数是使用更高层次的框架如Keras,这很麻烦),我发现必须创建一个掩码!回顾起来这似乎很简单。
这是创建一个由1和0组成的掩码并将其与矩阵(即成本值)进行逐元素乘法的代码
x = tf.placeholder(tf.float32)seq = tf.placeholder(tf.int32)def mask_by_length(input_matrix, length): ''' 输入矩阵是一个二维张量 [batch_size, time_steps] length 是一个一维张量 length 指的是输入矩阵轴1的长度 ''' length_transposed = tf.expand_dims(length, 1) # 创建范围以便与length进行比较 range = tf.range(tf.shape(input_matrix)[1]) range_row = tf.expand_dims(range, 0) # 使用逻辑运算创建掩码 mask = tf.less(range_row, length_transposed) # 将布尔值转换为整数以完成掩码 mask_result = tf.cast(mask, dtype=tf.float32) # 逐元素乘法以取消掩码中的值 result = tf.multiply(mask_result, input_matrix) return resultmask_values = mask_by_length(x, seq)
输入值(时间优先) [time_steps, batch_size]
[[ 0.71, 0.22, 1.42, -0.28, 0.99] [ 0.41, 2.24, 0.09, 0.74, 0.65]]
序列值 [batch_size]
[2, 3]
输出(时间优先) [time_steps, batch_size]
[[ 0.71, 0.22, 0, 0, 0, ] [ 0.41, 2.24, 0.09, 0, 0, ]]