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

如何解决Keras中两个CNN模型拼接时的形状不匹配问题?

解决共享输入CNN模型拼接时的形状不匹配问题

问题原因

膨胀卷积(dilation_rate)会扩大卷积核的有效感受野,导致model2的特征图尺寸逐步缩小的幅度和model1不一致,最终输出(None,4,4,60),而model1输出(None,5,5,60),无法直接拼接。

可行解决方案

方案1:通过填充/上采样对齐特征图尺寸

在model2的最后添加一层,将4x4的特征图调整为5x5,匹配model1的尺寸,同时保留膨胀卷积的特性:

方法A:使用ZeroPadding2D填充

input_layer = Input(shape=(227,227,3))
model1 = Sequential([
    Conv2D(20, kernel_size=(5,5), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(30, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(40, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(50, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(60, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
])(input_layer)

model2 = Sequential([
    Conv2D(20, kernel_size=(5,5), activation='relu', dilation_rate=(3)),
    MaxPooling2D((2,2)),
    Conv2D(30, kernel_size=(3,3), activation='relu', dilation_rate=(2)),
    MaxPooling2D((2,2)),
    Conv2D(40, kernel_size=(3,3), activation='relu', dilation_rate=(2)),
    MaxPooling2D((2,2)),
    Conv2D(50, kernel_size=(3,3), activation='relu', dilation_rate=(1)),
    MaxPooling2D((2,2)),
    Conv2D(60, kernel_size=(3,3), activation='relu', dilation_rate=(1)),
    MaxPooling2D((2,2)),
    # 在底部和右侧各填充1个像素,将4x4转为5x5
    ZeroPadding2D(padding=((0,1), (0,1)))
])(input_layer)

# 拼接并构建完整模型
merged_model = Concatenate(axis=-1)([model1, model2])
merged_model = Flatten()(merged_model)
merged_model = Dense(1024, activation='relu')(merged_model)
merged_model = Dense(4, activation='softmax')(merged_model)

final_model = Model(inputs=input_layer, outputs=merged_model)
final_model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

方法B:使用Conv2DTranspose上采样

如果希望通过学习的方式调整尺寸,而非固定填充,可使用转置卷积:

# model1结构不变,修改model2的最后一层
model2 = Sequential([
    Conv2D(20, kernel_size=(5,5), activation='relu', dilation_rate=(3)),
    MaxPooling2D((2,2)),
    Conv2D(30, kernel_size=(3,3), activation='relu', dilation_rate=(2)),
    MaxPooling2D((2,2)),
    Conv2D(40, kernel_size=(3,3), activation='relu', dilation_rate=(2)),
    MaxPooling2D((2,2)),
    Conv2D(50, kernel_size=(3,3), activation='relu', dilation_rate=(1)),
    MaxPooling2D((2,2)),
    Conv2D(60, kernel_size=(3,3), activation='relu', dilation_rate=(1)),
    MaxPooling2D((2,2)),
    # 转置卷积将4x4转为5x5,保持通道数不变
    Conv2DTranspose(60, kernel_size=(2,2), strides=(1,1), padding='valid')
])(input_layer)

方案2:调整dilation_rate和padding,使输出尺寸原生匹配

通过计算特征图尺寸公式output_size = ((input_size - kernel_size - (kernel_size-1)*(dilation_rate-1)) // stride) + 1,反向推导调整model2的参数,让每一步输出尺寸和model1同步:

input_layer = Input(shape=(227,227,3))
model1 = Sequential([
    Conv2D(20, kernel_size=(5,5), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(30, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(40, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(50, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(60, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
])(input_layer)

# 修改dilation_rate并添加padding='same',确保每一步尺寸和model1一致
model2 = Sequential([
    Conv2D(20, kernel_size=(5,5), activation='relu', dilation_rate=(3), padding='same'),
    MaxPooling2D((2,2)),
    Conv2D(30, kernel_size=(3,3), activation='relu', dilation_rate=(2), padding='same'),
    MaxPooling2D((2,2)),
    Conv2D(40, kernel_size=(3,3), activation='relu', dilation_rate=(2), padding='same'),
    MaxPooling2D((2,2)),
    Conv2D(50, kernel_size=(3,3), activation='relu', dilation_rate=(1), padding='same'),
    MaxPooling2D((2,2)),
    Conv2D(60, kernel_size=(3,3), activation='relu', dilation_rate=(1)),
    MaxPooling2D((2,2)),
])(input_layer)

# 后续拼接和编译逻辑同方案1
merged_model = Concatenate(axis=-1)([model1, model2])
merged_model = Flatten()(merged_model)
merged_model = Dense(1024, activation='relu')(merged_model)
merged_model = Dense(4, activation='softmax')(merged_model)

final_model = Model(inputs=input_layer, outputs=merged_model)
final_model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

方案3:将两个分支的特征图先Flatten再拼接

如果不想调整空间尺寸,可分别将两个分支的特征图展平后再拼接,这种方式无需对齐空间维度:

input_layer = Input(shape=(227,227,3))
model1 = Sequential([
    Conv2D(20, kernel_size=(5,5), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(30, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(40, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(50, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Conv2D(60, kernel_size=(3,3), activation='relu'),
    MaxPooling2D((2,2)),
    Flatten()  # 先展平model1的输出
])(input_layer)

model2 = Sequential([
    Conv2D(20, kernel_size=(5,5), activation='relu', dilation_rate=(3)),
    MaxPooling2D((2,2)),
    Conv2D(30, kernel_size=(3,3), activation='relu', dilation_rate=(2)),
    MaxPooling2D((2,2)),
    Conv2D(40, kernel_size=(3,3), activation='relu', dilation_rate=(2)),
    MaxPooling2D((2,2)),
    Conv2D(50, kernel_size=(3,3), activation='relu', dilation_rate=(1)),
    MaxPooling2D((2,2)),
    Conv2D(60, kernel_size=(3,3), activation='relu', dilation_rate=(1)),
    MaxPooling2D((2,2)),
    Flatten()  # 先展平model2的输出
])(input_layer)

# 直接拼接展平后的向量
merged_model = Concatenate(axis=-1)([model1, model2])
merged_model = Dense(1024, activation='relu')(merged_model)
merged_model = Dense(4, activation='softmax')(merged_model)

final_model = Model(inputs=input_layer, outputs=merged_model)
final_model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

内容的提问来源于stack exchange,提问作者Shivendra Pratap Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:44:56