我正在尝试使用 KerasCV 0.9.0 版本中的
DropBlock2D
层。但是,如果我指定 run_eagerly=True
,我只能将层编译为模型。这似乎与以下事实有关:当尝试从非急切运行进行前向传递时,符号张量会传递到该层,而该层需要一个具体值。 Keras 文档说run_eagerly
应该保留用于调试,那么为什么我需要在这里启用它呢?
我使用函数式方法建立模型:
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 的问题做些什么。
无需等待上游修复错误的最直接方法是对图层进行子类化并修复发生转换的调用函数。将 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)
)