简介:本资源是一套面向计算机及相关专业本科生的深度学习电力负荷预测实战项目,专为毕业设计、课程大作业与项目实训打造,解决时间序列建模与能源数据预测中的典型工程问题。压缩包共26个文件(64.2MB),包含4个核心Python脚本(含TCN模型构建、单变量训练与数据预处理)、2个Jupyter Notebook(data_process.ipynb与result.ipynb,完整呈现数据清洗、VMD分解、特征工程及结果可视化全流程)、7个Shell脚本(支持不同滑动窗口长度w=4/6/12/24/48/96/192/384的TCN批量训练)、5张关键原理图与结果图(如Dilated_Causal_Conv结构、TCN模型架构、预测结果对比表等),以及README.md、requirements.txt等工程必备文档。目前已有85人学习下载,所有代码均经本地实测可运行,含严格调试的日志验证与模块化函数封装,助学习者快速理解TCN时序建模逻辑、掌握REFIT真实家庭用电数据集处理方法,并复现高分毕设级预测效果。
1. 项目背景与整体设计思路
1.1 为什么电力负荷预测是深度学习毕设的“常青树”
我这些年看过不少毕设题目,也帮人改过不少代码,说实话,电力负荷预测几乎是每一届都有人做的经典题目。它受欢迎不是没道理的:数据公开好找、问题定义清晰、效果可以量化、和经济/能源/电网这些热门方向都能搭上边,最关键的是——它天生就是为深度学习准备的时间序列问题。
简单说,电力负荷预测就是根据历史用电数据、天气信息、日期类型等因素,预测未来某个时间段(比如下一小时、下一天)的用电负荷值。电网公司要用它安排发电计划、调度机组、制定电价,工厂用它安排生产班次,你家楼下的充电桩布局也得靠它。从学术角度看,它属于时间序列预测的核心场景,牵扯到周期性、趋势性、随机波动这些典型难点,特别适合用来展现深度学习模型的建模能力。
但我也得说实话,正因为它太常用了,网上随便一搜就是一大把代码。很多同学拿到手的所谓“毕设源码”,要么是MNIST手写数字识别改了改标签就硬说是负荷预测,要么是直接把某开源项目的仓库克隆下来连注释都没有,要么是训练完了只看loss曲线根本不看预测效果。如果把这些东西交上去,答辩老师随便问两句“你用的窗口长度是怎么定的”“为什么用MSE做损失函数”“你的模型在节假日预测效果怎么样”,基本就露馅了。
所以这篇文章我不会只丢给你一份能跑的代码,而是把整个项目从选题思路、数据处理、模型设计到训练调参、常见坑点,全部拆开讲清楚。你拿着这套思路去写代码、去写论文、去答辨,都能顶得住。
1.2 一套能拿高分的毕设该具备什么
先给还没开始做或者做到一半的同学一个判断标准。一套电力负荷预测的毕设项目,想要在答辩现场站稳脚跟,至少要满足这几点:
第一,数据来源要说得清楚。你用的是哪个数据集?公开数据集还是某地真实负荷数据?时间范围是多长?采样间隔是15分钟、1小时还是1天?这些基础信息必须随口能答。
第二,数据预处理不能糊弄。缺失值怎么处理的?异常值怎么检测的?归一化用了什么方法?训练集和测试集是怎么划分的?如果直接拿原始数据塞进模型,或者划分时把归一化的统计量偷看了测试集,这都属于硬伤。
第三,模型选择要有理有据。为什么用LSTM而不是RNN?为什么加了CNN分支?跟ARIMA、SVR这些传统方法比优势在哪?不需要多高深,但至少能讲清楚选择的动机。
第四,评价指标要规范。MAE、RMSE、MAPE这三个是最基本的,算清楚并且能解释数值含义,比如MAPE是5%意味着平均误差在5%左右。很多同学只关注loss曲线降到多低,这其实是个误区。
第五,要有可视化和分析。预测值和真实值的对比曲线、误差分布图、不同模型的效果对比表,这些都是论文里的加分项,也是答辩PPT里的重点页面。
坦白说,只要这五块都覆盖了,即便模型结构不复杂、准确率不是最优,也远比那些“能跑但说不清为什么”的项目拿分高。所以接下来的内容,我就围绕这五块展开,把每一步怎么落地讲透。
2. 数据获取与预处理,这是整个项目的根基
2.1 公开数据集怎么选,选错了模型再好也没用
负荷预测的数据集,目前国内用的人最多的就是两个方向。一个是国外的开放数据集,比如比利时的Elia公开数据、美国一些电力公司的负荷数据、Kaggle上各种电力预测比赛的数据集;另一个是国内部分高校或研究机构公开的某地区负荷数据,比如通过一些论文的附加材料拿到的数据。
如果你是做毕设,我的建议是首选公开且引用方便的数据集。原因很简单:论文里需要写数据来源,公开数据集可以直接给出引用和下载链接,审稿人、答辩老师都能去验证。而且很多公开数据集自带天气信息,你后面做特征工程会轻松很多。
具体到格式,常见的有CSV、Excel、JSON几种。大多数情况你会拿到一张表,每一行是一个时间点的记录,列包括时间戳、负荷值,可能还有温度、湿度、风速这些气象特征。
我提醒一句:一定先看数据的采样间隔和时间跨度。有的数据集是15分钟一个点,一天96个点;有的是1小时一个点,一天24个点;还有的是按天的。这个信息决定了你后面怎么设计滑动窗口,也决定了你模型的时间步长是多少,千万别拿到手就直接开跑。
2.2 缺失值、异常值和归一化,三步走才能把数据洗干净
拿到数据的头一件事不是看模型,而是画图。直接用matplotlib把负荷曲线画出来,肉眼看一遍。这一步能帮你快速发现数据里的明显问题:有没有突然掉到0的坑点?有没有高得离谱的尖峰?有没有大段空白?
缺失值的处理方式取决于缺失的比例和位置。如果缺失点很少(比如千分之几),用前后均值插值或者pandas的interpolate()就行;如果缺失比较集中(比如连续几个小时),可以考虑用同一天同一时刻的数据做填充;如果一个特征整列缺失超过三成,建议直接删掉那一列,别强行补。
再说异常值。电力负荷数据里最典型的异常就是“毛刺”——某个时间点的负荷突然跳到一个不合理的数值。这通常是由于数据采集设备故障、通信中断、或者某些极端事件导致的。检测方法也不复杂,可以设定一个上下阈值,也可以使用分位数法(比如超过99.5%分位数的点视为异常),把异常值替换为相邻点的均值,或者用中位数平滑掉。
归一化这里我要多说两句,因为这是一个学生最容易踩坑的地方。LSTM这类神经网络对输入特征的尺度非常敏感,如果负荷值的范围是几千到几万,温度是零下十度到四十度,直接把原始数值喂进去,数值大的特征会主导梯度更新,模型很难收敛。所以一般要做归一化,把数据映射到0到1之间或者变成均值为0方差为1的分布。常用的是MinMaxScaler或者StandardScaler,sklearn里直接有。
但有一个关键的细节:归一化的统计量只能从训练集计算,然后再应用到测试集。什么意思?你先把训练集切出来,用训练集算出最大值、最小值,保存下来,再用这一组最大值最小值去缩放训练集和测试集。绝对不能先对全部数据做归一化再切分,因为那样测试集的信息已经泄露到训练过程里了,你模型的评估结果就是虚高的。这个问题在答辩时被问到概率极高,一定要心里有数。
2.3 训练集、验证集、测试集怎么划分才不“作弊”
时间序列的划分跟普通分类任务不一样。普通机器学习任务可以随机打乱数据,但时间序列不行——你只能用过去预测未来,不能用未来预测过去。如果随机打乱,模型相当于“偷看”了未来的数据,测试结果毫无意义。
我的做法是:按时间顺序划分。比如你有一年365天的负荷数据,可以取前70%做训练,接下来15%做验证(用来调超参数和观察是否过拟合),最后15%做测试(最后评估模型性能)。如果数据量很大,也可以做成滚动划分,但毕设阶段用固定划分就足够。
有一个小技巧你可以用:在加载数据的时候,先根据时间戳排序一次,确保数据是按时间递增的。因为有些数据集的原始文件顺序可能是乱的,或者你在拼接多个数据源时导致顺序错乱,忘了排序的话全盘皆输。
验证集的作用我多说一句。很多同学训练的时候只看训练集loss降了没有,这是不对的。训练集loss必然降,你要关注的是验证集loss——如果训练集loss一直降但验证集loss到某个点开始反弹,那就是过拟合的信号,要早停或者加正则化。
3. 模型选型与特征工程,LSTM为什么是主力军
3.1 LSTM的核心思想,用大白话讲清楚
电力负荷数据是典型的时间序列,它的特点是你得结合过去一段时间的走势来推断未来。比如你预测明天下午3点的负荷,那你至少要知道昨天下午3点的负荷、今天同一时段的趋势、最近几天的整体水平,甚至还要知道明天是不是周末。
传统的RNN(循环神经网络)理论上能做这件事,但有一个致命的缺陷——长期依赖问题。简单说,当输入序列很长的时候,RNN的梯度在反向传播过程中会不断相乘,导致梯度消失(数值变得极小)或者梯度爆炸(数值变得极大)。结果就是模型记不住太久以前的信息。
LSTM(长短期记忆网络)就是为了解决这个问题提出来的。它引入了“门”的机制:遗忘门决定要丢弃哪些旧信息,输入门决定要把哪些新信息写入记忆,输出门决定要输出哪些信息。这个结构让信息可以在网络中“流动”很久而不衰减,所以LSTM特别适合捕捉电力负荷数据里的长期依赖——比如周期性、季节性这些规律。
跟LSTM经常一起考的是GRU(门控循环单元),它算是LSTM的简化版:参数更少、训练更快,效果在很多任务上跟LSTM差不多。毕设里你选LSTM还是GRU都不会错,关键是能说清楚它们的联系和区别。
3.2 预测目标怎么定,单步预测和多步预测的差别很大
在动手建模型之前,你得先想清楚一个问题:你要预测的是未来多久的负荷?这里分两种常见情况:
一种是单步预测。比如你用过去48小时的数据,预测下一个小时的负荷值。这种情况模型输出是一个数,问题最简单,效果也最好,适合作为毕设的第一版baseline。
另一种是多步预测。比如你用过去24小时的数据,预测未来24小时每一个小时的负荷值,也就是输出24个数。这种情况模型更复杂,可以直接让模型一次性输出多个时间步(seq2seq方式),也可以把预测结果当作输入去递归地预测下一步(递归方式),各有各的坑。
对毕设来说,我的建议是先把单步预测做扎实,然后再扩展多步预测。这样的结构也适合写论文——第一版是比较基准,第二版是改进,显得工作内容饱满。你可以在答辩的时候说“我在单步预测基础上,进一步设计了多步预测机制”,这句话本身就是加分项。
3.3 特征工程:别只盯着负荷值,这些特征能明显提升效果
很多初学者的模型输入只有一样东西:历史负荷值。能跑,但效果一般。实际上,电力负荷预测的特征工程大有文章可做。我按重要性排序给你捋一下:
时间特征。包括小时(0-23)、星期几(0-6)、是否节假日、月份、季节等。这些属于时间序列预测的基本特征,能帮模型区分工作日和周末的负荷差异、早晚高峰等。
滞后特征。就是过去n个小时的负荷值。比如你要预测t时刻的负荷,可以构造t-1、t-2、t-24、t-48这些滞后变量。其中t-24和t-48尤其重要,因为负荷有极强的日周期性——今天下午2点的负荷跟昨天下午2点的负荷相关性很高。
滑动窗口统计特征。比如过去24小时的平均负荷、最大负荷、最小负荷、标准差。这些特征能提供整体趋势信息,帮模型判断当前处于什么水平。
天气特征。温度是最重要的,因为制冷和采暖负荷跟温度强相关。如果有湿度、风速、光照这些数据,也一并放进去。但要注意,如果你预测的是未来时刻,那你只能使用“天气预报值”而不是实测值,否则就是数据泄露。
我自己做这个项目的时候,把时间特征和滞后特征加进去之后,RMSE下降了差不多15%,效果非常显著。所以如果你的模型还在“裸奔”只喂负荷值,先别急着换模型结构,把特征工程搞一搞,提升会很大。
3.4 模型结构怎么搭,从LSTM到CNN-LSTM再到Transformer
单层LSTM是最简单的结构,适合快速跑通流程。但想要更高的精度,通常会把网络加深或者组合其他结构。我介绍三种在毕设里经常用的结构,你可以根据自己数据量选择:
第一种是“LSTM + 全连接层”。这是最基础的结构:LSTM层提取时序特征,后面接一个或两个全连接层做输出。这种结构代码量少、训练快、不容易出问题,作为baseline非常合适。
第二种是“CNN + LSTM”。先用一维卷积核(Conv1D)对输入序列做局部特征提取,再把卷积的输出送入LSTM层。这种做法的好处是CNN能捕捉局部模式(比如短时间内负荷的突变趋势),LSTM捕捉长期依赖,两者互补。如果你发现单独的LSTM效果一般,试试加一层CNN,往往有惊喜。
第三种是“注意力机制+LSTM”或者直接用Transformer。注意力机制能自动学习序列中哪些位置的信息更重要。比如预测明天上午9点的负荷,模型可能更关注今天上午9点附近的数据,注意力机制就能自动给这些位置更高的权重。Transformer结构在这两年特别火,但需要注意的是它通常需要更多数据才能训练好,如果你的数据量只有几千条,LSTM反而更稳。
我不建议一上来就上很复杂的结构。先把基准模型做出来,跑通流程,看到合理的预测曲线,再一步一步加组件,每一步都用同一套评估指标去对比。这样做出来的消融实验数据,写论文时最有用。
4. 源码结构与核心实现,一步步拆开给你看
4.1 项目文件结构怎么安排,一目了然不乱套
一份合格的毕设代码,文件结构不能是“一堆.py堆在一起”。我建议至少按照功能划分成这样一个结构:
project/ │ ├── data/ # 存放原始数据和预处理后的数据 │ ├── raw/ │ └── processed/ │ ├── src/ # 核心代码 │ ├── __init__.py │ ├── data_preprocess.py # 数据预处理 │ ├── build_dataset.py # 构造训练/验证/测试数据集 │ ├── model.py # 模型定义 │ ├── train.py # 训练脚本 │ ├── evaluate.py # 评估脚本 │ └── visualize.py # 可视化脚本 │ ├── checkpoints/ # 保存模型权重 ├── logs/ # 训练日志 ├── results/ # 预测结果和图表 │ ├── requirements.txt ├── README.md └── main.py # 主入口这样组织代码的好处是:每一个文件职责清晰,答辩展示时你可以说“数据预处理在data_preprocess.py,模型定义在model.py,训练和评估分开”,逻辑感立刻就有了。而且后续你需要换数据集、换模型结构、做实验对比时,只需要修改对应的模块,不用改其他代码。
4.2 数据加载和数据集构造的核心代码
数据预处理部分,我用pandas写了一个示例,基本上覆盖了前面讲到的几个步骤:
import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler def load_and_preprocess(file_path): # 读取原始数据 df = pd.read_csv(file_path, parse_dates=['datetime']) df = df.sort_values('datetime').reset_index(drop=True) # 缺失值处理:线性插值 df['load'] = df['load'].interpolate(method='linear') # 异常值处理:超过99.5%分位数的点,用中位数替换 upper = df['load'].quantile(0.995) median = df['load'].median() df.loc[df['load'] > upper, 'load'] = median # 构造时间特征 df['hour'] = df['datetime'].dt.hour df['dayofweek'] = df['datetime'].dt.dayofweek df['is_weekend'] = (df['dayofweek'] >= 5).astype(int) return df def split_and_scale(df, feature_cols, target_col, train_ratio=0.7, val_ratio=0.15): n = len(df) train_end = int(n * train_ratio) val_end = int(n * (train_ratio + val_ratio)) train_df = df.iloc[:train_end] val_df = df.iloc[train_end:val_end] test_df = df.iloc[val_end:] # 注意:只用训练集的数据来fit scaler scaler_x = MinMaxScaler() scaler_y = MinMaxScaler() scaler_x.fit(train_df[feature_cols]) scaler_y.fit(train_df[[target_col]]) train_x = scaler_x.transform(train_df[feature_cols]) train_y = scaler_y.transform(train_df[[target_col]]) # val和test同理 return train_x, train_y, val_x, val_y, test_x, test_y, scaler_y这里有几个细节要强调。interpolate()是pandas自带的插值函数,默认是线性插值,数据量小的话够用了。异常值处理那边,我用的分位数法比较粗暴但有效,如果你观测到数据有明确的上下界,用固定阈值也很好。最重要的是scaler_x.fit()到底是拿的哪些数据,如果你拿测试集一起fit了,评估结果就不可信了,这一点我在前面已经强调过。
数据集构造是决定模型效果的一个关键步骤。我们要把数据组织成“滑动窗口”的形式,代码也很直接:
def create_sequences(data_x, data_y, window_size=48): X, Y = [], [] for i in range(window_size, len(data_x)): X.append(data_x[i - window_size:i]) Y.append(data_y[i]) return np.array(X), np.array(Y)这个window_size的取值非常关键。对于小时粒度的负荷数据,48意味着用过去两天预测下个小时。为什么不是48就是24?因为负荷有日周期,窗口至少得覆盖一个完整的日周期。如果数据是15分钟粒度的,那一个周期是96个点,你的窗口尺寸就应该往这个量级靠。这个参数不是拍脑袋定的,需要结合你的数据采样间隔和业务周期来定,答辩老师大概率会问。
4.3 模型定义,LSTM结构完整代码解释
用PyTorch做毕设是现在的绝对主流,因为Debug比TensorFlow直观,代码写起来也更灵活。下面是一个标准的LSTM预测模型:
import torch import torch.nn as nn class LSTMForLoadForecast(nn.Module): def __init__(self, input_size, hidden_size=64, num_layers=2, output_size=1, dropout=0.2): super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=num_layers, batch_first=True, dropout=dropout ) self.fc = nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Dropout(dropout), nn.Linear(32, output_size) ) def forward(self, x): # x shape: (batch, seq_len, input_size) out, _ = self.lstm(x) # out shape: (batch, seq_len, hidden_size) out = out[:, -1, :] # 取最后一个时间步的输出 out = self.fc(out) return outinput_size是你的特征数量。如果你的特征列有负荷、温度、湿度、小时、星期几5个特征,就填5。hidden_size是LSTM隐藏层的维度,我建议先从64试起。num_layers=2表示叠加两层LSTM,层数多能捕捉更复杂的模式,但也会增加训练时间和过拟合风险。batch_first=True表示输入张量的第一个维度是batch,这是PyTorch里很常用的写法,搞反了会报维度错误。
forward里唯一需要解释的是out[:, -1, :]这一步。LSTM对每个时间步都会输出一个隐藏状态,但我们最后只需要一个预测值,所以取最后一个时间步的隐藏状态,它聚合了整个序列的信息。如果你想做多步预测,就需要改这里,比如使用多个时间步的输出来构造目标序列。
4.4 训练流程里那些你必须写对的细节
训练部分最重要的几个东西:损失函数、优化器、学习率。代码逻辑各家都类似,但参数选择很有讲究。
损失函数我用的是nn.MSELoss(),也就是均方误差。为什么是MSE?因为负荷预测本质上是一个回归问题,MSE对大误差非常敏感,模型会优先减小那些偏差大的预测点。如果你想拿到更符合业务直觉的误差,在评估阶段再计算MAE和MAPE。
优化器我选Adam,这是目前时序预测里最好用也最稳的选择。学习率一般从0.001开始,如果训练发现loss下降很慢,适当调大;如果loss震荡厉害,调小。LSTM这种网络对学习率比较敏感,跑之前多试几个值没坏处。
训练循环的模板如下:
def train_epoch(model, train_loader, optimizer, criterion, device): model.train() total_loss = 0 for batch_x, batch_y in train_loader: batch_x = batch_x.to(device) batch_y = batch_y.to(device) optimizer.zero_grad() pred = model(batch_x) loss = criterion(pred, batch_y) loss.backward() optimizer.step() total_loss += loss.item() return total_loss / len(train_loader)optimizer.zero_grad()这行的作用是把上一次迭代保留的梯度清零,不写的话梯度会累积,模型就废了。model.train()和后面的model.eval()是两种模式,前者启用Dropout,后者关闭它——这是新手最容易踩的坑,评估时忘了切eval(),预测结果会抖动,因为Dropout随机丢弃了神经元。
还有一个大杀器我强烈建议你用:早停机制(Early Stopping)。在训练过程中记录验证集loss,如果连续N个epoch(比如15个)都没有下降,就停止训练,然后载入验证集loss最低的那个epoch的模型权重。这样做能有效防止过拟合,而且能省很多时间。代码实现不复杂,逻辑就是比对当前epoch和之前最好的loss。
4.5 评估和可视化,用一张图判断模型到底行不行
评估指标的计算代码其实很简单:
from sklearn.metrics import mean_absolute_error, mean_squared_error def evaluate(y_true, y_pred): mae = mean_absolute_error(y_true, y_pred) rmse = np.sqrt(mean_squared_error(y_true, y_pred)) # 计算MAPE,注意y_true不能为0 mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100 return {'MAE': mae, 'RMSE': rmse, 'MAPE': mape}注意一个问题:如果你的数据做了归一化,那么y_pred和y_true都是归一化后的值,直接算出来的指标没有业务含义。你必须先用归一化时的scaler把预测值还原到原始尺度,再计算评估指标。这一步漏掉的话,你的RMSE看起来非常小,实际上只是归一化后的数值,论文里根本不能用。
可视化方面,最关键的一张图就是“测试集真实值 vs 预测值”的曲线。选一个连续时间段(比如连续7天),横轴是时间,纵轴是负荷值,两条曲线一条用蓝色一条用红色,立刻能看出模型哪里有偏差。通常你会发现:模型在峰值处预测偏低,在低谷处预测偏高,这就是回归问题的“均值回归”倾向。能在论文里诚实分析这一点,比光放一张“效果很好”的图更有说服力。
5. 常见问题与排查技巧,踩过的坑都给你列出来
5.1 问题速查表,训练遇到bug先来这里对号入座
我把自己做这个项目时遇到过的、还有帮别人debug时见过的高频问题整理成了一张表。如果你训练过程中遇到指标不对、loss奇怪、报错不断的情况,先来这里对照一下。
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 训练loss不下降 | 学习率太大或太小 | 试着调整学习率到0.0001或0.01 |
| 训练loss很低但测试指标很差 | 数据泄露,归一化时用了全量数据 | 只用训练集fit scaler,再变换验证集和测试集 |
| 预测值比真实值变化平缓,峰值偏低 | 模型欠拟合或者特征不够 | 增加模型复杂度,加入滞后特征和时间特征 |
| 验证集loss先降后升 | 过拟合 | 增加Dropout、正则化,使用早停 |
| 预测曲线像是“平移”了真实曲线 | 模型过度依赖上一时刻的负荷值 | 减少直接滞后1步特征,加强日周期特征 |
| loss曲线震荡特别大 | 学习率过大或batch_size太小 | 降低学习率,增大batch_size |
| 训练到一半loss变成NaN | 梯度爆炸 | 降低学习率,增加梯度裁剪(clip_grad_norm) |
| 多步预测时误差一路累积 | 递归预测的误差传播 | 改用teacher forcing,或直接多输出步长训练 |
这张表我建议你直接做成论文里的“实验分析”或者“问题讨论”章节,比写在代码注释里管用多了。
5.2 数据泄露,最隐蔽也最致命的问题
数据泄露这个问题我前前后后提到了好几次,因为它真的太容易犯,而且犯了你可能毫无察觉,直到答辩时被老师一针见血地指出来。
最典型的一种情况就是我说的:先对整个数据集做归一化,再划分训练测试集。这样测试集的最大值和最小值已经被模型“看到”了,测试指标会虚高。第二常见的是:构造滞后特征时,用到了测试时间段内的数据,比如你预测测试集某一天的负荷,特征里却包含了同一天后面时刻的信息——这是时间穿越,不对的。第三常见的是:用t时刻的真实值参与t+1时刻的预测。有些同学把真实负荷当作特征输进去,模型当然能“预测”得很好,但这种模型没有任何实用价值。
怎么自查?我教你一个简单办法:挑出测试集里某一天的数据,只用这一天的输入特征和模型参数,手工走一遍forward计算预测值,看是否跟代码跑出来的结果一致。如果一致,并且整个流程中只有该时刻之前的信息参与计算,那基本没问题。另外,你可以故意把训练集和测试集打乱再训练一次,如果测试指标“奇迹般”变好了,大概率是原流程中测试集信息已经被泄露了。
5.3 LSTM训练为什么这么慢,以及怎么加快
LSTM天生比CNN慢,因为它的计算是串行的——每个时间步的隐状态都依赖前一个时间步。在CPU上训练更是煎熬,一个epoch可能要跑几分钟。我建议所有做深度学习的毕设,不管预算多少,至少租一张GPU云服务器来跑,现在很多云平台对学生有优惠,一小时几块钱,训练速度能快几十倍。
如果你只能在CPU上跑,那就从这几方面优化:第一,减小window_size,比如从48降到24;第二,减小hidden_size和num_layers,从128/2降到64/1;第三,增大batch_size,充分利用CPU的多核并行;第四,降低训练epoch次数,配合早停机制,不要无脑跑100轮。
还有一个能显著提升效率的小技巧:用torch.utils.data.DataLoader时设置num_workers>0,这样数据加载可以多进程并行。在Windows上如果报错就设成0,Linux上可以设成4或者8。
5.4 答辩前一定要准备的问题清单
说句实在话,代码能跑只是及格线,能讲清楚才是拿高分的关键。根据我多年帮人准备答辩的经验,下面这组问题你在答辩前一定要能流畅回答:
第一组关于数据处理:“你的数据来源是哪里?总共有多少条?训练集测试集怎么划分的?为什么这么划分?”——答清楚来源、数量、按时间顺序划分即可。
第二组关于特征:“你选了哪些特征?为什么选它们?如果去掉天气特征,效果会差多少?”——建议你真的做一个去特征的对比实验,用数据说话。
第三组关于模型:“为什么选LSTM?它比RNN好在哪里?比Transformer有什么优势和劣势?”——不需要长篇大论,能讲清楚门控机制解决了梯度消失问题即可。
第四组关于评估:“MAPE、RMSE各有什么优缺点?MAPE在你的数据上有没有什么问题?”——注意,如果负荷值接近0,MAPE会变得很大,这是它的缺陷,能主动说出来很加分。
第五组关于创新点:“你的项目跟现有的方法比,创新点是什么?”——不用强行吹,就说是数据预处理方法的组合、特征工程的改进、或者模型结构的轻量化,这些都是实打实的贡献。
最后再分享一点实操体会
我做了这么多次负荷预测项目,最大的感受是:这个题目看起来不难,但做好其实需要很细的功夫。数据预处理阶段多花点时间,后面模型训练就能少踩很多坑。我自己的习惯是先把数据可视化一遍、把统计特征打印出来看一遍,再动手写模型代码。很多同学一上来就写模型,最后发现问题出在数据上,回头改数据又得重新训练,白白浪费时间。
还有一点想提醒你:模型省下的每一分钟训练时间,都值得投资在跑实验对比上。同一个模型,分别用“只含负荷特征”和“含全特征”跑两遍;同一个特征集,分别用LSTM和CNN-LSTM跑两遍。这种横向对比实验在你写论文和准备答辩时就是最扎实的弹药。你自己心里有数,老师问起来也底气足。祝你的毕设顺利。
本文还有配套的精品资源,点击获取