Tensorflow中CNN的MaxPooling1D与输入维度不兼容问题排查
我有形状为(10000 x 2 x 51 x 1)的2D信号数据,将其视为高为2、宽为51的图像构建CNN架构,代码如下:
input_layer = Input(shape=(x_train.shape[1], x_train.shape[2], x_train.shape[3])) x = Conv2D(filters=32, kernel_size=(2, 3))(input_layer) x = PReLU()(x) x = MaxPooling1D(pool_size=2)(x) x = Dropout(0.2)(x) x = Flatten()(x) x = Dense(units=256)(x) x = PReLU()(x) output_layer = Dense(units=1, activation='linear')(x)
执行时第一个MaxPooling1D层报错:
ValueError: Input 0 of layer "max_pooling1d" is incompatible with the layer: expected ndim=3, found ndim=4. Full shape received: (None, 1, 49, 32)
我认为第一次Conv2D操作后数据形状变为(1, 49, 32)属于1D数据,但不清楚形状表示哪里出错。改用MaxPooling2D时又出现错误:
ValueError: Exception encountered when calling layer "max_pooling2d" (type MaxPooling2D).
Negative dimension size caused by subtracting 2 from 1 for '{{node max_pooling2d/MaxPool}} = MaxPoolT=DT_FLOAT, data_format="NHWC", explicit_paddings=[], ksize=[1, 2, 2, 1], padding="VALID", strides=[1, 2, 2, 1]' with input shapes: [?,1,49,32].
问题根源
MaxPooling1D维度不匹配:Conv2D输出的是4维张量
(None, height, width, channels)(即(None,1,49,32)),而MaxPooling1D要求输入是3维张量(None, steps, features),两者维度结构完全不同,直接使用必然报错。你误以为(1,49,32)是1D数据,但它本质还是包含高度、宽度、通道的2D空间特征图结构。MaxPooling2D负维度错误:默认MaxPooling2D的池化窗口是
(2,2),但此时特征图的高度已经被Conv2D压缩到1,用(2,2)窗口做VALID padding时,计算后高度维度变为1-2=-1,出现非法负维度导致报错。
修正方案
方案一:适配特征图尺寸的MaxPooling2D
既然Conv2D后高度已为1,只需对宽度维度做池化,指定池化窗口为(1,2)即可:
input_layer = Input(shape=(x_train.shape[1], x_train.shape[2], x_train.shape[3])) x = Conv2D(filters=32, kernel_size=(2, 3))(input_layer) x = PReLU()(x) # 仅对宽度维度池化,避免高度维度报错 x = MaxPooling2D(pool_size=(1, 2))(x) x = Dropout(0.2)(x) x = Flatten()(x) x = Dense(units=256)(x) x = PReLU()(x) output_layer = Dense(units=1, activation='linear')(x)
方案二:转3维结构后用MaxPooling1D
先通过Reshape去掉高度维度,将4维张量转为MaxPooling1D要求的3维结构:
input_layer = Input(shape=(x_train.shape[1], x_train.shape[2], x_train.shape[3])) x = Conv2D(filters=32, kernel_size=(2, 3))(input_layer) x = PReLU()(x) # 把(None,1,49,32)转为(None,49,32),适配MaxPooling1D输入要求 x = Reshape((x.shape[2], x.shape[3]))(x) x = MaxPooling1D(pool_size=2)(x) x = Dropout(0.2)(x) x = Flatten()(x) x = Dense(units=256)(x) x = PReLU()(x) output_layer = Dense(units=1, activation='linear')(x)
方案三:调整初始Conv2D参数,保留高度维度
修改Conv2D的卷积核尺寸为(1,3),只在宽度方向卷积,保留高度维度为2,后续可正常使用MaxPooling2D:
input_layer = Input(shape=(x_train.shape[1], x_train.shape[2], x_train.shape[3])) # 用(1,3)卷积核,避免高度维度被压缩到1 x = Conv2D(filters=32, kernel_size=(1, 3))(input_layer) x = PReLU()(x) x = MaxPooling2D(pool_size=(2, 2))(x) x = Dropout(0.2)(x) x = Flatten()(x) x = Dense(units=256)(x) x = PReLU()(x) output_layer = Dense(units=1, activation='linear')(x)
内容的提问来源于stack exchange,提问作者justRandomLearner

