卫星图像水域提取CNN模型model.fit输入形状不兼容错误求助
问题根源分析
- 输入数据与模型需求完全不匹配:你定义的卷积模型要求输入是
(224,224,3)的RGB图像,但代码里用随机生成的arr = np.random.rand(626,3),取x = arr[:,1:]后,x的形状是(样本数,2),和模型要求的4维输入(批量+高+宽+通道)完全不符。 - 图像数据处理逻辑失效:你把卫星图像转灰度后扁平化存到CSV,但后续根本没读取真实图像数据,反而用随机数替代,等于模型在学习随机噪音。
- 任务与模型不匹配:水域提取是语义分割任务,你用的是图像分类的VGG结构(最后输出1000类softmax),完全不适用——分割需要输出和输入尺寸一致的掩码,而非单类别标签。
- 代码结构混乱:循环内重复导入库、逻辑嵌套错误,导致数据处理完全走偏。
分步修正方案
1. 重新整理数据加载逻辑(正确读取卫星图像)
放弃存CSV的方式,直接加载图像并预处理成模型需要的格式:
import os import numpy as np from PIL import Image from sklearn.model_selection import train_test_split # 加载图像和对应的掩码(假设水域掩码与图像同名,存放在mask文件夹下) def load_data(img_dir, mask_dir, img_size=(224,224)): imgs = [] masks = [] for img_name in os.listdir(img_dir): if img_name.endswith('.jpg') or img_name.endswith('.png'): # 加载RGB图像并归一化 img_path = os.path.join(img_dir, img_name) img = Image.open(img_path).resize(img_size).convert('RGB') imgs.append(np.array(img)/255.0) # 加载对应二值掩码(水域为1,背景为0) mask_path = os.path.join(mask_dir, img_name.replace('.jpg', '.png')) mask = Image.open(mask_path).resize(img_size).convert('L') masks.append(np.array(mask)/255.0) return np.array(imgs), np.array(masks) # 替换为你的图像和掩码路径 X, y = load_data('FFOutput', 'MaskOutput') # 划分数据集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=200) X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=1) # 为掩码添加通道维度(分割模型要求输入输出维度一致) y_train = np.expand_dims(y_train, axis=-1) y_val = np.expand_dims(y_val, axis=-1) y_test = np.expand_dims(y_test, axis=-1)
2. 替换为语义分割模型(U-Net是水域提取的经典选择)
放弃分类用的VGG结构,改用适合分割的U-Net:
from tensorflow.keras.models import Model from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D, concatenate def build_unet(input_size=(224,224,3)): inputs = Input(input_size) # 下采样路径 c1 = Conv2D(64, (3,3), activation='relu', padding='same')(inputs) c1 = Conv2D(64, (3,3), activation='relu', padding='same')(c1) p1 = MaxPooling2D((2,2))(c1) c2 = Conv2D(128, (3,3), activation='relu', padding='same')(p1) c2 = Conv2D(128, (3,3), activation='relu', padding='same')(c2) p2 = MaxPooling2D((2,2))(c2) c3 = Conv2D(256, (3,3), activation='relu', padding='same')(p2) c3 = Conv2D(256, (3,3), activation='relu', padding='same')(c3) p3 = MaxPooling2D((2,2))(c3) # 瓶颈层 c4 = Conv2D(512, (3,3), activation='relu', padding='same')(p3) c4 = Conv2D(512, (3,3), activation='relu', padding='same')(c4) # 上采样路径(跳跃连接) u5 = UpSampling2D((2,2))(c4) u5 = concatenate([u5, c3]) c5 = Conv2D(256, (3,3), activation='relu', padding='same')(u5) c5 = Conv2D(256, (3,3), activation='relu', padding='same')(c5) u6 = UpSampling2D((2,2))(c5) u6 = concatenate([u6, c2]) c6 = Conv2D(128, (3,3), activation='relu', padding='same')(u6) c6 = Conv2D(128, (3,3), activation='relu', padding='same')(c6) u7 = UpSampling2D((2,2))(c6) u7 = concatenate([u7, c1]) c7 = Conv2D(64, (3,3), activation='relu', padding='same')(u7) c7 = Conv2D(64, (3,3), activation='relu', padding='same')(c7) # 输出层:单通道sigmoid激活(二分类:水域/非水域) outputs = Conv2D(1, (1,1), activation='sigmoid')(c7) model = Model(inputs=[inputs], outputs=[outputs]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) return model # 构建并训练模型 model = build_unet() model.summary() history = model.fit(X_train, y_train, epochs=20, batch_size=16, validation_data=(X_val, y_val)) # 评估模型 score = model.evaluate(X_test, y_test) print(f"测试集损失:{score[0]}, 测试集准确率:{score[1]}")
3. 关键注意事项
- 掩码准备:必须为每张卫星图像制作对应的二值掩码(水域标注为1,其他区域为0),这是语义分割的核心前提。
- 显存适配:U-Net显存占用较高,需根据你的GPU内存调整batch_size(建议8-16,不要用160这类超大值)。
- 损失函数:二分类分割用
binary_crossentropy,而非分类任务的categorical_crossentropy。
内容的提问来源于stack exchange,提问作者Suhail
相关产品推荐
相关产品推荐

