如何使用 'Keras symbolic inputs' 和 'tf.while_loop'?

How to use 'Keras symbolic inputs' with 'tf.while_loop'?

我正在尝试在我的自定义 Keras 层中使用 tf.while_loop 创建 N x N 张量。这里,N(代码中的 timesteps)是 Keras 符号张量(整数标量)。下面的代码是我在函数模型中自定义 Keras 层的 __call__ 方法。

import tensorflow as tf
from keras import backend as K

# timesteps = tf.constant(7) ## This makes this code work!!
timesteps = K.shape(inputs)[1] ## Or equivalently provided by timesteps = keras.layers.Input(shape= (), batch_size= 1, name= "timesteps")
# timesteps = tf.convert_to_tensor(timesteps) ## Does not work.
idx_outer = tf.constant(0)
timesteps_mixed_outer = tf.reshape(tf.Variable([]), (0, timesteps))
# timesteps_mixed_outer = Lambda(lambda timesteps : tf.reshape(tf.Variable([]), (0, timesteps)))(timesteps) ## Does not work
def body_inner(idx_inner, idx_outer, timesteps_mixed_inner):
    timesteps_mixed_inner = tf.concat([timesteps_mixed_inner, [tf.cond(idx_inner == idx_outer, lambda : True, lambda : False)]], axis = 0)
    return idx_inner + 1, idx_outer, timesteps_mixed_inner

def body_outer(idx_outer, timesteps_mixed_outer):
    timesteps_mixed_inner = tf.Variable([])
    idx_inner = tf.constant(0)

    idx_inner, idx_outer, timesteps_mixed_inner = tf.while_loop(lambda idx_inner, idx_outer, timesteps_mixed_inner: K.less(idx_inner, timesteps), body_inner, [idx_inner, idx_outer, timesteps_mixed_inner], shape_invariants= [idx_inner.get_shape(), idx_outer.get_shape(), tf.TensorShape([None])])

    timesteps_mixed_outer = tf.concat([timesteps_mixed_outer, [timesteps_mixed_inner]], axis = 0)
    return idx_outer + 1, timesteps_mixed_outer

idx_outer, timesteps_mixed_outer = tf.while_loop(lambda idx_outer, timesteps_mixed_outer: K.less(idx_outer, timesteps), body_outer, [idx_outer, timesteps_mixed_outer], shape_invariants= [idx_outer.get_shape(), tf.TensorShape([None, None])]) ## Here raises error

上面代码的最后一行引发了以下错误:

Exception has occurred: TypeError
Could not build a TypeSpec for <KerasTensor: shape=(0, None) dtype=float32 (created by layer 'tf.reshape')> with type KerasTensor

我试过的:

<tf.Tensor: shape=(7, 7), dtype=float32, numpy=
array([[1., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0., 0., 1.]], dtype=float32)>
Exception has occurred: TypeError
Keras symbolic inputs/outputs do not implement `__len__`. You may be trying to pass Keras symbolic inputs/outputs to a TF API that does not register dispatching, preventing Keras from automatically converting the API call to a lambda layer in the Functional Model. This error will also get raised if you try asserting a symbolic input/output directly.

我的环境如下:

编辑

在输入实际 Numpy 值之前构建 Keras 模型时出现此错误。 timesteps = K.shape(inputs)[1] 因输入而异,因此它被设置为 None,就像批次维度一样。

timesteps = K.shape(inputs)[1]
== 
<KerasTensor: shape=() dtype=int32 inferred_value=[None] (created by layer 'tf.__operators__.getitem_6')>
==
dtype:tf.int32
is_tensor_like:True
name:'tf.__operators__.getitem_6/strided_slice:0'
op:'Traceback (most recent call last):\n  File "/Users/imgspoints/.vscode/extensions/ms-python.python-2022.2.1924087327/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py", line 193, in _get_py_dictionary\n    attr = getattr(var, name)\n  File "/Users/imgspoints/.local/share/virtualenvs/experiments-m6CLaaa4/lib/python3.7/site-packages/tensorflow/python/keras/engine/keras_tensor.py", line 251, in op\n    raise TypeError(\'Keras symbolic inputs/outputs do not \'\nTypeError: Keras symbolic inputs/outputs do not implement `op`. You may be trying to pass Keras symbolic inputs/outputs to a TF API that does not register dispatching, preventing Keras from automatically converting the API call to a lambda layer in the Functional Model.\n'
shape:TensorShape([])
type_spec:TensorSpec(shape=(), dtype=tf.int32, name=None)
_inferred_value:[None]
_keras_history:KerasHistory(layer=<tensorflow.python.keras.layers.core.SlicingOpLambda object at 0x1774fac88>, node_index=0, tensor_index=0)
_name:'tf.__operators__.getitem_6/strided_slice:0'
_overload_all_operators:<bound method KerasTensor._overload_all_operators of <class 'tensorflow.python.keras.engine.keras_tensor.KerasTensor'>>
_overload_operator:<bound method KerasTensor._overload_operator of <class 'tensorflow.python.keras.engine.keras_tensor.KerasTensor'>>
_to_placeholder:<bound method KerasTensor._to_placeholder of <KerasTensor: shape=() dtype=int32 inferred_value=[None] (created by layer 'tf.__operators__.getitem_6')>>
_type_spec:TensorSpec(shape=(), dtype=tf.int32, name=None)

报错时,K.less(idx_outer, timesteps)可以求值成功:

timesteps == <KerasTensor: shape=() dtype=bool (created by layer 'tf.math.less')> 所以我相信错误来自 tf.concat,我现在正在尝试将 tf.concat 替换为另一个操作(例如 Keras Concatenate 层)。

更简单的例子

以下代码在 end = tf.constant(7) 但引发

时有效
Keras symbolic inputs/outputs do not implement `__len__`. You may be trying to pass Keras symbolic inputs/outputs to a TF API that does not register dispatching, preventing Keras from automatically converting the API call to a lambda layer in the Functional Model. This error will also get raised if you try asserting a symbolic input/output directly.

end = Input(shape= (), batch_size= 1, name= "timesteps", dtype= tf.int32)_, final_output = tf.while_loop(cond, body, loop_vars=[step, output]) 处出错。

mport tensorflow as tf
from keras.layers import Input

# end = Input(shape= (), batch_size= 1, name= "timesteps", dtype= tf.int32) ## not works :(
end = tf.constant(7) ## works :)
array = tf.Variable([1., 1., 1., 1., 1., 1., 1.])
step = tf.constant(0)
output = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True)

def cond(step, output):
    return step < end

def body(step, output):
    output = output.write(step, tf.gather(array, step))
    return step + 1, output

_, final_output = tf.while_loop(cond, body, loop_vars=[step, output])

timesteps_mixed_outer = tf.concat([timesteps_mixed_outer, [timesteps_mixed_inner]], 轴 = 0)

你必须检查 timesteps_mixed_outer 和 timesteps_mixed_inner 的形状。尝试更改轴值。

或者试试这个。

timesteps_mixed_outer = tf.concat([timesteps_mixed_outer.numpy(), timesteps_mixed_inner.numpy()], axis = 0)

尝试将您的逻辑包装在自定义层中并使用 tf 操作:

import tensorflow as tf

class CustomLayer(tf.keras.layers.Layer):
  def __init__(self):
    super(CustomLayer, self).__init__()
              
  def call(self, inputs):

    input_shape = tf.shape(inputs)
    end = input_shape[-1]
    array = tf.ones((input_shape[-1],))
    step = tf.constant(0)
    output = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True)

    def cond(step, output):
        return step < end

    def body(step, output):
        output = output.write(step, tf.gather(array, step))
        return step + 1, output

    _, final_output = tf.while_loop(cond, body, loop_vars=[step, output])
    return tf.reshape(final_output.stack(), (input_shape))


inputs = tf.keras.layers.Input(shape= (None, ), batch_size= 1, name= "timesteps", dtype= tf.int32)
cl = CustomLayer()
outputs = cl(inputs)
model = tf.keras.Model(inputs, outputs)
random_data = tf.random.uniform((1, 7), dtype=tf.int32, maxval=50)
print(model(random_data))
tf.Tensor([1. 1. 1. 1. 1. 1. 1.], shape=(7,), dtype=float32)