ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WOA-GPR:小样本边坡稳定预测的高效机器学习方案

WOA-GPR:小样本边坡稳定预测的高效机器学习方案

做边坡稳定性预测这块,很多同行一开始想到的就是BP网络或者随机森林,数据喂进去一顿调参,最后效果勉强能看。但如果你手里的样本量只有一两百条,甚至几十条,神经网络那种典型的“数据饥渴”毛病就会立刻暴露出来。我试过几轮之后,换成了高斯过程回归(GPR),再配合鲸鱼优化算法(WOA)去调它的超参数,也就是标题里的WOA-GPR方案——多输入单输出,把重度、粘聚力这些现场参数输进去,直接给出安全系数Fs的预测值,还附赠一个置信区间。这篇文章就把这套流程从数据到代码完整展开,适合想在边坡稳定分析里引入机器学习,又被样本量卡住的研究生和工程师参考。

这套方案最大的价值在于实用。Excel存数据,格式灵活,不用强行改造你的报表习惯;代码分段拆解,每一块干什么、为什么这么干,我都尽量写清楚。下面直接从需求分析开始聊。

1. 为什么边坡稳定预测,我最终选了WOA-GPR这个组合

1.1 传统极限平衡法的效率瓶颈

边坡稳定性分析,传统上靠极限平衡法,比如瑞典条分法、简化Bishop法、Morgenstern-Price法。这些方法不是不好,问题在于每个剖面都要人工划分条块、试算滑面、反复迭代,遇到一个大型边坡的几十个剖面,工作量马上上来。更麻烦的是,现场勘查提供的地层参数往往存在试验离散性,你算出来的那个安全系数,本质上只是一个带有很强经验色彩的估计值。这就给了机器学习方法一个切入空间:把历史上算过、验算过的案例汇总起来,训练一个回归模型,下次遇到类似工况直接出结果,速度能快几个数量级。

1.2 GPR在小样本非线性回归上的先天优势

边坡稳定性问题带明显的非线性特征:安全系数随粘聚力、内摩擦角、坡高、坡角的变化不是简单线性关系。神经网络、随机森林这类模型也能做非线性拟合,但都有各自的短板。

神经网络需要足够多的样本才稳定,在几十到两三百条样本的边坡案例库里很容易过拟合,而且调网络结构、调学习率的过程极其痛苦。随机森林在小样本上的泛化能力还行,但它只能给出点预测,无法给出预测的不确定性。实际工程里,我们做边坡安全评估时恰恰想知道“这个预测值靠谱吗”,不确定性信息很重要。

GPR走的是完全不同的一条路。它假设目标函数服从一个高斯过程,对每一个输入点不仅给出预测均值,还给出预测方差。样本量少的时候,它依靠核函数刻画样本之间的相关性,依然能取得不错的预测精度;样本量稍微增加,预测方差会明显收窄。这个特性跟边坡工程案例库“样本少、质量参差、需要可靠性参考”的局面非常匹配。

1.3 WOA解决了GPR最头疼的超参数选择问题

GPR本身并不是一个完全自动化的模型,它的性能跟核函数里的超参数高度绑定。比如径向基核函数RBF的长度尺度、信号方差,再加上噪声方差,这几个参数稍一变化,预测结果就可能天差地别。很多教程里直接调GaussianProcessRegressor的默认优化器,用的还是局部优化策略,第一步走不好,很容易掉进局部最优解。

鲸鱼优化算法(Whale Optimization Algorithm,简称WOA)是模拟座头鲸气泡网捕食行为发展出来的群智能算法,结构简单、需要调整的参数少、全局搜索能力强,很适合做GPR的超参数寻优。比起网格搜索那种暴力枚举,WOA每次迭代都保留一批候选解,通过包围捕食、气泡网攻击和随机搜索三种机制更新位置,可以用较少的评估次数找到一组较好的超参数。

所以在我的实践流程里,GPR负责做回归预测并输出不确定性,WOA负责寻找GPR核函数的最优超参数。两个模型各司其职,组合出来的方案兼顾了精度、稳定度和可解释性。

2. 数据准备:多输入单输出的Excel组织与预处理

2.1 输入特征选哪些,输出定为安全系数

先明确“多输入单输出”指什么。以最常见的边坡稳定问题为例,输入特征通常包括:

  • 重度 γ(kN/m³)
  • 粘聚力 c(kPa)
  • 内摩擦角 φ(°)
  • 坡高 H(m)
  • 坡角 β(°)
  • 孔隙水压力比 ru(无量纲)

有时还会加入地震影响系数或坡顶超载,取决于手里能拿到哪些资料。输出则定为安全系数 Fs。

这些特征基本覆盖了极限平衡法里影响边坡稳定性的主要因素。注意一点:特征并不是越多越好。如果样本量只有100条,你塞进20个特征,GPR的核函数在高维空间里会变得很稀疏,预测效果反而变差。我一般建议控制在6到10个特征之间。

2.2 Excel数据表怎么摆最顺手

标题里强调“Excel存储数据格式灵活”,这个设计很务实。现实情况是,工程勘察数据本来就存放在Excel里,做预测还要专门去转格式,既费时间又容易出错。我推荐的表格组织方式有两种:

第一种,每行一个样本,最后一列是Fs:

编号gammacphibetaHruFs
118.525.420.135120.31.35
219.230.122.542180.51.12

这种格式最直观,pandas读取后直接就是 DataFrame,列名清晰可读。

第二种,某些报告习惯把参数按行放,每组样本是一列。这种格式也能读,读进来之后转置一下即可,代码里一行df.T就解决了。

还有一点需要考虑的是特征分布在不同Sheet里的情况。工程资料里经常出现“土工参数表”和“稳定计算结果表”分开存放的现象,这时用pd.read_excel(path, sheet_name='参数表')分别读取,再按编号列合并。这个用到的是Excel自带的多Sheet能力,灵活性比CSV要好。

2.3 读取与预处理的完整代码

以下是读取和预处理部分的核心代码,我加了必要的注释:

import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler from sklearn.model_selection import train_test_split # 读取Excel,表头在第二行时可加 header=1 df = pd.read_excel('slope_data.xlsx', sheet_name='Sheet1', header=0) # 多输入特征列,按实际表头名称填写 feature_cols = ['gamma', 'c', 'phi', 'beta', 'H', 'ru'] target_col = 'Fs' # 剔除存在空值的行,避免后续矩阵运算报错 df = df.dropna(subset=feature_cols + [target_col]) X = df[feature_cols].values.astype(float) y = df[target_col].values.astype(float) # 归一化,GPR对输入尺度敏感,这一步非常关键 scaler_X = MinMaxScaler(feature_range=(-1, 1)) X_scaled = scaler_X.fit_transform(X) # 如果Fs数值范围不大,输出也可以做一次归一化,加速核函数收敛 scaler_y = MinMaxScaler(feature_range=(-1, 1)) y_scaled = scaler_y.fit_transform(y.reshape(-1, 1)).ravel() # 划分训练集和测试集,随机种子固定方便复现 X_train, X_test, y_train, y_test = train_test_split( X_scaled, y_scaled, test_size=0.2, random_state=42 ) print(f'训练样本数: {X_train.shape[0]}, 测试样本数: {X_test.shape[0]}')

这里有个细节值得展开。为什么我用MinMaxScaler而不用标准化StandardScaler?因为GPR的核函数是定义在输入空间上的距离度量,MinMaxScaler能把所有特征映射到同一个量纲区间,不会出现某个特征因为数值大而主导核相似度的情况。标准化虽然也能处理均值方差,但在极端情况下对核矩阵的条件数改善不如MinMax直接。两套方案我都试过,MinMax在边坡数据上表现更稳定。

输出变量Fs的归一化也不是必须的,但它能让ConstantKernel的初始尺度更贴近数据实际范围,从而减少WOA搜索压力。预测完成后记得用scaler_y.inverse_transform还原。

# 还原预测值到原始量纲 y_pred_original = scaler_y.inverse_transform(y_pred.reshape(-1, 1)).ravel()

3. GPR核心原理与核函数参数到底在优化什么

3.1 从协方差函数理解GPR的工作机制

高斯过程回归的本质,是假设所有样本点的目标函数值联合服从一个高斯分布。这个分布的均值函数通常取0或训练集均值,协方差函数则由核函数来定义,用来刻画任意两个输入样本之间的相似度。

直观理解是这样的:GPR的泛化能力不是靠学习出一个固定的权重向量,而是靠“记住”训练样本之间的相关性。预测一个新样本时,它先计算这个样本与所有训练样本的核相似度,再通过这个相似度矩阵对训练标签做加权组合。样本量小、相似性结构合理的时候,这种记忆式回归往往比参数化模型更稳。

数学上,给定训练集 ((X, y)),对新输入 (x_*) 的预测均值是:

[ \mu_* = K_*^T (K + \sigma_n^2 I)^{-1} y ]

预测方差是:

[ \sigma_^2 = k(x_, x_) - K_^T (K + \sigma_n^2 I)^{-1} K_* ]

所以GPR的核心计算集中在核矩阵 (K) 的构建和求逆。这也意味着,核函数选得好不好,直接决定了预测精度。

3.2 核函数选型与超参数含义

边坡稳定预测最常用的核函数是RBF,也就是“径向基函数”,它的表达式是:

[ k(x_i, x_j) = \sigma_f^2 \exp\left(-\frac{|x_i - x_j|^2}{2l^2}\right) ]

这里面有两个核心超参数:

  • (l) 是长度尺度,控制特征的全局相关性范围。(l) 越大,模型认为样本间相关性可以传播得更远,预测曲线越平滑;(l) 越小,模型倾向于捕捉局部细节,容易抖动。
  • (\sigma_f^2) 是信号方差,控制预测值的整体幅度。

再加上GPR框架里的噪声方差 (\sigma_n^2),用来吸收观测数据里的随机误差。在scikit-learn中,一个标准的组合写法是:

from sklearn.gaussian_process import GaussianProcessRegressor from sklearn.gaussian_process.kernels import ConstantKernel, RBF, WhiteKernel # ConstantKernel对应信号方差,RBF对应长度尺度,WhiteKernel对应噪声 kernel = ConstantKernel(1.0) * RBF(length_scale=1.0) + WhiteKernel(noise_level=0.1)

这三个参数就是WOA要优化的对象。

3.3 为什么sklearn默认优化器不够用

scikit-learn的GaussianProcessRegressor默认情况下会用L-BFGS-B这类局部优化算法去最大化对数边际似然。问题在于,对数边际似然函数在超参数空间里是一个多峰函数,局部优化程序如果初始点不合适,就很容易收敛到某个不合理的峰上。

我实际测试过一组边坡数据,同一个GPR模型用默认优化器跑三次,分别设置不同的初始超参数,得到的预测R²能差出近0.2。这个差异放到工程场景里,就是安全系数预测偏差0.1到0.2的差别,足以影响工程决策。

所以正确的做法是:关闭sklearn内部的超参数优化(设置optimizer=None),把超参数寻优交给WOA来处理。这是整个代码设计中最容易出现错误的地方,后面我会专门强调。

4. WOA优化GPR超参数的完整代码实现

4.1 鲸鱼优化算法的三种位置更新策略

WOA的核心机制模拟座头鲸捕食时的气泡网策略。整个优化过程围绕三条规则展开:

第一,随机搜索。当系数 (|A| \ge 1) 时,当前鲸鱼不参考历史最优位置,而是在搜索空间里随机选一条鲸鱼作为参照,向它移动。这让算法具备跳出局部最优的能力。

第二,包围捕食。当 (|A| < 1) 且随机数 (p < 0.5) 时,鲸鱼向当前最优位置收缩包围,位置更新公式为朝着最优解靠拢,搜索步长由 (A) 的系数控制。

第三,气泡网攻击。当随机数 (p \ge 0.5) 时,采用螺旋更新路径,鲸鱼一边向最优位置靠近一边螺旋吐气泡。这个机制精细刻画了局部搜索,能显著提高收敛精度。

通过这三条策略的交替执行,WOA在前期有足够能力探索全局空间,后期又能收缩到局部精修,对GPR超参数这种连续值优化问题非常合适。

4.2 超参数编码与搜索空间设计

前文提到WOA要优化的三个参数是:

  • log_c:对应ConstantKernel的常数项,控制信号幅度
  • log_l:对应RBF的长度尺度 (l)
  • log_noise:对应WhiteKernel的噪声水平

之所以对三个参数取对数编码,是因为它们的取值范围跨度很大。长度尺度可能从0.01变化到10,噪声水平甚至可能到1e-6。直接在线性空间搜索,小值区间很难被有效覆盖。取对数后,搜索空间变成连续的、尺度均匀的区间,WOA的搜索效率会高很多。

我设定的搜索范围是:

  • log_c:[-1, 3],对应常数项 0.1 到 1000
  • log_l:[-2, 1],对应长度尺度 0.01 到 10
  • log_noise:[-6, 0],对应噪声水平 1e-6 到 1

这些范围也就是代码里的lb和ub数组。具体边界还需要根据你的数据做微调,但大体上在这个范围里跑,RBF核不会出现太离谱的退化。

4.3 适应度函数设计与WOA主循环代码

适应度函数决定了WOA往哪个方向搜索。GPR框架内有一个天然指标——对数边际似然。我们训练一个GPR模型,调用log_marginal_likelihood()就能拿到这个值,它同时考虑了拟合程度和模型复杂度,相当于一种自带正则化的评价标准。WOA的目标就是最小化负对数边际似然。

为什么不直接用常见的交叉验证RMSE?因为交叉验证需要把训练集再拆分多次,每次都要重新训练GPR,而GPR的单次训练成本是 (O(n^3)),样本量稍大就会很慢。对数边际似然只需要在完整训练集上拟合一次,速度快得多,而且在贝叶斯框架下理论依据更充分。

def objective(params): log_c, log_l, log_noise = params kernel = ( ConstantKernel(10.0 ** log_c) * RBF(length_scale=10.0 ** log_l) + WhiteKernel(noise_level=10.0 ** log_noise) ) gpr = GaussianProcessRegressor( kernel=kernel, normalize_y=True, optimizer=None, random_state=42 ) gpr.fit(X_train, y_train) # 负对数边际似然,越小越好 return -gpr.log_marginal_likelihood()

然后是WOA主循环的实现,我保留了比较完整的结构方便你直接改:

def whale_optimization(objective, lb, ub, dim=3, n_whales=20, max_iter=100, seed=42): rng = np.random.default_rng(seed) lb = np.asarray(lb) ub = np.asarray(ub) # 初始化种群,每个个体是一个三维向量 positions = rng.uniform(lb, ub, size=(n_whales, dim)) fitness = np.array([objective(pos) for pos in positions]) leader_pos = positions[np.argmin(fitness)].copy() leader_score = fitness.min() conv_curve = [] for t in range(max_iter): a = 2.0 - 2.0 * t / max_iter # a从2线性降到0 for i in range(n_whales): r1, r2 = rng.random(), rng.random() A = 2.0 * a * r1 - a C = 2.0 * r2 p = rng.random() b = 1.0 l = rng.uniform(-1, 1) if p < 0.5: if abs(A) < 1: # 包围捕食 D = abs(C * leader_pos - positions[i]) positions[i] = leader_pos - A * D else: # 随机搜索 rand_idx = rng.integers(0, n_whales) rand_whale = positions[rand_idx] D = abs(C * rand_whale - positions[i]) positions[i] = rand_whale - A * D else: # 气泡网攻击(螺旋更新) D = abs(leader_pos - positions[i]) positions[i] = D * np.exp(b * l) * np.cos(2 * np.pi * l) + leader_pos # 边界处理,越界拉回边界 positions[i] = np.clip(positions[i], lb, ub) # 重新计算适应度 fitness[i] = objective(positions[i]) # 更新全局最优 current_best_idx = np.argmin(fitness) if fitness[current_best_idx] < leader_score: leader_score = fitness[current_best_idx] leader_pos = positions[current_best_idx].copy() conv_curve.append(leader_score) if (t + 1) % 20 == 0: print(f'Iteration {t+1}/{max_iter}, best obj: {leader_score:.4f}') return leader_pos, leader_score, conv_curve # 运行优化 lb = [-1.0, -2.0, -6.0] ub = [3.0, 1.0, 0.0] best_params, best_score, curve = whale_optimization( objective, lb, ub, dim=3, n_whales=25, max_iter=120, seed=42 ) print(f'最优超参数 log_c={best_params[0]:.4f}, log_l={best_params[1]:.4f}, log_noise={best_params[2]:.4f}')

4.4 优化完成后的GPR落地训练

WOA找到最优超参数后,下一步是用这组参数重新构建GPR模型,这时就不需要再关心优化了,直接训练并预测即可。

kernel_best = ( ConstantKernel(10.0 ** best_params[0]) * RBF(length_scale=10.0 ** best_params[1]) + WhiteKernel(noise_level=10.0 ** best_params[2]) ) gpr_best = GaussianProcessRegressor( kernel=kernel_best, normalize_y=True, optimizer=None, random_state=42 ) gpr_best.fit(X_train, y_train) # 预测测试集,返回均值和标准差 y_pred, y_std = gpr_best.predict(X_test, return_std=True) # 还原到原始量纲 y_pred_orig = scaler_y.inverse_transform(y_pred.reshape(-1, 1)).ravel() y_std_orig = y_std * scaler_y.scale_[0] # 标准差按缩放比例还原

5. 训练、预测与结果评价的完整流程

5.1 数据划分和归一化的边界问题

很多人做机器学习时,先把全量数据归一化,再切分训练集和测试集,这个做法在GPR上有一点点小隐患。归一化用的scaler如果是在包含测试集的全量数据上拟合的,相当于测试集的信息提前泄露给了训练过程。严格做法是:先切分数据,再在训练集上拟合scaler,然后用同样的scaler转换测试集。

我刚才的代码里是先归一化再切分,原因在于GPR对尺度高度敏感,如果只拿训练集拟合scaler,测试集里偶尔出现的极端值可能会超出映射范围,导致预测退化。两种方案我都用过,对于边坡数据这种分布相对稳定的场景,先归一化再切分引入的偏差很小,而数值稳定性收益更明显。不过如果你是做严谨的学术对比实验,建议改用先切分再拟合scaler的流程。

切分方式也要多说一句。当样本总量只有一百多条时,随机切出一个20%的测试集,可能会让某块地势参数在测试集里没有任何代表。这种情况下,可以考虑用分层抽样或者直接采用K折交叉验证来评估模型,而不是迷信单次划分的R²。

5.2 回归性能指标怎么算

回归任务绕不开三个指标:决定系数R²、均方根误差RMSE、平均绝对误差MAE。在边坡场景里,R²代表模型解释了多少安全系数的方差,RMSE和MAE直接反映预测安全系数和实际安全系数之间的偏差量级。

比如某组测试结果R²=0.91,RMSE=0.065,MAE=0.052,这意味着预测的安全系数平均偏差在0.05左右,工程上完全可以接受。如果MAE超过0.2,这个模型就得不到现场专家信任了,因为安全系数在小范围内波动时,0.2的偏差可能改变工程结论。

from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error r2 = r2_score(y_test_orig, y_pred_orig) rmse = np.sqrt(mean_squared_error(y_test_orig, y_pred_orig)) mae = mean_absolute_error(y_test_orig, y_pred_orig) print(f'R² = {r2:.4f}') print(f'RMSE = {rmse:.4f}') print(f'MAE = {mae:.4f}')

注意y_test_orig需要用同样的scaler还原,保证和y_pred_orig处于同一量纲。这里的还原方法是把测试集的原始标签用scaler处理时顺便保留一份逆变换后的版本。

5.3 收敛曲线与预测结果可视化

WOA跑完之后第一件事是画收敛曲线,判断优化是否收敛。一个健康的收敛曲线应该是前期快速下降,中后期逐渐趋于平稳。如果曲线还在大幅波动,说明迭代次数不够或者搜索空间设得太宽。

import matplotlib.pyplot as plt plt.figure(figsize=(8, 5)) plt.plot(curve, 'o-', linewidth=1.5, markersize=4) plt.xlabel('Iteration') plt.ylabel('Negative Log Marginal Likelihood') plt.title('WOA Convergence Curve') plt.grid(alpha=0.3) plt.show()

预测结果的对比散点图也很有用。横轴是实测安全系数,纵轴是预测安全系数,理想情况下所有点都落在45度对角线附近。同时可以把y_std转换成置信区间,在图上用误差棒画出来。GPR能提供这种逐样本的不确定性表达,是它区别于其他回归模型的最大亮点。

plt.figure(figsize=(8, 8)) plt.errorbar( y_test_orig, y_pred_orig, yerr=np.abs(y_std_orig), fmt='o', alpha=0.7, capsize=3, markersize=5 ) plt.plot( [min(y_test_orig), max(y_test_orig)], [min(y_test_orig), max(y_test_orig)], '--', color='black', linewidth=1 ) plt.xlabel('Actual Fs') plt.ylabel('Predicted Fs') plt.title('Predicted vs Actual with Confidence Intervals') plt.grid(alpha=0.3) plt.show()

误差棒的实际含义要解释清楚:它表示GPR对这个预测值的置信程度。如果某条样本的误差棒很长,说明该样本落在训练数据分布稀疏的区域,这时候即使预测均值看着合理,使用它做工程判断也需要格外谨慎。这个信息是点预测模型给不出来的,也是我坚持用GPR的核心原因。

6. 跑通代码之后,我踩过而且值得警惕的坑

6.1 GPR的计算量随样本量呈立方增长

GPR最让人头疼的短板是复杂度。核矩阵求逆是 (O(n^3)),这意味着样本量从100涨到300,单次训练耗时大约会涨27倍。我最早在一份两千多条样本的数据集上跑GPR,单次适应度评估就要十几秒,WOA跑120次迭代乘以25条鲸鱼,直接变成不可接受的耗时。

所以如果你手里的数据量超过一千条,最好先抽样或者对样本做聚类精简,再用WOA-GPR。边坡工程里常见的案例库规模多在几十到几百条,这个范围内GPR反而是最舒服的。如果未来数据量持续增长,备选方案是用稀疏高斯过程回归或者直接切换到别的模型。

6.2 必须设置optimizer=None,否则WOA白跑

这个坑我在前面提过,但值得单独再说一次。scikit-learn的GaussianProcessRegressor默认会在训练时对核函数超参数执行L-BFGS-B局部优化。如果不设置optimizer=None,那么每次WOA评估适应度时,sklearn都会在内部重新做一轮超参数寻优,把WOA传进去的那组参数覆盖掉。结果就是:不管WOA怎么搜索,最终模型内部实际使用的超参数都已经被局部优化改写过,WOA的搜索过程完全失去意义。

我检查过很多网上流传的WOA-GPR代码,这个问题非常普遍。正确的写法就是在构建GPR时显式传入optimizer=None。

6.3 核函数退化与过拟合的判断

用WhiteKernel加RBF的组合时,偶尔会遇到噪声方差被优化到极小值,比如1e-8,同时长度尺度也偏小的情况。这时模型看起来在训练集上R²很高,但测试集一测就露馅。从贝叶斯角度看,这通常是数据量太少、标签存在噪声时,模型把噪声也当成信号吸收了。

遇到这种情况,我的处理方式是给噪声水平加一个更紧的下界,比如把log_noise的下界从 -6 抬高到 -4。另一个办法是在WOA的适应度函数里同时叠加测试集的RMSE作为正则项,相当于做一点交叉验证的味道,但代价是训练时间增加。多数情况下,调整搜索边界就足够。

6.4 Excel读取时那些不起眼的坑

Excel数据格式灵活是优点,但也带来一些麻烦。最常见的是表头不统一,同一个文件里phi写成φ或内摩擦角,代码里feature_cols匹配不上。更好用的是按位置选列,比如df.iloc[:, 1:7],这样至少不会因为列名符号问题而挂掉。其次是单元格里混入空格或全角字符,astype(float)会直接抛异常。我有一条经验:读入Excel后先跑一次数据清洗,把非数值字符替换为NaN,再统一dropna。

6.5 随机种子与结果的可复现性

群智能算法有随机性,这既是优势也是麻烦。同一份数据跑两遍WOA,最优超参数可能略有差别,导致最终预测结果也不完全一致。对于学术研究或者工程报告,结果不可复现是一个致命伤。

解决方案其实很简单:所有随机数入口都固定种子。train_test_split里设置random_state,WOA内部用np.random.default_rng(seed),每跑一次实验记录好种子值。这样别人复现你的流程时,只要用同一个种子就能得到完全一致的结果。

最后再分享一条个人体会。刚上手WOA-GPR的时候,我老想着把模型调得越复杂越好,换了很多高级核函数,比如Matern、Rational Quadratic,最后发现RBF加WhiteKernel在这个问题上的表现已经足够优秀。复杂的核函数只是在测试集分数上好看了一点,真正到现场数据上,鲁棒性反而不如简单组合。对边坡稳定预测这种强物理背景的问题,模型的“可解释性”和“稳定输出”远比一个百分点的精度提升重要得多。这套代码目前在我这里已经用得很顺手,数据换一换、参数边界微调一下就能迁移到类似的岩土工程问题上,算是性价比非常高的一个方案。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进