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

为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 16:05:38