ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

随机森林与锂离子电池剩余寿命预测:完整建模流程与避坑指南

随机森林与锂离子电池剩余寿命预测:完整建模流程与避坑指南 简介一套基于Python随机森林模型实现锂离子电池剩余寿命预测的完整项目资料面向机器学习入门及进阶学习者可用于毕业设计、课程设计、大作业或工程实训旨在解决电池健康管理中的剩余寿命估计与预测建模问题。资源压缩包共117个文件整体约152.98MB其中包含106个xlsx原始充放电数据文件、5个py数据处理与预测脚本、4个csv中间结果文件以及1个说明文档文件类型划分明确便于对照学习。目前已有245人学习。项目内容覆盖现有寿命预测方法的调研与对比、从Excel中提取充电和放电阶段数据、借助pandas和numpy完成数据清洗与格式转换并构建随机森林模型完成剩余寿命预测。通过完整的数据集和可运行代码读者能够清晰理解从原始数据预处理到模型结果输出的整套流程并可在具备一定Python编程基础的前提下自行调试、扩展算法功能或调整模型参数。1. 随机森林做锂离子电池剩余寿命预测是不是靠谱选择很多做电池预测性维护的工程师一上来就尝试LSTM或者Transformer结果发现手里的数据往往只有几十到几百次循环深度模型连一个稳定的容量退化趋势都拟合不出来。随机森林在这个场景下反而更稳几十个循环的数据就能形成可用基线支持非线性容量衰减拟合训练快、无分布假设、还能给出特征重要性。这篇笔记围绕锂离子电池剩余寿命预测完整给出从原始循环数据到随机森林模型的一整套落地路径包括目标口径选择、特征构造、标签生成、时序划分、随机森林超参数、避坑经验与留一电池验证。适合已经装好Python和scikit-learn、准备交付第一版可解释RUL结果的工程读者。2. 电池RUL的数据与特征先想清楚预测容量还是预测循环数2.1 为什么是随机森林而不是经验拟合法或LSTM电池容量衰减曲线不是一条纯指数曲线。常见锂离子动力电池的容量随循环数下降大致分三段早期快速衰减、中期近似线性、后期加速衰减。传统做法是拿双指数经验模型、多项式拟合或对数模型去拟合这条曲线然后外推到失效阈值。这个方法的问题在参数依赖电池化学体系和工况同一配方在A电池上拟合得很好换一个温度区间或充放电倍率就完全失效。LSTM这类时序模型能捕捉循环之间的依赖关系但训练需求大电池寿命预测的样本量根本喂不饱。随机森林回归把容量衰减当作表格回归问题每个循环计算一组特征输入模型输出该循环的放电容量或剩余寿命。森林通过bootstrap采样和随机特征子集构建多棵树每棵树的叶子输出均值最终预测是所有树的平均。它不需要假设表达式几十条样本就能训练还自带oob_score做自检。工程上另一个现实理由是scikit-learn的实现足够简洁调参范围小后期刷新模型只需要重新fit不需要排查学习率、梯度累积这些环节。随机森林有一个明确短板它的本质是把输入空间划分成若干矩形区域做分段常数近似对训练数据范围以外几乎没有外推能力。而寿命预测的核心恰恰是预测未来。所以实用的工程结构不是“用整段训练集拟合然后一条曲线外推到退役”而是“用过去N个循环的特征预测未来M个循环的容量”配合滚动窗口做迭代推送。这一点在第4章和第6章会展开。2.2 预测容量还是直接预测RUL两个口径RUL有两套建模口径第一套是直接把“剩余循环数”当标签。假如电池在第80圈失效在第150圈标签就是70。随机森林内部会把目标值分布到不同叶子节点取叶子均值作为输出。问题出在样本分布上整个训练集里越接近退役点的样本越少剩余寿命趋近于0的区域几乎是空的直接回归RUL会让模型在尾部输出平台化常数误差急剧放大。第二套口径是预测“当前循环的放电容量”或容量保持率SOH先把容量衰减曲线拟合出来再用失效阈值求交点。我更常用这一套。它的好处是每个样本的标签都在同一个物理尺度上模型不会在尾部被少量大误差样本带偏对外报告RUL时再用“预测容量曲线下穿阈值的位置减去当前循环数”换算。对比项直接回归RUL先预测容量再换算RUL目标变量剩余循环数放电容量Ah或SOH样本分布靠近退役端几乎无样本容量分布全程相对连续容量再生响应标签容易反向跳变可作为噪声处理工程交付直接给数字解释性差物理含义清晰便于解释选择先预测容量还有一个附加值即便随机森林拟合效果一般容量曲线和阈值线叠加画出来业务方一眼就能看懂“预测预期在第几圈退役”。直接回归RUL等于把模型当成黑匣子交付出了问题很难定位是标签的问题还是模型的问题。2.3 数据从哪来公开循环数据集与可用的充放电特征最常被采用的锂离子电池公开数据是NASA PCoE的锂电池数据集一批额定容量约2Ah的电池在多工况下做充放电循环记录每圈循环的电压、电流、温度序列并定期做阻抗测试。行业里做寿命预测几乎都把这份数据当基线。CALCE、牛津大学也有不同采样规格的数据字段结构大同小异。工程里常用的每圈循环特征包括循环数、恒流充电时间、放电容量、等压降时间、温度均值、内阻。恒流充电时间从充电曲线上取恒流转恒压的切换点随着老化会明显缩短等压降时间取放电过程中电压从4.2V降到3.8V的耗时这个时长随老化下降很快是论文里出现频率很高的特征。直接在模型中使用的特征不需要贪多。随机森林对冗余特征不敏感但特征含义必须稳定、可重复计算。有两个点要提醒容量绝对数值受环境温度影响做纵向对比时尽量保持同一温度条件容量本身存在再生现象静置或重新充电后容量会短期回升后续生成标签时务必针对这个现象设防。2.4 容量衰减曲线先可视化建模前的数据体检拿到数据先别急着写模型我会先把每个电池的容量随循环数变化的曲线画出来。这一步能直接暴露三个问题第一容量再生现象是否存在曲线尾部是否呈现小锯齿状爬升第二整段数据是否已经穿过失效阈值如果整块电池退役前都高于阈值说明这块电池没有EOL标签不适合直接做监督训练第三初始几圈容量可能异常偏高或偏低往往和测试前的静置时长、校准流程有关。import matplotlib.pyplot as plt plt.plot(cycles[cycle], cycles[capacity], labelcapacity) plt.axhline(1.4, colorred, linestyle--, labelEOL threshold) plt.xlabel(cycle) plt.ylabel(capacity (Ah)) plt.legend() plt.show()画完曲线后要做判断如果曲线末端已经明显低于阈值说明数据覆盖完整寿命可以直接用如果末端仍高于阈值要么继续补充循环数据要么只能用未失效样本做半监督外推不能硬撑成回归标签。这条曲线还决定了后面的阈值取值不要一上来就写死70%先看一眼额定容量再定。3. 从原始循环数据到特征表Python数据处理脚本与EOL标签生成3.1 循环级特征聚合把充放电曲线变成一行特征NASA原始数据的格式是每组循环下分charge、discharge、impedance等时间序列。我通常先不纠结格式细节先写一个聚合函数把每个循环的原始曲线缩成一行特征。下面的代码假设每个循环一个csv文件列名已经做过统一映射。import pandas as pd import numpy as np from pathlib import Path # 假设所有循环csv文件放在 battery_dir 下文件名是 1.csv, 2.csv ... def aggregate_cycle(battery_dir): rows [] for p in sorted(Path(battery_dir).glob(*.csv), keylambda x: int(x.stem)): df pd.read_csv(p) charge df[df[type] charge] discharge df[df[type] discharge] # 恒流充电时间电流维持在较高水平的时间区间 cc_time charge.loc[charge[current] 0.2 * charge[current].max(), time].max() # 放电容量优先用数据源自带字段否则对电流做时间积分 if capacity in discharge.columns: capacity discharge[capacity].iloc[-1] else: capacity np.abs(np.trapz(discharge[current], discharge[time])) / 3600 # 等压降时间放电电压从 high_v 降到 low_v 的耗时 v_high, v_low 4.2, 3.8 mask_high discharge[voltage] v_high mask_low discharge[voltage] v_low if mask_high.any() and mask_low.any(): t_high discharge.loc[mask_high, time].min() t_low discharge.loc[mask_low, time].max() dtime t_low - t_high else: dtime np.nan rows.append({ cycle: int(p.stem), cc_time: cc_time, capacity: capacity, idt_voltage: dtime, avg_temp: discharge[temperature].mean(), }) return pd.DataFrame(rows).sort_values(cycle).reset_index(dropTrue) cycles aggregate_cycle(data/B0005/)逻辑说明恒流充电时间用“电流大于0.2倍最大电流”来判断恒流段是因为很多数据源不直接给充电阶段标签只能从电流曲线反推0.2这个系数是经验值实际要看充电截止电流。放电容量优先信任数据源自带的capacity字段没有的话就用梯形积分换算电流单位是安培、时间单位是秒除3600得到安时。等压降时间这里t_high取首次到达4.2V的时刻t_low取首次跌破3.8V的时刻两者差值是区间长度这样取值才不会被多段波动干扰。如果数据里没有恒流段比如脉冲充电数据集就把cc_time这一列去掉留下capacity、idt_voltage、avg_temp模型依然能跑。特征聚合函数要设计成可扩展的后续增加新特征只改这一个函数就行。3.2 时序划分训练集和测试集不能打散RUL问题本质是时间序列回归测试集里的循环数一定大于训练集。很多从业者写train_test_split(X, y, random_state42)这在电池数据上是严重的时间泄漏。未来循环的容量信息混入训练集测试集误差小到像假数据但部署到新电池上立刻失效。# 按时序划分前70%作为训练后30%作为测试 cutoff int(len(cycles) * 0.7) train cycles.iloc[:cutoff].copy() test cycles.iloc[cutoff:].copy() # 特征列与标签列 feature_cols [cycle, cc_time, idt_voltage, avg_temp] label_col capacity X_train, y_train train[feature_cols], train[label_col] X_test, y_test test[feature_cols], test[label_col]逻辑说明用位置切分而不是train_test_split可以保证测试集在时间上永远晚于训练集模拟的是“用过去预测未来”的真实使用方式。如果要做交叉验证同样不能用默认KFold需要改用sklearn的TimeSeriesSplit它会按时间把数据切成多段每段训练集只包含更早的样本。提示我在这里坚持位置切分的原因很朴素——RUL预测要解决的是“未来”任何混入未来信息的训练过程都等于考试漏题。3.3 EOL标签与RUL标签生成别在容量再生处算错寿命失效阈值一般取额定容量的70%或80%。以额定容量2Ah为例70%就是1.4Ah。寿命终点定义为放电容量首次低于阈值的那一圈。这里的关键词是“首次”因为锂离子电池存在容量再生到了寿命末端附近曲线可能先下穿阈值又回到阈值以上如果按最后记录算EOL会把寿命延长很多圈。threshold 1.4 # 根据电池额定容量和验收标准调整 # 首次低于阈值的索引才是EOL不能用 reverse 找最后一个 eol_idx cycles.index[cycles[capacity] threshold].min() # RUL 达到EOL剩余循环数 cycles[RUL_from_capacity] eol_idx - cycles.index def rul_from_capacity(cap_pred, current_cycle): # 预测容量低于阈值的第一个点就是模型认为的寿命终点 below np.where(cap_pred threshold)[0] if len(below) 0: return None # 预测区间内未失效需要外推 return below[0] current_cycle逻辑说明eol_idx用.min()而不是.max()就是为了防止容量再生把寿命终点推迟到末段。反过来生成RUL标签时也要用首次下穿索引尾部回升样本的RUL保持为0或负值不能变成正数。实际数据里偶尔会遇到第一条记录就已经低于阈值的情况说明这块电池入库时已经不满足验收标准整个数据集的RUL标签全部无效需要换一批数据或者调整阈值。3.4 缺失值与异常循环NaN、0值和第一圈异常随机森林在sklearn实现里不接受NaN所以特征里一旦出现空值训练会直接报错。特征工程量少的时候我会先看每列缺失比例再用中位数填充不用均值。原因是容量特征有明确量纲尾部衰减会让均值偏低中位数对离群点更稳健。# 恒流充电时间缺失用中位数填充 cycles[cc_time] cycles[cc_time].fillna(cycles[cc_time].median()) # 容量为0通常是采样中断用插值补上 cycles[capacity] cycles[capacity].replace(0, np.nan).interpolate(limit_areainside)逻辑说明等压降时间在早期电压未达到4.2V时会产生NaN用中位数填充即可。容量为0大概率是单圈采样中断直接替换成NaN再插值可以保留前后趋势。不要轻易删行删掉某一行会让cycle索引不连续后面滚动预测对齐时会变得很麻烦。第一条循环异常时我会保留数据但观察它对训练集首段的影响随机森林对少量异常点有天然抗性没必要过度清洗。4. 随机森林回归算法训练与调参参数选择、交叉验证与评估指标4.1 最小可运行代码训练、预测、RMSE一条龙如果你还没装scikit-learn先跑一条pip install scikit-learn然后把pandas、numpy、matplotlib一起装上。随机森林回归的训练代码很短核心就是fit和predict两步。import numpy as np from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error from sklearn.ensemble import RandomForestRegressor rfr RandomForestRegressor( n_estimators300, max_depth8, min_samples_leaf3, max_featuresauto, oob_scoreTrue, random_state42, n_jobs-1, ) rfr.fit(X_train, y_train) y_pred rfr.predict(X_test) rmse np.sqrt(mean_squared_error(y_test, y_pred)) mape mean_absolute_percentage_error(y_test, y_pred) * 100 print(fRMSE: {rmse:.4f} Ah, MAPE: {mape:.2f}%)逻辑说明n_estimators设为300在电池数据几十到几百个样本的规模下树的数量已经足够稳定再往上RMSE不会有明显变化但推理时间线性增长。max_depth限制到8防止树在少量样本上长到过深把测量噪声当规律记住。min_samples_leaf设为3保证叶子节点至少带3个样本否则末端预测容易被个别异常循环带偏。max_features使用默认的auto在特征只有四五个时等同于取全部特征随机性主要来自bootstrap采样。oob_scoreTrue用来输出袋外误差可以快速判断模型所处的拟合状态不用额外划分验证集。随机森林对输入特征的尺度不敏感不需要标准化。但要注意cycle列数值从1到一百多capacity在1到2之间树分裂时特征内部的数值尺度差异不影响计算因为分裂阈值是按每个特征自身分布找的。4.2 关键参数表与随机搜索调参TimeSeriesSplit避免时间泄漏随机森林的有效参数不多调参优先级从高到低大致是max_depth、min_samples_leaf、n_estimators、max_features。电池数据量小网格搜索很快。但交叉验证必须用TimeSeriesSplit否则调参过程本身也会引入时间泄漏。from sklearn.model_selection import TimeSeriesSplit, RandomizedSearchCV tscv TimeSeriesSplit(n_splits4, gap0) param_dist { n_estimators: [200, 300, 500], max_depth: [5, 8, 10, 12], min_samples_leaf: [1, 3, 5], max_features: [0.6, 0.8, 1.0], } search RandomizedSearchCV( RandomForestRegressor(oob_scoreFalse, random_state42, n_jobs-1), param_distributionsparam_dist, n_iter20, cvtscv, scoringneg_root_mean_squared_error, refitTrue, ) search.fit(X_train, y_train) best_model search.best_estimator_逻辑说明TimeSeriesSplit把数据依次切成4段第1段训练后验证第2段前两段训练后验证第3段以此类推这是对滚动预测最贴近的模拟。gap参数在时间序列回归里可以保持为0如果担心相邻循环的自相关性太强也可以设成2到3电池每圈循环之间的独立性尚可我一般不做保守gap。RandomizedSearchCV在20组参数里跑4折每折都是一次完整的随机森林训练数据量小时几分钟内能跑完。参数建议范围说明n_estimators200~500样本少300左右足够max_depth5~12防止记住噪声min_samples_leaf2~5叶子太小预测曲线锯齿严重max_features0.6~1.0特征少时取1.0oob_scoreTrue训练阶段快速自检random_state固定保证复现注意RandomizedSearchCV里的n_iter20只是常规起步如果参数空间大可以提到30或40。电池数据量小单次训练快多跑几十组不亏。4.3 评估指标选什么RMSE之外寿命误差才是工程关心的容量预测的RMSE是建模指标单位是Ah。但业务方真正关心的是“这块电池还能跑多少个循环”。所以评估要同时看两个维度容量拟合误差和寿命点误差。寿命点误差是把测试集中每一圈的预测容量换算成RUL再求与真实EOL的偏差。# 将预测容量转换为RUL再评估寿命偏差 eol_pred_cycle [] for i in range(len(X_test)): cur_cycle X_test[cycle].iloc[i] cap_pred_future best_model.predict(X_test.iloc[[i]]) if cap_pred_future[0] threshold: # 当前循环预测已经低于阈值RUL为0 eol_pred_cycle.append(cur_cycle) else: # 简化用当前预测容量和一段平均衰减斜率估算剩余寿命 slope -0.002 if len(y_train) 50 else -0.004 rul_pred (cap_pred_future[0] - threshold) / abs(slope) eol_pred_cycle.append(cur_cycle rul_pred) life_error np.array(eol_pred_cycle) - eol_idx print(f寿命误差均值: {np.mean(np.abs(life_error)):.1f} cycles)逻辑说明这里用一个近似斜率做容量到RUL的换算是“先预测容量再换算寿命”的简版方案。更严谨的做法是生成一条到退役前的容量轨迹再把轨迹下穿阈值的位置作为EOL后面第6章会说到。注意RMSE再小如果寿命偏差在几十个循环以上交付时依然会被质疑同样的容量RMSE在衰减较快的中期换算出的寿命误差很小到了衰减平缓的早期寿命误差反而大。报告中应当把两个指标同时列出来。4.4 运维交付时RUL预测必须写清训练截止位置模型训练完不是终点交付给运维时任何RUL结论都要带着预测范围的上下文。只写“模型RMSE0.03Ah”业务方无法判断这个结论能外推多远。我会在模型报告里固定写两行行训练截止循环数和预测起点。train_cutoff_cycle train[cycle].max() print(f模型训练截止于第{train_cutoff_cycle}圈) print(f预测范围从第{train_cutoff_cycle 1}圈开始)这样业务方就清楚模型不承诺预测到无限远期只保证训练覆盖范围内的规律是可靠的。超出训练范围的滚动预测会随距离增大逐渐退化这个退化速度也可以画出来作为模型置信区间的重要参考。5. 电池RUL预测的5个常见坑时间泄漏、容量再生与尾部外插5.1 随机打散数据集让测试集RMSE漂亮到可疑现象训练集切分用了train_test_split(X, y, random_state42)测试集RMSE只有0.01到0.02预测曲线几乎贴在真实值上但换一块新电池测试误差直接跳到两三百个循环。原因随机打散把未来的循环放进了训练集随机森林学到的是同一段容量曲线内部插值而不是时间外推。时间序列数据一旦打散信息泄漏就已经发生指标再好看也没有参考价值。解决改用按时间位置截断的划分方式并用TimeSeriesSplit做交叉验证。同时检查代码里有没有出现shuffleTrue或者没有传shuffle参数的train_test_split调用。我自己第一版就是这样看到漂亮的RMSE以为模型选对了后来复盘才发现是泄漏。5.2 容量再生导致EOL标签算错现象同一条数据换个阈值寿命预测偏差几十个循环把训练标签打印出来发现RUL在电池退役后又变回正数。原因锂离子电池在静置或浅充浅放后容量会回升。EOL计算如果取整段记录中容量最后一次低于阈值的点寿命终点会被推迟到数据末端如果取最低点RUL序列又会过早归零。解决EOL标签必须用首次下穿阈值的索引。同时在特征工程里加一列min_capacity_so_far记录历史最低容量让随机森林明确“容量接近历史最低就是接近寿命终点”。这样即使容量回升标签也不会反向跳变。5.3 训练集尾部样本少导致RUL外插塌陷现象测试集预测曲线在训练截止点附近还能贴合真实值再往后几十个循环就变成水平线预测容量停在1.6左右不再下探。原因随机森林是分段常数模型输入特征一旦超出训练范围分裂区间外侧没有节点可用预测值等于最后一个叶子节点的均值。训练集尾部样本稀疏外推段自然会塌陷成常数。解决训练截止点不要取数据末尾至少留出5到10个循环作为尾部缓冲。对外报告RUL时使用多点外推比如先预测容量序列再对序列做直线拟合求下穿点或者改造预测目标为容量增量变化率让模型预测小步变化而不是大步外推。5.4 直接回归RUL在接近退役端误差放大现象同一模型在早期循环预测RUL误差只有几十圈到了距离EOL只剩20圈时预测偏差反而超过80圈。原因直接回归RUL时接近退役端的标签趋近于0样本少且离散度大。随机森林在这些区域叶子更粗预测值被平均化RUL越小相对误差越明显。解决优先采用先预测容量再换算RUL的两步法。预测输出在换算前必须加截断RUL计算为负时直接置0。训练损失按容量做加权不要对RUL本身加权因为容量误差的尺度更均匀。5.5 oob_score和测试集评分不一致现象oob_score的R²达到0.98TimeSeriesSplit交叉验证的R²只有0.85两个数差距明显不知道该信哪个。原因oob_score评估的是训练集内的袋外样本这些样本的时间范围与训练集重叠本质上是测试插值能力。测试集是未来的时间点测试的是外推能力。两者天然有差距差距越大说明模型越依赖训练集的分布。解决以TimeSeriesSplit的验证结果为准oob_score只作为训练自检。如果两者差距过大优先降低max_depth和min_samples_leaf提高模型平滑度而不是盲目增加树的棵数。6. 特征重要性与模型验证让随机森林RUL结果可解释、可交付6.1 特征重要性随机森林自带的解释性入口随机森林在工程里最实用的部分不是预测精度而是scikit-learn直接暴露了feature_importances_。训练完成后把它画出来通常会发现cc_time和capacity类特征排在前列循环数本身反而被冗余特征分流。注意随机森林的基尼重要性在特征有相关性时会分散权重cc_time与容量高度相关重要性排名不能直接解读成因果关系。import pandas as pd import matplotlib.pyplot as plt importance pd.Series( best_model.feature_importances_, indexfeature_cols, namefeature_importance, ).sort_values(ascendingFalse) importance.plot.barh(figsize(6, 4)) plt.title(Random Forest Feature Importance) plt.tight_layout() plt.savefig(rf_feature_importance.png)做完特征重要性分析后我一般会拿三个电池的数据交叉验证一遍如果某个特征在不同电池上重要性排名波动很大说明它采集精度不稳定部署时要格外小心。最终保留的特征集越少模型越不容易被采集误差打穿。6.2 后处理平滑容量预测锯齿与Savitzky-Golay滤波电池循环数据本身有噪声容量再生也会让预测序列出现锯齿。直接用预测容量序列下穿阈值可能在阈值附近来回穿越EOL点抖动明显。常见做法是先把容量预测序列做Savitzky-Golay滤波再用平滑后的序列求下穿点。from scipy.signal import savgol_filter import numpy as np # 预测未来30个循环的容量序列 future_cycles np.arange(X_test[cycle].iloc[-1] 1, X_test[cycle].iloc[-1] 31) X_future pd.DataFrame({cycle: future_cycles}) for col in feature_cols: if col not in X_future.columns: X_future[col] X_train[col].tail(10).median() cap_pred_seq best_model.predict(X_future[feature_cols]) cap_smooth savgol_filter(cap_pred_seq, window_length5, polyorder2) below_idx np.where(cap_smooth threshold)[0]逻辑说明savgol_filter的window_length5、polyorder2对随机森林输出的短序列比较友好。窗口过大比如超过7可能把真实的容量衰减拐点也抹平polyorder取2或3都行取太高会出现滤波过拟合。这种“预测未来序列再找下穿点”的方式才算完整交付单点预测乘斜率估算RUL只适合内部快速验证。6.3 留一电池验证让评估结果更接近真实交付参数调完最后一步建议做留一电池法验证。每个电池的循环数据轮流作为测试集其余电池作训练集得到的寿命误差分布比单电池的时序切分更接近现场情况因为现场遇到的往往是全新电池个体。# batteries 是以电池编号为 key 的 DataFrame 字典 for battery_id, df_cycle in batteries.items(): train_ids [b for b in batteries if b ! battery_id] train_df pd.concat([batteries[b] for b in train_ids], ignore_indexTrue) test_df df_cycle threshold test_df[rated_capacity].iloc[0] * 0.7 model RandomForestRegressor( n_estimators300, max_depth8, min_samples_leaf3, random_state42, n_jobs-1, ) model.fit(train_df[feature_cols], train_df[label_col]) pred model.predict(test_df[feature_cols]) true_eol test_df.index[test_df[label_col] threshold].min() pred_eol_points np.where(pred threshold)[0] if len(pred_eol_points) 0: pred_eol len(test_df) else: pred_eol pred_eol_points[0] print(f{battery_id}: EOL error {pred_eol - true_eol} cycles)这样一次验证跑下来每个电池个体差异会直接反映在误差分布上。如果发现某一类电池的系统性偏差偏大可以回到第5章去查尾部样本的处理方式或者按电池批次增加特征。我第一版随机森林RUL脚本犯的错到现在还留在commit记录里随机打散数据测试集RMSE漂亮得像教学案例后来在真实退役电池上一验证二十个循环级别的寿命偏差把我彻底打醒。从那以后这个方向我再也不敢碰shuffle、不敢碰单点外插、不敢只看RMSE不看EOL误差。如果你也在搭自己的第一版先把这套随机森林流程完整跑通站稳了再往高斯过程或LSTM升级。希望帮到你。本文还有配套的精品资源点击获取
返回列表