简介:这份资源面向制造业能源管理与数据分析方向的学习者,聚焦工业用电量的机器学习预测问题,帮助读者理解如何用数据科学手段识别高能耗环节、优化能效管理。压缩包共15个文件,以11个Python脚本为主,涵盖数据读取与预处理、多模型对比实验及图形界面展示,另含3张预测结果与数据可视化图片和1个模型训练检查点文件,整体约175KB,体量轻便、便于本地复现。内容围绕XGBoost、随机森林、逻辑回归、LightGBM、AdaBoost、SVM、KNN等模型展开横向对比,并配有Transformer时间序列预测结果,可直观比较不同算法在用电量预测任务上的表现差异。已有234人学习,适合希望掌握完整建模流程、对照实验设计与结果可视化思路的读者参考借鉴。
1. 从一张对不上的电费单说起:这套预测方案到底解决什么问题
某制造企业的能源管理岗每个月都要做一件事:把下个月的电费预算报上去。过去这套流程靠的是老师傅翻台账、看去年同期、再拍脑袋加个安全系数,结果常常是预算批下来和实际账单差出一大截,年底一算,偏差累积起来够买一条产线的小设备。问题不在于人不认真,而在于工业用电量本身受排产计划、季节温度、设备检修、班次调整多重因素叠加影响,人脑很难同时把这些变量捋顺。这套「机器学习预测工业用电量」的方案,就是要把这件靠经验的事变成靠模型的事:用历史用电数据加上生产与气象特征,训练出能给出未来月度、周度用电量预测值的模型,并配套一套可复现的训练与评估流程。它适合两类人——正在做数据类毕设或课设、需要一套完整可交付方案的学生,以及要给工厂做能源预算、需要落地一套预测工具链的从业者。制造场景下的用电预测不是学术玩具,它直接连着预算准确率和能耗考核,所以方案从第一天就得按「能验收」的标准来搭。
2. 需求与数据底座:先把预测目标和特征口径定死
2.1 预测粒度与预测窗口怎么选
动手写代码之前,第一件要定死的事是预测粒度和预测窗口。粒度指模型输出的时间单位,常见的有小时级、日级、月级;窗口指提前多久给出预测。工业用电场景里,这两个参数不是拍脑袋定的,而是由业务用途倒推:如果是给月度电费预算用,粒度取日、窗口取提前一个月最合适,因为预算审批周期就是月;如果是给车间做错峰生产调度用,粒度要细到小时、窗口缩到提前一天甚至几小时。我一般会建议学生项目先做日粒度、提前 7 天和提前 30 天两套窗口,既覆盖了课程设计对「多步预测」的要求,又不至于把数据量撑到跑不动。粒度定得太细而样本不足,模型学到的全是噪声,这是很典型的翻车点。
确定粒度后要同步确定评估口径。回归预测不能只看一个指标,通常的做法是同时报 MAE、RMSE 和 MAPE 三个:MAE 告诉你平均差多少度电,直观;RMSE 对大误差更敏感,能暴露偶发的极端偏差;MAPE 是百分比,方便跨月份、跨产线横向比较。三个指标一起看,才能判断模型是「整体偏」还是「偶尔崩」。
2.2 数据表结构与特征工程口径
工业用电预测的数据底座一般由三张表拼起来:用电量表、生产排产表、气象表。用电量表是主表,字段包括日期、表计编号、有功电量、峰平谷分时电量;排产表提供产量、班次数、设备开机台数;气象表提供日均温度、湿度。三张表按日期对齐,形成宽表。下面是一个最小可用的建表与特征构造示例,用 SQL 写,方便直接落到数据库里:
-- 用电主表:按日聚合,保留分时结构 CREATE TABLE fact_power_daily ( stat_date DATE NOT NULL, -- 统计日期 meter_id VARCHAR(32) NOT NULL, -- 表计编号 energy_kwh DECIMAL(12,2), -- 当日总有功电量 peak_kwh DECIMAL(12,2), -- 峰段电量 flat_kwh DECIMAL(12,2), -- 平段电量 valley_kwh DECIMAL(12,2), -- 谷段电量 PRIMARY KEY (stat_date, meter_id) ); -- 特征宽表:用电 + 排产 + 气象按日对齐 CREATE TABLE feat_power_daily AS SELECT p.stat_date, p.meter_id, p.energy_kwh, p.peak_kwh, p.flat_kwh, p.valley_kwh, s.output_qty, -- 当日产量 s.shift_cnt, -- 班次数 s.machine_on, -- 开机台数 w.avg_temp, -- 日均温度 w.avg_humidity, -- 日均湿度 DAYOFWEEK(p.stat_date) AS weekday, -- 星期几,捕捉周周期 MONTH(p.stat_date) AS month -- 月份,捕捉季节周期 FROM fact_power_daily p LEFT JOIN fact_production_daily s ON p.stat_date = s.stat_date AND p.meter_id = s.meter_id LEFT JOIN dim_weather_daily w ON p.stat_date = w.stat_date;这段 SQL 的关键设计点有三个。第一,用电量保留峰平谷分时结构而不是只留总量,因为分时电量里藏着生产班次信息,是强特征。第二,排产表和气象表都用 LEFT JOIN,保证用电主表的每一行都保留,缺失的生产或气象数据后续在 Python 侧统一填充,避免在 SQL 阶段就把样本丢掉。第三,weekday 和 month 两个派生字段是周期特征的显式编码,工业用电有非常明显的周内节律(周末减产)和季节节律(夏季降温负荷),把这两个周期显式喂给模型,比让模型自己从日期里猜要稳得多。
提示:特征宽表建好后先做一次缺失率和异常值体检,温度缺失超过 5% 的月份要单独标记,否则模型会把「没数据」误学成「用电低」。
2.3 技术选型:为什么是梯度提升树而不是深度学习
选型上,这个场景我一般直接上梯度提升树(LightGBM 或 XGBoost),而不是 LSTM 那类时序深度模型。原因很实在:工业用电的样本量通常是「几年 × 若干表计」,撑死几千到几万行,深度模型在这个量级上很容易过拟合,调参成本还高;而梯度提升树对中小样本、混合类型特征(数值 + 类别)天然友好,训练快、可解释性强,特征重要性还能直接拿去做业务解释,答辩时被问「为什么这个特征重要」能答得上来。只有当数据量到十万级以上、且需要做很细粒度的多步时序建模时,才值得考虑深度模型。这个取舍判断本身就是方案里要写清楚的一段,别跳过。
3. 总体架构与模块划分:从原始表到预测值的四层链路
3.1 四层架构与模块职责
整套方案按数据流分成四层,每层职责单一,方便单独调试和替换。第一层是数据接入层,负责从数据库或 CSV 拉取原始表并做时间对齐;第二层是特征工程层,负责缺失填充、异常处理、周期编码和滞后特征构造;第三层是建模层,负责训练、验证、超参搜索和模型持久化;第四层是预测与评估层,负责滚动预测、指标计算和结果落表。四层之间用中间文件或数据表衔接,任何一层出问题都能单独定位,不会牵一发动全身。
模块划分上,我习惯拆成五个 Python 模块:data_loader.py管接入,feature_builder.py管特征,trainer.py管训练与验证,predictor.py管滚动预测,evaluator.py管指标。每个模块对外只暴露一个主函数,输入输出都是 DataFrame,这样单元测试好写,答辩演示时也能一层层跑给评委看。
3.2 特征工程模块的实现要点
特征工程是这套方案里最影响效果的一环,也是最容易被学生做薄的一环。除了前面 SQL 里的周期字段,还要构造滞后特征和滚动统计特征:滞后 1 天、7 天、30 天的用电量,以及过去 7 天、30 天的均值和标准差。滞后特征让模型能利用近期水平,滚动统计让模型感知波动幅度。下面这段是特征构造的核心实现:
import pandas as pd import numpy as np def build_features(df: pd.DataFrame) -> pd.DataFrame: df = df.sort_values(["meter_id", "stat_date"]).copy() # 按表计分组做滞后,避免不同表计的数据串行 grp = df.groupby("meter_id")["energy_kwh"] for lag in (1, 7, 30): df[f"lag_{lag}"] = grp.shift(lag) # 滚动均值与标准差,窗口 7 和 30 for win in (7, 30): df[f"roll_mean_{win}"] = grp.shift(1).rolling(win).mean().reset_index(level=0, drop=True) df[f"roll_std_{win}"] = grp.shift(1).rolling(win).std().reset_index(level=0, drop=True) # 缺失填充:滞后特征用前向填充,滚动统计用中位数兜底 lag_cols = [c for c in df.columns if c.startswith(("lag_", "roll_"))] df[lag_cols] = df.groupby("meter_id")[lag_cols].ffill() df[lag_cols] = df[lag_cols].fillna(df[lag_cols].median()) return df这段代码有两个必须讲清的设计点。第一,所有滞后和滚动特征都先shift(1)再计算,确保预测目标日 t 的特征只用到 t 之前的信息,这是防止数据泄漏的红线,很多毕设模型指标虚高就是因为把当天数据也算了进去。第二,分组用meter_id,因为不同表计之间没有可比性,混在一起做滞后会把 A 表计的用电量错配到 B 表计上。滚动标准差用中位数兜底而不是均值,是因为用电序列里偶发的高值会把均值拉偏,中位数更稳。
3.3 训练与验证模块:时间序列不能随机切分
训练模块的核心不是调参,而是验证方式。时间序列数据绝对不能随机切分训练集和测试集,否则模型会「看到未来」,指标好看但上线就崩。正确做法是按时间顺序切:前 70% 做训练,中间 15% 做验证调参,最后 15% 做测试,测试集只在最后评估时用一次。下面是一个按时间切分并训练 LightGBM 的骨架:
import lightgbm as lgb from sklearn.metrics import mean_absolute_error, mean_squared_error def time_split(df, train_ratio=0.7, val_ratio=0.15): dates = sorted(df["stat_date"].unique()) n = len(dates) tr_end = dates[int(n * train_ratio)] va_end = dates[int(n * (train_ratio + val_ratio))] train = df[df["stat_date"] < tr_end] val = df[(df["stat_date"] >= tr_end) & (df["stat_date"] < va_end)] test = df[df["stat_date"] >= va_end] return train, val, test def train_model(train, val, feature_cols): params = dict( objective="regression", metric="mae", learning_rate=0.05, num_leaves=31, min_data_in_leaf=20, feature_fraction=0.8, bagging_fraction=0.8, bagging_freq=5, verbose=-1 ) dtrain = lgb.Dataset(train[feature_cols], label=train["energy_kwh"]) dval = lgb.Dataset(val[feature_cols], label=val["energy_kwh"], reference=dtrain) model = lgb.train( params, dtrain, num_boost_round=2000, valid_sets=[dval], callbacks=[lgb.early_stopping(100), lgb.log_evaluation(200)] ) return model参数上,learning_rate=0.05配合early_stopping(100)是中小样本的稳妥组合,学习率再低训练慢、再高容易欠拟合;min_data_in_leaf=20是防止叶子节点过细导致过拟合的关键,样本量小的时候这个值要往上调。feature_fraction和bagging_fraction都设 0.8,是给模型加一点随机性,提升泛化。这些参数不是抄来的默认值,而是按「几千行样本、几十个特征」这个量级调的,换数据量要重新试。
3.4 预测与评估模块:滚动预测怎么落地
预测模块要解决的是「用训练好的模型,按天往前滚,输出未来 N 天预测值」。难点在于滞后特征在预测期是未知的,必须用预测值回填再继续往前推,这叫递归多步预测。实现上要维护一个滑动窗口,每预测一天就把预测值塞回历史序列,再构造下一天的特征。评估模块则把预测值和真实值对齐,按月度、按表计分别算 MAE、RMSE、MAPE,输出一张评估表。这张表是验收时最有说服力的材料,比任何口头解释都管用。
4. 避坑与排查:五个最容易翻车的地方
4.1 指标虚高:数据泄漏的典型症状
现象是验证集 MAPE 低到 3% 以下,测试集一跑就飙到 20% 以上。原因几乎都是特征里混进了未来信息,最常见的是滚动统计没做shift(1),或者把目标当天的排产数据也当成了已知特征。解决办法是把特征构造函数单独抽出来做一次「时间穿越测试」:随机抽一天,检查它的所有特征是否只依赖该天之前的数据,发现依赖当天的就改。这个测试我每次都会写进单元测试里。
4.2 缺失值填错方向:把停产日填成了正常日
现象是模型在春节、检修期这类停产日附近预测值明显偏高。原因是缺失填充用了全局均值或前向填充,把停产日的用电量填成了正常生产水平。解决办法是区分「数据缺失」和「真实低负荷」:停产日如果有排产表记录(产量为 0),就应该保留 0 而不是填充;只有排产数据也缺失时才走填充逻辑。填充策略要按业务含义分情况,不能一刀切。
4.3 表计混算:不同产线数据串行
现象是某条产线的预测误差特别大,其他产线正常。原因是特征构造时没有按表计分组,滞后特征把别的表计数据错配过来。解决办法是所有涉及shift、rolling、groupby的操作都显式带上meter_id分组,并在特征宽表里保留表计维度,训练时要么按表计分别建模,要么把表计作为类别特征喂进去。
4.4 极端天气样本太少:模型没见过就不会预测
现象是夏季连续高温那几天的预测偏差远大于平时。原因是历史数据里极端高温样本占比很低,模型对这类尾部情况学得不够。解决办法有两个方向:一是给样本按温度分桶做加权,让极端天气样本权重高一些;二是保留一个基于规则的兜底修正,比如温度超过历史 95 分位时,在模型输出上叠加一个经验修正系数。纯靠模型硬扛尾部,在中小样本下不现实。
4.5 评估口径不一致:答辩时数字对不上
现象是自己算的 MAPE 和评委复算的对不上。原因通常是 MAPE 的分母处理不一致:真实值为 0 的停产日怎么算、是按天算还是按月聚合后再算,不同口径结果差很多。解决办法是在方案里把评估口径写死:MAPE 只在真实值大于某个阈值的样本上计算,月度指标先按月聚合再算百分比,并在评估脚本里把口径注释清楚。口径写清楚,答辩时被追问也能当场解释。
5. 验收与答辩:用一张对照表把方案讲透
验收这套方案,我一般不看模型多花哨,只看三件事能不能说清楚:预测准不准、为什么准、不准的时候怎么办。具体做法是准备一张「基线对照表」,把三种方法放在一起比:朴素基线(直接用去年同期值)、移动平均基线(过去 30 天均值)、本方案的梯度提升树模型。三种方法在同一测试集上算同一套指标,表格一摆,提升幅度一目了然。评委最认这种对照,因为它证明你的模型不是自说自话。
| 方法 | MAE(度) | RMSE(度) | MAPE |
|---|---|---|---|
| 去年同期基线 | 待填 | 待填 | 待填 |
| 30 天移动平均 | 待填 | 待填 | 待填 |
| 梯度提升树模型 | 待填 | 待填 | 待填 |
表格里的数字要自己跑出来填,别编。答辩时按「需求 → 数据 → 特征 → 模型 → 对照 → 局限」六步讲,每步控制在两分钟内,重点放在特征设计和防泄漏上,因为这两块最能体现你是不是真懂时序预测。局限部分主动讲,比如极端天气样本不足、新产线冷启动没有历史数据,主动暴露问题比被问出来强。
还有一个具体技巧:把某一天的特征向量和预测过程打印出来,现场演示「输入什么、模型输出什么、和真实值差多少」。这种可复现的演示比 PPT 上的曲线图有说服力得多,评委一看就知道这套东西是真跑通的。
血泪经验是,我早期做这类项目时总想着把模型调得更复杂,结果答辩被问「你这个复杂模型比移动平均强多少」,答不上来。从那以后我每次做预测类方案,都强制先跑通基线对照表,再谈模型优化,基线打不过的复杂模型一律不上。希望这套结构能帮到你,把方案搭起来、把数字跑出来,剩下的就是讲清楚。
本文还有配套的精品资源,点击获取