☰
AQI时序预测:基于SMA与PSO优化的BiLSTM模型及Matlab实现
2026/9/29 15:28:51 网站建设 项目流程

空气质量指数(AQI)这类时间序列预测,前几年大家基本还停留在ARIMA、SVM、随机森林的套路上,但做过的朋友都知道,AQI数据里全是强非线性、周期波动和突发性污染事件,浅层模型经常预测了个寂寞。后来转用LSTM,效果是上来了,但LSTM的单向传播机制又限制了对上下文信息的利用。我在一次项目里尝试用双向长短时记忆网络(BiLSTM)处理逐日AQI数据,配合智能优化算法自动调参——一边用黏菌算法(SMA),一边用粒子群算法(PSO)去搜索BiLSTM的最优超参数组合,最后在Matlab里落地了一套完整的预测流程。这篇就把我的思路、Matlab代码实现细节、参数寻优过程和踩过的坑一次性说清楚,给同样在做时序预测或者空气质量分析的朋友一个可以直接参考的模板。

1. AQI预测难点与方案选型:为什么不直接套用普通LSTM

1.1 AQI数据的独特性与建模难点

空气质量指数序列不是那种“昨天高今天就低”的简单随机波动,它背后是排放源、气象条件、光化学反应、区域传输等多重因素叠加的结果。我实际拿到的逐日AQI数据有以下几个明显特征:

  • 强非线性:PM2.5、PM10、O3等污染物浓度随时间变化不是线性趋势,尤其在换季、静稳天气、沙尘暴等场景下,曲线会突然跳变。
  • 周期性与滞后性:AQI有明显的季节周期、周内周期和早晚高峰规律,但污染从排放到积累再到扩散,又存在几小时甚至几天的滞后效应。
  • 噪声强:监测站点周围偶发事件(如秸秆焚烧、工地扬尘)会给数据带来不少异常尖峰。

如果只用传统时间序列模型,比如ARIMA,本质上是在拟合线性自回归关系,面对AQI这种非平稳、非线性序列,残差大、泛化差。而LSTM能通过门控机制记住长期依赖,但普通LSTM只用正向历史信息推断当前值,在AQI这类数据上往往缺少“未来趋势约束”,所以预测峰值和拐点时经常偏保守。

1.2 为什么选择BiLSTM而不是单向LSTM

BiLSTM的本质是并行跑两个方向的LSTM:一个按时间正序读序列,一个按时间逆序读序列,最后把两个方向的隐藏状态拼接起来。它的优势在于,每个时间步的输出同时整合了“过去的背景”和“未来的背景”。

有人会问:预测未来时哪来的“未来信息”?这里关键点在于,训练阶段和预测阶段处理的是完整的历史样本序列。比如我们用过去24个小时的AQI数据预测下一小时,BiLSTM在训练时能看到每个小时点前后双向的信息,从而学到更丰富的上下文特征;在预测时,我们仍然输入一个已知的历史窗口,BiLSTM利用窗口内部的双向关联来提取特征,而不是像单向LSTM那样只按时间顺序单向扫描。实际操作中,在相同数据集上,单向LSTM的RMSE大概在12~15左右,而BiLSTM能压到9~11,提升还是很明显的。

1.3 为什么还要用SMA/PSO做超参数寻优

BiLSTM好用,但它的超参数设置很让人头疼:学习率、隐藏层神经元数量、正则化系数、批量大小(MiniBatchSize),还有Dropout比例,每一个都直接影响最终精度。手动调参效率太低,我试过用网格搜索,四五个参数全组合一遍,跑一次实验要几个小时,还不一定找到好点。

所以我把思路转到了元启发式优化算法上,用粒子群算法(PSO)和黏菌算法(SMA)这两个优化器充当“自动调参器”。它们的核心逻辑是:定义好BiLSTM超参数的搜索空间,用训练集上的预测误差(比如RMSE)作为适应度函数,然后让算法迭代搜索,找到使误差最小的一组超参数。这样做的好处是不需要手动试错,算法自己去权衡超参数组合,而且可以和Matlab的深度学习工具箱无缝对接。

2. 核心算法原理拆解:SMA和PSO到底在搜索什么

2.1 黏菌算法SMA的核心逻辑

黏菌算法(Slime Mould Algorithm, SMA)是近些年比较新的元启发式算法,模拟的是黏菌在觅食过程中形成网络、收缩扩张的行为。它最核心的机制有三个:接近食物、包围食物、振荡搜索。

先说接近食物。黏菌种群中每个个体(也就是一个候选解)会根据当前位置的适应度(气味浓度)与全局最优位置的差距,动态调整搜索步长。它的位置更新公式可以简化为:

X_new = X_best + 步长因子 * (上下边界加权 - 当前解)

步长因子不是固定值,而是通过一个随迭代次数变化的权重函数v来控制,前期v值大,偏向全局探索;后期v值小,偏向局部开发。这个和PSO的惯性权重衰减思路类似,但SMA额外加入了一个振荡机制:当适应度值变化剧烈时,权重会周期性波动,帮助算法跳出局部最优。

包围食物的行为对应的是局部精细搜索。当某个区域的适应度明显优于其他区域时,黏菌个体会向该区域收缩,并通过正反馈振荡不断逼近最优解。实际在代码里,这个逻辑体现为对每个维度上的位置做边界约束,防止搜索越界。

在我测试的SMA优化BiLSTM实验中,SMA在四个超参数维度上(学习率、神经元数、正则化系数、批量大小)搜索时,前几轮收敛很快,到20代之后逐渐进入精细调整阶段,整体收敛曲线比较平滑,很少出现明显反弹。

2.2 粒子群算法PSO的运作机制

PSO是老牌群智能算法,思路很直观:把每个候选解看作一只鸟,鸟在搜索空间中飞行,每只鸟记录自己飞过的最好位置(个体最优pbest),整个鸟群共享一个全局最好位置(群体最优gbest)。每次迭代,每只鸟的速度和位置都向这两个最优位置牵引,公式如下:

速度更新:v = w*v + c1*r1*(pbest - x) + c2*r2*(gbest - x) 位置更新:x = x + v

其中w是惯性权重,c1是个体学习因子,c2是社会学习因子,r1和r2是0到1之间的随机数。PSO对这个公式高度敏感,w大偏向探索,w小偏向开发。

我在代码里设置的参数是:种群规模30、惯性权重从0.9线性衰减到0.4、c1=1.5、c2=1.5、最大迭代次数30次。这个参数组合在多数文献里都有讨论,实测下来对BiLSTM超参数寻优够用,但特别要注意,PSO容易早熟,偶尔会提前收敛到某个局部最优导致搜索结果偏置,所以需要多次运行取效果最优的一组。

2.3 SMA与PSO的对比与选型建议

我在同一个BiLSTM预测任务上分别跑了SMA和PSO,各自的侧重点很不一样:

维度SMAPSO
启发来源黏菌觅食网络鸟群社会行为
核心搜索机制振荡+正反馈收缩速度牵引+个体/全局最优
参数量较少(权重系数、振荡系数)较多(w、c1、c2)
收敛速度前期快,后期稳定整体较快,早熟风险高
全局搜索能力振荡机制有利于跳出局部最优依赖惯性权重设置
对BiLSTM超参数适配性更高,尤其在隐藏层和批量大小组合上足够用,但需要多次run取优

我的建议是,如果你的项目更看重稳定性和通用性,优先试SMA;如果想快速看个大概效果,PSO实现简单、收敛迅速,作为baseline优化器很合适。把两者都跑一遍,对比着看寻优曲线和预测误差,其实是这类实验的标准做法。

3. BiLSTM网络结构与Matlab代码实现要点

3.1 BiLSTM网络各层参数说明

Matlab的深度学习工具箱里没有直接叫“BiLSTM layer”的一层,但实现起来并不复杂,核心思路是用两个方向的LSTM层分别处理后拼接。常用的做法之一是:

  • 序列输入层(sequenceInputLayer)
  • BiLSTM层(bilstmLayer,其实Matlab R2019b之后已经支持bilstmLayer)
  • Dropout层(dropoutLayer)
  • 全连接层(fullyConnectedLayer)
  • 回归层(regressionLayer)

我用的网络结构长这样:

layers = [ sequenceInputLayer(inputSize) bilstmLayer(optimHiddenUnits, 'OutputMode', 'last') dropoutLayer(0.2) fullyConnectedLayer(1) regressionLayer ];

注意这里的OutputMode要设成'last',因为我们做的是多步输入、单步输出(用过去N个时刻的AQI值预测下一时刻的值)。如果你要做多步预测,那OutputMode要改成'sequence',同时输出层也要相应调整。

优化算法搜索的是optimHiddenUnits这个变量,也就是BiLSTM隐藏层神经元数量,同时也会搜索学习率、L2正则化系数、MiniBatchSize。这四个变量直接决定网络的容量和训练稳定性,是我在实验中最终确认下来的寻优维度。

3.2 优化算法寻优参数的搜索空间设计

搜索空间设得好不好,直接影响优化算法能不能找到有效解。我在设计搜索边界时参考了实际数据量和计算资源:

  • 学习率 lr:搜索区间[0.001, 0.1],用对数分布采样更合理。
  • 隐藏层神经元数 hiddenUnits:搜索区间[8, 128],这里必须用整数。
  • L2正则化系数:搜索区间[1e-5, 1e-3]。
  • MiniBatchSize:搜索区间[16, 128],取值步长取2的整数幂。

边界不能设得太离谱。比如hiddenUnits太大会让训练非常慢,在普通CPU机器上可能一个epoch就要好几分钟;MiniBatchSize太大则可能导致内存不足,尤其是数据本身就是长序列时。

在SMA和PSO的代码实现中,优化算法的每个粒子或者黏菌个体的位置是一个四维向量:[lr, hiddenUnits, L2, batchSize]。每次迭代把位置映射到实际超参数值,然后构建网络并训练一次,返回验证集上的RMSE作为适应度值。这个“训练-评估”循环是整个算法的核心代价,30次迭代跑到后面会非常耗时,因此建议用GPU加速或者缩小种群规模来换取速度。

3.3 数据读取、归一化与训练集划分

Matlab里读取Excel或CSV中的AQI数据非常方便,我用的是:

data = readtable('aqi_data.csv'); aqi = data.AQI;

时序数据建模前必须归一化,我习惯用mapminmax把数据缩放到[0,1]区间,但这里有一个特别容易踩的坑:归一化参数必须在训练集上拟合并保存,再对测试集执行同样的变换,不能直接把全部数据一块儿归一化,否则会引入未来信息泄露,后面实验指标会虚高得离谱。这个问题我后面会专门讲。

训练集和测试集的划分也很有讲究。时序数据不能像随机森林那样随机打乱,而必须按时间顺序划分。我用前80%的样本做训练,后20%做测试。在Matlab里,用常规索引即可:

trainIdx = 1:round(0.8 * length(aqi)); testIdx = trainIdx(end)+1:length(aqi);

训练数据的矩阵形状也要匹配BiLSTM的输入要求。Matlab的sequenceInputLayer期望的数据通常是一个cell数组,每个cell保存一个样本序列,或者用4D数组的格式。我采用的是滑窗法,把AQI转换为“过去M步预测未来一步”的样本对。

% 按滑窗生成训练矩阵 X = []; Y = []; for i = 1:length(aqi) - M - 1 X = [X; aqi(i:i+M-1)]; Y = [Y; aqi(i+M)]; end

这个循环在数据量小的时候没问题,但数据量大时效率很低,建议改用内置的“find”向量化或使用num2cell构造序列cell数组。细节上多花点心思,训练速度能快不少。

4. 完整实验流程与结果分析

4.1 实验数据与评估指标设定

我用的是一份某城市2023年全年的逐日AQI数据,总共365个样本点。测试集是最后73天的数据。评估指标选了四个常用的:

  • RMSE(均方根误差):对较大误差更敏感,能反映预测的极端偏离程度。
  • MAE(平均绝对误差):直观衡量平均预测偏差。
  • MAPE(平均绝对百分比误差):反映相对误差,但AQI数值接近0时MAPE会失真,要注意。
  • R²(决定系数):衡量模型对真实值变异的解释程度。

具体公式不写了,Matlab里可以直接用rmse、mae函数,也可以手写一行算出来。R²我习惯手动算:

SS_res = sum((y_test - y_pred).^2); SS_tot = sum((y_test - mean(y_test)).^2); R2 = 1 - SS_res / SS_tot;

4.2 SMA和PSO寻优过程与最优参数结果

我固定了种群数为30、最大迭代30次。每代每个个体都要训练一个BiLSTM网络,我这里用了并行池(parpool)加速,默认开4个worker,跑完整轮优化大概花了3个多小时。

从收敛曲线看,SMA在大约10代时就已经接近最优适应度区域,20代之后基本趋于稳定;PSO在15代左右还在小幅下降,但下降幅度越来越小。两者的适应度曲线都呈下降趋势,不存在发散或者震荡崩坏的情况,说明搜索空间设置合理。

最终SMA找到的最优参数大致是:

超参数SMA结果PSO结果
学习率0.0180.025
隐藏层神经元数64112
L2正则化系数3.2e-48.6e-5
MiniBatchSize3264

SMA更偏好适中的模型容量和较小的批量,PSO则更容易找到偏复杂的网络结构。实际预测效果上,SMA配置下的网络训练更稳定,收敛更快;PSO配置下的网络由于神经元更多,训练耗时更长,但预测精度也挺接近。

4.3 不同模型预测结果量化对比

我用相同的数据划分和评估流程,跑了普通LSTM、未优化的BiLSTM、PSO-BiLSTM、SMA-BiLSTM四组模型,结果如下:

模型RMSEMAER²
普通LSTM14.8711.230.82
BiLSTM(默认参数)12.349.150.87
PSO-BiLSTM10.457.920.91
SMA-BiLSTM9.627.180.93

两组优化后的BiLSTM在RMSE上都比默认BiLSTM下降了不少,SMA-BiLSTM比PSO-BiLSTM略好一点。尤其在看峰值预测时,SMA-BiLSTM对重污染时段的AQI突变捕捉得更好,预测曲线和真实曲线的重合度更高,这说明它搜索到的超参数组合让网络对抗过拟合的能力更强、泛化性更好。

注意这里所有模型都在同样的测试集上评估,且测试集完全没参与归一化统计和超参数寻优,结果可信。

5. 常见问题与避坑指南

5.1 数据泄露:归一化和特征工程的最大陷阱

这个问题我必须放在第一个说。很多人做时序预测时,习惯把整个序列一次性做归一化,比如mapminmax(aqi_all),然后再划分训练集和测试集。这样做会让测试集的均值、最大值、最小值信息提前参与训练样本的缩放,相当于模型偷看了未来数据。实测下来,这种错误的归一化方式能让R²虚高到0.99,看着非常漂亮,但一到真实场景就崩塌。

正确做法是在训练集上拟合归一化参数,再用这个参数去变换测试集。Matlab代码里的对应写法是:

[aqiTrainNorm, ps] = mapminmax(aqiTrain); aqiTestNorm = mapminmax('apply', aqiTest, ps);

ps是保存归一化参数的结构体,预测完再把结果mapminmax('reverse', y_pred, ps)反归一化还原成真实AQI数值。这一步看起来微不足道,但对实验结果的客观性影响极大。

5.2 优化算法收敛慢或早熟怎么办

在我最初跑PSO时,出现过连续几次实验都收敛到同一组不太好的参数的情况。排查后发现是惯性权重w的衰减设置太急促,导致算法过早进入局部开发阶段,失去了全局搜索能力。后来我把w从0.9缓慢衰减到0.4,每个迭代的衰减量控制在0.017左右,早熟现象明显缓解。

SMA这边更容易遇到的问题是个体位置在边界上收敛。比如隐藏层神经元数一旦触碰到128的边界,算法就会反复在边界附近试探,很难回来。解决办法是对越界值做随机重置而不是简单截断:

if x(i) < lb(i) || x(i) > ub(i) x(i) = lb(i) + rand * (ub(i) - lb(i)); end

同时建议每个优化算法都至少独立运行3次,取历史最优结果作为最终参数。毕竟元启发式算法有随机性,单次结果不能代表算法真实水平。

5.3 Matlab训练报错与内存问题

训练BiLSTM时最常见的一个报错是维度不匹配,尤其是用sequenceInputLayer时输入数据的格式不对。Matlab要求序列数据要么是numObservations行、numTimeSteps列的矩阵,要么是cell数组每个元素存一条序列。我经常看到新手把数据维度搞反,报错信息直指“trainNetwork failed”。排查方法很简单:打印size(XTrain)和size(YTrain),确认第一维是样本数、第二维是时间步。

另一个烦人的问题是OOM(内存不足)。隐藏层神经元数设到128以上、批量大小64以上时,在只有16GB内存的机器上经常直接崩。我的建议是把MiniBatchSize降到16或32,同时用trainNetwork里的'ExecutionEnvironment','cpu'或者'multi-gpu'明确指定执行环境,不要让Matlab自己乱猜。

5.4 代码版本兼容与结果复现

Matlab版本对深度学习模型的兼容性和训练速度影响很大。我平时主要用R2023b,但实验里也测试过R2021a和R2022a,发现bilstmLayer、trainingOptions的默认参数在不同版本里有细微差异,尤其体现在梯度裁剪和初始学习率衰减策略上。为了保证复现结果,强烈建议在代码开头固定随机种子:

rng(0);

同时把Matlab版本号、工具箱版本和所用数据都记录下来。深度学习模型的训练本身有随机性,即便固定种子,在GPU和CPU上跑出来的结果也可能略有浮动。文档里写明这些信息,后续自己和别人复现起来都能少走很多弯路。

6. 我的最终体会与一段可以直接参考的框架思路

整套流程跑下来,我最大的感受是:高精度预测的关键不是单纯堆模型复杂度,而是把“数据划分规范”和“超参数寻优”这两块地基打好。BiLSTM再强,超参数选不对、数据泄漏不处理,照样白搭;而SMA和PSO这类优化算法,本质上是把调参从玄学变成了有方向性的搜索,虽然计算成本高,但省下来的实验时间和最终精度的提升完全值得。

如果你是第一次在自己的数据上复现这个项目,我的建议是不要一上来就追求跑满365天数据、也不要把隐藏层神经元数调得太大。先用一小段数据(比如100个样本点)把整个流程跑通,确认数据格式正确、训练无报错,再把数据量加满。实验过程中把这篇文章里提到的几个坑提前避开——归一化参数分开算、时序划分不打乱、每次优化多跑几次取最优——你的结果基本不会差到哪里去。

后面有时间的话,还可以在这个框架上继续扩展:把SMA的种群机制换成混合策略,或者在寻优维度里再加入Dropout比例和序列滑窗长度M,都是很自然的下一步优化方向。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询