Tensorflow 自定义损失计算导致RuntimeError:尝试在不构建函数的情况下捕获张量
环境:Windows 10 x64上的tensorflow 2.2处于仅CPU模式。使用tf.keras 我想建立一个简单的图像到文本识别模型(有时称为OCR) 为此,我使用具有CTC丢失功能的体系结构 有一个很好的功能,适合我的目的。注意:我不能使用,因为它没有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个参数才能工作: 一批预测登录 罗吉斯长度 一批真序列 真序列长度 我的数据集返回:(图像、图像宽度、真序列、真序列长度)。 我想使用
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))
我已经尝试了以下方面的答案:
-他们没有帮忙
请帮助我理解,为什么它不工作,以及如何修复它?