【零基础学智能仿真-07】随机森林——用多棵树获得更稳定的力学预测

一、本节学习目标

完成本节后,你将能够:

  1. 解释单棵决策树为什么容易波动;

  2. 理解Bootstrap有放回抽样;

  3. 掌握Bagging的并行集成思想;

  4. 说明随机森林与普通Bagging的区别;

  5. 建立结构最大应力随机森林代理模型;

  6. 使用袋外分数评价模型;

  7. 比较单树、Bagging与随机森林;

  8. 正确解释随机森林特征重要性。


二、为什么需要多棵决策树?

上一节建立的决策树具有较强的可解释性,但也有一个明显问题:对数据变化比较敏感。

假设原始数据中某个样本的J积分从:

变为:

决策树选择的裂纹长度阈值就可能发生变化,后面的分支结构也可能随之改变。

这说明单棵树具有较高方差:

更换少量训练样本,模型结构和预测结果可能明显变化。

解决方法不是只寻找一棵"完美的树",而是训练许多不同的树,再综合它们的判断。


三、Bagging的核心思想

Bagging是Bootstrap Aggregating的缩写,可以理解为"自助采样与聚合"。

其基本过程为:

  1. 从原始训练集中进行Bootstrap抽样;

  2. 使用每一份抽样数据训练一棵决策树;

  3. 重复训练多棵树;

  4. 对所有树的结果进行平均或投票。

图7-1 Bagging与随机森林工作流程

对于回归问题,最终预测为:

其中为树的数量。

对于分类问题,通常采用多数投票:


四、什么是Bootstrap抽样?

假设训练集中有5个样本:

Bootstrap仍然抽取5次,但每次抽取后都把样本放回。因此某一次抽样可能得到:

另一次可能得到:

可以看到:

  • 某些样本会被重复抽到;

  • 某些样本不会出现;

  • 每棵树看到的训练数据都不完全相同。

对于包含个样本的数据集,一个指定样本一次未被抽中的概率约为:

所以每棵树大约有36.8%的原始样本没有参与训练。这些样本称为袋外样本,可用于评价模型。


五、Bagging与随机森林有什么区别?

Bagging主要引入数据随机性:

随机森林在此基础上又增加了特征随机性:

每个节点分裂时,只从部分随机选出的特征中寻找最佳划分。

因此:

方法 样本随机抽取 特征随机选择
单棵决策树
Bagging 通常否
随机森林

如果所有树总是选择同一个最强特征,它们可能长得十分相似。随机特征选择可以增加树之间的差异,使平均结果更加稳定。


六、建立结构最大应力数据集

假设有限元参数化计算包含以下输入:

  • 载荷

  • 结构长度

  • 截面宽度

  • 截面厚度

  • 弹性模量

  • 缺口长度

输出为结构最大应力:

下面使用公式构造一组模拟仿真数据。

复制代码
import numpy as np
import pandas as pd

rng = np.random.default_rng(42)
n_samples = 500

load_kN = rng.uniform(10, 100, n_samples)
length_mm = rng.uniform(500, 1200, n_samples)
width_mm = rng.uniform(20, 80, n_samples)
thickness_mm = rng.uniform(4, 15, n_samples)
elastic_modulus_GPa = rng.uniform(70, 210, n_samples)
notch_length_mm = rng.uniform(0, 8, n_samples)

nominal_stress = (
    1000 * load_kN
    / (width_mm * thickness_mm)
)

stress_concentration = (
    1 + 0.12 * notch_length_mm
)

noise = rng.normal(0, 5, n_samples)

max_stress_MPa = (
    nominal_stress * stress_concentration
    + 0.015 * length_mm
    + noise
)

data = pd.DataFrame({
    "载荷_kN": load_kN,
    "长度_mm": length_mm,
    "宽度_mm": width_mm,
    "厚度_mm": thickness_mm,
    "弹性模量_GPa": elastic_modulus_GPa,
    "缺口长度_mm": notch_length_mm,
    "最大应力_MPa": max_stress_MPa
})

print("数据形状:", data.shape)
print("缺失值数量:", data.isna().sum().sum())

预期输出

复制代码
数据形状: (500, 7)
缺失值数量: 0

这里的公式只用于构建教学数据。在真实项目中,最大应力_MPa应来自Abaqus、ANSYS或FEniCS计算结果。


七、划分训练集和测试集

复制代码
from sklearn.model_selection import train_test_split

feature_names = [
    "载荷_kN",
    "长度_mm",
    "宽度_mm",
    "厚度_mm",
    "弹性模量_GPa",
    "缺口长度_mm"
]

X = data[feature_names]
y = data["最大应力_MPa"]

X_train, X_test, y_train, y_test = (
    train_test_split(
        X,
        y,
        test_size=0.2,
        random_state=42
    )
)

print("训练集:", X_train.shape)
print("测试集:", X_test.shape)

预期输出

复制代码
训练集: (400, 6)
测试集: (100, 6)

树模型根据特征大小进行分裂,一般不要求特征标准化。


八、建立单棵决策树基准模型

复制代码
from sklearn.tree import DecisionTreeRegressor

single_tree = DecisionTreeRegressor(
    max_depth=None,
    min_samples_leaf=1,
    random_state=42
)

single_tree.fit(X_train, y_train)

tree_prediction = single_tree.predict(
    X_test
)

print(
    "前5个预测值:",
    np.round(tree_prediction[:5], 2)
)

预期输出

程序会输出5个最大应力预测值,例如:

复制代码
前5个预测值: [268.41 113.57 396.82 182.35 521.76]

具体末位数值可能因scikit-learn版本而略有不同。

这棵树没有限制深度,很容易将训练样本划分得非常细,因此可以作为过拟合程度较高的基准模型。


九、建立Bagging回归模型

复制代码
from sklearn.ensemble import BaggingRegressor

bagging_model = BaggingRegressor(
    estimator=DecisionTreeRegressor(
        min_samples_leaf=2,
        random_state=42
    ),
    n_estimators=100,
    max_samples=1.0,
    bootstrap=True,
    random_state=42,
    n_jobs=-1
)

bagging_model.fit(X_train, y_train)

bagging_prediction = bagging_model.predict(
    X_test
)

print(
    "Bagging包含的树数量:",
    len(bagging_model.estimators_)
)

预期输出

复制代码
Bagging包含的树数量: 100

n_estimators=100表示训练100棵决策树。

如果旧版本scikit-learn提示:

复制代码
unexpected keyword argument 'estimator'

可以将estimator=改成:

复制代码
base_estimator=

十、建立随机森林模型

复制代码
from sklearn.ensemble import (
    RandomForestRegressor
)

random_forest = RandomForestRegressor(
    n_estimators=300,
    max_features="sqrt",
    min_samples_leaf=2,
    bootstrap=True,
    oob_score=True,
    random_state=42,
    n_jobs=-1
)

random_forest.fit(X_train, y_train)

forest_prediction = random_forest.predict(
    X_test
)

print("树的数量:", len(
    random_forest.estimators_
))

print(
    f"袋外R²:"
    f"{random_forest.oob_score_:.4f}"
)

预期输出

复制代码
树的数量: 300
袋外R²:0.95左右

袋外分数的具体数值可能随软件版本出现小幅变化。


十一、比较三种模型

复制代码
from sklearn.metrics import (
    mean_absolute_error,
    mean_squared_error,
    r2_score
)

models_and_predictions = {
    "单棵树": tree_prediction,
    "Bagging": bagging_prediction,
    "随机森林": forest_prediction
}

records = []

for name, prediction in (
    models_and_predictions.items()
):
    mae = mean_absolute_error(
        y_test,
        prediction
    )

    rmse = np.sqrt(
        mean_squared_error(
            y_test,
            prediction
        )
    )

    r2 = r2_score(
        y_test,
        prediction
    )

    records.append({
        "模型": name,
        "MAE": mae,
        "RMSE": rmse,
        "R2": r2
    })

evaluation = pd.DataFrame(records)

print(
    evaluation.to_string(
        index=False,
        float_format=lambda value: f"{value:.3f}"
    )
)

预期现象

典型结果表现为:

复制代码
   模型    MAE   RMSE    R2
 单棵树   较大    较大    较低
Bagging   较小    较小    较高
随机森林   较小    较小    较高

由于数据随机生成以及软件版本差异,具体数值可能不同,但集成模型通常比未剪枝的单棵树更稳定。

图7-2 单树与集成模型误差示意

图中的数值用于说明典型趋势,实际课程运行时应以程序输出为准。


十二、为什么平均能够降低波动?

假设每棵树的预测可以写成:

其中表示第棵树的预测波动。

对多棵树取平均:

当不同树的误差不完全相同时,正负误差会部分抵消,从而降低整体方差。

但如果所有树完全相同,它们的误差也会高度相关,平均效果就会减弱。这正是随机森林需要同时引入样本随机性和特征随机性的原因。


十三、查看特征重要性

复制代码
importance = pd.Series(
    random_forest.feature_importances_,
    index=feature_names
).sort_values(ascending=False)

print(importance.round(4))

预期现象

通常,载荷、厚度、宽度和缺口长度会获得较高重要性;弹性模量在当前力控制应力模型中的贡献可能较低。

复制代码
载荷_kN         较高
厚度_mm         较高
宽度_mm         较高
缺口长度_mm     较高
长度_mm         较低
弹性模量_GPa    较低

图7-3 随机森林特征重要性示意

特征重要性表示变量对降低树节点误差的贡献,不等于严格的物理因果关系。


十四、绘制实际运行得到的重要性图

复制代码
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = [
    "Microsoft YaHei",
    "SimHei"
]
plt.rcParams["axes.unicode_minus"] = False

importance.sort_values().plot(
    kind="barh",
    color="#1769aa"
)

plt.xlabel("特征重要性")
plt.ylabel("输入特征")
plt.title("随机森林特征重要性")
plt.grid(axis="x", alpha=0.3)
plt.tight_layout()
plt.show()

预期绘图结果

程序会按照重要性从低到高绘制水平柱状图。柱越长,表示该特征在当前森林的节点划分中贡献越大。


十五、袋外评价有什么用?

每棵树约有36.8%的训练样本未被抽中。可以让这些袋外样本通过对应的树进行预测,再汇总得到袋外分数。

袋外评价的优点是:

  • 不需要额外划分验证集;

  • 可以充分利用小样本仿真数据;

  • 能快速检查森林是否具有基本泛化能力。

但袋外分数不能完全替代独立测试集。最终模型仍应使用未参与训练和调参的数据进行检验。


十六、关键超参数

n_estimators

森林中的树数量。树越多通常越稳定,但计算量也越大。

max_features

每次节点分裂时允许参与竞争的特征数量。数值越小,树之间的差异通常越大。

max_depth

限制每棵树的最大深度。

min_samples_leaf

限制叶节点的最小样本数。适当增大可以让预测曲面更加平滑。

max_samples

控制每棵树使用多少训练样本。

n_jobs=-1

使用可用CPU核心并行训练。


十七、树的数量越多越好吗?

增加树的数量通常会使模型误差逐渐稳定,但不会无限提高精度。

当:

模型方差逐渐趋于稳定,继续增加树数量主要只会增加:

  • 训练时间;

  • 内存占用;

  • 模型文件体积;

  • 推理计算量。

工程中可以逐步尝试:

复制代码
50, 100, 200, 300, 500

并观察测试误差是否已经进入平台区。


十八、随机森林的优势与局限

主要优势

  • 能处理明显的非线性关系;

  • 不要求特征标准化;

  • 对异常值和数据扰动相对稳健;

  • 可以估计特征重要性;

  • 训练过程容易并行;

  • 通常比单棵树具有更好的泛化能力。

主要局限

  • 不如单棵树容易直接解释;

  • 模型文件可能较大;

  • 不能自然地进行远距离外推;

  • 普通重要性可能偏向连续或取值较多的特征;

  • 预测曲面仍由大量分段区域构成;

  • 不能保证满足守恒定律和本构约束。


十九、力学建模中的注意事项

1. 不要随机拆分强相关网格点

相邻节点的应力高度相关。如果同一有限元模型的节点同时进入训练集和测试集,可能造成数据泄漏。

更可靠的方法是按照完整工况或完整模型划分数据。

2. 特征重要性不等于因果关系

载荷重要性高,不代表其他参数没有物理作用。

3. 不要信任训练范围外的预测

随机森林主要依赖已有样本区域进行分段预测,不擅长外推。

4. 检查预测的物理合理性

例如:

  • 载荷增大时最大应力是否总体增大;

  • 厚度增大时应力是否总体减小;

  • 预测值是否出现负应力或异常跳变;

  • 高风险工况是否被训练数据覆盖。


二十、本节练习

基础练习

将树数量依次修改为:

复制代码
10, 50, 100, 300, 500

记录测试集RMSE和训练时间。

进阶练习

分别设置:

复制代码
max_features = 1.0
max_features = "sqrt"
max_features = 0.5

观察树之间差异和模型精度的变化。

工程挑战

将输出由最大应力改为最大位移,并判断弹性模量的重要性是否会上升。

思考练习

如果随机森林认为"弹性模量"对最大应力几乎没有贡献,这是否一定是错误的?请结合力控制与位移控制边界条件进行解释。


二十一、本节小结

Bagging通过Bootstrap抽样训练多棵不同的树:

随机森林进一步在节点划分时随机选择部分特征:

它的核心价值不是让每一棵树都非常准确,而是让多棵存在差异的树共同形成稳定判断。

下一节将学习梯度提升机GBM,理解另一种集成思路:不再并行训练独立的树,而是让后一棵树专门纠正前面模型尚未解决的预测误差。

相关推荐
Tom·Ge1 小时前
【AI前沿】2026.08.17 SpaceX 600亿收购Cursor正式完成·DeepSeek V4 Pro万亿开源·人形机器人生态全面爆发
人工智能·大模型·ai前沿
郑州光合科技余经理2 小时前
本地生活服务系统:成品模块和定制接口怎么划界
java·前端·人工智能·后端·系统架构·php·ai编程
2603_965148112 小时前
家居百货蓝海:API挖掘高复购率生活小商品
大数据·服务器·人工智能·python·生活
2501_930472442 小时前
踩坑|CodeBuddy权限配置:AI误删文件、乱跑命令、.env泄露怎么防
人工智能·ai编程
小淮AI5 小时前
从“刷题”到“追问”:AI课堂正在重塑哪些学习旧习惯?
人工智能·学习
cui_ruicheng6 小时前
LangChain 应用开发(十四):Agent 上下文与记忆机制
服务器·人工智能·python·langchain
飞哥数智坊6 小时前
直播半小时,我聊了聊 Agent 最常见的 3 个疑问
人工智能
ggb喔7 小时前
AI 原生攻防时代:2026 年渗透测试与逆向开发的范式重构
人工智能·重构
宸津-代码粉碎机7 小时前
AI攻防战升级!基于Spring AI构建Java应用自动免疫安全体系
java·大数据·开发语言·人工智能·python·安全·spring