You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CNN训练报错:Shapes (893,)与(893,16,16,15)不兼容

问题:CNN训练时形状不兼容错误及数据维度异常

我尝试用带标签的图像数组训练CNN,区分含目标和不含目标的图像。原本计划用边界框坐标实现但失败,暂用当前方案。训练时触发错误:

ValueError: Shapes (893,) and (893, 16, 16, 15) are incompatible

训练数据的NumPy数组形状为:

  • 图像:[256,256,3](单样本),整体数据集为[893,256,256,3]
  • 标签:[256](单样本),整体数据集为[893,]
    通过tf.data.Dataset.from_tensors((trainimg,trainlab))转为张量dsx,不清楚额外维度来源,求解决。

代码片段

ntwk = x = layers.Input(shape=(256, 256, 3))
x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.MaxPool2D()(x)
x = layers.BatchNormalization()(x) # size: 64x64x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.BatchNormalization()(x)  # size: 64x64x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.MaxPool2D()(x)
x = layers.BatchNormalization()(x)  # size: 32x32x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.MaxPool2D()(x)
x = layers.BatchNormalization()(x)  # size: 16x16x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.MaxPool2D()(x)
x = layers.BatchNormalization()(x) # size: 8x8x

x_prob = layers.Conv2D(1, kernel_size=3, padding='same', activation='sigmoid', name='x_prob')(x)#1
x_boxes = layers.Conv2D(4, kernel_size=3, padding='same', name='x_boxes')(x)
x_cls = layers.Conv2D(10, kernel_size=3, padding='same', activation='sigmoid', name='x_cls')(x)

gate = tf.where(x_prob > 0.5, tf.ones_like(x_prob), tf.zeros_like(x_prob))
x_boxes = x_boxes * gate
x_cls = x_cls * gate

x = layers.Concatenate()([x_prob, x_boxes, x_cls])
model = tf.keras.models.Model(ntwk, x)
model.summary()
model.compile(optimizer='rmsprop', loss="CategoricalCrossentropy")

model.fit(dsx, batch_size=256, epochs=30, shuffle=True)
print("done")

return None

问题分析与解决步骤

1. 核心矛盾:模型输出与标签不匹配

你的模型最后输出的是(None, 8, 8, 15)形状的张量(1+4+10=15个通道),这是目标检测模型的输出风格(每个空间格子输出目标存在概率、边界框、类别),但你实际要做的是图像级二分类(仅判断整图是否含目标),标签是一维数组(893,),两者维度完全不兼容,直接导致形状错误。

2. 额外维度来源

  • 模型输出的(8,8,15):经过4次MaxPool2D(每次尺寸减半),输入256x256逐步压缩到8x8,卷积层保留空间维度,所以输出带HxW特征图。
  • 数据集的错误创建:tf.data.Dataset.from_tensors会把整个数据集打包成单个张量元素(比如((893,256,256,3), (893,))),而非按样本拆分。模型期望每个样本输入是(256,256,3),标签是标量或一维向量,这里的维度混乱也加剧了错误。

3. 修正方案(适配二分类需求)

方案一:调整模型为图像二分类结构

修改模型,去掉空间维度,输出图像级分类结果:

ntwk = x = layers.Input(shape=(256, 256, 3))
# 补回你注释中漏掉的卷积层(原代码注释有重复,这里修正逻辑)
x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.MaxPool2D()(x)
x = layers.BatchNormalization()(x)

x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.MaxPool2D()(x)

x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.MaxPool2D()(x)

x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.MaxPool2D()(x)

# 全局池化压缩空间维度为一维
x = layers.GlobalAveragePooling2D()(x)
# 二分类输出(sigmoid对应0/1概率)
x = layers.Dense(1, activation='sigmoid')(x)

model = tf.keras.models.Model(ntwk, x)
model.summary()
# 二分类用二元交叉熵损失,而非多分类的CategoricalCrossentropy
model.compile(optimizer='rmsprop', loss="BinaryCrossentropy", metrics=['accuracy'])

# 修正数据集创建:用from_tensor_slices拆分每个样本
dsx = tf.data.Dataset.from_tensor_slices((trainimg, trainlab))
# 打乱+分批
dsx = dsx.shuffle(len(trainimg)).batch(256)

model.fit(dsx, epochs=30)
print("done")

方案二:若坚持目标检测风格(需重构标签)

如果要保留当前输出结构,标签必须是(样本数, H, W, 15)的形状,每个格子对应:

  • 第0通道:目标存在标记(0/1)
  • 第1-4通道:边界框坐标
  • 第5-14通道:类别one-hot编码
    同时需要自定义损失函数,但这完全偏离你当前的二分类需求,不推荐。

关键修正点总结

  • 替换from_tensors为from_tensor_slices,正确拆分单个样本
  • 调整模型结构,新增全局池化去掉空间维度,输出二分类结果
  • 匹配损失函数:二分类用BinaryCrossentropy,而非CategoricalCrossentropy

内容的提问来源于stack exchange,提问作者Tom Leahy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 06:14:51