在上一篇总结中我们使用XGBoost算法预测了发动机的剩余使用寿命,结果差强人意。今天,我们继续学习一种新的算法:随机森林(Random Forest)。和XGBoost一样,随机森林也是一种集成学习算法。由多个决策树组成,通过多个弱学习器形成一个强学习器。决策树是随机森林的基本组成单元,每棵决策树通过一系列的if-then规则将特征空间划分为不同的区域,每个区域对应一个输出值。随机森林通过构建多个这样的决策树,并将它们的结果进行汇总,来得到最终的预测结果。在回归任务中,最终预测值为所有决策树的预测值的平均值: | | |
| Bagging(Bootstrap Aggregating) | |
| | |
| | |
| | |
| | |
| | |
从上述对比不难看出,RF比较适合数据量较小、数据特征较简单的任务。如果需求是快速建模和基础预测分析,可以优先考虑随机森林。怎么理解Bagging(Bootstrap Aggregating,自助采样)呢?它的核心思想其实就是通过对数据进行随机采样和重复训练多个弱学习器,最终将多个预测结果组合来获得更稳健的预测结果。简单的说就是:多次抽样 + 多模型训练 + 结果集成。它其实是一种统计方法:假设我们有一个班级所有学生的考试成绩,而我们要估计所有学生的平均分。于是我们随机抽取了5名学生的分数作为样本:[85、90、75、98、56]。有放回抽样(Bootstrap)就是每次抽取一组样本后,将其再放回原始样本中,然后再次抽取。这样,我们可以生成多个新的样本集合,每个集合都包含5个分数数据,当然这些数据可能包含重复的值。例如:第二次抽样得到:[100、78、95、67、89]以此类推,如果我们进行100次抽样,就能得到100个新的样本集合。对于每个样本集合,我们计算均值:第一个样本集合的平均值是:(85+90+75+98+56)/5=80.8第二个样本集合的平均值是:(100+78+95+67+89)/5=85.8然后取这100个均值的平均值来作为全班平均分数的估计。讲到这里,不知道有没有人会有这样一个疑问:既然已经知道所有学生的成绩了,为啥不直接算平均,还要用有放回抽样来估算呢? 因为在现实生活中,我们手上的总体数据往往是非常大的,处理起来非常困难!有时我们无法获得整个总体数据。这时,随机采样就会显得非常有用!上面举的例子只是为了便于大家理解。RandomForestRegressor 类是scikit-learn中实现随机森林回归的强大工具,它是一种特化版的 Bagging 回归模型,除了对样本进行有放回抽样,每棵树的训练还会随机选择部分特征用于分裂,而且每棵树通常会生长到最大深度。这样增加了模型的多样性,降低了过拟合的风险。模型的构建和训练非常简单,首先用RandomForestRegressor创建一个随机森林回归模型,再用fit方法来训练模型即可。我们稍微优化一下,和上一篇的XGBoost类似,先设置一个超参数空间并随机生成 50 组参数组合,再在每组参数上执行交叉验证训练和评估模型,计算均方误差(MSE)。然后从随机搜索过程中找到一个最佳参数组合,重新训练模型以获得最佳的泛化能力(前面的代码是一样的啊,下面只展示模型训练部分代码):import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from tqdm import tqdm
# 定义超参数分布
param_distributions = {
'n_estimators': [100, 200, 300, 400, 500],
'max_depth': [None, 10, 20, 30, 40, 50],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4],
'max_features': ['sqrt', 'log2'],
'bootstrap': [True, False]
}
# 初始化随机森林回归模型
model = RandomForestRegressor(random_state=42)
param_list = []
for _ in range(50): # 随机采样 50 组参数组合
param_list.append({
'n_estimators': random.choice(param_distributions['n_estimators']),
'max_depth': random.choice(param_distributions['max_depth']),
'min_samples_split': random.choice(param_distributions['min_samples_split']),
'min_samples_leaf': random.choice(param_distributions['min_samples_leaf']),
'max_features': random.choice(param_distributions['max_features']),
'bootstrap': random.choice(param_distributions['bootstrap'])
})
# 设置交叉验证
cv_splitter = KFold(n_splits=3, shuffle=True, random_state=42)
# 遍历所有参数组合
results = []
with tqdm(total=len(param_list), desc="Random Search Progress") as pbar:
for params in param_list:
model.set_params(**params) # 更新模型参数
scores = []
for train_idx, val_idx in cv_splitter.split(flat_seq_array, label_array.ravel()):
# 在训练集上训练模型
model.fit(flat_seq_array[train_idx], label_array.ravel()[train_idx])
# 在验证集上预测并计算误差
preds = model.predict(flat_seq_array[val_idx])
score = mean_squared_error(label_array.ravel()[val_idx], preds)
scores.append(score)
avg_score = np.mean(scores) # 计算平均误差
results.append({'params': params, 'mean_test_score': avg_score}) # 记录结果
pbar.update(1) # 更新进度条
# 获取最佳参数和结果
best_result = min(results, key=lambda x: x['mean_test_score']) # 找到误差最小的结果
best_params = best_result['params'] # 最优超参数
best_score = best_result['mean_test_score'] # 最优得分
# 输出最佳参数和分数
print(f"最佳参数: {best_params}")
print(f"最佳得分 (MSE): {best_score}")
# 使用最佳参数重新训练模型
best_rf_model = RandomForestRegressor(**best_params, random_state=42) # 根据最佳参数初始化模型
best_rf_model.fit(flat_seq_array, label_array.ravel()) # 在全部训练数据上重新训练模型
# 预测
preds = best_rf_model.predict(flat_test_seq_array)
# 评估模型
mae = mean_absolute_error(test_label_array, preds)
mse = mean_squared_error(test_label_array, preds)
r2 = r2_score(test_label_array, preds)
print(f"Optimized MAE: {mae}, Optimized MSE: {mse}, Optimized R^2: {r2}")
受电脑性能限制,我这里采取的是手动随机搜索。如果你的电脑性能好一些,也可以用RandomizedSearchCV库快速找到相对较好的参数组合(设置内置参数n_jobs=-1,利用多核CPU加快训练速度)。# 定义随机搜索对象
random_search = RandomizedSearchCV(
estimator=RandomForestRegressor(random_state=42),
param_distributions=param_distributions,
n_iter=50, # 搜索次数
scoring='neg_mean_squared_error', # 评估指标
cv=3, # 3折交叉验证
random_state=42,
n_jobs=-1 # 并行计算
)
两种方法各有优缺点,手动随机搜索灵活性高,但没法并行计算,在大规模参数搜索时效率低。而RandomizedSearchCV虽然难以对训练过程做细粒度控制,但有交叉验证内置支持,几行代码就能完成超参数搜索,计算效率更高。最终的预测效果比XGBoost还略差一些,都没有LSTM模型预测得准确。本来还想再尝试用一下另一种集成学习的方法:LightGBM,再写一期,现在看来是没有必要了。对于这种时间依赖性强、样本序列较长的多变量时序数据,深度学习模型比传统的机器学习模型(如 XGBoost、LightGBM、RandomForest)要更合适一些。
感谢大家对机务论坛的支持,关注机务论坛,倾听机务心声!航企优秀的方面必定宣传,不足的地方也必须指出,让领导们重视问题,解决问题,营造更好的机务维修环境。同时,征集劳动仲裁案例,分享案例,让更多的小伙伴能了解劳动纠纷的解决方式,通过劳动仲裁维护自己的合法权益。