Tensorflow 自定义损失计算导致RuntimeError:尝试在不构建函数的情况下捕获张量

Tensorflow 自定义损失计算导致RuntimeError:尝试在不构建函数的情况下捕获张量,tensorflow,customization,tf.keras,loss-function,Tensorflow,Customization,Tf.keras,Loss Function,环境:Windows 10 x64上的tensorflow 2.2处于仅CPU模式。使用tf.keras 我想建立一个简单的图像到文本识别模型(有时称为OCR) 为此,我使用具有CTC丢失功能的体系结构 有一个很好的功能,适合我的目的。注意:我不能使用,因为它没有blank\u索引参数。但实际上它只是在内部调用tf.nn.ctc\u loss 问题是至少需要4个参数才能工作: 一批预测登录 罗吉斯长度 一批真序列 真序列长度 我的数据集返回:(图像、图像宽度、真序列、真序列长度)。 我想使用

环境:Windows 10 x64上的tensorflow 2.2处于仅CPU模式。使用tf.keras

我想建立一个简单的图像到文本识别模型(有时称为OCR)

为此,我使用具有CTC丢失功能的体系结构

有一个很好的功能,适合我的目的。注意:我不能使用,因为它没有
blank\u索引
参数。但实际上它只是在内部调用
tf.nn.ctc\u loss

问题是至少需要4个参数才能工作:

  • 一批预测登录
  • 罗吉斯长度
  • 一批真序列
  • 真序列长度
我的数据集返回:
(图像、图像宽度、真序列、真序列长度)
。 我想使用这些值以及预测的Logit来计算损失值

但tensorflow很难定制。因此,起初我试图创建自己的类,该类的子类为
tf.keras.Loss.Loss
。然后,我还必须创建一个子类
tf.keras.Model
,其中包含重写的
train\u step
test\u step
方法,因为tensorflow可以通过所有必需的输入,而不会改变

它成功了。直到我保存了我的模型并尝试在外部使用它,使用另一种语言(Java)。 它无法加载我保存的模型,因为它不知道我的类

所以我不得不回到创建模型的功能性方法。 现在,我的计划是将损失计算为训练期间模型的输出。 导出模型图时,只保存输出概率层的计算

以下是我如何创建模型并进行CTC损失计算:

def KerasLighterConvGRU(n_classes: int, blank_class: int, height=32, in_channels=3, rnn_hidden_size=64, training=True):

    # Input
    images = layers.Input(shape=(height, None, in_channels), dtype='float32', name='images')
    # (B, 32, W, 3)

    model = tf.keras.Sequential([
        # Convolution layer (VGG)
        layers.Conv2D(64, (5, 5), padding='same', name='conv1', kernel_initializer='he_normal'),
        layers.BatchNormalization(),
        layers.LeakyReLU(),
        layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), name='max1'),
        # (B, 16, W/2, 64)
        
        layers.Conv2D(128, (3, 3), padding='same', name='conv2', kernel_initializer='he_normal'),
        layers.BatchNormalization(),
        layers.LeakyReLU(),
        layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), name='max2'),
        # (B, 8, W/4, 128)
        
        layers.Conv2D(128, (3, 3), padding='same', name='conv3', kernel_initializer='he_normal'),
        layers.BatchNormalization(),
        layers.LeakyReLU(),
        layers.MaxPooling2D(pool_size=(2, 1), strides=(2, 1), name='max3'),
        # (B, 4, W/4, 128)
        
        layers.DepthwiseConv2D((3, 3), padding='same', depth_multiplier=2, kernel_initializer='he_normal', name='dconv4'),
        layers.BatchNormalization(),
        layers.LeakyReLU(),
        layers.MaxPooling2D(pool_size=(2, 1), strides=(2, 1), name='max4'),
        # (B, 2, W/4, 256)
        
        layers.Conv2D(128, (1, 1), padding='same', kernel_initializer='he_normal', name='conv5'),
        layers.BatchNormalization(),
        layers.LeakyReLU(),
        # (B, 2, W/4, 128)
        
        layers.MaxPooling2D(pool_size=(2, 1), strides=(2, 1), name='max5'),
        # (B, 1, W/4, 128)
        
        # CNN to RNN
        layers.Lambda(lambda x: K.squeeze(x, axis=1), name='cnn_to_rnn'),
        # (B, W/4, 128)
        
        # RNN layer
        layers.Bidirectional(layers.GRU(rnn_hidden_size, return_sequences=True, kernel_initializer='he_normal'),
            merge_mode='sum', name='rnn1'),
        
        layers.Bidirectional(layers.GRU(rnn_hidden_size, return_sequences=True, kernel_initializer='he_normal'),
            merge_mode='concat', name='rnn2'),
        # (B, W/4, 2*rnn_hidden_size)
        
        layers.Dense(n_classes, kernel_initializer='he_normal', name='dense2'),
        layers.Activation('softmax', name='output'),
    ])
    
    y_probs = model(images)
    
    if training:
        targets = layers.Input(name='targets', shape=(None,), dtype='int32')  # (B, ?)
        target_lengths = layers.Input(name='target_lengths', shape=[], dtype='int64')  # (B)
        blank_class = K.constant(blank_class, dtype='int32', shape=None, name=None)
        loss = layers.Lambda(lambda x: ctc_loss(x), name='ctc_loss')((y_probs, targets, target_lengths, blank_class))
        return tf.keras.Model(inputs=[images, targets, target_lengths], outputs=loss)

    else:
        return tf.keras.Model(inputs=[images], outputs=y_probs)


def ctc_loss(args):
    y_probs, targets, target_lenghts, blank_index = args
    # Compute log(probabilities)
    logits = K.log(y_probs)
    # Make a fake logit lengths vector as the maximum length of a predicted sequence
    logit_lengths = K.ones((K.shape(logits)[0],), dtype='int32') * K.shape(logits)[1]
    # Return a batch of CTC loss values
    return tf.nn.ctc_loss(
        labels=targets,
        logits=logits,
        label_length=target_lenghts,
        logit_length=logit_lengths,
        blank_index=blank_index,
        logits_time_major=False
    )
模型以以下方式初始化:

    model = KerasLighterConvGRU(n_classes=train_dataset.n_classes, height=height, blank_class=blank_class, training=True)
    
    # The loss calculation occurs elsewhere, so use a dummy lambda func for the loss
    model.compile(
        optimizer=Adam(learning_rate=learning_rate),
        loss={'ctc_loss': lambda y_true, y_pred: y_pred}
    )

但是
model.compile(…)
会导致错误:
RuntimeError:试图在不构建函数的情况下捕获一个函数。

它不喜欢这一行代码:

loss = layers.Lambda(lambda x: ctc_loss(x), name='ctc_loss')((y_probs, targets, target_lengths, blank_class))
我已经尝试了以下方面的答案: -他们没有帮忙

请帮助我理解,为什么它不工作,以及如何修复它?