Python神经网络预测质量量化求助:R²值异常排查与评估方法
解决MLP回归模型R²为负的问题及优化评估方案
首先得纠正一个常见误区:R²值并非只能限制在-1到1之间。它的计算公式是 $R^2 = 1 - \frac{SS_{res}}{SS_{tot}}$,其中$SS_{res}$是预测值与真实值的残差平方和,$SS_{tot}$是真实值与均值的平方和。如果你的模型预测效果比直接用训练集y的均值还差(也就是$SS_{res} > SS_{tot}$),R²就会变成负数,甚至远小于-1——这正是你当前遇到的情况。
接下来我们一步步排查问题,并给出更全面的模型评估方案:
一、排查代码中的潜在问题
1. 缺失值处理不彻底
你用df.dropna(how='all')只删除了全为空值的行,但单个特征或目标列的空值依然存在。MLPRegressor无法处理缺失值,这会直接导致模型训练异常、预测效果极差。建议修改为:
# 删除任何包含空值的行 df2 = df.dropna()
之后可以再验证数据是否还有空值:
print(df2.isnull().sum())
2. 未对目标变量y做标准化
神经网络对输入输出的数值范围非常敏感。你仅标准化了特征X,但如果Fuel Rate的数值范围较大,模型输出层很难拟合这么大的数值,直接导致残差平方和暴增。建议对y也做标准化处理:
from sklearn.preprocessing import StandardScaler # 初始化y的标准化器 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)) # 用标准化后的y训练模型 rna.fit(X_train, y_train_scaled.ravel()) # 预测后反标准化回原始尺度 y_predicted_scaled = rna.predict(X_test) y_predicted = scaler_y.inverse_transform(y_predicted_scaled.reshape(-1, 1))
3. MLP模型参数可能不合理
- 迭代次数不足:
max_iter=2000不一定能让模型收敛,你可以查看损失曲线判断:
如果曲线还在持续下降,说明需要增加plt.plot(rna.loss_curve_) plt.title('MLP Training Loss Curve') plt.xlabel('Iterations') plt.ylabel('Loss') plt.show()max_iter(比如调到5000)或调大学习率learning_rate_init(默认0.001,可尝试0.01)。 - 网络结构不合适:
hidden_layer_sizes=(13,13,13)可能过于复杂或简单。如果特征数量少,三层13神经元可能过拟合;如果特征维度高,又可能欠拟合。可以先尝试简化结构(比如(6,6))再调整。 - 随机初始化影响:MLP权重是随机初始化的,单次训练可能得到极差结果。建议设置固定随机种子保证可复现,或多次训练取平均:
rna = MLPRegressor(hidden_layer_sizes=(13,13,13), max_iter=2000, random_state=42)
4. 数据相关性与划分问题
- 检查特征与
Fuel Rate的相关性:如果特征和目标完全无关,模型自然学不到有效规律。可以用热力图查看:import seaborn as sns corr = df2.corr() sns.heatmap(corr, annot=True, cmap='coolwarm') plt.show() - 验证训练集和测试集的分布一致性:对比两者的特征均值、方差,避免划分后数据分布差异过大。
二、更全面的模型评估方案
除了R²,建议结合以下方法全面评估模型性能:
1. 基础回归指标
同时计算MSE、RMSE、MAE,这些指标更直观反映预测误差的大小:
from sklearn.metrics import mean_squared_error, mean_absolute_error mse = mean_squared_error(y_test, y_predicted) rmse = np.sqrt(mse) mae = mean_absolute_error(y_test, y_predicted) r2 = r2_score(y_test, y_predicted) print(f"MSE: {mse:.4f}") print(f"RMSE: {rmse:.4f}") print(f"MAE: {mae:.4f}") print(f"R²: {r2:.4f}")
2. 可视化评估
- 真实值vs预测值散点图:直观查看拟合程度,理想情况下点应集中在y=x直线附近:
plt.scatter(y_test, y_predicted) plt.plot([y.min(), y.max()], [y.min(), y.max()], 'r--') # y=x参考线 plt.xlabel('True Fuel Rate') plt.ylabel('Predicted Fuel Rate') plt.title('True vs Predicted Values') plt.show() - 残差分布图:残差(真实值-预测值)应随机分布在0附近,无明显趋势,否则说明模型存在偏差:
residuals = y_test - y_predicted.ravel() plt.scatter(y_predicted, residuals) plt.axhline(y=0, color='r', linestyle='--') plt.xlabel('Predicted Fuel Rate') plt.ylabel('Residuals') plt.title('Residual Plot') plt.show()
3. 交叉验证
用k折交叉验证替代单次训练测试划分,避免数据划分的偶然性,结果更可靠:
from sklearn.model_selection import cross_val_score from sklearn.pipeline import Pipeline # 用Pipeline整合预处理和模型 pipeline = Pipeline([ ('scaler_x', StandardScaler()), ('scaler_y', StandardScaler()), ('mlp', MLPRegressor(hidden_layer_sizes=(13,13,13), max_iter=2000, random_state=42)) ]) # 交叉验证,用负MSE作为评分指标(cross_val_score默认越高越好) scores = cross_val_score(pipeline, X, y, cv=5, scoring='neg_mean_squared_error') rmse_scores = np.sqrt(-scores) print(f"Cross-validated RMSE scores: {rmse_scores}") print(f"Mean RMSE: {rmse_scores.mean():.4f}")
4. 超参数调优
用网格搜索优化MLP的参数,找到最优的网络结构和训练配置:
from sklearn.model_selection import GridSearchCV param_grid = { 'mlp__hidden_layer_sizes': [(6,), (6,6), (13,13)], 'mlp__activation': ['relu', 'tanh'], 'mlp__learning_rate_init': [0.001, 0.01], 'mlp__max_iter': [2000, 5000] } grid_search = GridSearchCV(pipeline, param_grid, cv=5, scoring='neg_mean_squared_error') grid_search.fit(X, y) print(f"Best parameters: {grid_search.best_params_}") print(f"Best cross-validated RMSE: {np.sqrt(-grid_search.best_score_):.4f}")
内容的提问来源于stack exchange,提问作者Moacir Andretti
相关产品推荐
相关产品推荐

