将OpenCV加载的视频帧输入CNN时出现负维度错误求助
解决3D CNN训练时的负维度错误问题
这个错误的核心原因是:某一层Conv3D的卷积核尺寸大于输入特征图的对应维度,导致计算后维度变为负数(或0)。我们可以通过以下步骤定位并解决问题:
第一步:定位问题层
先打印模型的完整结构和每一层的输出形状,这样能直观看到哪一步维度被压缩到过小:
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv3D, MaxPooling3D, Flatten, Dense, Dropout model = Sequential() model.add(Conv3D(32, (3,3,3), activation='relu', input_shape=(1, 40, 40, 15), data_format='channels_first')) model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Conv3D(64, (3,3,3), activation='relu')) model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Conv3D(128, (3,3,3), activation='relu')) model.add(Conv3D(128, (3,3,3), activation='relu')) model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Conv3D(256, (2,2,2), activation='relu')) model.add(Conv3D(256, (2,2,2), activation='relu')) # 这大概率是出问题的层 model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Flatten()) model.add(Dense(1024)) model.add(Dropout(0.5)) model.add(Dense(1024)) model.add(Dropout(0.5)) model.add(Dense(4, activation='softmax')) # 打印模型结构和各层输出形状 model.summary()
运行后你会发现,经过前面的卷积和池化,某一个空间维度会被压缩到1,此时再用(2,2,2)的卷积核计算,就会出现1-2+1=0的无效维度,触发错误。
第二步:选择修复方案
根据你的需求,以下几种方案都可以解决问题:
方案1:给Conv3D层添加padding
设置padding='same'可以让卷积后特征图的维度保持和输入一致,避免维度过度缩小:
model = Sequential() model.add(Conv3D(32, (3,3,3), activation='relu', input_shape=(1, 40, 40, 15), data_format='channels_first', padding='same')) model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Conv3D(64, (3,3,3), activation='relu', padding='same')) model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Conv3D(128, (3,3,3), activation='relu', padding='same')) model.add(Conv3D(128, (3,3,3), activation='relu', padding='same')) model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Conv3D(256, (2,2,2), activation='relu', padding='same')) model.add(Conv3D(256, (2,2,2), activation='relu', padding='same')) model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Flatten()) model.add(Dense(1024)) model.add(Dropout(0.5)) model.add(Dense(1024)) model.add(Dropout(0.5)) model.add(Dense(4, activation='softmax'))
方案2:调整卷积核尺寸
如果不想用padding,可以针对缩小到1的维度,把卷积核对应位置的尺寸改成1,比如你发现深度维度变成了1,就把卷积核改成(1,2,2):
# 修改出问题的Conv3D层 model.add(Conv3D(256, (1,2,2), activation='relu')) model.add(Conv3D(256, (1,2,2), activation='relu'))
方案3:减少池化操作
可以去掉最后一个MaxPooling3D层,或者调整池化的步长和尺寸,避免维度被过度压缩:
model.add(Conv3D(256, (2,2,2), activation='relu')) model.add(Conv3D(256, (2,2,2), activation='relu')) # 注释掉最后一个池化层,避免维度进一步缩小 # model.add(MaxPooling3D(pool_size=(1, 2, 2), strides=(1, 2, 2))) model.add(Flatten())
方案4:调整输入维度
如果数据允许,可以把输入的帧尺寸从40x40改成更大的(比如64x64),或者把时间维度从15增加到30,给卷积和池化留下足够的维度空间。
内容的提问来源于stack exchange,提问作者user9165727
相关产品推荐
相关产品推荐

