Keras中VGG16替换全连接层后出现形状不兼容错误求助
Keras VGG16替换全连接层后训练报错:形状不兼容问题
问题描述
我正在用Keras做VGG16模型的迁移学习练习,已加载预训练模型,需要将原模型最后3个全连接层替换为3个新层:前两层分别为50和20个神经元,采用ReLU激活函数,最后一层为对应类别的神经元数量。
我执行的代码如下:
# 冻结预训练模型层的权重 for layer in base_model.layers: layer.trainable = False # 替换最后3个全连接层为新层 x = base_model.output x = Dense(50, activation='relu')(x) x = Dense(20, activation='relu')(x) x = Dense(num_classes, activation='softmax')(x) # 拼接VGG16基础模型和新层,创建新模型 model = Model(inputs=base_model.input, outputs=x)
运行model.summary()输出如下:
================================================================= input_13 (InputLayer) [(None, 224, 224, 3)] 0 block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 dense_6 (Dense) (None, 7, 7, 50) 25650 dense_7 (Dense) (None, 7, 7, 20) 1020 dense_8 (Dense) (None, 7, 7, 21) 441 ================================================================= Total params: 14,741,799 Trainable params: 27,111 Non-trainable params: 14,714,688 _________________________________________________________________
训练时出现以下错误:
ValueError: Shapes (None, 21) and (None, 7, 7, 21) are incompatible
问题原因
从模型summary可以看到,base_model.output的形状是*(None,7,7,512)*——这是VGG16最后一个池化层的输出,保留了7x7的空间维度和512个通道。直接在这个输出上叠加Dense层时,Keras会将Dense层的计算应用到每个空间位置,导致每一层的输出都保留7x7的空间维度,最终模型输出形状为*(None,7,7,21)*。而你的训练标签是*(None,21)*(单样本对应一个类别概率向量),两者维度不匹配,因此触发形状兼容错误。
解决方法
在添加Dense层之前,需要将空间维度的特征压缩为一维向量,可通过以下两种方式实现:
方法1:使用Flatten层(完整保留特征)
将7x7x512的特征展平为一维向量,保留所有空间特征信息:
x = base_model.output x = Flatten()(x) # 将(None,7,7,512)转换为(None,25088) x = Dense(50, activation='relu')(x) x = Dense(20, activation='relu')(x) x = Dense(num_classes, activation='softmax')(x)
方法2:使用全局池化层(减少参数,缓解过拟合)
通过全局平均池化或全局最大池化,将每个通道的空间特征压缩为单个值,大幅减少后续全连接层的参数数量:
x = base_model.output x = GlobalAveragePooling2D()(x) # 输出形状为(None,512) # 也可替换为GlobalMaxPooling2D()实现全局最大池化 x = Dense(50, activation='relu')(x) x = Dense(20, activation='relu')(x) x = Dense(num_classes, activation='softmax')(x)
修改后,模型最终输出形状会变为*(None,21)*,与训练标签的形状匹配,即可正常训练。
内容的提问来源于stack exchange,提问作者JLL
相关产品推荐
相关产品推荐

