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
相关产品推荐
相关产品推荐

