小波神经网络预测代码详解:两种架构与Python实现 简介一份面向毕业设计场景的小波神经网络预测代码包以 MATLAB 为主要实现语言定位帮助需要复现 WNN 模型、研究非平稳信号预测的学生与科研人员快速上手适合具备一定信号处理和机器学习基础的学习者进一步深入。包内共 6 个文件以 .m 脚本为主另含 1 个 .mat 数据文件压缩后仅约 5KB体量小但模块划分清晰主程序负责整合小波变换与神经网络自定义 Morlet 小波基函数及其导数用于构造小波层层析分析模块对输入数据做深度处理神经网络模块承担训练与预测.mat 数据文件则提供交通流量实测样本便于直接运行体验。目前已有 98 人学习。借助这套代码读者可以完整了解小波变换与神经网络融合建模的思路从数据加载、小波分解、特征提取到模型训练与预测的各个环节均有对应实现对于小波基函数选择、导数计算等关键细节也有直观展示还附带自定义基函数便于深入理解 WNN 内部机制适合毕业设计复现、WNN 入门或算法对比实验的参考整体代码量小、可读性好便于逐行阅读和二次修改。1. 小波神经网络预测代码.rar先分清里面是哪一种小波加网络收到一份命名规整的「小波神经网络预测代码.rar」时别急着解压跑 main.py。这类毕设代码里的“小波神经网络”并不是一个固定模型而是小波变换加神经网络的统称——里面可能是小波分解后接 BP 或 Elman 的串行脚本也可能是一个把 Morlet 小波当激活函数的紧凑网络。它解决的核心问题是电力负荷、股价、水位这类非平稳时间序列的预测适合正在做毕设、需要对论文方法做复现或者想在一周内跑通一条完整预测链路的人。第一件事是搞懂这个 rar 属于哪种接法否则后面所有调参都在猜。2. 两种小波神经网络架构预处理接法和激活函数接法的差异2.1 串行式小波分解只做前置处理打开 rar 后如果看到wavedec、waverec、db4这些函数名大概率是串行式先用小波变换把原始序列拆成高频细节和低频近似再让神经网络去预测每一层系数或只预测低频分量最后把预测结果反变换回时域。这种写法在毕业设计里占了大多数核心逻辑是原始序列太吵神经网络直接拟合容易翻车小波分解相当于先做了一圈“频段切分”让网络分工明确。它的优点是把“小波”和“神经网络”解耦你可以先单独验证分解效果再换网络结构。缺点是代码文件数量多通常有wavelet.py、model.py、train.py任何一个文件的小波参数和特征拼接方式对不上最终曲线就断在中间。2.2 嵌入式小波基函数充当隐藏层激活另一类代码更紧凑r 包里可能只有一个YourModel.py里面有一个看起来奇怪的激活函数定义类似np.cos(1.75 * x) * np.exp(-0.5 * x * x)。这就是嵌入式小波神经网络也就是通常说的 WNNWavelet Neural Network把小波基函数直接放在隐藏层替换掉 sigmoid 或 ReLU。在这里输入先做线性加权得到net再用尺度参数a和平移参数b做一次伸缩z (net - b) / a最后把z送进 Morlet 小波函数。训练时不只是更新权重连a和b也在被梯度下降调整——这就是“小波”参与学习的方式。这种方式代码量少参数更少但对初值敏感调起来更像在做“玄学”调参。2.3 两种架构怎么选看你的数据性质和毕设题目对比维度串行式小波分解 网络嵌入式小波激活函数 WNN代码复杂度文件多链路长单模型文件链路短可解释性分解特征可单独可视化小波参数抽象解释性弱对数据量要求依赖后续网络规模参数少更适应小样本调参重点分解层数、小波族、重构方式尺度和平移初值、隐层节点数适合题目负荷/水位/交通量预测快速复现论文、小样本回归如果毕设题目里写的是“基于小波神经网络的 XXX 预测”多数老师默认的是串行式因为它能在论文里多画几张分解图工作量看起来更扎实。如果题目是“小波网络”或“WNN”那嵌入式更贴题。看 rar 里的文件名能直接判断只有一个模型文件的更可能是嵌入式。2.4 小波分解的核心参数小波族、分解层数、重构方式串行式代码里最常踩坑的是三个参数。第一是小波族db4、sym8、coif3最常见db系列滤波器短、计算快sym系列对称性更好、边界畸变小数据量小就优先db4数据长且平滑就选sym8。第二是分解层数默认level3或level4层数越高频率分辨率越细但每高一层数据长度折半边界效应越严重预测步长较长时不要把层数堆到 5 以上。第三是重构方式用pywt.waverec(coeffs, wavelet)时必须保证传入的系数长度和小波分解输出完全一致哪怕差一个点重构出来的序列长度会漂移后面跟真实标签对齐时直接出维度错。3. 数据与标签怎么接进预测代码切分、归一化与反归一化3.1 时间序列切分不能用 train_test_split很多拿 rar 里的代码直接跑的人第一轮翻车就死在数据切分上。回归预测代码里如果看到from sklearn.model_selection import train_test_split基本可以断定这份代码是拿非时序数据写的改版随机切分会把未来样本混进训练集测试指标会好看得离谱等换到真实验证时就崩。时间序列必须按时间顺序切分常见做法是前 80% 训练、后 20% 测试再把最后一段留作验证。下面这段切分逻辑可以直接接进任何预测代码import numpy as np def create_sequence_data(series, n_steps8, train_ratio0.8): X, y [], [] for i in range(len(series) - n_steps): X.append(series[i:i n_steps]) # 用前 n_steps 个点 y.append(series[i n_steps]) # 预测下一点 X np.array(X).reshape(-1, n_steps) y np.array(y) split int(len(X) * train_ratio) X_train, X_val X[:split], X[split:] y_train, y_val y[:split], y[split:] return X_train, X_val, y_train, y_valn_steps是滑动窗口长度也就是用多少历史点预测下一个点。不要只看训练误差选窗口窗口越长输入维度越高小样本网络反而学不动一般从 5 到 10 之间开始试。注意返回的X_train和X_val下标是连续的没有打乱这是时间序列预测的底线。3.2 归一化和反归一化的位置预测代码里最容易改错的一步归一化参数只能用训练集计算然后同时应用到训练集和测试集。如果先对整个序列做归一化再切分测试集里的均值方差已经泄露到训练过程里这种信息泄漏会让指标虚高。常见做法是用sklearn.preprocessing.MinMaxScaler拟合并转换训练集再用同一个scaler转换测试集。预测结束后要把预测值按逆变换还原成原始量纲再算误差否则 RMSE 永远小到像 0.0001答辩时一被问就露馅。3.3 单步预测与多步预测的标签构造上面的create_sequence_data构造的是单步标签。实际毕设需求经常是预测未来 24 小时有两种做法一种是滚动预测每次用预测出的值补回窗口末尾再预测下一点误差会累积另一种是直接构造多步标签把 y 设为未来h个点网络输出维度变成h。两种都有人用但小波神经网络预测代码里多数写的还是滚动式因为它和后端反归一化写起来更直白。def create_multi_step_data(series, n_steps8, n_horizon3): X, y [], [] for i in range(len(series) - n_steps - n_horizon 1): X.append(series[i:i n_steps]) y.append(series[i n_steps:i n_steps n_horizon]) return np.array(X), np.array(y)n_horizon是预测步长。多步标签会让训练样本数减少因为每个样本的末尾必须留出n_horizon个真实点。如果数据只有几百个点建议先用单步滚动模型结构简单训练也稳定。4. 用 Python 实现小波神经网络并跑通预测核心参数与最小可运行代码4.1 为什么隐层用小波函数而不是 sigmoid在神经网络里激活函数的作用是给线性变换引入非线性。sigmoid 的梯度在两端趋近于零堆多层后梯度消失严重ReLU 在负半轴直接截断输出不平滑。Morlet 小波在零附近有清晰的局部振荡它只对输入落在某个局部区间里的样本产生显著响应这种“局部敏感”特性让它特别适合拟合带突变、带周期性成分的时间序列。在小波神经网络里隐层的每个节点本质上是不同尺度、不同位置的小波基函数训练过程就是在找一组最能匹配数据波动形态的小波组合。4.2 Morlet 小波神经网络的完整实现最小可运行代码下面这份代码是一个可运行的迷你版 WNN隐藏层 8 个节点输出层是线性加权训练用简单梯度下降。细节上我刻意没有加动量、没有早停先让结构透明方便你替换成自己的数据。import numpy as np def morlet(x): # 常用近似 Morlet 小波1.75 是满足容许性条件的常用取值 return np.cos(1.75 * x) * np.exp(-0.5 * x * x) def morlet_deriv(x): # Morlet 对 x 的导数用于反向传播 d -1.75 * np.sin(1.75 * x) * np.exp(-0.5 * x * x) d -x * np.cos(1.75 * x) * np.exp(-0.5 * x * x) return d class MinimalWNN: def __init__(self, n_input, n_hidden8, init_scale1.0, lr0.01): # 输入权重、输出权重、尺度、平移都初始化成小随机数 self.w_in np.random.randn(n_input, n_hidden) * 0.5 self.w_out np.random.randn(n_hidden) * 0.5 self.a np.full(n_hidden, init_scale) # 尺度初值 self.b np.zeros(n_hidden) # 平移初值 self.lr lr def forward(self, x): # net 是线性加权z 是经尺度和平移后的输入 self.net np.dot(x, self.w_in) self.z (self.net - self.b) / self.a self.h morlet(self.z) return np.dot(self.h, self.w_out) def train_step(self, x, y_true): y_pred self.forward(x) delta_out y_pred - y_true # 输出层误差 # 小波函数的链式梯度对 z 的导数除以 a 归一化 delta_hidden delta_out * self.w_out * morlet_deriv(self.z) / self.a self.w_out - self.lr * delta_out * self.h self.w_in - self.lr * np.outer(x, delta_hidden) self.b - self.lr * (-delta_hidden) # 平移项梯度 self.a - self.lr * (-delta_hidden * self.z) # 尺度项梯度 return float(delta_out ** 2)关键点有三个delta_hidden里的morlet_deriv(self.z)是对小波函数本身求导这一项把小波的“局部振荡”特性传回输入权重。除以self.a是因为z (net - b) / a对net求导会多出一个1/a很多代码丢掉这一项训练会变得极不稳定。尺度a的梯度带了self.z这表示网络会动态改变小波的频率数据波动快的地方会把尺度压小平缓区间会把尺度放大。初值init_scale设成 1.0 适合归一化到 0~1 的序列如果输入没有归一化这里要跟着放大。train_step返回的是平方误差方便外层打印训练曲线。训练时把序列窗口样本逐条喂进去不要做 mini-batch简化为单样本更新更稳。训练主循环series np.sin(np.linspace(0, 20, 600)) 0.3 * np.random.randn(600) X, X_val, y, y_val create_sequence_data(series, n_steps6) # 自己补一步先对序列做 MinMax 归一化再建序列这里不再重复切分 model MinimalWNN(n_input6, n_hidden8, init_scale1.0, lr0.01) for epoch in range(300): total_loss 0.0 for xi, yi in zip(X, y): total_loss model.train_step(xi, yi) if epoch % 50 0: print(fepoch {epoch}: loss{total_loss / len(X):.5f})4.3 三组必调参数隐层节点、学习率、小波尺度初值参数取值范围参考调低/调高影响隐层节点数4~12过小欠拟合曲线平滑无细节过大训练变慢且小波基互相重叠测试集更差学习率lr0.001~0.05过高 loss 震荡、NaN过低前面几百轮 loss 几乎不动尺度初值init_scale0.5~2.0输入取值跨度大则调大对 0~1 归一化序列1.0 相对安全隐层节点按输入长度的一半向上取整起步输入 6 个历史点就先试 8 个。学习率如果发现前 50 轮 loss 不降优先检查归一化而不是继续调学习率。尺度初值方向错了会出现所有小波基都被压缩到很小范围输出怎么训练都是均值附近的一条直线。4.4 小波 Elman 神经网络的混合写法先分解后预测热词里常看到“小波 Elman 神经网络”它更常见的是串行式变体先用小波分解把序列拆开再把分解出的若干系数作为 Elman 网络的输入特征。Elman 网络本质上是带承接层context layer的循环结构适合捕捉时间上的动态依赖。因为小波分解已经过滤掉了高频毛刺Elman 只需要对低频趋势做记忆即可。import pywt def wavelet_elman_features(series, waveletdb4, level3): coeffs pywt.wavedec(series, wavelet, levellevel) # 返回 cA 低频 各层 cD 高频拼成特征矩阵 max_len min(len(c) for c in coeffs) features np.column_stack([c[:max_len] for c in coeffs]) return features注意pywt.wavedec返回的每一层系数长度不同用min截到公共长度是最省事的对齐方式如果特征长度差异太大说明分解层数选得过高降一层再试。拼接后的特征可以直接替换前面MinimalWNN的输入或者喂给任意 RNN 实现。小波系数是“当期”值而预测目标通常是“下一时刻”所以拼接时要记得把标签向后错开否则模型只用当期系数就预测下期在现实数据集上会因为信息重叠而虚高。5. 小波神经网络预测代码避坑清单5 个高发问题与排查顺序5.1 预测曲线整体像一条直线几乎不跟随真实值波动现象训练集 loss 一路下降但测试集预测值基本贴着均值线细节全丢。原因最常见的是尺度初值init_scale太大或太小导致所有小波基函数的有效响应区间都不覆盖输入分布另一种可能是输出层初始化权重全部接近零梯度信号传不回输入层。还有学习率过低的情况但那些至少会看到曲线缓慢变化。解决先把输入数据归一化到 0~1再把init_scale从 1.0 往 0.5 和 2.0 两头各试一次同时把输出权重初始化范围从0.5缩到0.1。如果试完曲线仍然是直线打印model.z的分布如果大半绝对值超过 3说明小波位于饱和区朝缩小尺度方向调。5.2 训练集误差极低测试集误差爆炸现象R² 类指标在训练集上接近满分测试集上一塌糊涂。原因这是信息泄漏的经典特征。产生泄漏的路径有三条归一化在建序列之前就做了全样本统计切分时用了train_test_split随机打乱或者小波分解时wavedec作用在整条序列上再切窗口。任一条都会让模型“偷看”未来信息。解决严格按“先切分、后归一化、再分解”的顺序执行。任何统计量均指、方差、小波系数都只从训练段计算测试段用同一套参数。改完以后测试误差通常会上升那才是真实水平。这个坑在毕业设计答辩被问的概率极高务必自查。5.3 小波重构后序列长度与原数据对不上现象pywt.waverec(coeffs, wavelet)重建出来的序列长度和原始序列长度不一致导致跟标签对齐时报维度错误。原因wavedec输出的系数长度由数据长度、小波滤波器长度和延拓模式共同决定。db4的滤波器长度是 8做一次分解数据长度会减半或缩短多层分解后边缘信息被截断直接对不完全系数做重构长度自然漂移。这个不是 bug是小波变换的固有属性。解决不要手工截断系数直接用pywt.waverec(coeffs, wavelet)完整重构再把重构结果截到原长度如果代码里出现过coeffs[0][:n]这类手动切片检查切片是否影响重构维度。宁可后截断不要前截断。5.4 训练时 loss 出现 NaN或者某一步突然变成 inf现象前几十轮还正常某轮开始 loss 跳成 NaN之后恢复正常不了。原因尺度a被更新到接近 0导致morlet_deriv(self.z) / self.a直接爆掉。a的梯度里带了self.z本身当某个输入样本的加权和偏大时梯度一步更新过头尺度崩塌。解决给a的更新加约束一个简单可靠的做法是每轮参数更新后执行self.a np.clip(self.a, 0.2, 5.0)把尺度限制在一个安全区间。另外把学习率从 0.01 降到 0.005双管齐下基本能避免。这条属于血泪经验我第一版 WNN 代码就卡在 NaN 上查了两天。5.5 小波 Elman 混合模型特征维度对不上训练直接报错现象小波分解出了 4 组系数想拼成特征矩阵但每组系数长度不一样拼接报错或者拼完矩阵出现错位。原因不同层的系数长度天然不同直接np.column_stack会因为长度不一致而失败。强行用resample会改变频率结构用min(len(c))截取又可能把有效的趋势信息切掉都对预测有影响。解决先检查各层长度比如果最高层系数长度不到最底层的四分之一说明分解层数太高降到level2再试。对齐策略上我更推荐把每层系数单独做末端补齐到一致长度补的值取该层最后有效值的重复值比零填充温和模型也更稳定。6. 当心预测曲线失真验证误差指标与可视化结论6.1 训练集和测试集分开看误差统一用原始量纲预测代码跑通后最容易被忽略的动作是把两个误差拆开看训练集 RMSE 和测试集 RMSE。它们的比值能告诉你模型是欠拟合比值接近 1都偏高还是过拟合比值大于 2测试明显恶化。计算时使用反归一化后的数值否则你看不到真实物理含义的误差水平。from sklearn.metrics import mean_absolute_error, mean_squared_error pred_inv scaler.inverse_transform(pred.reshape(-1, 1)).ravel() y_inv scaler.inverse_transform(y_val.reshape(-1, 1)).ravel() rmse np.sqrt(mean_squared_error(y_inv, pred_inv)) mae mean_absolute_error(y_inv, pred_inv) print(fRMSE{rmse:.3f} MAE{mae:.3f} MAPE{np.mean(np.abs((y_inv-pred_inv)/y_inv))*100:.2f}%)RMSE 对大误差敏感MAPE 对零点附近的数据很不稳定如果序列里出现过零值MAPE 直接剔除改用 MAE 加 RMSE 组合。6.2 可视化只看三个要素训练拟合段、测试段、边界放大图曲线图不要只放完整序列那是给答辩 PPT 凑数的。第一张图放训练集拟合叠加真实值证明模型能学第二张图放测试段预测对比证明模型能复习第三张图截取测试段里波动最大的 50 个点做局部放大这是判断小波局部拟合能力的关键。如果局部放大图里尖峰被削平说明小波基数量不够往隐层节点数上加如果尖峰处出现大角度超调说明学习率偏高调到 0.005 以下。6.3 对比实验至少要有一个这是答辩的底气小波神经网络常见的问题不是“不好用”而是“看不出优势”。任何预测模型都能画一条好看的训练拟合曲线所以你的结论里必须有一组对比——比如让小波神经网络的参数不变只把 Morlet 激活函数换回 tanh训练同样的轮数测试 RMSE 会有什么变化。这一组消融实验就能证明你加了小波的收益比单纯说“我的模型效果好”有力得多。我自己的教训是当初跑完 WNN 只放了一组曲线答辩被评委一句“它的优势在哪里”问住后来补了这组对比才过关。代码里最好提前预留一个activation参数切换只需一行吃亏的就是没留这手。希望帮到你。本文还有配套的精品资源点击获取