關鍵詞:Tensorflow
業(yè)務背景
最近有個需求使用KPRN算法預測一組輸入序列的得分,問題在于不同端點之間的這一組序列數(shù)量不一致,而每一個最小顆粒度序列都需要過一個LSTM層,在LSTM之后歸屬于同一對端點的序列需要合并為一組,例如輸入100個序列在經(jīng)過LSTM+2層全連接之后得到100個值[V1,V2,V3....,V100],然后歸屬于同一組的至需要聚合,比如[[V1, V2], [V3, V4, V5] ,[V100]],此時已經(jīng)不能使用tensorflow的reshape算子,引出不規(guī)則矩陣tf.RaggedTensor

測試使用tf.RaggedTensor能否完成靜態(tài)圖loss優(yōu)化
工程采用tensorflow 1.X,引入不規(guī)則張量的原因是必須使用全流程使用張量構建靜態(tài)圖,如果因此聚合操作導致中間引入Python集合操作,勢必斷開了靜態(tài)圖導致無法訓練,而要完成聚合操作目前看只能引入不規(guī)則張量。下面一個簡單例子調試下,
- 每次輸入一個固定數(shù)量比如64端點對的batch,但是一對之間可能有多組序列(特征embedding),因此炸開之后每個batch的序列數(shù)不一樣,比如這批100,下次150,在下次143
- 每個batch雖然數(shù)量不一樣,但是會同時輸入一個rowids,這個長度也是None,所有rowids里面的最大值應該相同
- KPRN是每個序列LSTM+兩層全鏈接輸出一個值,本次實驗直接使用一個一層全鏈接輸出一個值代替
- LSTM+兩層全鏈接之后是logsumexp,本次使用保持一致,并且論文中參數(shù)設置為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
---------------
整體沒有問題可以跑通進行l(wèi)oss迭代
以上代碼主要是這一行,其他都是小場面
agg_layers = tf.RaggedTensor.from_value_rowids(full_connect, value_rowids=input_rowids)
tf.RaggedTensor.from_value_rowids輸入了兩個tensor,一個tensor是上一個操作產(chǎn)生的tensor,在例子中那就是全鏈接之后的向量,另一個tensor是每一個輸入所屬的組號,組號從0開始,如果某個組元素為空則直接跳過這個組號往后寫。因此在這個地方解決了炸開之后的每個最小顆粒度輸入,在經(jīng)過神經(jīng)網(wǎng)絡變化之后,如果再根據(jù)分組號聚合改變形狀的問題。
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的其他簡單操作實現(xiàn)
tf.RaggedTensor官方文檔學習
參考這個https://tensorflow.google.cn/guide/ragged_tensor

(1)構造不規(guī)則張量
最簡單方式是使用 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))
除此之外還可以通過將扁平的值張量與行分區(qū)張量進行配對來構造不規(guī)則張量,行分區(qū)張量使用 tf.RaggedTensor.from_value_rowids、tf.RaggedTensor.from_row_lengths和 tf.RaggedTensor.from_row_splits,本例就是采用的from_value_rowids
文檔里面說扁平的值張量測試就算不是值張量,只要是規(guī)則的多維矩陣都可以使用這種轉化為不規(guī)則的張量
>>> 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集合,也可以是自定義的另外一個規(guī)則的tensor。