Tensorflow:不规则张量tf.RaggedTensor学
关键词:Tensorflow
业务背景
最近有个需求使用KPRN算法预测一组输入序列的得分,问题在于不同端点之间的这一组序列数量不一致,而每一个最小颗粒度序列都需要过一个LSTM层,在LSTM之后归属于同一对端点的序列需要合并为一组,例如输入100个序列在经过LSTM+2层全连接之后得到100个值[V1,V2,V3....,V100],然后归属于同一组的至需要聚合,比如[[V1, V2], [V3, V4, V5] ,[V100]],此时已经不能使用tensorflow的reshape算子,引出不规则矩阵tf.RaggedTensor
测试使用tf.RaggedTensor能否完成静态图loss优化
工程采用tensorflow 1.X,引入不规则张量的原因是必须使用全流程使用张量构建静态图,如果因此聚合操作导致中间引入Python集合操作,势必断开了静态图导致无法训练,而要完成聚合操作目前看只能引入不规则张量。下面一个简单例子调试下,
- 每次输入一个固定数量比如64端点对的batch,但是一对之间可能有多组序列(特征embedding),因此炸开之后每个batch的序列数不一样,比如这批100,下次150,在下次143
- 每个batch虽然数量不一样,但是会同时输入一个rowids,这个长度也是None,所有rowids里面的最大值应该相同
- KPRN是每个序列LSTM+两层全链接输出一个值,本次实验直接使用一个一层全链接输出一个值代替
- LSTM+两层全链接之后是logsumexp,本次使用保持一致,并且论文中参数设置为1
- 最后套一层sigmod和y值计算交叉熵计算loss,y等于batch聚合分组后的大小
import tensorflow as tf
input_x = tf.placeholder(tf.float32, [None, 5])
input_rowids = tf.placeholder(tf.int64, [None])
input_y = tf.placeholder(tf.float32, [None, 1])
full_connect = tf.layers.dense(input_x, units=1, activation=tf.nn.sigmoid)
agg_layers = tf.RaggedTensor.from_value_rowids(full_connect, value_rowids=input_rowids)
logsumexp_layers = tf.log(tf.reduce_sum(tf.exp(agg_layers), axis=1))
sigmoid_out = tf.sigmoid(logsumexp_layers)
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=input_y, logits=logsumexp_layers))
optimizer = tf.train.AdamOptimizer(learning_rate=0.01)
train_step = optimizer.minimize(loss)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(10):
full_connect_val, agg_layers_val, logsumexp_layers_val, sigmoid_out_val, loss_val, _ = sess.run(
[full_connect, agg_layers, logsumexp_layers, sigmoid_out, loss, train_step],
feed_dict={input_x: [[1.0, 2.0, 3.0, 4.0, -1.0],
[2.0, -2.0, -1.0, 1.0, -2.0],
[3.0, -3.0, 3.0, 2.0, -3.0],
[4.0, 1.0, 3.0, 2.0, 2.0],
[5.0, 1.0, 2.0, 4.0, 3.0]],
input_rowids: [0, 0, 0, 1, 1],
input_y: [[1.0], [0.0]]})
print(full_connect_val)
print(agg_layers_val)
print(logsumexp_layers_val)
print(sigmoid_out_val)
print("loss:", loss_val)
print("---------------")
运行输出最后一轮迭代如下
[[0.67166984]
[0.7236427 ]
[0.82996327]
[0.12828942]
[0.04120561]]
<tf.RaggedTensorValue [[[0.6716698408126831], [0.7236427068710327], [0.8299632668495178]], [[0.12828941643238068], [0.04120561480522156]]]>
[[1.8425585]
[0.7788424]]
[[0.86325103]
[0.6854306 ]]
loss: 0.6518001
---------------
整体没有问题可以跑通进行loss迭代
以上代码主要是这一行,其他都是小场面
agg_layers = tf.RaggedTensor.from_value_rowids(full_connect, value_rowids=input_rowids)
tf.RaggedTensor.from_value_rowids输入了两个tensor,一个tensor是上一个操作产生的tensor,在例子中那就是全链接之后的向量,另一个tensor是每一个输入所属的组号,组号从0开始,如果某个组元素为空则直接跳过这个组号往后写。因此在这个地方解决了炸开之后的每个最小颗粒度输入,在经过神经网络变化之后,如果再根据分组号聚合改变形状的问题。
tf.RaggedTensor支持大部分tensorflow的运算,但是本例子不支持tf.reduce_logsumexp
logsumexp_layers = tf.reduce_logsumexp(agg_layers)
TypeError: Failed to convert object of type <class 'tensorflow.python.ops.ragged.ragged_tensor.RaggedTensor'> to Tensor. Contents: tf.RaggedTensor(values=Tensor("dense/Sigmoid:0", shape=(?, 1), dtype=float32), row_splits=Tensor("RaggedFromValueRowIds_2/concat_1:0", shape=(?,), dtype=int64)). Consider casting elements to a supported type.
因此本例子手动使用tf的其他简单操作实现
tf.RaggedTensor官方文档学习
参考这个https://tensorflow.google.cn/guide/ragged_tensor
(1)构造不规则张量
最简单方式是使用 tf.ragged.constant
,它会构建与给定的嵌套 Python list
或 NumPy array
相对应的 RaggedTensor
>>> tf.ragged.constant([[1, 2, 3], [2, 3]])
tf.RaggedTensor(values=Tensor("RaggedConstant/values:0", shape=(5,), dtype=int32), row_splits=Tensor("RaggedConstant/Const:0", shape=(3,), dtype=int64))
除此之外还可以通过将扁平的值张量与行分区张量进行配对来构造不规则张量,行分区张量使用 tf.RaggedTensor.from_value_rowids
、tf.RaggedTensor.from_row_lengths
和 tf.RaggedTensor.from_row_splits
,本例就是采用的from_value_rowids
文档里面说扁平的值张量测试就算不是值张量,只要是规则的多维矩阵都可以使用这种转化为不规则的张量
>>> b = tf.RaggedTensor.from_value_rowids(values=[[1, 2], [3, 4], [5, 6]], value_rowids=[0,0,1])
>>> with tf.Session() as sess:
... print(sess.run(b))
...
<tf.RaggedTensorValue [[[1, 2], [3, 4]], [[5, 6]]]>
另外输入values可以是python集合,也可以是自定义的另外一个规则的tensor。