keras_cv DropBlock2D 在没有 eager run 的 Keras 模型中不起作用

问题描述 投票:0回答:1

我正在尝试使用 KerasCV 0.9.0 版本中的

DropBlock2D
层。但是,如果我指定
run_eagerly=True
,我只能将层编译为模型。这似乎与以下事实有关:当尝试从非急切运行进行前向传递时,符号张量会传递到该层,而该层需要一个具体值。 Keras 文档
run_eagerly
应该保留用于调试,那么为什么我需要在这里启用它呢?

交互式示例(Google Colab)

我使用函数式方法建立模型:

input = keras.layers.Input(shape=input_shape)
x = keras.layers.Conv2D(32, (1, 1))(input)
x = keras.layers.BatchNormalization()(x)
x = keras.layers.ReLU()(x)
x = keras_cv.layers.DropBlock2D(rate=0.05, block_size=(14, 14))(x)
x = keras.layers.GlobalAveragePooling2D()(x)
output = keras.layers.Dense(num_classes)(x)

model = keras.Model(
    inputs=input,
    outputs=output,
)

然后我编译模型:

model.compile(
  loss=keras.losses.SparseCategoricalCrossentropy(),
  optimizer=keras.optimizers.Adam(learning_rate=1e-3),
  # Uncommenting below will make things work
  #run_eagerly=True 
)

然后我加载数据进行训练:

mnist_train, mnist_test = keras.datasets.fashion_mnist.load_data()
mnist_x_train, mnist_y_train = mnist_train

model.fit(
    mnist_x_train[0:20,:,:],
    mnist_y_train[0:20],
    epochs=2
)

model.fit
行之后,如果编译时
run_eagerly
不是
True
,我会收到错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-15f407e6a023> in <cell line: 41>()
     39 mnist_x_train, mnist_y_train = mnist_train
     40 
---> 41 model.fit(
     42     mnist_x_train[0:20,:,:],
     43     mnist_y_train[0:20],

1 frames
/usr/local/lib/python3.10/dist-packages/keras_cv/src/layers/regularization/dropblock_2d.py in call(self, x, training)
    190         valid_block = ops.logical_and(
    191             ops.logical_and(
--> 192                 w_i >= int(dropblock_width // 2),
    193                 w_i < width - (dropblock_width - 1) // 2,
    194             ),

TypeError: Exception encountered when calling DropBlock2D.call().

int() argument must be a string, a bytes-like object or a real number, not 'SymbolicTensor'

Arguments received by DropBlock2D.call():
  • x=tf.Tensor(shape=(None, 28, 28, 32), dtype=float32)
  • training=True

我知道

int
转换不适用于符号张量,但我想知道是否可以针对使用 KerasCV 提交 bug 的问题做些什么。

tensorflow keras keras-cv
1个回答
0
投票

无需等待上游修复错误的最直接方法是对图层进行子类化并修复发生转换的调用函数。将 python

int
转换替换为对
ops.cast(x, dtype="int32")
:

的调用
from keras_cv.src.backend import ops 
from keras_cv.src.backend import random

class PatchedDropBlock2D(keras_cv.layers.DropBlock2D):
    def call(self, x, training=None):
        if not training or self._rate == 0.0:
            return x

        _, height, width, _ = ops.split(ops.shape(x), 4)

        # Unnest scalar values
        height = ops.squeeze(height)
        width = ops.squeeze(width)

        dropblock_height = ops.minimum(self._dropblock_height, height)
        dropblock_width = ops.minimum(self._dropblock_width, width)

        gamma = (
            self._rate
            * ops.cast(width * height, dtype="float32")
            / ops.cast(dropblock_height * dropblock_width, dtype="float32")
            / ops.cast(
                (width - self._dropblock_width + 1)
                * (height - self._dropblock_height + 1),
                "float32",
            )
        )

        # Forces the block to be inside the feature map.
        w_i, h_i = ops.meshgrid(ops.arange(width), ops.arange(height))
        valid_block = ops.logical_and(
            ops.logical_and(
                w_i >= ops.cast(dropblock_width // 2, dtype="int32"),
                w_i < width - (dropblock_width - 1) // 2,
            ),
            ops.logical_and(
                h_i >= ops.cast(dropblock_height // 2, dtype="int32"),
                h_i < width - (dropblock_height - 1) // 2,
            ),
        )

        valid_block = ops.reshape(valid_block, [1, height, width, 1])

        random_noise = random.uniform(
            ops.shape(x), seed=self._random_generator, dtype="float32"
        )
        valid_block = ops.cast(valid_block, dtype="float32")
        seed_keep_rate = ops.cast(1 - gamma, dtype="float32")
        block_pattern = (1 - valid_block + seed_keep_rate + random_noise) >= 1
        block_pattern = ops.cast(block_pattern, dtype="float32")

        window_size = [1, self._dropblock_height, self._dropblock_width, 1]

        # Double negative and max_pool is essentially min_pooling
        block_pattern = -ops.max_pool(
            -block_pattern,
            pool_size=window_size,
            strides=[1, 1, 1, 1],
            padding="SAME",
        )

        # Slightly scale the values, to account for magnitude change
        percent_ones = ops.cast(ops.sum(block_pattern), "float32") / ops.cast(
            ops.size(block_pattern), "float32"
        )
        return (
            x
            / ops.cast(percent_ones, x.dtype)
            * ops.cast(block_pattern, x.dtype)
        )
© www.soinside.com 2019 - 2024. All rights reserved.