为什么Keras自定义RoIPooling层call方法第二次调用时张量形状不符
问题根因分析
1 两次call调用形状不同的原因
第一次调用是Keras构建计算图阶段的占位符检查,此时输入没有真实数据,所以形状第一维是None(代表动态batch维度),属于正常现象,不是报错原因。第二次调用是模型接收真实输入后的执行阶段,你设置的batch size为32,所以特征图第一维变为32。
2 reshape报错的核心原因
你当前的RoIPooling层代码是仅适配batch size=1的场景,跑batch size=32时出现维度不匹配:
- 你循环处理4个RoI,每个RoI resize后的输出形状为
(32, 7, 7, 512)(第一维是32张图的batch),沿axis=0拼接后得到形状(128,7,7,512),总元素数为128*7*7*512=3211264 - 你硬编码reshape目标为
(1, 4,7,7,512),总元素数仅为100352,两者数量完全不匹配,触发报错。
3 其他存在的逻辑问题
- RoI索引硬编码错误:你代码里写死
rois[0, roi_idx, 0],仅会读取batch中第0张图的RoI,剩余31张图的RoI完全没有用到,属于逻辑错误。 - num_rois参数与实际输入不匹配:你初始化RoI层时传的
num_rois=4,但实际每张图生成了16个RoI,输入RoI张量形状为(32, 16, 4),两者不一致,会导致你只用到了每张图前4个RoI,剩下12个RoI被丢弃。 - 预处理冗余:你代码中对单张图的维度转换可以简化,不需要来回transpose,直接
resized_image = np.expand_dims(resized_image, axis=0)即可得到(1, 224,224,3)的形状,只要最后把32张图的列表沿axis=0合并得到(32, 224,224,3)的输入即可,这部分你当前的结果是正确的,没有错误。
解决方法
- 临时快速跑通:把batch size改为1,即可匹配你当前写死reshape第一维为1的逻辑,验证代码正确性。
- 适配多batch场景修改代码:
def call(self, x, mask=None): assert (len(x) == 2) img = x[0] rois = x[1] batch_size = tf.shape(img)[0] # 动态获取当前batch大小 outputs = [] for roi_idx in range(self.num_rois): # 取出当前batch所有样本的第roi_idx个RoI x = rois[:, roi_idx, 0] y = rois[:, roi_idx, 1] w = rois[:, roi_idx, 2] h = rois[:, roi_idx, 3] x = tf.cast(x, tf.int32) y = tf.cast(y, tf.int32) w = tf.cast(w, tf.int32) h = tf.cast(h, tf.int32) # 逐样本裁剪RoI再resize,用tf.map_fn处理batch维度的每个样本 def process_single_sample(args): single_img, single_x, single_y, single_w, single_h = args crop = single_img[single_y:single_y+single_h, single_x:single_x+single_w, :] return tf.image.resize(crop, (self.pool_size, self.pool_size)) rs = tf.map_fn(process_single_sample, (img, x, y, w, h), fn_output_signature=tf.float32) outputs.append(rs) final_output = K.concatenate(outputs, axis=0) # 动态适配batch大小,不要硬写1 final_output = K.reshape(final_output, (batch_size, self.num_rois, self.pool_size, self.pool_size, self.nb_channels)) return final_output
- 修正参数匹配:初始化RoI层时将
num_rois设为16,和你实际生成的RoI数量保持一致。
内容的提问来源于stack exchange,提问作者Karol E. Mikołajczuk
相关产品推荐
相关产品推荐

