首页 / 资讯中心 / 文章详情

贝叶斯优化随机森林的故障诊断流程与Matlab实现

贝叶斯优化随机森林的故障诊断流程与Matlab实现 ★ FEATURED ARTICLE
做故障诊断的朋友估计都遇到过这种场景辛辛苦苦从传感器信号里提了一批特征喂给随机森林结果准确率卡在某个水平上不去。不是模型不行是几个超参数怎么调都不对劲。手动试参像开盲盒网格搜索慢得让人怀疑人生。后来我把贝叶斯优化和随机森林搭在一起让算法自己去找合适的超参数准确率上去了时间还省了一大半。这篇文章就把这套贝叶斯优化随机森林的故障诊断流程完整拆开讲讲并且附上Matlab实现代码。正在做设备故障诊断、或者想提高分类模型调参效率的朋友可以从这里直接入手。我先说明一下自己的使用背景免得大家对着流程走偏。我一般处理的是旋转机械的振动信号、电流信号这类工业现场数据特征维度不高但样本量往往不大而且故障样本比正常样本少很多。随机森林在这种场景下非常稳前提是它的几个关键超参数别太离谱。用贝叶斯优化调参之后不需要手动试几十组参数大概几十次迭代就能收敛到一组不错的结果。下面我就按思路、原理、实现、避坑、扩展五个部分来写整个流程可以直接照抄再替换成自己的数据。1. 为什么在故障诊断里选随机森林1.1 故障诊断任务的数据特点故障诊断本质上是一个多分类或二分类问题输入是各种传感器信号里提取的特征输出是设备状态类别比如正常、不对中、磨损、松动等等。这类数据和普通的图像分类、文本分类有很大差别最大的特点是“干净样本不够脏样本更不够”。工业现场采集到的特征通常存在几个问题。第一正常工况下的样本占绝大多数故障样本稀少类别分布严重不平衡。第二现场噪声大传感器偶尔还会丢包或者漂移特征里会有离群点。第三不同工况、不同负载下同类故障的特征分布也会漂移导致模型在新工况下性能下降。第四特征之间往往高度相关比如时域均值、峰值、有效值这些指标可能本质上都来源于同一段振动波形。随机森林对这种数据非常友好。它本身是Bagging加随机特征选择的集成方法每棵树只看到一部分训练样本和一部分特征最终结果通过投票产生。单棵树即使过拟合多棵树投票之后也能把方差压下来对离群点和噪声有一定容忍度。而且随机森林能直接计算特征重要性这在做故障诊断时特别关键因为很多时候我们不光要判断“有没有故障”还要知道“哪些传感器特征起了主要作用”方便后续做传感器布点和特征降维。1.2 随机森林的主要超参数与调参难点随机森林虽然好用但不是建好模型就完事它有一堆超参数需要确定。最核心的几个包括树的数量、每棵树的深度或最大分裂数、叶子节点最少样本数、以及每次分裂时随机抽样的特征数量。树的数量越多模型越稳定但训练时间和内存占用也会线性增长。树太深容易把某些故障样本的局部噪声学进去树太浅又可能整体欠拟合。叶子节点最少样本数控制着模型的平滑程度设成1时模型会对每一个训练样本都记下来非常容易过拟合。最大分裂数则直接限制每棵树的复杂度。这些参数之间的相互影响不是线性的。比如树的数量可以稍微多一点来补偿单棵树的方差但叶子节点数过少又会引入过拟合。手动调参时经常出现调了A参数B参数又不行了的情况。网格搜索虽然可以系统遍历但超参数空间大到一定程度后计算量会爆炸。比如树数量取50个候选值、最大分裂数取50个候选值、最小叶子数取50个候选值组合数就是125000次每次都要完整训练一个随机森林在故障诊断场景下完全不现实。所以我们需要一种更聪明的搜索策略。它应该能在之前的评估结果基础上判断下一组参数在哪里更有可能出好效果。这正是贝叶斯优化的核心价值。2. 用贝叶斯优化替代网格搜索的关键思路2.1 贝叶斯优化为什么适合超参数搜索贝叶斯优化不是一种模型而是一种黑盒全局优化策略。它假设目标函数是未知的我们每次评估一组超参数都会得到一个目标值比如交叉验证错误率。然后它用一个概率代理模型通常是高斯过程去拟合“超参数到目标值”的关系。这个拟合结果不仅给出每一点的预测均值还给出预测的不确定性。有了均值和不确定性贝叶斯优化就可以计算“在哪个点继续评估最有价值”。它的采集函数会平衡两方面一是往已知的低目标值区域靠拢这叫利用二是往不确定性高、还没被探索过的区域采样这叫探索。网格搜索是纯粹的盲试随机搜索也只是增加了盲试的随机性而贝叶斯优化像是一个有经验的寻宝者每走一步都会根据之前的痕迹判断下一步该往哪挖。Matlab中的bayesopt函数就是基于这套理论实现的。我们只需要定义超参数空间、目标函数和迭代次数剩下的采样策略、代理模型更新都会自动处理。实际用下来通常20到50次评估就能找到一组相当不错的参数对比网格搜索动辄几百上千次效率提升非常明显。2.2 优化目标该怎么定义贝叶斯优化的目标是让一个标量损失函数尽量小。很多初学者直接把测试集准确率当成目标这很容易翻车。因为如果直接用测试集指导搜索就等于让测试集参与了训练最终的准确率是虚高的换一批新数据马上打回原形。正确的做法是在训练集内部做交叉验证用交叉验证损失作为目标函数。我习惯用5折分层交叉验证每次训练随机森林然后算平均的分类损失。分层的意思是每一折训练集和验证集中各类别比例尽量保持一致这对故障诊断这种不平衡数据很重要。如果数据特别不平衡直接使用分类错误率会造成模型严重偏向多数类这时候最好把目标函数改成F1分数的负值或者AUC的负值。因为bayesopt默认最小化目标所以F1分数要取负数才能让“F1越大”对应“损失越小”。另外还要注意目标函数中是否包含随机性。随机森林训练本身有随机性同样的超参数在不同随机种子下会得到略有不同的错误率。bayesopt有一个选项叫IsObjectiveDeterministic默认是false意思是它会认为目标函数带有噪声。如果强行设成true可能会导致优化过程过于自信把某些偶然的低错误率当成真实的最佳点。3. Matlab下的完整实现流程3.1 数据准备与特征工程为了让大家能直接跑通流程我这里用一个仿真特征数据来演示。假设已经通过振动信号处理得到了一张特征表X每一行是一个样本每一列是一个统计特征比如均值、峰值、有效值、峭度、频域重心等。标签向量Y是故障类型编码1表示正常2表示某类故障3表示另一类故障。第一步是划分训练集和测试集。这里有一个必须遵守的顺序先划分数据再做归一化。如果先对整个数据集做归一化再把训练集和测试集分开就会出现信息泄漏测试集的信息已经通过均值和标准差混进了训练过程。正确的做法是只从训练集中计算归一化用的均值和标准差然后用这套参数去变换训练集和测试集。rng(42); holdout cvpartition(Y, Holdout, 0.3); idTrain training(holdout); idTest test(holdout); Xtrain X(idTrain, :); Ytrain Y(idTrain, :); Xtest X(idTest, :); Ytest Y(idTest, :); % 基于训练集计算归一化参数 mu mean(Xtrain, 1); sigma std(Xtrain, 0, 1); sigma(sigma 0) 1; % 防止常数列导致除零 Xtrain (Xtrain - mu) ./ sigma; Xtest (Xtest - mu) ./ sigma;很多特征可能不在一个量纲上比如均值和峭度差了几十倍如果不归一化树模型虽然在分裂时按阈值找不太受量纲影响但后续做特征重要性和模型解释时会别扭。更关键的是某些特征工程步骤需要算距离比如后续要加SMOTE或做聚类归一化就是必须的了。3.2 随机森林训练与贝叶斯优化参数设置这一步是整个流程的核心。我先定义一个目标函数输入一组超参数通过交叉验证返回一个标量损失。这个函数要做的事情很简单用当前的树数量和叶子节点限制训练一个随机森林然后计算交叉验证错误率。function loss rfObjFun(params, X, Y) cv cvpartition(Y, KFold, 5, Stratify, true); tpl templateTree(... MinLeafSize, params.MinLeafSize, ... MaxNumSplits, params.MaxNumSplits); mdl fitcensemble(X, Y, ... Method, Bag, ... NumLearningCycles, params.NumTrees, ... Learners, tpl, ... CVPartition, cv); loss kfoldLoss(mdl); end然后定义超参数搜索空间并调用bayesopt。这里我选择三个最关键的参数树的数量、叶子节点最少样本数、每棵树最大分裂数。搜索范围不要设得太夸张比如树数量从50到500最小叶子从1到50最大分裂从5到200。范围太大高斯过程会很难拟合出有意义的代理模型范围太小真正的最优值可能被排除在外。vars [ optimizableVariable(NumTrees, [50, 500], Type, integer) optimizableVariable(MinLeafSize, [1, 50], Type, integer) optimizableVariable(MaxNumSplits, [5, 200], Type, integer) ]; objFun (params) rfObjFun(params, Xtrain, Ytrain); results bayesopt(objFun, vars, ... MaxObjectiveEvaluations, 40, ... AcquisitionFunctionName, expected-improvement-plus, ... IsObjectiveDeterministic, false, ... UseParallel, true, ... Verbose, 1);这里有几处细节值得说明。MaxObjectiveEvaluations设为40对于三维参数空间来说已经足够如果数据量很大、每次训练很耗时可以先设成15跑通流程再增加到40。AcquisitionFunctionName用的是expected-improvement-plus它对带噪声的目标函数更稳不容易陷入局部最优。UseParallel设为true时可以让多个超参数组合在不同worker上同时训练但前提是Matlab并行池已经开启可以用parpool命令手动启动。3.3 优化结果分析贝叶斯优化跑完后bayesopt会在命令行输出一张收敛图显示每一轮评估的最小目标值。正常情况下目标值会先快速下降然后逐渐趋于平稳。如果最后几十轮还在明显波动说明目标函数噪声太大或者搜索范围不合适。取出最优超参数然后重新训练最终的随机森林模型。注意这次是在全部训练数据上训练不再做交叉验证。bestParams results.XAtMinObjective; disp(bestParams); tplFinal templateTree(... MinLeafSize, bestParams.MinLeafSize, ... MaxNumSplits, bestParams.MaxNumSplits, ... Reproducible, true); finalModel fitcensemble(Xtrain, Ytrain, ... Method, Bag, ... NumLearningCycles, bestParams.NumTrees, ... Learners, tplFinal); pred predict(finalModel, Xtest); acc mean(pred Ytest); disp([测试集准确率: , num2str(acc)]); confMat confusionmat(Ytest, pred); disp(confMat);故障诊断场景下我一般不会只看准确率至少要看混淆矩阵。比如一个二分类问题中正常样本有900个故障样本只有100个就算把所有样本都判成正常准确率也有90%但这个模型完全没有诊断能力。所以我会额外计算每一类的召回率重点看少数类是不是被大量错判。特征重要性也是随机森林的一大卖点可以在训练完之后直接拿到imp predictorImportance(finalModel); [~, idx] sort(imp, descend); disp(idx(1:10));这样就能看出是哪几个传感器特征对故障决策贡献最大。如果发现前几位特征和我们做机理分析时判断的特征不一致就需要回头检查特征提取流程或数据质量。4. 实操中的避坑与问题排查4.1 常见错误与解决第一个坑是数据泄漏。除了归一化要在划分之后做特征选择也要小心。如果先用全样本做了卡方检验或互信息筛选再划分训练集和测试集筛选时已经看过了测试集的分布后面评估的准确率就会虚高。一种安全的做法是把特征筛选也放到每一折交叉验证里面去做或者用随机森林自身的特征重要性来辅助判断但不要在优化流程外面用全样本筛选后再送入模型。第二个坑是类别不平衡导致优化方向跑偏。我刚开始做某套故障诊断流程时正常样本占85%故障样本占15%直接优化交叉验证错误率最后贝叶斯优化找到的参数偏向于把大多数样本判成正常。后来我把目标函数改成了宏平均F1的负值少数类的召回率才明显提升。在目标函数里使用F1需要先获得每一折的预测标签再按类别计算精确率和召回率。代码可以写成循环交叉验证的每一折然后汇总也可以使用kfoldPredict先生成所有样本的预测标签再和真实标签计算F1后者写起来更简洁。第三个坑是搜索范围设置不当。有一回我把MaxNumSplits的上限设到了500但训练样本一共才200个这个上限完全没有约束力高斯过程反而觉得这个参数不重要搜索效率下降。后来我根据样本量把上限改成差不多样本数的三分之一效果就好多了。经验法则是最大分裂数不用超过样本数叶子节点最少样本数不超过总样本量的10%树的数量在100到300之间往往比较够用。第四个坑是并行计算时随机种子不一致。UseParallel开启后每个worker的随机数流是独立的同一组超参数在不同worker上可能得到不太一样的损失值。如果发现每次重复运行bayesopt得到的最优参数差异很大可以试着固定随机种子或者减少并行度先保证流程稳定再考虑速度。4.2 参数选择与性能权衡速查表我把常用的超参数范围整理成一个速查表方便大家在开始调参前有一个基准。注意这些范围只是起点具体数据还是要适当调整尤其是样本量很小的时候。超参数建议范围对模型的影响补充说明树的数量50~500越多越稳定训练越慢超过1000后收益很小叶子节点最少样本数1~50越小越容易过拟合样本少时适当调大每棵树最大分裂数10~样本数越大越能拟合细节样本量小时别设太大每次训练交叉验证折数5或10折数多估值更稳但更慢用分层抽样贝叶斯优化迭代次数20~50越多越接近最优先跑小迭代验证流程这里还有一个容易忽略的点树模型在训练前不需要做特征归一化但我在前面还是做了归一化原因是为了后续扩展方便。比如后面要加入SMOTE过采样或者用距离类算法对比就需要标准化特征。如果你确定只用一个随机森林模型那归一化可以省去。不过我个人建议还是保留因为一旦流程中加入了其他对比模型统一的数据预处理会让结果更公平。4.3 如何理解贝叶斯优化的收敛曲线很多第一次用bayesopt的朋友看到控制台里每轮输出的目标值上下跳动会以为程序跑飞了。其实这是非常正常的。因为目标函数本身带有随机性交叉验证错误率会有波动而且贝叶斯优化前期会刻意探索一些不确定的区域目标值自然会有起伏。我判断收敛的指标有三个。第一最小目标值是否在最后十轮内没有明显下降第二最优超参数是否集中在某个小范围内第三results对象里的ObjectiveMinimumTrace曲线是否已经进入平台期。如果满足这三点基本可以放心把当前最优参数用到最终模型上。如果曲线还是一路走低说明搜索还没收敛可以增加评估次数或者适当缩小搜索空间。5. 扩展从离线诊断到在线监测5.1 模型部署与预测流程优化完成的随机森林模型不只是用来跑一次测试集最后还是要部署到监测流程里。我在实际项目中会把模型和归一化参数保存成一个文件在线预测时一起加载。modelData.model finalModel; modelData.mu mu; modelData.sigma sigma; save(modelData.mat, modelData);在线预测时新来的传感器数据要先提取同样的特征再利用保存的mu和sigma做归一化然后调用predict。这里常见的错误是部署端忘记了归一化或者用新数据的均值重新归一化这会导致训练和预测时特征分布不一致模型性能大打折扣。S load(modelData.mat); model S.modelData.model; mu S.modelData.mu; sigma S.modelData.sigma; newFeature extractFeatureFromSignal(currentSignal); newFeature (newFeature - mu) ./ sigma; label predict(model, newFeature);实时监测系统里还需要考虑一个延迟问题每段信号要计算多少个特征、多长时间输出一次诊断结论。随机森林的预测速度很快几百棵树处理一条样本也只需要毫秒级瓶颈通常在特征提取环节。如果特征计算太慢可以考虑把滑动窗口重叠率降低或者用更简单的统计特征。5.2 后续可以做的优化方向这套贝叶斯优化随机森林流程跑通之后还有很多可以扩展的地方。第一个方向是增加更多超参数。比如每次分裂时的特征抽样数量Matlab中对应NumVariablesToSample它控制每棵树的随机性调好后往往能进一步降低错误率。但参数维度越高贝叶斯优化需要的评估次数也越多不能盲目往上加。第二个方向是做特征选择。先用一次随机森林算出特征重要性去掉后30%的不重要特征再重新跑贝叶斯优化。这样做的好处是降低特征维度和噪声模型效果可能会更好训练速度也会更快。第三个方向是和其他模型对比。贝叶斯优化不只适用于随机森林也适用于SVM、K近邻、神经网络。关键是把目标函数设计成通用的输入超参数结构体内部调用对应模型训练和交叉验证这样就可以在同一份数据上公平对比不同算法的上限。第四个方向是处理极度不平衡的故障样本。除了修改目标函数还可以在训练集内部做SMOTE过采样但千万不能对测试集做过采样。把过采样放到交叉验证的每一折内部流程会稍微复杂一点但结果更可信。最后再分享一点我个人的体验。贝叶斯优化最大的价值不是玄学调参而是把“试参数”变成有信息积累的搜索过程。但也不要指望模型能靠调参一步登天特征工程和数据质量才是故障诊断效果的地基。我踩过不少坑之后学到的习惯是先用一小部分数据快速跑通整套流程确认没有数据泄漏再上全量数据先观察交叉验证和测试集指标是否一致再去优化参数。这套方法本身没有太多门槛真正拉开差距的是分析数据的细致程度。
阅读完成 · 觉得有帮助?
咨询建站