资讯详情 电工杯B题人工智能.zip拆解:从数据清洗到模型部署的72小时竞赛全流程
📅 2026/10/28 18:17:11
简介这份资源是2023年电工杯B题人工智能赛题的完整Matlab实现方案面向计算机、电子信息工程、数学等专业的大学生可用于课程设计、期末大作业与毕业设计帮助读者快速复现赛题建模与求解流程。压缩包共28个文件约7.3MB以m脚本、xlsx数据表、mat结果文件为主另含csv与pdf思路说明覆盖数据转换、评价得分、综合得分等环节并附赠可直接运行的案例数据。代码采用参数化编程参数修改方便思路清晰、注释详细兼容Matlab2014a、2019b与2024b。读者可据此获得三问的完整代码与中间结果对照思路文档理解建模逻辑借助数据表与mat文件复现计算过程并在此基础上调整参数、迁移方法完成自己的赛题方案与论文写作。目前已有45人学习下载。1. 电工杯B题人工智能.zip一份竞赛题包到底该怎么拆电工杯B题人工智能.zip 这个标题第一次看到的人多半会愣一下它既不像论文题目也不像开源项目名更像某个竞赛群里传出来的压缩包。事实也确实如此——全国大学生电工数学建模竞赛电工杯的 B 题近几年连续押在人工智能与电力系统交叉的方向上赛题下发时通常就是一个压缩包里面塞着题目 PDF、若干 CSV 或 Excel 数据、可能还有一份说明文档。你拿到它真正要做的不是「跑一个模型」而是在 72 小时内完成从读题、数据清洗、特征构造、建模到论文写作的全链路。这篇文章面向三类人正在备赛想提前摸清套路的学生、带队的指导老师、以及想拿这道题当教学案例的从业者。我会把这类题包拆成可复现的工程流程告诉你数据长什么样、模型怎么选、参数怎么调、论文里哪些图必须画以及我踩过的那些坑。人工智能这个词现在几乎成了日常帮手但在竞赛场景里它意味着你必须用可解释、可复现的方法把电力数据里的规律讲清楚而不是调个库就交差。2. 先看清题包结构电工杯B题的数据到底长什么样2.1 压缩包里的四类文件与它们的用途电工杯 B 题的题包结构历年变化不大常见做法是四类文件。第一类是赛题说明 PDF通常 3 到 8 页包含背景描述、任务要求、数据字段解释和论文格式规范。第二类是训练数据格式以 CSV 为主偶尔是 Excel字段一般包括时间戳、负荷值、温度、湿度、风速、电价、设备状态等。第三类是测试数据或待预测数据结构与训练集一致但缺少目标列。第四类是附件说明可能是一个 README 或者字段对照表。我一般拿到包后第一件事不是打开数据而是把 PDF 完整读两遍。第一遍通读标记出所有带数字的要求比如「预测未来 24 小时负荷」「误差控制在 5% 以内」「需给出特征重要性排序」。第二遍逐条拆解把每个要求翻译成技术动作预测 24 小时是多步时序预测误差 5% 是 MAPE 指标特征重要性排序意味着模型要可解释。这一步做扎实后面少走一半弯路。提示题包里如果有多个版本的说明文档以文件名带「最终」或日期最新的为准竞赛期间题目偶尔会发勘误。2.2 用 pandas 做一次五分钟数据体检不管题目要求什么先跑一遍数据体检。下面这段代码是我每次拿到新数据必跑的模板能在一分钟内告诉你数据的基本形态、缺失情况和异常分布。import pandas as pd import numpy as np # 读取数据注意编码问题电工杯数据常见 gbk 或 utf-8-sig df pd.read_csv(train.csv, encodinggbk, parse_dates[timestamp]) # 基础信息 print(形状:, df.shape) print(字段类型:\n, df.dtypes) print(缺失值:\n, df.isnull().sum()) print(描述统计:\n, df.describe()) # 时间连续性检查 df df.sort_values(timestamp) time_diff df[timestamp].diff().value_counts() print(时间间隔分布:\n, time_diff.head()) # 目标列异常值粗筛以负荷为例 if load in df.columns: q1, q3 df[load].quantile([0.25, 0.75]) iqr q3 - q1 outliers df[(df[load] q1 - 3*iqr) | (df[load] q3 3*iqr)] print(极端离群点数量:, len(outliers))这段代码的逻辑是先确认数据规模和字段类型再看缺失值集中在哪些列描述统计帮你判断量纲是否统一。时间间隔分布是关键——电力负荷数据理论上应该是等间隔的如果出现大量非标准间隔说明数据有缺时刻或重复时刻需要重采样。离群点用 3 倍 IQR 粗筛注意这里只是标记不要急着删电力数据里的尖峰可能是真实的高温负荷或故障信号。参数方面encoding要根据实际文件调整中文环境下的 CSV 常见 gbkparse_dates指定时间列后可以直接用 pandas 的时间索引功能。如果数据量超过百万行describe()会变慢可以改成抽样后再统计。2.3 字段含义与电力场景的对应关系电工杯 B 题的数据字段通常围绕电力系统展开理解每个字段的物理含义比盲目建模重要得多。常见字段和它们的场景对应如下字段名物理含义建模时的角色timestamp采样时刻时间索引构造滞后和周期特征load有功负荷预测目标或关键输入temperature环境温度外生变量影响负荷humidity相对湿度外生变量影响较小wind_speed风速新能源场景关键输入price电价需求响应场景的目标或输入status设备状态分类特征需编码温度对负荷的影响通常有滞后效应不是当前温度决定当前负荷而是前几小时甚至前一天的累积温度。我一般会构造温度的一阶差分和 24 小时滑动平均作为额外特征。风速在风电预测题里是核心输入但要注意风速和功率之间是非线性关系直接线性回归效果很差常见做法是分段或做三次方变换。3. 特征工程把原始 CSV 变成模型能吃的矩阵3.1 时间特征周期编码比直接塞时间戳管用电力负荷有极强的周期性日周期、周周期、年周期。直接把 timestamp 转成整数喂给模型是新手最容易犯的错模型会认为时间 23 点和 0 点相差 23实际上它们只差 1 小时。正确做法是做周期编码。def add_time_features(df, time_coltimestamp): df df.copy() df[hour] df[time_col].dt.hour df[dayofweek] df[time_col].dt.dayofweek df[month] df[time_col].dt.month df[is_weekend] (df[dayofweek] 5).astype(int) # 周期编码sin/cos 让 23 点和 0 点在数值上接近 df[hour_sin] np.sin(2 * np.pi * df[hour] / 24) df[hour_cos] np.cos(2 * np.pi * df[hour] / 24) df[dow_sin] np.sin(2 * np.pi * df[dayofweek] / 7) df[dow_cos] np.cos(2 * np.pi * df[dayofweek] / 7) return df df add_time_features(df)周期编码的原理是把周期映射到单位圆上23 点和 0 点在圆上相邻数值距离也小。hour_sin和hour_cos必须成对使用单独一个会丢失信息。is_weekend是布尔特征电力负荷在工作日和周末的模式差异明显这个特征几乎总是有效。3.2 滞后特征与滑动窗口多步预测的关键如果题目要求预测未来 24 小时你不能只用当前时刻的特征必须构造滞后特征。常见做法是用过去 24 小时、48 小时、168 小时一周的负荷作为输入。def add_lag_features(df, targetload, lags[1, 2, 3, 24, 48, 168]): df df.copy() for lag in lags: df[flag_{lag}] df[target].shift(lag) # 滑动统计 for window in [6, 12, 24]: df[froll_mean_{window}] df[target].shift(1).rolling(window).mean() df[froll_std_{window}] df[target].shift(1).rolling(window).std() return df df add_lag_features(df) df df.dropna() # 滞后会产生 NaN训练前必须处理这里有个细节rolling之前先shift(1)否则当前时刻的负荷会泄露到滑动统计里造成数据穿越。数据穿越是时序预测里最隐蔽的坑模型在验证集上表现很好一到测试集就崩多半是这个原因。滞后阶数的选择要看题目要求预测 1 小时lag 1 到 24 够用预测 24 小时lag 24 到 168 更关键。3.3 外生变量的处理温度和风速怎么进模型温度和风速这类外生变量处理方式和负荷不同。温度通常做滑动平均因为建筑热惯性导致负荷对温度的响应有延迟。风速在风电场景要做功率曲线变换。# 温度24 小时滑动平均 一阶差分 df[temp_ma24] df[temperature].rolling(24, min_periods1).mean() df[temp_diff] df[temperature].diff() # 风速简化的功率曲线变换切入风速 3额定 12切出 25 def wind_to_power(v): if v 3 or v 25: return 0 elif v 12: return 1.0 else: return (v**3 - 3**3) / (12**3 - 3**3) df[wind_power] df[wind_speed].apply(wind_to_power)功率曲线变换的参数3、12、25是常见风机的典型值如果题目给了具体机型参数以题目为准。这个变换把非线性的风速-功率关系拉直后续线性模型也能用。如果不做变换树模型还能扛线性模型基本废掉。4. 模型选型与训练从基线到集成的递进路线4.1 先跑一个基线线性回归和移动平均竞赛里最忌讳一上来就上深度学习。我一般先用两个基线移动平均和线性回归。移动平均用过去 24 小时均值作为预测代码一行线性回归用 sklearn 的 LinearRegression特征用前面构造的完整矩阵。这两个基线的意义是给你一个误差下界后面所有复杂模型都必须显著优于它们否则说明特征工程有问题不是模型不够强。from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_percentage_error # 按时间切分前 80% 训练后 20% 验证 split int(len(df) * 0.8) train, val df.iloc[:split], df.iloc[split:] feature_cols [c for c in df.columns if c not in [timestamp, load]] X_train, y_train train[feature_cols], train[load] X_val, y_val val[feature_cols], val[load] lr LinearRegression() lr.fit(X_train, y_train) pred lr.predict(X_val) print(线性回归 MAPE:, mean_absolute_percentage_error(y_val, pred))注意切分必须按时间顺序不能随机切分。随机切分会让未来数据泄露到训练集验证指标虚高。MAPE 是电工杯常用的误差指标但它在真实值接近零时会爆炸如果负荷数据有接近零的时刻改用 MAE 或 RMSE 更稳。4.2 树模型XGBoost 和 LightGBM 的参数怎么调树模型是电力负荷预测的主力XGBoost 和 LightGBM 都常用。我一般优先 LightGBM训练快对缺失值友好。关键参数有五个n_estimators、learning_rate、max_depth、num_leaves、subsample。import lightgbm as lgb params { objective: regression, metric: mape, learning_rate: 0.05, num_leaves: 63, max_depth: 7, subsample: 0.8, colsample_bytree: 0.8, n_estimators: 1000, early_stopping_rounds: 50, verbose: -1 } dtrain lgb.Dataset(X_train, y_train) dval lgb.Dataset(X_val, y_val, referencedtrain) model lgb.train(params, dtrain, valid_sets[dval])参数逻辑learning_rate0.05 配合n_estimators1000 是稳妥组合学习率再低需要更多树竞赛时间紧不划算。num_leaves63 和max_depth7 控制模型复杂度电力数据噪声大树太深容易过拟合。early_stopping_rounds50 让模型在验证集不再提升时自动停省去手调轮数。subsample和colsample_bytree0.8 是防过拟合的常规操作。如果验证集 MAPE 比线性回归还差先检查特征里有没有穿越再检查时间切分是否正确最后才怀疑参数。4.3 时序专用模型LSTM 和 Prophet 的适用边界电工杯 B 题有时会明确要求用深度学习或者数据量足够大时 LSTM 确实有优势。LSTM 的输入需要构造成三维张量样本数、时间步长、特征数。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout def make_sequences(data, target, window24): X, y [], [] for i in range(window, len(data)): X.append(data[i-window:i]) y.append(target[i]) return np.array(X), np.array(y) # data 是标准化后的特征矩阵target 是负荷 X_seq, y_seq make_sequences(data_scaled, target_scaled, window24) split int(len(X_seq) * 0.8) model Sequential([ LSTM(64, return_sequencesTrue, input_shape(24, X_seq.shape[2])), Dropout(0.2), LSTM(32), Dense(1) ]) model.compile(optimizeradam, lossmse) model.fit(X_seq[:split], y_seq[:split], validation_split0.1, epochs50, batch_size32)LSTM 的坑在于数据必须标准化否则梯度爆炸window选择 24 还是 168 要看周期训练轮数不是越多越好验证损失回升就停。Prophet 适合有明显趋势和季节性的数据但它对多变量外生特征的支持较弱电工杯这种多因素场景Prophet 通常只作为对比基线不作为主力。5. 避坑与排查竞赛里最容易翻车的五个地方5.1 数据穿越验证集漂亮测试集崩盘现象验证集 MAPE 只有 3%提交后测试集误差 15% 以上。原因特征构造时用了未来信息比如滑动统计没 shift或者标准化时用了全量数据的均值方差。解决所有涉及时间聚合的操作先 shift 再 rolling标准化参数只在训练集上 fit再 transform 验证集和测试集。5.2 时间戳解析错误数据顺序全乱现象模型训练正常但预测结果完全无规律。原因CSV 读取时时间列被当成字符串排序按字典序导致 10 点排在 2 点前面。解决pd.read_csv时加parse_dates读入后立即sort_values并检查diff()是否等间隔。5.3 缺失值填充方式不当引入虚假规律现象填充后模型在缺失段附近误差异常大。原因用全局均值填充时序数据破坏了时间连续性。解决时序数据优先用前向填充或线性插值缺失超过 6 小时的段考虑整段剔除或标记为独立特征。5.4 评估指标选错MAPE 在零值附近爆炸现象MAPE 出现几百甚至无穷大。原因真实负荷有接近零的时刻分母极小。解决改用 MAE、RMSE 或 SMAPE或者在计算 MAPE 前过滤掉真实值小于某阈值的样本并在论文里说明。5.5 论文图表与代码结果不一致现象论文里的图和代码跑出来的图对不上。原因多次实验后忘了固定随机种子或者用了不同版本的数据。解决所有涉及随机的环节设random_state数据版本用文件名带日期区分论文定稿前用最终代码重跑一遍所有图表。6. 从交卷到进阶把竞赛代码变成可复用的预测框架竞赛结束后这套代码不该扔。我一般会做三件事把它变成可复用的东西。第一把特征工程和模型训练封装成函数输入原始 CSV输出预测结果和评估报告下次遇到类似赛题改改字段名就能跑。第二把验证策略从单次切分改成滚动预测更接近真实部署场景。def rolling_forecast(df, feature_cols, targetload, train_window720, test_window24): 滚动预测每次用过去 train_window 小时训练预测未来 test_window 小时 results [] for start in range(train_window, len(df) - test_window, test_window): train df.iloc[start-train_window:start] test df.iloc[start:starttest_window] model lgb.LGBMRegressor(n_estimators500, learning_rate0.05, num_leaves63) model.fit(train[feature_cols], train[target]) pred model.predict(test[feature_cols]) results.append(pd.DataFrame({ timestamp: test[timestamp].values, true: test[target].values, pred: pred })) return pd.concat(results)滚动预测的逻辑是模拟真实部署每次只用历史数据训练预测下一段然后窗口前移。train_window720 小时约一个月test_window24 小时对应题目要求。这个策略下得到的误差比单次切分更可信论文里放这张滚动误差曲线说服力比单一 MAPE 数字强得多。第三件事是写一份字段映射表。电工杯每年的字段名会变但物理含义大同小异。我维护了一个映射字典把「load」「power」「demand」都映射到统一的目标列名把「temp」「temperature」「air_temp」映射到温度列。下次拿到新题包改字典比改代码快。最后说个血泪经验竞赛期间不要频繁换模型。我见过太多队伍在最后 12 小时把 LightGBM 换成 LSTM结果调参来不及论文里的结果还不如基线。定好路线就深耕把特征工程做扎实把验证做严谨比追新模型有用得多。这套流程我用了几年从读题到交卷的节奏基本稳定希望帮到你。本文还有配套的精品资源点击获取