Keras中U-Net立体视觉模型形状不兼容问题求助
解决U-Net立体视觉任务中的形状不兼容问题
首先,你的报错核心原因是模型的输出尺寸和标签(视差图)的尺寸不匹配,这是因为你模型中的大部分卷积层没有设置padding='same',导致每次卷积都会缩小特征图的尺寸,后续上采样后无法还原到原始输入的尺寸,最终出现形状冲突。
问题分析
看你的模型结构:
- 多个
Convolution2D层(比如Convolution2D(64, (3, 3)))没有设置padding='same',使用默认的padding='valid',这会让特征图的宽高每次减少kernel_size - 1(比如3x3卷积会减少2)。 - 经过3次
MaxPooling2D下采样后,再通过UpSampling2D上采样,中间的卷积操作已经改变了特征图尺寸,最终输出的尺寸比原始输入的视差图尺寸更大,导致[1,370,1242,3] vs [1,368,1240,3]的冲突。
解决方案:保证卷积层的尺寸一致性
要让模型输出和输入视差图尺寸完全匹配,你需要给所有的卷积层(包括Convolution2D和Conv2DTranspose)添加padding='same',这样卷积操作不会改变特征图的宽高(除了池化和上采样的倍数变化)。
另外,标准U-Net是包含跳跃连接的(编码器层和解码器层的特征融合),你的Sequential模型没有实现这一点,虽然不是当前报错的原因,但会影响最终的视差图效果,我也会在修改后的代码里补充这部分。
修改后的完整代码
import pickle from keras.models import Model from keras.layers import Input, Convolution2D, MaxPooling2D, UpSampling2D, Conv2DTranspose, concatenate import numpy as np import cv2 # 加载数据 pkl_file = open('data.p', 'rb') data_dict = pickle.load(pkl_file) X_data = data_dict['images'] Y_data = data_dict['disparity'] data_num = len(X_data) train_num = int(data_num * 0.8) X_train = X_data[:train_num] X_test = X_data[train_num:] Y_train = Y_data[:train_num] Y_test = Y_data[train_num:] # 数据生成器 def gen(X, Y): while True: for x, y in zip(X, Y): yield x, y # 使用Functional API构建标准U-Net(带跳跃连接) inputs = Input(shape=(None, None, 6)) # 编码器部分 c1 = Convolution2D(64, (3, 3), activation='relu', padding='same')(inputs) c1 = Convolution2D(64, (3, 3), activation='relu', padding='same')(c1) p1 = MaxPooling2D(pool_size=(2, 2))(c1) c2 = Convolution2D(128, (3, 3), activation='relu', padding='same')(p1) c2 = Convolution2D(128, (3, 3), activation='relu', padding='same')(c2) p2 = MaxPooling2D(pool_size=(2, 2))(c2) c3 = Convolution2D(256, (3, 3), activation='relu', padding='same')(p2) c3 = Convolution2D(256, (3, 3), activation='relu', padding='same')(c3) p3 = MaxPooling2D(pool_size=(2, 2))(c3) # 瓶颈层 c4 = Convolution2D(512, (3, 3), activation='relu', padding='same')(p3) c4 = Convolution2D(512, (3, 3), activation='relu', padding='same')(c4) # 解码器部分(带跳跃连接) u5 = UpSampling2D(size=(2, 2))(c4) u5 = Conv2DTranspose(256, (3, 3), activation='relu', padding='same')(u5) concat5 = concatenate([c3, u5]) c5 = Convolution2D(256, (3, 3), activation='relu', padding='same')(concat5) c5 = Convolution2D(256, (3, 3), activation='relu', padding='same')(c5) u6 = UpSampling2D(size=(2, 2))(c5) u6 = Conv2DTranspose(128, (3, 3), activation='relu', padding='same')(u6) concat6 = concatenate([c2, u6]) c6 = Convolution2D(128, (3, 3), activation='relu', padding='same')(concat6) c6 = Convolution2D(128, (3, 3), activation='relu', padding='same')(c6) u7 = UpSampling2D(size=(2, 2))(c6) u7 = Conv2DTranspose(64, (3, 3), activation='relu', padding='same')(u7) concat7 = concatenate([c1, u7]) c7 = Convolution2D(64, (3, 3), activation='relu', padding='same')(concat7) c7 = Convolution2D(64, (3, 3), activation='relu', padding='same')(c7) # 输出层:视差图,这里假设你的视差图是3通道?如果是单通道可以改成1 outputs = Convolution2D(3, (3, 3), activation='relu', padding='same')(c7) model = Model(inputs=[inputs], outputs=[outputs]) model.compile(loss=['mse'], optimizer='adam', metrics=['accuracy']) # 训练模型 model.fit_generator(gen(X_train, Y_train), steps_per_epoch=len(X_train), epochs=5) scores = model.evaluate(X_test, Y_test, verbose=0)
关键修改点说明
- 所有卷积层添加
padding='same':确保每次卷积操作后特征图的宽高不变,只有池化和上采样会改变尺寸(倍数关系)。 - 使用Functional API构建带跳跃连接的U-Net:标准U-Net的跳跃连接能保留编码器的细节特征,大幅提升视差图的预测效果,这比Sequential模型更适合你的任务。
- 调整通道数匹配:根据你的任务需求调整输出层的通道数(如果视差图是单通道,把
Convolution2D(3, ...)改成Convolution2D(1, ...))。
额外注意事项
- 如果你的输入尺寸不是8的倍数,3次MaxPooling2D(2,2)后再上采样3次,最终输出尺寸会和输入尺寸完全一致(因为padding='same'保证了卷积不改变尺寸),所以不需要提前调整图片尺寸。
- 如果你坚持使用Sequential模型,只需要给每一个
Convolution2D和Conv2DTranspose添加padding='same'即可,但还是推荐使用带跳跃连接的U-Net结构。
内容的提问来源于stack exchange,提问作者zs123
相关产品推荐
相关产品推荐

