为SRGAN适配单通道图像的VGG19网络构建报错排查
问题描述
我正在编写适用于单通道图像的VGG19神经网络,除输入层外其余结构与三通道VGG19一致。在卷积张量后接Flatten层并沿用经典VGG19的全连接层参数后,编译训练时出现报错:
ValueError: Shapes (None, 64, 64, 1) and (None, 1000) are incompatible
我理解全连接层神经元数需匹配输入维度,64×64图像经Flatten后应为4096维向量,经典模型最后一层1000神经元对应分类任务。但我的需求是为SRGAN提取图像特征,怀疑不需要分类用的全连接层,附上完整模型及训练代码,希望得到正确的特征提取方案。
class VGG19DeepConvolutionNetwork: __model = None def __init__(self): self.model() def model(self, inputShape=(64, 64, 1)): inputLayer = Input(shape=inputShape) layerSet = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(inputLayer) layerSet = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) layerSet = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(layerSet) layerSet = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) layerSet = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(layerSet) layerSet = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(layerSet) layerSet = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(layerSet) layerSet = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv4')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv4')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv4')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) layerSet = Flatten()(layerSet) layerSet = Dense(4096, activation='relu')(layerSet) layerSet = Dropout(0.5)(layerSet) layerSet = Dense(4096, activation='relu')(layerSet) layerSet = Dropout(0.5)(layerSet) outputLayer = Dense(1000, activation='relu')(layerSet) self.__model = Model(inputs=[inputLayer], outputs=[outputLayer]) self.__model.compile(optimizer='adam', loss='categorical_crossentropy') print(self.__model.summary()) def train(self, imageDataPath:string='srgangImageData.h5', weightsPath:string='vgg19Weights.h5', sliceSize=32, epochsNumber=100): if self.__model is None: self.model((sliceSize, sliceSize, 1)) imageData = ImageDataProcessing() sourceTrain, targetTrain, sourceTest, targetTest = imageData.readImageData(imageDataPath) del imageData print( 'train source', sourceTrain.shape ) print( 'train target', targetTrain.shape ) print( 'test source', sourceTest.shape ) print( 'test target', targetTest.shape ) checkpoint = ModelCheckpoint(weightsPath, verbose=1, save_best_only=True, save_weights_only=False, mode='min') callbacks_list = [checkpoint] history = self.__model.fit(sourceTrain, targetTrain, batch_size=128, steps_per_epoch=len(sourceTrain)//128, validation_data=(sourceTest, targetTest), callbacks=callbacks_list, shuffle=True, epochs=epochsNumber, verbose=1)
解决方案
报错原因分析
报错核心是模型输出形状(None,1000)与训练数据的目标形状(None,64,64,1)不匹配。你沿用了分类任务的全连接层,但SRGAN的VGG是用来提取图像卷积特征,而非做分类,全连接层完全多余,还会导致形状不兼容。
针对性修改方案
1. 移除分类全连接层,保留卷积特征输出
SRGAN通常使用VGG中间卷积层的特征(比如block5_conv4或block4_conv4的输出)作为感知损失的计算依据,不需要Flatten和全连接层。修改后的model方法如下:
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D from tensorflow.keras.models import Model class VGG19DeepConvolutionNetwork: __model = None def __init__(self): self.model() def model(self, inputShape=(64, 64, 1), feature_layer='block5_conv4'): inputLayer = Input(shape=inputShape) # Block 1 layerSet = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(inputLayer) layerSet = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) # Block 2 layerSet = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(layerSet) layerSet = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) # Block 3 layerSet = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(layerSet) layerSet = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(layerSet) layerSet = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(layerSet) layerSet = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv4')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) # Block 4 layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv4')(layerSet) layerSet = MaxPooling2D(strides=(2,2), padding='same')(layerSet) # Block 5 layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(layerSet) layerSet = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv4')(layerSet) # 可选:移除最后的MaxPooling,根据特征粒度需求决定 # 选择指定卷积层作为特征输出 self.__model = Model(inputs=[inputLayer], outputs=[layerSet]) print(self.__model.summary()) def extract_features(self, image_data): # 替换原train方法,直接提取特征(SRGAN中VGG通常不单独训练) return self.__model.predict(image_data)
2. 加载预训练权重(可选但推荐)
由于是单通道输入,无法直接加载官方三通道VGG19权重,可采用两种方式:
- 先训练三通道VGG19,将卷积层权重取均值,作为单通道模型的卷积核初始权重
- 直接在你的单通道数据集上微调VGG卷积层
3. SRGAN中的使用逻辑调整
在SRGAN训练流程中:
- 用该VGG模型分别提取低分辨率生成图和高分辨率原图的特征
- 计算两者特征的MSE作为感知损失,与像素损失、对抗损失结合训练生成器
内容的提问来源于stack exchange,提问作者Artur Mangus
相关产品推荐
相关产品推荐

