Keras实现Inception模块拼接报Concatenate无compile属性错误
问题描述
参考IEEE收录的相关论文搭建卷积神经网络,尝试对Inception模块输出的特征图做拼接操作时,编写代码运行抛出属性错误。
原实现代码如下:
model_1 = Sequential() model_1.add(Conv2D(3,kernel_size=(3,3), strides=(1),activation='relu',input_shape=(48,48,3),padding='same')) model_1.add(Conv2D(3,kernel_size=(7,1), strides=(1), activation='relu',padding='same')) model_1.add(Conv2D(3,kernel_size=(1,7), strides=(1), activation='relu',padding='same')) model_1.add(BatchNormalization()) model_1.add(MaxPool2D(pool_size=(2, 2), strides=(2))) model_1.add(Dropout(rate=0.2)) model_2 = Sequential() model_2.add(Conv2D(64,kernel_size=(3,1), strides=(1), activation='relu',padding='same')) model_2.add(Conv2D(64,kernel_size=(1,3), strides=(1), activation='relu',padding='same')) model_2.add(Conv2D(64,kernel_size=(1,7), strides=(1), activation='relu',padding='same')) model_2.add(Conv2D(64,kernel_size=(7,1), strides=(1), activation='relu',padding='same')) model_2.add(BatchNormalization()) model_3 = Sequential() model_3.add(BatchNormalization()) model_3.add(MaxPool2D(pool_size=(2, 2), strides=(2))) model_3.add(Dropout(rate=0.2)) model_3.add(Conv2D(128,kernel_size=(3,3), strides=(1), activation='relu')) model_3.add(Conv2D(128,kernel_size=(3,3), strides=(1), activation='relu')) model_3.add(MaxPool2D(pool_size=(2, 2), strides=(2))) model_3.add(Dropout(rate=0.3)) model_3.add(Dense(NUM_CLASS)) model_3.add(Dropout(rate=0.4)) model_3.add(Dense(NUM_CLASS, activation='softmax')) #Concatenate merged = Concatenate([model_1,model_2]) result = Concatenate([merged,model_3]) result.compile(optimizer="adam",metrics=["accuracy"]) result.summary()
网络结构参考示意图:
运行代码抛出的错误信息:
AttributeError: 'Concatenate' object has no attribute 'compile'
错误产生原因
- 核心错误是混淆了Keras中的层对象和模型对象:
Concatenate是用于张量拼接的网络层类,实例化后得到的是层对象,仅负责完成张量拼接计算,不具备compile、summary、fit等仅模型对象才有的方法,直接对Concatenate实例调用编译方法必然报错。 - 多分支Inception结构无法通过纯
Sequential模型实现:Sequential是单输入单输出的线性堆叠结构,不支持多分支张量拼接的拓扑逻辑;且原代码直接将Sequential模型实例作为参数传入Concatenate,没有传递实际计算的张量,逻辑完全不成立。 - 原代码存在多处维度不匹配隐患:分支1做了步长为2的最大池化,输出特征图尺寸为输入的1/2,分支2无池化操作,输出特征图尺寸和输入一致,二者空间维度不匹配无法直接拼接;卷积层输出为3维特征图,直接接全连接Dense层也会报维度错误。
修复方案
使用Keras函数式API构建多分支拓扑,修复所有维度匹配问题,最终通过Model类封装完整可编译的模型:
- 定义全局统一的输入张量,所有分支从该输入开始前向计算
- 调整各分支层参数,保证待拼接的特征图宽高维度完全一致,仅在通道维度做拼接
- 按正确语法调用Concatenate层完成张量拼接,拼接后继续堆叠后续卷积、分类层
- 在卷积层和全连接层之间补充Flatten层,完成特征维度展平
- 绑定输入张量和最终输出张量,实例化Model对象后再执行编译、summary操作
修复后的可运行代码如下:
from tensorflow.keras.models import Model from tensorflow.keras.layers import ( Input, Conv2D, BatchNormalization, MaxPool2D, Dropout, Dense, Concatenate, Flatten ) # 定义全局输入,按任务设置类别数 input_tensor = Input(shape=(48, 48, 3)) NUM_CLASS = 10 # 替换为自身任务的实际类别数 # 分支1 b1 = Conv2D(3, kernel_size=(3,3), strides=1, activation='relu', padding='same')(input_tensor) b1 = Conv2D(3, kernel_size=(7,1), strides=1, activation='relu', padding='same')(b1) b1 = Conv2D(3, kernel_size=(1,7), strides=1, activation='relu', padding='same')(b1) b1 = BatchNormalization()(b1) b1 = MaxPool2D(pool_size=(2,2), strides=2, padding='same')(b1) b1_out = Dropout(rate=0.2)(b1) # 分支2 b2 = Conv2D(64, kernel_size=(3,1), strides=1, activation='relu', padding='same')(input_tensor) b2 = Conv2D(64, kernel_size=(1,3), strides=1, activation='relu', padding='same')(b2) b2 = Conv2D(64, kernel_size=(1,7), strides=1, activation='relu', padding='same')(b2) b2 = Conv2D(64, kernel_size=(7,1), strides=1, activation='relu', padding='same')(b2) b2 = BatchNormalization()(b2) # 补充池化层,对齐和分支1的特征图尺寸 b2 = MaxPool2D(pool_size=(2,2), strides=2, padding='same')(b2) b2_out = Dropout(rate=0.2)(b2) # 第一次拼接两个Inception分支 merged = Concatenate()([b1_out, b2_out]) # 拼接后的后续分类网络 x = BatchNormalization()(merged) x = MaxPool2D(pool_size=(2,2), strides=2)(x) x = Dropout(rate=0.2)(x) x = Conv2D(128, kernel_size=(3,3), strides=1, activation='relu')(x) x = Conv2D(128, kernel_size=(3,3), strides=1, activation='relu')(x) x = MaxPool2D(pool_size=(2,2), strides=2)(x) x = Dropout(rate=0.3)(x) # 补充Flatten层展平特征,适配全连接层输入要求 x = Flatten()(x) x = Dense(NUM_CLASS)(x) x = Dropout(rate=0.4)(x) output_tensor = Dense(NUM_CLASS, activation='softmax')(x) # 构建完整模型 result = Model(inputs=input_tensor, outputs=output_tensor) result.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"]) result.summary()
注:如果你的标签是整数型而非独热编码,需要将loss参数替换为
sparse_categorical_crossentropy。
内容的提问来源于stack exchange,提问作者LIM XIN ROY
相关产品推荐
相关产品推荐

