news 2026/10/10 17:48:38

小波神经网络预测代码实战:从分解到重构的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小波神经网络预测代码实战:从分解到重构的完整指南

简介:小波神经网络预测代码包源自一个毕业设计项目,面向具备信号处理与机器学习基础的研究者和学习者,旨在演示小波变换多分辨率分析与神经网络自适应学习的融合方法。压缩包共六个文件,包括五个脚本与一个数据文件,整体仅约五千字节;脚本涵盖模型主程序、自定义小波基及其导数计算、层析分析等模块,数据文件提供实际交通流样本,便于直接运行测试。通过研读代码,可以梳理出从原始数据加载、小波变换、特征构造到神经网络训练与预测的完整流程,理解各模块的分工与接口关系;对于毕业设计、课程设计或科研入门而言,这是一份轻量且可扩展的参考实现。目前已有九十八人学习浏览。使用时需注意遵循原始许可协议,仅限交流学习,禁止商业用途;同时建议结合相关理论知识进行调试与优化。

1. 小波神经网络预测代码:不是把所有序列丢进一个黑匣子

接过一个“小波神经网络预测代码.rar”,里面躺着几个 Python 脚本和一份数据,很多人第一反应是直接跑train.py,然后对着 Loss 曲线发愣。这套资源真正做的事是把时间序列先做小波分解,再用神经网络分别预测低频和高频分量,最后重构出未来值;小波也可以作为激活函数嵌进网络里,这份代码包两种路线基本都覆盖了。

适合正在做毕业设计、需要完成“预测类”模块的同学,也适合想把小波 Elman 神经网络从论文搬进实际数据的从业者。先用默认配置跑通,再换自己的数据、调分解层数和网络结构,比从头写pywt和反向传播省太多时间。下面按我拆包后的实践顺序,从原理到踩坑一条条讲清楚。

2. 小波神经网络不是玄学:从分解到重构的四个组件

2.1 离散小波分解和小波包分解:选几层、用哪个小波基

小波分解做的事情是把一条非线性时间序列按照频率拆开。调用pywt.wavedec,一次得到低频近似系数cA_n和多组高频细节系数cD_n...cD_1。低频系数保留趋势,高频系数保留局部波动,预测任务里趋势是主菜,细节是调味料。这份代码里默认做 3 层分解,用的是db4小波基,分界模式用periodic。

import pywt def dwt_decompose(data, level=3, wavelet='db4'): coeffs = pywt.wavedec(data, wavelet=wavelet, level=level, mode='periodic') names = [f'cA_{level}'] + [f'cD_{i}' for i in range(level, 0, -1)] return dict(zip(names, coeffs))

level=3表示分解到第 3 层,periodic是周期延拓模式,边界处不会产生明显的人为跳变,适合预测序列。如果换zero模式,重构后的首尾会出现振荡,这是预测里最常见的坑之一。分解层数不是越多越好,我一般用int(log2(len(series) / input_len))作为上限,超过 5 层后每个系数序列样本数太少,网络根本学不够。

小波基的选择与信号形态相关。常用的是db4,计算快、紧支撑、正交性质好;sym8对称性更好,对相位失真更小;morl是连续小波基,通常不用在离散分解里,而是用作小波神经网络的激活函数。具体对比在第四章参数部分给出表格。

2.2 神经网络建模:低频用 Elman 还是全连接,尺度怎么进网络

拆完后要给每个分量建预测模型。这里热词“小波 Elman 神经网络”指的就是把小波分解和 Elman 循环网络结合的方式。Elman 网络和普通全连接的区别是多了一个承接层,把上一时刻隐藏层的输出反馈到当前输入,因此对短时序依赖非常敏感,非常适合预测小波系数这种强自相关序列。

import numpy as np def elman_cell(x, h_prev, Wxh, Whh, bh): h = np.tanh(np.dot(Wxh, x) + np.dot(Whh, h_prev) + bh) return h

这份代码里常见做法是每个分量单独建模:cA_n代表趋势,用稍大一点的 Elman 网络;cD代表高频扰动,用更浅的网络,防止高频分量过度拟合。另一种混合路线是把小波函数作为隐藏层激活,这时网络变成了真正的小波神经网络,需要额外设置尺度参数a和平移参数b。两种路线在资源里都有实现,但先用分解加 Elman 的方式跑通,效果往往比直接魔改激活函数稳定。

2.3 逆小波重构预测值:长度、延拓和系数对齐

预测完成后,要回到原始尺度才能看到真实数值。逆变换最烦的不是公式,而是系数长度对齐。pywt.waverec要求cA和cD长度匹配,否则要么抛异常,要么重构出的序列尾部多一段毛刺。

def reconstruct(pred_cA, pred_cD, level=3, wavelet='db4'): coeffs = [pred_cA] + list(pred_cD) # 高频分量需要对齐到 cA 长度,使用后向填充 coeffs = [np.pad(c, (0, len(pred_cA) - len(c)), mode='edge') for c in coeffs] series = pywt.waverec(coeffs, wavelet=wavelet, mode='periodic') return series

np.pad的edge模式用最后一个有效值向外延拓,比补零更自然。重构后返回的序列长度不是固定不变的,它取决于分解层数和你给进去的系数长度,所以后续要精确截取预测区段,不要直接认为len(series)等于原始长度。

2.4 标准化顺序:先归一化还是先分解

很多人拿到数据就直接MinMaxScaler.fit_transform全序列,再分解、训练、预测,测试集指标好看到离谱,一换新数据就崩。这是典型的信息泄漏。正确顺序是:先用训练集拟合 scaler,再分别归一化训练集和测试集,然后分解;预测后先对每个分量的预测结果做逆变换,再通过waverec重构。如果先分解再归一化,则每个分量需要单独保存 scaler,重构前要逐个逆变换。

from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler(feature_range=(-1, 1)) train_scaled = scaler.fit_transform(train.reshape(-1, 1)).ravel() test_scaled = scaler.transform(test.reshape(-1, 1)).ravel()

注意scaler只能fit在训练段上,测试段只能用transform。这个习惯直接决定了你的结果是真预测还是自欺欺人。

3. 把代码跑起来:目录结构、依赖与第一次运行

3.1 拿到 rar 之后先看这几个文件

解压后别急着找main.py,先看目录结构。我拆过的这套小波神经网络预测代码包,常见结构是这样:

小波神经网络预测/ ├── data/ │ └── load_data.csv ├── model/ │ ├── dwt.py │ ├── elman.py │ └── activation.py ├── config.py ├── train.py ├── predict.py └── requirements.txt

dwt.py负责分解和重构,elman.py是网络主体,activation.py里是小波激活函数实现,config.py是所有参数集中地。我的习惯是先打开config.py看一眼默认的WAVELET、LEVEL、INPUT_LEN、HORIZON四个值,这几个参数决定你后面需要动多少代码。

3.2 配置环境:Python 版本与依赖清单

这份代码基于 Python,依赖集中在数值计算、小波变换和机器学习三块。建议新建一个干净环境,避免把老项目的包混进来。

conda create -n wnn python=3.9 -y conda activate wnn pip install numpy pandas matplotlib PyWavelets scikit-learn

PyWavelets 就是pywt,如果看到import pywt失败,先查这个包。后续如果代码里有torch或tensorflow版本,再按需补装,先用 CPU 版把流程跑通,不要一上来就配置 GPU,会分散排查重点。

3.3 运行示例:修改数据路径、预测步长和输出

数据文件要求至少两列,第一列是时间,第二列是数值。运行默认训练时会自动切分训练集和测试集,并输出每个分量的 RMSE。命令加参数的方式比改代码方便:

python train.py --data ./data/load_data.csv --input_len 48 --horizon 6 python predict.py --model ./checkpoints/best.pkl --data ./data/test.csv

--input_len 48表示用过去 48 个点预测未来,--horizon 6表示预测未来 6 个点。这两个参数直接决定训练样本的张成范围;--horizon设得越大,模型误差越大,第一次跑建议保持 1 到 6 之间。model加载后的输出是重构回原始量纲的预测序列,不是归一化后的值。

4. 核心参数怎么调:层数、学习率、小波基和预测步长

4.1 小波基函数选择:db4、sym8、morl 的取舍

小波基的选择对我这个项目影响很明显,同一个数据集用db4和sym8跑出来的 RMSE 能差百分之十几。下面是我给毕设组学生常放的对比表:

小波基类型特点适用场景
db1(Haar)离散正交最简单,计算最快,边界易有台阶噪声小的短序列
db4离散正交紧支撑,计算省,默认首选大多数工程数据,先用它
sym8离散正交对称性更好,相位失真小周期性明显的负荷、流量数据
morl(Morlet)连续小波震荡衰减,非正交做激活函数和时频分析,不做离散分解

切换小波基只改一处配置,但注意这个选择同时影响分解层数和重构节奏。

# config.py 中修改 WAVELET = 'db4' # 离散分解用 db4 或 sym8 ACTIVATION = 'morlet' # 隐藏层激活函数用 morlet

如果你把WAVELET改成morl,pywt.wavedec会直接报错,因为离散多分辨率分析需要正交或双正交基。这块的“玄学”不在选哪个基,而在你改了之后有没有同步调整分解层数。

4.2 网络结构与输入步长:隐藏层神经元数和样本切窗

序列预测里输入步长是最容易被忽略的参数。我用一个基本规则:输入步长至少是预测长度的 4 到 8 倍。比如预测未来 6 个点,输入步长取 24 到 48。给神经网络切片时,用下面的函数生成监督学习的样本:

def make_samples(series, input_len=48, horizon=6): X, y = [], [] for i in range(len(series) - input_len - horizon + 1): X.append(series[i: i + input_len]) y.append(series[i + input_len: i + input_len + horizon]) return np.array(X), np.array(y)

返回的X形状是(样本数, input_len),y形状是(样本数, horizon)。如果是多尺度合并输入,就把多个分量的对应窗口纵向拼在一起,但我不推荐一开始就这么干,每个分量单独建模、单独调参,更容易定位问题。隐藏层神经元数常见范围是int(sqrt(input_len + horizon)) + 5到 20,先取 16,看损失下降情况再增减。

4.3 训练超参:学习率、批量大小与早停

时序数据切出来的样本不能像图像那样随机打乱,我在代码里保留了原始顺序,并按 batch 取连续段。训练时优先关注学习率和早停策略:

from torch.optim import Adam from torch.optim.lr_scheduler import ReduceLROnPlateau optimizer = Adam(model.parameters(), lr=0.001) scheduler = ReduceLROnPlateau(optimizer, mode='min', patience=10, factor=0.5)

lr=0.001是多数小波神经网络比较稳的起点,如果 loss 前十几轮完全不动,可以提高到 0.005;如果出现 NaN 或剧烈震荡,直接降到 0.0001。patience=10表示验证集指标连续 10 轮不下降才把学习率减半,这是防止过拟合的后悔药。批量大小常用 32 或 64,样本少时用 16。

4.4 预测效果评估:RMSE、MAPE、R2 怎么打印

毕设答辩里最常被问的是“你的模型到底好不好”,不要只给一张预测曲线图。建议每次跑完都打印三个指标:

import numpy as np def metrics(y_true, y_pred): y_true = np.asarray(y_true).ravel() y_pred = np.asarray(y_pred).ravel() mse = np.mean((y_true - y_pred) ** 2) rmse = np.sqrt(mse) mae = np.mean(np.abs(y_true - y_pred)) mape = np.mean(np.abs((y_true - y_pred) / (y_true + 1e-6))) * 100 ss_res = np.sum((y_true - y_pred) ** 2) ss_tot = np.sum((y_true - np.mean(y_true)) ** 2) r2 = 1 - ss_res / ss_tot return rmse, mae, mape, r2

MAPE分母加上1e-6,防止原始序列里有 0 导致除零。R2接近 1 不代表没有滞后,遇到曲线错位的情况,R2 照样很高,这就是为什么要用下一章的滚动验证补充判断。

5. 避坑:五次典型翻车记录

5.1 现象:预测曲线整体滞后一个时间步

第一次跑通,看着测试集曲线和真实曲线几乎贴合,但放大后发现每一条预测都慢了一拍。这个现象在小波 Elman 网络上特别常见,原因是输入窗口里有大量历史值,网络学到的捷径是“把最后一个观测值搬出来当预测”,低频趋势很容易被这种捷径欺骗。解决办法是改成预测差分序列:对原始系数做一阶差分,模型预测的是变化量,再累加回真实值,滞后问题会明显缓解。

diff = np.diff(series) pred_diff = model.predict(window) pred_value = series[-1] + pred_diff

如果改动差分后 RMSE 反而更高,说明原始序列本身噪声很小,不需要差分,可以只在高频cD分量上使用差分训练。

5.2 现象:小波系数输入网络后梯度爆炸,loss 变成 NaN

小波分解后cA_n低频系数量级通常很大,而cD高频系数量级则很小,两个分量未经处理直接喂进同一个网络,大的主导梯度,小的学不到,最后优化器更新幅度过大直接溢出。解决方法是每个分量单独使用一个 scaler,并且把压缩范围设成(-1, 1)而不是(0, 1),让神经元的输入更均匀。

x_scaler = MinMaxScaler(feature_range=(-1, 1)) cA_norm = x_scaler.fit_transform(cA.reshape(-1, 1)).ravel()

每次训练前检查np.isnan(loss)的时机,如果第一轮就出现 NaN,大概率不是学习率问题,而是某个系数序列没有归一化。

5.3 现象:测试集分数好得离谱,换一段新数据直接崩

这是信息泄漏的典型症状。常见原因有两个:一是对全序列做了归一化才划分训练测试,scaler 看到了全局统计量;二是小波分解时使用了跨越训练/测试边界的延拓,把未来的信息带到了训练段。解决方法是先切分出训练集和测试集,再分别用同一个 scaler 转换;滚动预测时,每一轮只能使用当前时刻之前的数据做分解,不能把未来段拼进系数序列再重构,否则你的测试集成了开卷考试。

5.4 现象:pywt.waverec报错长度不匹配,或重构结果多出几个点

小波分解每个系数长度不同,重构时如果直接waverec就会报错。更隐蔽的情况是重构不报错,但输出长度不等于原序列,造成预测点和时间轴对不上。我在给 n 层分解时,重构前会强制把所有系数pad到和cA_n一致,然后只截取预测对应区间。

coeffs = [np.pad(c, (0, len(cA) - len(c)), mode='edge') for c in coeffs] reconstructed = pywt.waverec(coeffs, wavelet='db4', mode='periodic') forecast = reconstructed[-horizon * (2 ** level - 1):]

截取步长和分解层数有关,别直接用[-horizon:]。这里每次改level都要同步检查截断长度,是我血泪经验换来的地方。

5.5 现象:morlet 激活函数让网络输出全部接近 0,loss 卡住不动

小波函数作为隐藏层激活时,Morlet 的表达式是cos(1.75x) * exp(-x^2/2),它只在x靠近 0 时有较强响应,一旦线性加权和超出一段范围,梯度会快速消失。解决方法是让权重初始化小一点,并在网络外部包一层尺度缩放变量a和平移变量b。

class WaveletLayer: def __init__(self, in_dim, out_dim): self.W = np.random.randn(in_dim, out_dim) * 0.1 self.a = np.ones((1, out_dim)) self.b = np.zeros((1, out_dim)) def forward(self, x): z = np.dot(x, self.W) + self.b return np.cos(1.75 * z) * np.exp(-z * z / 2)

初始化权重用0.1缩放置在[-1, 1]范围内,等训练几轮之后再逐步放开a和b为可训练参数。如果一上来就放开,模型基本学不进去。

6. 验证你的模型不是过拟合:滚动预测与残差检验

训练测试集上的 RMSE 都很好,不代表模型真能用。最有效的验证方式是滚动预测:每次只预测下一个点,然后把预测值当作已知输入,继续往后滚。这样模拟的是真实部署时只能拿到历史数据的状态。

def rolling_predict(model, data, input_len=48, steps=24): x = data[-input_len:].copy() preds = [] for _ in range(steps): p = model.predict(x.reshape(1, -1))[0] preds.append(p) x = np.append(x[1:], p) return np.array(preds)

model.predict要确保网络处于验证模式,如果用了 dropout,请先切换到eval。滚动步数steps和时间步长越拉越长,曲线大概率会漂移,这是正常现象,但如果 30 步后预测值直接变成常数,说明模型已经退化成“复制最近值”。

滚动预测之后再做一个残差自相关检查:残差是预测值和真实值的差,如果残差序列仍然有强自相关,说明模型没有把序列中的规律学干净。

res = y_true - y_pred corr1 = np.corrcoef(res[:-1], res[1:])[0, 1] if abs(corr1) > 0.3: print("残差仍有自相关,尝试增大输入步长或加入外部特征")

第一阶自相关超过 0.3 时,我一般会回头把input_len加大一倍,或者把原始序列做一次差分后再小波分解。以前我交毕设前只盯着 RMSE 曲线,后来被老师问了一句“如果一步一步滚动预测还能不能稳住”,才发现看起来完美的模型在第 30 步之后已经变成复读机。从那以后我每次训练完都强制走一遍滚动预测和残差自相关检验,这两步比任何花哨的网络结构都管用。这份小波神经网络预测代码包我已经整理好,需要的直接去拿,跑通默认参数后再改自己的数据,坑都在第 5 章,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 17:47:33

嵌入式HVAC双路温度监测:PJ85718DM与MK20DN128VFM5实战

1. 从一颗温度传感器说起:为什么本地与远程双路监测在嵌入式 HVAC 里绕不开做嵌入式 HVAC 控制板的人都有一个共识:温度采样不准,后面所有控制逻辑都是空中楼阁。不管是压缩机启停、风机调速,还是电子膨胀阀开度调节,全…

作者头像 李华
网站建设 2026/10/10 17:46:30

大脑肿瘤分割2分割实战:从数据加载到后处理全流程

简介:这份资源面向医学图像分割方向的初学者与算法实践者,提供416416分辨率下的人脑MRI肿瘤二分割数据集,前景为Tumor区域,mask以1表示肿瘤、0表示背景,标注清晰、背景简洁,适合直接用于分割模型训练与验证…

作者头像 李华
网站建设 2026/10/10 17:43:51

STM32L081CB与PJ85718DM低功耗温度监测方案:本地与远程HVAC应用

1. 项目缘起与整体设计思路温度监测这件事,听起来像是电子工程里最基础的入门课题,但真正落到工业级或楼宇级的嵌入式场景里,坑远比想象中多。我这次做的项目,核心就是用一颗 PJ85718DM 温度传感芯片配合 STM32L081CB 微控制器&am…

作者头像 李华
网站建设 2026/10/10 17:43:46

Python申请评分卡模型实战:从数据集准备到分数校准全流程

简介:本资源面向金融风控与数据挖掘方向的学习者,提供一套用Python实现申请评分卡模型的完整数据集与配套代码,帮助理解从原始申请数据到信用评分的全流程。压缩包共14个文件,约9.22MB,包含1个application.csv原始数据…

作者头像 李华