使用GridSearchCV调Keras模型时保存模型报错,需添加.keras扩展名
问题解决:GridSearchCV配合KerasClassifier时的
.keras扩展名报错 问题场景
使用GridSearchCV自动化搜索Keras模型超参数,执行grid_result.fit(X_train, y_train, verbose=0)时触发报错,提示需添加.keras扩展名,但找不到类内的文件路径设置项。
报错原因
TensorFlow/Keras新版本(2.10+)默认采用.keras作为模型保存格式,而Scikeras的KerasClassifier在交叉验证过程中会自动临时保存模型,默认配置未指定新格式,与新版本Keras的保存要求冲突。
解决步骤
- 初始化KerasClassifier时指定保存格式:通过
model__save_format='keras'参数,告知Scikeras使用.keras格式保存临时模型; - 修复模型创建函数的全局变量依赖:原
create_model函数依赖全局X_train,改为动态传入输入维度参数,避免跨作用域问题; - 调整最佳模型的保存方式:改用Keras推荐的
.keras格式保存最终模型; - 补充模型编译步骤:原代码遗漏模型编译,GridSearchCV无法正常训练模型。
修改后的完整代码
from sklearn.metrics import make_scorer from sklearn.metrics import accuracy_score, precision_score, recall_score from sklearn.model_selection import RandomizedSearchCV import librosa import pandas as pd import numpy as np import tensorflow as tf import matplotlib.pyplot as plt from scikeras.wrappers import KerasClassifier from sklearn.model_selection import GridSearchCV import os import dill as pickle import csv import joblib # Preprocessing from keras.layers import BatchNormalization from keras import backend as K from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder, StandardScaler # Keras from keras import models from keras import layers import keras def generate_dataset(): # 生成数据集 header = 'filename chroma_stft rmse spectral_centroid spectral_bandwidth rolloff zero_crossing_rate' for i in range(1, 21): header += f' mfcc{i}' header += ' label' header = header.split() file = open('data.csv', 'w', newline='') with file: writer = csv.writer(file) writer.writerow(header) # 修改标签类别 types = ['ukrainian', 'other'] for t in types: for filename in os.listdir(f'./music/{t}/'): songname = f'./music/{t}/{filename}' y, sr = librosa.load(songname, mono=True) chroma_stft = librosa.feature.chroma_stft(y=y, sr=sr) rmse=librosa.feature.rms(y=y)[0] spec_cent = librosa.feature.spectral_centroid(y=y, sr=sr) spec_bw = librosa.feature.spectral_bandwidth(y=y, sr=sr) rolloff = librosa.feature.spectral_rolloff(y=y, sr=sr) zcr = librosa.feature.zero_crossing_rate(y) mfcc = librosa.feature.mfcc(y=y, sr=sr) to_append = f'{filename} {np.mean(chroma_stft)} {np.mean(rmse)} {np.mean(spec_cent)} {np.mean(spec_bw)} {np.mean(rolloff)} {np.mean(zcr)}' for e in mfcc: to_append += f' {np.mean(e)}' # 为每行添加标签 to_append += f' {t}' file = open('data.csv', 'a', newline='', encoding='UTF-8') with file: writer = csv.writer(file) writer.writerow(to_append.split()) return pd.read_csv('data.csv', encoding='latin-1', on_bad_lines='skip') def open_dataset(): # 从CSV读取数据集 data = pd.read_csv('data.csv', encoding='latin-1', on_bad_lines='skip') # 删除不必要的列 data = data.drop(['filename'], axis=1) return data def create_model(input_dim, optimizer='adam', activation='relu'): # 动态传入输入维度,避免依赖全局变量 model = tf.keras.models.Sequential() model.add(layers.Dense(1024, activation, input_shape=(input_dim,))) model.add(BatchNormalization()) model.add(layers.Dense(512, activation)) model.add(BatchNormalization()) model.add(layers.Dense(256, activation)) model.add(BatchNormalization()) model.add(layers.Dense(32, activation)) model.add(BatchNormalization()) # 根据标签数量调整输出单元 model.add(layers.Dense(2, activation='sigmoid')) # 编译模型,GridSearchCV需要模型已编译 model.compile(optimizer=optimizer, loss='sparse_categorical_crossentropy', metrics=['accuracy']) return model # 加载并预处理数据 data = pd.read_csv('data.csv', encoding='latin-1', on_bad_lines='skip') # 删除不必要的列 data = data.drop(['filename'], axis=1) # 编码标签 encoder = LabelEncoder() y = encoder.fit_transform(data['label']) # 标准化特征 scaler = StandardScaler() X = scaler.fit_transform(np.array(data.iloc[:, :-1], dtype=float)) # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) # 初始化KerasClassifier,指定模型保存格式为keras model = KerasClassifier( build_fn=create_model, input_dim=X_train.shape[1], # 传入输入维度参数 model__save_format='keras' # 解决扩展名报错的关键参数 ) param_grid = { 'epochs':[10, 50, 100], 'batch_size': [32, 64, 128], 'activation': ['relu', 'softmax', 'sigmoid'], 'optimizer': ['adam', 'adamw', 'rmsprop'] } grid_result = GridSearchCV(estimator=model, param_grid=param_grid) grid_result.fit(X_train, y_train, verbose=0) best_model = grid_result.best_estimator_ # 用keras格式保存最佳模型 best_model.model.save('best_model.keras') print("最佳得分: %f,使用参数: %s" % (grid_result.best_score_, grid_result.best_params_))
关键修改说明
- 在
KerasClassifier初始化时添加model__save_format='keras',强制Scikeras使用新的模型保存格式,解决扩展名报错; - 重构
create_model函数,新增input_dim参数,替换原全局变量X_train.shape[1],提升代码健壮性; - 为
create_model添加model.compile()步骤,补全模型训练必要流程; - 将模型保存格式从
.h5改为.keras,符合Keras新版本的推荐规范。
内容的提问来源于stack exchange,提问作者Bird
相关产品推荐
相关产品推荐

