keras.Model ValueError/TypeError 当输出来自自定义层时

keras.Model ValueError/TypeError when output is from customized layer

我正在尝试将函数建模为结合了自定义 Keras Layer class 的 Keras 函数模型。这个想法是简单地让 Keras 层的 call 方法使用预定义的函数。该函数将输入一个给定的张量,比如 tensor1,并为 tensor1 中的每个值 t 计算一个特定的点积。点积依赖于不同长度的张量(向量),但输出的是与 tensor1.

相同长度的张量(向量)

当将 tensor1 传递给函数 TheFunction(tensor)(定义如下)时,它 returns 是我预期的输出。此外,构建TheFunctionLayer对象并传入tensor1时,输出也符合预期,与TheFunction(tensor1).

相同。

当我尝试在同一事物上使用 Keras 的功能模型时出现问题。我正在尝试拥有它,以便我可以构建模型对象并将 tensor1 传递给它。但是,在尝试构建对象时,我得到了关于点积中张量维度的 ValueError。但是如果我对模型定义中的输出声明做一点小改动,我会得到 TypeError。下面是我的源代码和出现的问题:

进口:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import Input, Layer
import numpy as np

涉及的张量:

tensor1 = [1,2,3,4,5,6,7,8,9,10]
tensor1 = tf.convert_to_tensor(tensor1, dtype=tf.float32)

tensor2 = [1,2,3,4,5]
tensor2 = tf.convert_to_tensor(tensor2, dtype=tf.float32)

tensor3 = [6,7,8,9,10]
tensor3 = tf.convert_to_tensor(tensor3, dtype=tf.float32)

点积函数:

def TheFunction(tensor):
    tensor = tf.convert_to_tensor(tensor)
    def prod_term(t):
        return tensor3*t
    
    def dot(t):
        return tf.tensordot(tensor2,prod_term(t),axes=1)
    
    return tf.map_fn(lambda t: dot(t), tensor)

点积函数的结果(维度没有问题):

<tf.Tensor: shape=(10,), dtype=float32, numpy=
array([ 130.,  260.,  390.,  520.,  650.,  780.,  910., 1040., 1170.,
       1300.], dtype=float32)>

自定义Keras层(只需要call,不需要__init__):

class TheFunctionLayer(Layer):
    
    def call(self,tensor):
        return TheFunction(tensor)

TheFunctionLayer()(tensor1) 的结果(同样,尺寸没有问题):

<tf.Tensor: shape=(10,), dtype=float32, numpy=
array([ 130.,  260.,  390.,  520.,  650.,  780.,  910., 1040., 1170.,
       1300.], dtype=float32)>

Keras 函数模型:

def TheFunctionModel(input_shape):
    x = Input(shape=input_shape)
    y = TheFunctionLayer()(x)
    
    model = keras.Model(inputs=x, outputs=y)
    
    return model

构建模型对象的结果TheFunctionModel(tf.shape(tensor1))(与维度冲突):

ValueError: Dimensions must be equal, but are 5 and 10 for '{{node the_function_layer_19/map/while/mul}} = Mul[T=DT_FLOAT](the_function_layer_19/map/while/mul/x, the_function_layer_19/map/while/TensorArrayV2Read/TensorListGetItem)' with input shapes: [5], [10].

这里可能发生了什么?我对 TensorFlow 和 Keras 的工作方式还很陌生,但似乎如果我的函数的输出有效并且与张量维度没有冲突,为什么尝试构建模型对象会给我这个维度问题?

我要添加的另一件事是 TypeError 我在将 TheFunctionModel 的定义中的输出 y = TheFunctionLayer()(x) 更改为 y = TheFunctionLayer().call(x):

def TheFunctionModel(input_shape):
    x = Input(shape=input_shape)
    y = TheFunctionLayer().call(x)
    
    model = keras.Model(inputs=x, outputs=y)
    
    return model

TheFunctionModel(tf.shape(tensor1)) 的结果(TypeError 而不是原来的 ValueError):

TypeError: Could not build a TypeSpec for <KerasTensor: shape=(None, 10) dtype=float32 (created by layer 'tf.convert_to_tensor')> with type KerasTensor

如果更改模型定义中的输出是消除值错误的方法,那么我不确定我是否理解我遇到的类型错误以及如何修复它。

非常感谢对第一个值错误甚至第二个类型错误的建议和/或解决方案。

您忘记了批次维度,这就是它不起作用的原因。您将不得不像这样重写 TheFunction

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import Input, Layer
import numpy as np

tensor2 = tf.constant([1,2,3,4,5], dtype=tf.float32)

tensor3 = tf.constant([6,7,8,9,10], dtype=tf.float32)

def TheFunction(tensor):
    
    def prod_term(t):
        return tensor3*t
    
    def dot(t):
        return tf.tensordot(tensor2,prod_term(t),axes=1)
    
    e = tf.zeros_like(tensor, dtype=tf.float32)
    i = tf.constant(0)

    while_condition = lambda i, e, tensor: tf.math.less(i, tf.shape(tensor)[0])
    def body(i, ta, tensor):
      tensor_shape = tf.shape(tensor)
      j = tf.repeat([i], tensor_shape[-1])
      indices = tf.stack([j, tf.range(tensor_shape[-1])], axis=1)
      ta = tf.tensor_scatter_nd_update(tf.cast(ta, dtype=tf.float32), indices, tf.map_fn(lambda t: dot(t), tensor[i]))
      return tf.add(i, 1), ta, tensor
    _, e, _ = tf.while_loop(while_condition, body, loop_vars=(i, e, tensor))
    return e

tf.while_loop 确保批次中的每个样本独立于其他样本进行计算。

您的模特:

tensor1 = [1,2,3,4,5,6,7,8,9,10]
tensor1 = tf.convert_to_tensor(tensor1, dtype=tf.float32)

class TheFunctionLayer(Layer):
    
    def call(self,tensor):
        return TheFunction(tensor)

def TheFunctionModel(input_shape = (10, )):
    x = Input(shape=input_shape)
    y = TheFunctionLayer()(x)
    
    model = keras.Model(inputs=x, outputs=y)
    
    return model

model = TheFunctionModel()
print(model(tf.expand_dims(tensor1, axis=0)))
tf.Tensor([[ 130.  260.  390.  520.  650.  780.  910. 1040. 1170. 1300.]], shape=(1, 10), dtype=float32)