非正方形栅格数组作为CNN输入的问题及报错解决求助
我有6个单波段滑坡参数栅格,转换为numpy数组后堆叠成形状为(1612, 2885, 6)的数组,同时有1个形状为(1612, 2885, 1)的滑坡编目栅格作为目标数据。
因为数组是非正方形的,我在定义CNN输入形状时很困惑——是否需要把数组调整为更小的正方形?尝试直接用数组尺寸定义输入形状时,一直出现以下报错:
ValueError: Input 0 of layer "conv2d" is incompatible with the layer:
expected min_ndim=4, found ndim=3. Full shape received: (None, 2885, 6)
已经被这个问题困扰一个月,求解决方案,同时希望获取输入形状相关的学习建议,方便以后解决类似问题。
我的代码
读取栅格并转为数组
#Reading raster stack Elevation = gdal.Open(r"C:/Users/Neo/NEO/Belajar Python/S3/Data jadi/Resampled data/Elevation.tif") Wetness= gdal.Open(r"C:/Users/Neo/NEO/Belajar Python/S3/Data jadi/Resampled data/Wetness.tif") Slope= gdal.Open(r"C:/Users/Neo/NEO/Belajar Python/S3/Data jadi/Resampled data/Slope.tif") Distanceroad= gdal.Open(r"C:/Users/Neo/NEO/Belajar Python/S3/Data jadi/Resampled data/DistanceRoad.tif") Distanceriver= gdal.Open(r"C:/Users/Neo/NEO/Belajar Python/S3/Data jadi/Resampled data/DistanceRiver.tif") Aspect= gdal.Open(r"C:/Users/Neo/NEO/Belajar Python/S3/Data jadi/Resampled data/Aspect.tif") Target= gdal.Open(r"C:/Users/Neo/NEO/Belajar Python/S3/Data jadi/Resampled data/inventory.tif") #Read raster as array ElevationArray = np.array(Elevation.GetRasterBand(1).ReadAsArray()) WetnessArray = np.array(Wetness.GetRasterBand(1).ReadAsArray()) SlopeArray = np.array(Slope.GetRasterBand(1).ReadAsArray()) DistanceRoadArray = np.array(Distanceroad.GetRasterBand(1).ReadAsArray()) DistanceRiverArray = np.array(Distanceriver.GetRasterBand(1).ReadAsArray()) AspectArray = np.array(Aspect.GetRasterBand(1).ReadAsArray()) TargetArray = np.array(Target.GetRasterBand(1).ReadAsArray()) #Array stacking Dataarray = np.dstack((ElevationArray,WetnessArray,SlopeArray,DistanceRoadArray,DistanceRiverArray,AspectArray)) print(Dataarray.shape)
输出:(1612, 2885, 6)
划分训练测试集
#Target and factor array row = Dataarray.shape[0] col = Dataarray.shape[1] bands = Dataarray.shape[2] X_data = Dataarray y_data = TargetArray #data split into X and Y X_train, X_test, y_train, y_test = train_test_split(X_data, y_data, test_size=0.3, random_state=42) print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)
输出:(1128, 2885, 6) (484, 2885, 6) (1128, 2885) (484, 2885)
CNN模型定义
# CNN model # inputshape = (row, col, bands) model = Sequential() model.add(Conv2D(6, (3,3), activation='relu', input_shape=inputshape[1:])) model.add(MaxPool2D(2)) model.add(Flatten()) model.add(Dense(50, activation='sigmoid')) model.add(Dense(15, activation='sigmoid')) model.add(Dense(1, activation='softmax')) optimizer = optimizers.Adam(lr=0.01) model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy']) def lr_scheduler(epoch): if epoch % 100 == 0: K.set_value(optimizer.lr, K.eval(optimizer.lr) * 0.1) return K.eval(optimizer.lr) reduce_lr = LearningRateScheduler(lr_scheduler) EarlyStop = EarlyStopping(monitor='val_loss', patience=200, verbose=0, mode='min') modelcheck = ModelCheckpoint('CNN_model.h5', monitor='val_acc', verbose=1, save_best_only=True, mode='max') callable = [modelcheck, reduce_lr, EarlyStop] model.fit(X_train, y_train, batch_size=100, callbacks=callable, verbose=2, epochs=10) model.save('CNN_model.h5') print(model.summary())
运行后报错:
ValueError: Input 0 of layer "conv2d" is incompatible with the layer: expected min_ndim=4, found ndim=3. Full shape received: (None, 2885, 6)
1. 核心问题:输入维度与样本逻辑错误
Keras的Conv2D层要求输入是4维张量:(样本数, 高度, 宽度, 通道数),但你的数据处理完全搞错了样本定义:
- 你用
train_test_split直接分割第一维度,把栅格的行(高度)当成了样本数,导致输入变成(1128, 2885, 6)——这是3维数据,缺少了CNN要求的样本维度,同时原本的高度维度被错误识别为样本数,最终引发维度不匹配报错。
正确的数据处理方式
遥感栅格用CNN训练,必须把局部像素窗口作为样本,步骤如下:
- 用滑动窗口从大栅格中截取固定大小的小块(比如
32x32或64x64,长方形也可以,无需强制正方形),每个小块对应一个样本,窗口中心的标签作为该样本的目标值。 - 可以用
skimage.util.view_as_windows实现滑动窗口切割,既符合CNN局部特征学习的逻辑,也能避免大尺寸栅格带来的内存溢出问题。
快速验证模型的临时修复
如果只是想验证模型逻辑,可临时给数据增加样本维度(仅适合小尺寸栅格,你的大尺寸图可能会内存溢出):
# 给X增加样本维度 X_train = X_train[np.newaxis, ...] X_test = X_test[np.newaxis, ...] # 给y增加样本和通道维度(适配像素级分类) y_train = y_train[np.newaxis, ..., np.newaxis] y_test = y_test[np.newaxis, ..., np.newaxis]
2. 模型定义的其他修正点
input_shape参数应设为(row, col, bands),你之前取inputshape[1:]导致缺失高度维度,必须保证输入形状与数据的单样本维度一致。- 损失函数与激活层不匹配:滑坡编目通常是二分类任务,应将最后一层改为
sigmoid激活,损失函数用binary_crossentropy;如果是多分类且标签未做独热编码,用sparse_categorical_crossentropy替代categorical_crossentropy。
- 牢记CNN输入维度规则:TensorFlow后端的2D卷积层输入必须是4维,顺序为
(batch_size, height, width, channels),PyTorch则是(batch_size, channels, height, width),注意区分框架差异。 - 掌握栅格样本切割逻辑:遥感栅格几乎都用滑动窗口切割小样本训练,这是解决大尺寸数据内存问题和适配CNN局部特征学习的核心方法。
- 模型与任务匹配:根据任务类型(二分类/多分类/语义分割)选择对应的激活层和损失函数,不要随意混用。
- 无需强制正方形输入:CNN支持任意长方形输入,只要卷积核尺寸能适配即可(比如输入宽度2885用3x3卷积核,步长1时输出宽度为2885-3+1=2883,完全合法)。
内容的提问来源于stack exchange,提问作者Neo1990

