本节介绍:九种回归模型的多指标性能比较,以及极坐标柱状图与雷达图的组合绘制。本文参考论文 Figure 4 的 I–P 面板,根据原图结构和个人对模型评价流程的理解,补全模拟数据生成、数据划分、模型训练、指标计算和图形输出过程。
数据采用模拟数据无任何现实意义,作者根据个人对机器学习和科研绘图的理解进行代码实现与图表输出,仅供参考。正式用于论文时,应替换为真实实验数据、实际模型参数和经过验证的评价结果。
论文研究围绕金属有机框架复合材料去除持久性有机污染物展开,并比较了 SVM-Linear、GRU、KNN、LSTM、MLP-NN、MLR、RF、XGBoost 和 XGBoost-CMAES 共九种模型。
Figure 4 使用 R2、RMSE、MAPE 和 MSE 四项指标评价模型表现。其中,R2 越接近 1,说明预测结果对目标变量变化的解释能力越强;RMSE、MAPE 和 MSE 属于误差指标,通常越小越好。只看单一指标可能忽略模型在不同误差口径下的差异,因此原图把四项指标分别绘制,并进一步比较训练集、验证集和测试集的结果。
原图上排 I–L 为测试集指标。每个模型对应一个极坐标柱体,柱体长度表示指标大小,颜色渐变用于增强模型之间的视觉区分。需要注意,R2 与三项误差指标的判断方向相反:R2 较高通常更好,而误差柱越短通常越理想。
下排 M–P 为雷达图。每个顶点对应一种模型,Training、Validation 和 Testing 三条多边形分别表示训练集、验证集和测试集表现。与普通指标表相比,这种组合图可以同时观察模型排序、误差大小以及不同数据集之间的稳定性,更适合放在论文的模型评价、预测性能验证或模型筛选部分。
下面代码使用模拟数据进行流程演示,主要用于说明数据结构、模型训练和绘图逻辑。正式用于论文时,应替换为真实数据和模型计算结果。
论文描述的数据库包含 667 组记录。复刻代码按照论文给出的污染物类别及样本数量构造类别列,并生成比表面积、颗粒尺度、孔径、反应时间、pH、温度、初始浓度、LogKow 等 14 个数值变量。加入污染物类别和回归目标后,最终得到 667×16 的演示数据表。
# 注意:以下代码仅用于生成演示数据,不代表真实实验结果RANDOM_SEED = 42N_SAMPLES = 667rng = np.random.default_rng(RANDOM_SEED)pollutant_counts = { 'p-Nitroaniline': 62, 'Bisphenol A': 184, 'Phenol': 28, '2,4-D': 220, 'Alachlor': 30, 'Chipton': 30, 'Bisphenol S': 38, 'MCPP': 12, 'Bisphenol F': 21, 'Bisphenol AF': 21, 'Sulfamethoxazole': 8, 'DDT': 13,}pollutants = np.concatenate([ np.repeat(name, count) for name, count in pollutant_counts.items()])rng.shuffle(pollutants)surface_area = np.clip( rng.lognormal(np.log(700), 0.75, N_SAMPLES), 6, 3234)particle_scale = np.clip( rng.lognormal(np.log(2.0), 1.35, N_SAMPLES), 0.05, 2800)reaction_time = np.clip( rng.lognormal(np.log(2.0), 1.05, N_SAMPLES), 0.1, 168)initial_concentration = np.exp( rng.uniform(np.log(0.2), np.log(500), N_SAMPLES))
连续变量没有简单使用均匀随机数,而是根据论文描述设置偏态分布、取值范围和变量关联。例如,比表面积使用对数正态分布并限制在 6–3234 m2/g;初始浓度使用对数均匀分布覆盖 0.2–500 mg/L;反应时间则构造大量短时实验与少量长时实验并存的形态。
回归目标由饱和效应、非线性项和交互项共同生成,并加入适度异方差噪声。这样可以让线性模型、邻近模型、神经网络和树模型表现出可观察的差异。
# 非线性、饱和效应和交互项共同构造模拟目标signal = ( 65 + 115 * np.log1p(surface_area) / np.log1p(3234) + 1.25 * initial_concentration ** 0.78 + 70 * (1 - np.exp(-reaction_time / 18)) + 38 * np.exp(-((ph - 6.5) / 3.0) ** 2) + 0.030 * np.sqrt(surface_area * initial_concentration) * aromatic_rings + 0.55 * cc_ratio + 13 * log_kow)noise = rng.normal(0, 8 + 0.020 * signal, N_SAMPLES)removal_performance = np.clip(signal + noise, 25, None)
若替换为自己的数据,可以删除模拟数据生成部分,改为使用 `pd.read_excel()` 读取实际数据。随后更新 `FEATURE_NAMES` 和 `TARGET_NAME` 即可衔接后续训练流程
正式训练前,代码先绘制初始浓度与目标变量的散点关系,并用颜色表示比表面积;右侧直方图和核密度曲线用于检查目标分布。
fig, axes = plt.subplots(1, 2, figsize=(14, 5.2))points = axes[0].scatter( scatter_data['初始浓度'], scatter_data['去除表现'], c=scatter_data['比表面积'], cmap='viridis', s=34, alpha=0.72)axes[0].set_xscale('log')axes[0].set_xlabel('Initial concentration (mg/L)')axes[0].set_ylabel('Removal performance')sns.histplot( data=simulation_data, x='去除表现', bins=28, kde=True, color='#65B7C5', ax=axes[1])
论文采用两阶段数据划分:先将 80% 数据作为初始训练集、20% 作为测试集,再从初始训练集中划分 90% 用于训练、10% 用于验证。复刻代码沿用这一比例。
X = simulation_data[FEATURE_NAMES].copy()y = simulation_data[TARGET_NAME].copy()X_pretrain, X_test, y_pretrain, y_test = train_test_split( X, y, test_size=0.20, random_state=42)X_train, X_val, y_train, y_val = train_test_split( X_pretrain, y_pretrain, test_size=0.10, random_state=42)
在当前固定随机种子下,最终得到 479 个训练样本、54 个验证样本和 134 个测试样本。中位数插补器与标准化器只在训练集上拟合,再应用到验证集和测试集,从而减少数据泄漏。
def build_pipeline(estimator, scale=False, scale_target=False): if scale_target: estimator = TransformedTargetRegressor( regressor=estimator, transformer=StandardScaler() ) steps = [('imputer', SimpleImputer(strategy='median'))] if scale: steps.append(('scaler', StandardScaler())) steps.append(('model', estimator)) return Pipeline(steps)
主图绘制前,代码额外输出两张训练结果图。第一张比较 XGBoost-CMAES 在训练集、验证集和测试集上的真实值与预测值;虚线表示理想预测位置,散点越接近虚线,说明预测误差越小。
for ax, (split_name, y_true) inzip(axes, split_payload): y_pred = prediction_store[(focus_model, split_name)] ax.scatter( y_true, y_pred, s=30, alpha=0.70, color='#5AAFC8' ) ax.plot( [all_min, all_max], [all_min, all_max], '--', color='#D65F5F' )
主复刻图完全读取模型训练后形成的 `metric_table`,再通过透视表整理为“模型×数据集”的指标矩阵。这样可以避免主图与前面的训练结果脱节。
metric_values = { metric: metric_table.pivot( index='模型', columns='数据集', values=metric ) for metric in ['R2', 'RMSE', 'MAPE', 'MSE']}
上排极坐标柱状图使用测试集指标。代码先将不同量纲的指标映射到统一绘图半径,但原始指标数值仍保留在 `metric_table` 中。
raw_values = metric_values[metric].loc[ top_model_order, 'Testing'].to_numpy(dtype=float)scaled = np.clip(raw_values / upper, 0, 1)ax.bar( theta_top, 0.70 * scaled, width=0.54, bottom=0.22, color=colors, edgecolor='white', linewidth=1.2, alpha=0.94)
`bottom=0.22` 用于形成内环留白。不同指标使用不同渐变色,模型名称排列在极坐标外围,并通过 I、J、K、L 标注与论文面板保持对应。
下排雷达图直接使用三个数据集的原始指标,每条线在首尾处闭合。
for split_name in ['Training', 'Validation', 'Testing']: values = metric_values[metric].loc[ radar_model_order, split_name ].to_numpy(dtype=float) values_closed = np.r_[values, values[0]] ax.plot( theta_closed, values_closed, color=radar_colors[split_name], linewidth=1.7, label=split_name ) ax.fill( theta_closed, values_closed, color=radar_colors[split_name], alpha=0.24 )
在无 TensorFlow、xgboost 和 cma 的完整回退环境中,当前固定随机种子的验证结果显示:XGBoost-CMAES 工程回退模型在测试集上的 R2 约为 0.8665,RMSE 约为 19.83,MAPE 约为 4.60%,MSE 约为 393.11;标准 XGBoost 回退模型的测试集 R2 约为 0.8480。
这些数值只用于验证代码链路和图形结构,不代表论文原始结果。安装真实 XGBoost、CMA-ES、GRU 和 LSTM 依赖后,模型实现和最终指标都会发生变化。
从图形阅读方式看,上排 I 面板中柱体越长,表示测试集 R2 越高;J–L 面板则相反,柱体越短代表误差越小。下排雷达图用于观察同一指标在训练、验证和测试阶段的差异。如果某个模型的训练指标明显优于验证和测试指标,可能存在过拟合;如果三组轮廓较接近,则说明当前划分下的模型稳定性相对较好。
本案例全部采用模拟数据,图中模型排序和指标数值没有现实研究意义。
论文使用 10 次随机划分,当前代码为了控制运行时间展示 1 次固定划分。正式分析建议循环多个随机种子,并报告均值、标准差或置信区间。
GRU 和 LSTM 处理表格特征时,特征顺序及张量组织方式会影响结果。原文未公开完整网络细节,当前实现只能作为可运行复刻方案。
缺少 TensorFlow、xgboost 或 cma 时,代码会启用明确标注的工程回退模型。回退结果不能冒充真实 GRU、LSTM 或 XGBoost-CMAES 结果。
中位数插补、标准化和目标变换必须仅在训练集上拟合,不能先处理完整数据再划分,否则容易造成数据泄漏。
👇关注公众号【嗡嗡的Python日常】
✅ 支持代码定制|承接各类定制化科研绘图|质量放心
📌 关于源码:本文核心代码为原创定制,默认不免费公开。
✅ 获取源码方式:
1、直接添加号主微信,付费购买完整源码 + 数据
2、全部完整合集158元,后续将会持续更新,决定购买请联系作者,仅分享代码文件及模拟数据,不提供答疑服务,购买后不退不换!!!
👉请直接添加号主微信联系☕️:Wjtaiztt0406