Keras Tuner贝叶斯优化CNN时出现Graph execution error报错
错误核心原因
你遇到的InvalidArgumentError: Graph execution error是多段代码逻辑冲突、维度不匹配共同导致的,具体问题点如下:
- 分类配置完全不匹配:你的标签是
num_classes=2的二分类独热编码(形状为(样本数, 2)),但模型最后一层写的是Dense(10, activation="softmax")输出10分类结果,同时损失函数用了二分类交叉熵binary_crossentropy,三者维度、任务逻辑完全冲突,是报错的核心诱因。 - 模型构建结构逻辑错误:你把
Flatten()、全连接层定义、model.compile()、return model全写在了卷积块的for循环内部。第一次循环执行完第一个卷积块后就会直接编译返回模型,num_blocks参数选2时根本不会构建第二层卷积;且如果循环执行第二次,第一次循环已经把特征图拉平为一维向量,无法输入到要求4维张量的Conv2D层,直接触发维度不匹配错误。 - 优化器分支逻辑无效:你定义了
hp_optimizer选择Adam/SGD的逻辑,但SGD分支下仅声明了momentum、nesterov参数,没有实际实例化SGD优化器,无论超参选什么,编译时永远固定用Adam优化器,SGD相关超参完全不生效。 - 数据生成器使用错误:你启用了
featurewise_center、featurewise_std_normalization按全局统计量归一化,但没有提前调用datagen.fit(X_trainRusReshaped)计算训练集的均值、标准差,归一化逻辑无法正常执行;同时用生成器训练时没有传入验证集,但调参目标设为了val_loss,框架找不到验证数据计算目标指标。 - 参数传递错误:用
datagen.flow()生成批次数据时,batch_size需要在flow()方法内指定,写在tuner.search()的参数里不会生效。
修复方案
按照以下步骤调整代码即可解决问题:
- 统一二分类配置:匹配现有2类独热标签,将输出层改为2单元+softmax激活,损失函数换成
categorical_crossentropy。 - 调整模型构建结构:把Flatten、全连接层、优化器选择、模型编译、返回逻辑全部移到卷积块for循环外部,保证所有卷积块构建完成后再接分类头。
- 修正优化器逻辑:根据超参选择实际实例化对应优化器对象,SGD要传入提前定义的动量、Nesterov参数。
- 修正数据生成器逻辑:调用flow前先在训练集上fit计算归一化统计量,分别构造训练、验证生成器并指定batch_size。
- 调参器初始化时加上
overwrite=True,清除之前错误运行产生的模型缓存,避免旧缓存干扰。
修正后的核心代码如下:
# 修正模型构建函数 def model_builder(hp): model = Sequential() model.add(Input(shape=(50,50,3))) # 循环内仅构建卷积块,不接分类头 for i in range(hp.Int('num_blocks', 1, 2)): hp_padding=hp.Choice(f'padding_{i}', values=['valid', 'same']) hp_filters=hp.Choice(f'filters_{i}', values=[32, 64]) model.add(Conv2D(hp_filters, (3, 3), padding=hp_padding, activation='relu', kernel_initializer='he_uniform')) model.add(MaxPooling2D((2, 2))) model.add(Dropout(hp.Choice(f'dropout_{i}', values=[0.0, 0.1, 0.2]))) # 卷积块构建完成后再接分类头 model.add(Flatten()) hp_units = hp.Int('units', min_value=25, max_value=150, step=25) model.add(Dense(hp_units, activation='relu', kernel_initializer='he_uniform')) model.add(Dense(2, activation="softmax")) # 匹配2分类输出 hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3]) hp_optimizer=hp.Choice('Optimizer', values=['Adam', 'SGD']) # 实际实例化选中的优化器 if hp_optimizer == 'Adam': optimizer = tf.keras.optimizers.Adam(learning_rate=hp_learning_rate) elif hp_optimizer == 'SGD': optimizer = tf.keras.optimizers.SGD(learning_rate=hp_learning_rate, momentum=0.9, nesterov=True) # 用匹配独热标签的多分类交叉熵损失 model.compile(loss=keras.losses.categorical_crossentropy, optimizer=optimizer, metrics=['accuracy']) return model # 修正数据生成器逻辑 datagen = ImageDataGenerator( featurewise_center=True, featurewise_std_normalization=True, rotation_range=180, horizontal_flip=True, vertical_flip = True ) # 先在训练集上计算归一化统计量 datagen.fit(X_trainRusReshaped) batch_size = 256 # 分别构造训练、验证生成器 train_gen = datagen.flow(X_trainRusReshaped, Y_trainRusHot, batch_size=batch_size) val_gen = datagen.flow(X_testRusReshaped, Y_testRusHot, batch_size=batch_size, shuffle=False) # 修正调参器初始化,加上overwrite清除旧缓存 tuner_cnn = kt.tuners.BayesianOptimization( model_builder, objective='val_loss', max_trials=100, directory='.', project_name='tuning-cnn', overwrite=True ) # 传入训练、验证生成器执行搜索 tuner_cnn.search(train_gen, epochs=50, validation_data=val_gen, callbacks=[stop_early])
如果要直接传入数组格式数据运行,把datagen的featurewise_center、featurewise_std_normalization参数关掉,手动将像素值除以255归一化到0-1区间,再传入训练、验证数组即可,不需要构造生成器。
内容的提问来源于stack exchange,提问作者user979974
相关产品推荐
相关产品推荐

