使用Keras计算ANN模型R²分数出现异常负值,求问题排查
需要对比随机森林与Keras构建的ANN模型的R²(决定系数)以判断预测精度,随机森林的R²已正常获取,但ANN模型计算出的R²数值异常:训练集-3170.0261、测试集-1360.6658。数据集为石油生产相关数据,共2421行、13列,当前实现代码如下:
# Initialize the constructor model = Sequential() # Specifying input layer model.add(Dense(2000, input_shape=(13,))) # Add an first hidden layer model.add(Dense(1500, activation='relu')) model.add(Dropout(0.2)) # second hidden layer model.add(Dense(1000, activation='relu')) # third hidden layer model.add(Dense(500, activation='relu')) # Add an output layer with one neuron and no activation specified model.add(Dense(1)) from keras import backend as K def r2_score(y_test, y_pred): SS_res = K.sum(K.square( y_test-y_pred )) SS_tot = K.sum(K.square( y_test - K.mean(y_test) ) ) return ( 1 - SS_res/(SS_tot + K.epsilon()) ) model.compile(loss='mean_squared_error', optimizer='adam' , metrics = ['mae', 'mse', r2_score]) epochs = 50 history = model.fit(X_train, y_train, epochs=epochs, batch_size=200, validation_split=0.3) pred_train= model.predict(X_train) print(K.get_value(r2_score(K.constant(y_train),pred_train))) pred= model.predict(X_test) print(K.get_value(r2_score(K.constant(y_test),pred)))
核心错误点
数据未做标准化/归一化
ANN对输入数据的尺度极度敏感,石油生产数据的特征(如产量、压力等)和目标值可能存在量级差异,未做预处理会导致模型无法有效学习特征与目标的关联,预测结果偏差极大,最终R²为负。模型结构过于臃肿
输入仅13个特征,却设置了2000、1500、1000、500这样超大规模的隐藏层,不仅会引发严重过拟合,还可能导致梯度消失/爆炸,模型根本无法收敛到有效权重。R²计算的维度不匹配
model.predict()返回的是二维数组(形状为(样本数,1)),而y_train/y_test大概率是一维数组,直接代入自定义的r2_score函数会导致平方和计算逻辑错误,结果失真。训练参数不合理
50轮epoch可能不足以让模型收敛,且batch_size设置为200(占训练集近1/10),会降低模型的学习效率,加上无数据预处理,模型无法学到有效模式。
修正方案
1. 数据标准化处理
使用StandardScaler对特征和目标值做标准化,消除尺度差异:
from sklearn.preprocessing import StandardScaler # 标准化特征 scaler_X = StandardScaler() X_train_scaled = scaler_X.fit_transform(X_train) X_test_scaled = scaler_X.transform(X_test) # 标准化目标值(若目标值量级大) scaler_y = StandardScaler() y_train_scaled = scaler_y.fit_transform(y_train.values.reshape(-1, 1)) y_test_scaled = scaler_y.transform(y_test.values.reshape(-1, 1))
2. 简化模型结构
根据输入特征数量调整隐藏层规模,匹配数据复杂度:
model = Sequential() model.add(Dense(128, input_shape=(13,), activation='relu')) model.add(Dropout(0.2)) model.add(Dense(64, activation='relu')) model.add(Dense(32, activation='relu')) model.add(Dense(1))
3. 修正R²计算逻辑
直接使用sklearn的r2_score函数,避免Keras后端的维度问题,同时注意对预测结果做反变换:
from sklearn.metrics import r2_score # 训练集预测与R²计算 pred_train_scaled = model.predict(X_train_scaled) pred_train = scaler_y.inverse_transform(pred_train_scaled) print("训练集R²:", r2_score(y_train, pred_train.flatten())) # 测试集预测与R²计算 pred_test_scaled = model.predict(X_test_scaled) pred_test = scaler_y.inverse_transform(pred_test_scaled) print("测试集R²:", r2_score(y_test, pred_test.flatten()))
4. 调整训练参数
适当增加epoch数,缩小batch_size,提升模型收敛效果:
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['mae', 'mse']) history = model.fit(X_train_scaled, y_train_scaled, epochs=100, batch_size=64, validation_split=0.3)
内容的提问来源于stack exchange,提问作者Joy Eromonsele

