TensorFlow中VGG16集成SENet后预测异常问题求助
问题解决:VGG16重构后预测异常(熊猫识别为斗篷)
问题现象
尝试在VGG16的关键位置集成SENet挤压激励模块,代码运行无报错,但预测结果完全异常:输入熊猫图片,模型识别为“cloak(斗篷)”。即使注释掉所有SENet模块,仅重构原始VGG16结构,依然出现相同错误,而直接使用官方VGG16测试则正常。对比模型结构和参数总量与官方一致,但预测结果完全偏离。
核心原因
重构模型时,虽然复用了VGG16的卷积层权重,但顶层的3个全连接层(Dense+Dropout)是全新初始化的随机参数,没有加载官方预训练权重,导致模型完全不具备ImageNet分类能力。此外代码还存在几处语法和配置错误:
- SENet模块类定义有语法缺失(括号未闭合、build方法参数错误)
- 分类任务误用MAE损失函数(应为交叉熵损失)
- 禁用Eager Execution可能导致的张量处理异常
- 部分层类未导入(如Layer、Dense、ReLU等)
解决步骤
1. 复用官方VGG16的全连接层权重
无需手动重新创建全连接层,直接从预训练VGG16实例中提取顶层层结构复用。
2. 修正SENet模块的语法错误
补全类定义中的括号、修正build方法参数、添加缺失的层导入。
3. 修正模型配置
- 移除
tf.compat.v1.disable_eager_execution(),保持默认的Eager模式 - 分类任务使用
categorical_crossentropy损失函数(或sparse_categorical_crossentropy)
修正后的完整代码
import tensorflow as tf import numpy as np from tensorflow.keras.layers import ( Activation, Conv2D, Input, BatchNormalization, Reshape, GlobalAveragePooling2D, Layer, Dense, ReLU, Flatten, Dropout ) from tensorflow.keras.applications.vgg16 import preprocess_input, decode_predictions, VGG16 from tensorflow.keras.preprocessing.image import load_img # 修正后的SENet模块 class SENET_Attn(Layer): def __init__(self, out_dim, ratio, layer_name="SENET"): super(SENET_Attn, self).__init__() self.out_dim = out_dim self.ratio = ratio self.layer_name = layer_name def build(self, input_shape): # build方法参数应为input_shape,而非自定义参数 self.Global_Average_Pooling = GlobalAveragePooling2D(keepdims=True) # 确保Dense单元数为整数 self.Fully_connected_1_1 = Dense( units=int(self.out_dim / self.ratio), name=f"{self.layer_name}_fully_connected1" ) self.Relu = ReLU() self.Fully_connected_2 = Dense( units=self.out_dim, name=f"{self.layer_name}_fully_connected2" ) self.Sigmoid = Activation("sigmoid") super().build(input_shape) def call(self, inputs): squeeze = self.Global_Average_Pooling(inputs) excitation = self.Fully_connected_1_1(squeeze) excitation = self.Relu(excitation) excitation = self.Fully_connected_2(excitation) excitation = self.Sigmoid(excitation) excitation = tf.reshape(excitation, [-1, 1, 1, self.out_dim]) scale = inputs * excitation return scale # 加载预训练VGG16 Vgg = VGG16(weights='imagenet', include_top=True) ratio = 8 # 重构模型,复用卷积层和全连接层权重 input_layer = tf.keras.Input(shape=(224, 224, 3)) out = input_layer # 遍历VGG16的卷积层(前19层,到block5_pool) for layer in Vgg.layers[1:19]: out = layer(out) # 在block1_pool、block2_pool、block3_pool、block4_pool、block5_pool后添加SENet if layer.name in ["block1_pool", "block2_pool", "block3_pool", "block4_pool", "block5_pool"]: out = SENET_Attn(out.shape[-1], ratio, layer_name=f"senet_{layer.name.split('_')[1]}")(out) # 复用VGG16的全连接层 for layer in Vgg.layers[19:]: out = layer(out) model = tf.keras.Model(inputs=input_layer, outputs=out) # 分类任务使用正确的损失函数 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.summary() # 预测测试 img = load_img('Panda.jpg', target_size=(224,224)) x = tf.keras.utils.img_to_array(img) x = np.expand_dims(x, axis=0) x = preprocess_input(x) preds = decode_predictions(model.predict(x), top=3)[0] print('Top3预测结果:', preds)
关键说明
- 复用预训练权重:通过遍历Vgg.layers的方式直接使用官方层,确保所有参数(包括卷积层和全连接层)都加载了ImageNet预训练权重。
- SENet模块修正:修复了build方法参数错误、Dense单元数类型错误、括号缺失等语法问题,确保模块能正常工作。
- 损失函数修正:分类任务必须使用交叉熵损失,MAE损失不适合用于类别预测任务。
内容的提问来源于stack exchange,提问作者Nelson Lefebvre
相关产品推荐
相关产品推荐

