十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于TCN与分位数回归的时间序列区间预测Matlab实现

基于TCN与分位数回归的时间序列区间预测Matlab实现 简介基于时间卷积神经网络与分位数回归的时间序列区间预测模型配套Matlab完整源码与数据集面向需要开展时序不确定性分析的科研人员和工程开发者。模型将时间卷积网络的特征提取能力与分位数回归的分位点估计相结合可输出多置信水平下的预测区间适合电力负荷预测、风速预测等单变量时序场景。资源包共九个文件以八个Matlab脚本和一个Excel数据集为主代码覆盖数据处理、网络构建、分位数损失层、区间覆盖率与区间平均宽度百分比等模块主程序可直接运行自动产出多种结果图并计算平均绝对误差、均方根误差、决定系数等指标注释清楚易于二次开发。压缩包仅二十三KB轻量便捷已有八十八人学习。整体而言这份资源从数据到模型再到评价指标形成完整闭环既有教学参考价值也可直接迁移到实际预测任务对初学和进阶者均有帮助。1. QRTCN把时间序列预测从一根线变成一条区间QRTCN这个缩写拆开是三件事时间卷积神经网络TCN负责把时间序列特征抽出来分位数回归负责给出不同置信水平下的预测区间Matlab把这些组织成一套能直接改参数、直接画图的源码。很多做负荷预测、汇率分析、设备预警的人一开始奔着LSTM去但LSTM在Matlab里调试慢长序列还容易遇到梯度问题换成分位数回归加时间卷积的组合后训练更稳定输出也从一根预测线变成带上下界的区间。这篇文章要讲清楚TCN为什么适合时间序列特征提取分位数损失和普通MSE差在哪Matlab源码里数据怎么整理、网络怎么搭建、参数怎么调以及区间预测必须看的PICP、PINAW指标。适合已经用深度学习做时间序列预测、想从点预测升级到区间预测的人也适合拿到源码想搞清楚每个参数含义的初学者。2. 从TCN到QRTCN为什么用时间卷积而不是LSTM2.1 因果卷积与膨胀卷积感受野是怎么扩大的TCN的核心是因果卷积causal convolutiont时刻的输出只看得到t时刻及以前的输入这正好对应时间序列预测场景——预测未来时不能偷看未来。你在Matlab里写convolution1dLayer默认卷积是双向的要保证因果性得做左侧padding或者掩码很多源码里其实用左侧padding后截断实现。接着是膨胀卷积dilated convolution。它的作用是在不增加参数量的前提下扩大感受野。设kernel size为k第i层的dilation factor为d_i第i层每个卷积核覆盖的输入跨度是(k-1)×d_i1。多层叠加后感受野按公式累加RF 1 Σ (k_i - 1) × d_i例如k3dilation按1、2、4、8递增4层网络在深度12的时候感受野已经覆盖到31个时间步。这个覆盖范围直接决定模型能看到多长的历史时间序列预测中常见的周期项比如24小时的日周期、168小时的周周期就是靠这个机制被吃进模型里的。如果你只是把TCN当作换掉LSTM的卷积网络不理解感受野后面调参数会非常被动。组件Matlab常见写法作用因果卷积convolution1dLayer配合padding裁剪保证输出只依赖当前及过去膨胀卷积convolution1dLayer的DilationFactor参数成倍扩大感受野不增加参数量残差连接additionLayer或自定义层缓解深层网络梯度衰减分位数输出fullyConnectedLayer(numQuantiles)同时输出多个分位点预测值2.2 残差块深层TCN训练不塌的秘密TCN堆叠十几层后梯度可能不稳定残差块就是解决办法。块内结构通常是1D卷积→ReLU→Dropout→1D卷积→ReLU→残差相加。如果输入输出通道数不一致残差连接中间要插一个1×1卷积对齐维度。你在Matlab里看到的源码多数实现是把残差块展开成一层一层的layer array而不是封装成独立模块这么做是为了能用trainingOptions直接跑通。权重归一化是个容易忽略的细节。Matlab里没有直接对应weight normalization的层常见做法是换用batchNormalizationLayer或者在自定义层里手动做。用batch norm要小心时间序列输入是三维的特征×时间×样本batch norm默认按通道做效果尚可但模型的稳定性对batch size比较敏感。如果你的训练集只有几百个样本batch size设太小batch norm的统计量会抖这时候宁可去掉bn层把学习率调低。2.3 分位数损失QRTCN和普通TCN的本质差别普通TCN用MSE拟合的是条件均值输出的是点预测。QRTCN用分位数损失拟合的是条件分位数输出的是多个分位数。这是整个模型最关键的一处改动。function loss quantileLoss(yPred, yTrue, tau) % yPred: 网络输出, 形状 [numQuantiles, numSamples] % yTrue: 真实值, 形状 [1, numSamples] % tau: 分位点列向量, 例如 [0.05; 0.50; 0.95] err yTrue - yPred; % err0 表示预测偏低, 权重为 tau % err0 表示预测偏高, 权重为 (1-tau) loss mean(max(tau .* err, (tau - 1) .* err), all); end这段代码的逻辑是当预测值低于真实值err 0损失权重大小是tau当预测值高于真实值err 0权重是1-tau。比如tau0.95时预测低于真实值的惩罚远大于预测高于真实值网络就会偏向给出偏保守的偏上估计——这正是上边界的行为。下边界同理取0.05中间0.5对应中位数相当于一种更稳健的均值估计。这里要强调一个常见误区不要对三个分位点分别训练三个模型。QRTCN的价值在于共享TCN特征提取层三个分位数一起训练网络学到的是同一个特征空间下的三种映射。分开训练不仅慢而且三个分位点之间会互相矛盾最终画出来的区间可能扭曲。你在看Matlab源码时确认输出层是3个神经元、损失是分位数损失基本就能判断这是不是真QRTCN。3. Matlab源码落地从数据整理到完整训练流程3.1 滑动窗口把单变量序列变成监督学习样本时间序列预测的第一步不是搭网络而是把原始序列改造成输入-输出对。常见做法是固定窗口长度按步长1滑动要预测未来horizon步就把第thorizon时刻的值当作第t窗口的标签。function [XTrain, YTrain] createSlidingWindow(data, windowSize, horizon) % data: 列向量, 单变量时间序列 % windowSize: 输入窗口长度 % horizon: 预测步长, 1表示下一步 n length(data) - windowSize - horizon 1; XTrain zeros(windowSize, n); YTrain zeros(1, n); for i 1:n XTrain(:, i) data(i : i windowSize - 1); YTrain(i) data(i windowSize horizon - 1); end % Deep Learning Toolbox 的 sequenceInputLayer 接受 [特征数, 时间步, 样本数] XTrain reshape(XTrain, windowSize, 1, n); end这段代码里有几个点要说明。第一XTrain的维度是windowSize×1×n对应sequenceInputLayer要求的[特征维度, 时间步, 样本数]所以windowSize放在第一维。如果你之后改用LSTM输入格式一样这也是把这套代码改成LSTM做对比实验很方便的原因。第二步长固定为1样本之间高度重叠训练时网络会反复看到相似的窗口这会导致训练损失看起来很低但测试集上的表现更接近真实水平。第三horizon大于1时预测的是thorizon这个单点的值不是未来一段曲线想要预测未来一段区间需要把输出层改成输出horizon个数。3.2 用dlnetwork搭建QRTCN而不是regressionLayer我一般不用regressionLayer因为它的内置损失是MSE而QRTCN的训练目标是分位数损失用trainingOptions跑regressionLayer网络学出来的是均值而非分位点。正确做法是用dlnetwork手动定义前向传播和损失再用dlfeval计算梯度。% 搭建一个轻量QRTCN主干 layers [ sequenceInputLayer(1, Name, in) convolution1dLayer(3, 8, Padding, causal, DilationFactor, 1, Name, c1) reluLayer(Name, r1) convolution1dLayer(3, 8, Padding, causal, DilationFactor, 2, Name, c2) reluLayer(Name, r2) convolution1dLayer(3, 8, Padding, causal, DilationFactor, 4, Name, c3) reluLayer(Name, r3) fullyConnectedLayer(3, Name, fc_quantile) % 3个输出对应3个分位点 ]; dlnet dlnetwork(layers);你需要注意Padding, causal这个设置是Matlab里比较特殊的关键字它会自动保证因果性不需要手动裁剪。如果你的Matlab版本不支持这个参数就得改用手动左侧padding再加convolution1dLayer。训练循环部分是这样for epoch 1:maxEpochs [loss, gradients] dlfeval(modelLoss, dlnet, dlX, dlY, tau); [dlnet, state] dlupdate(sgdmupdate, dlnet, gradients, state, learnRate); end function [loss, gradients] modelLoss(dlnet, dlX, dlY, tau) dlYPred forward(dlnet, dlX); loss quantileLoss(dlYPred, dlY, tau); gradients dlgradient(loss, dlnet.Learnables); end这段代码里dlX是dlarray格式用dlarray(X, CTB)转换CTB表示Channel、Time、Batch三个维度。forward之后输出的形状是[3, 1, numSamples]三行分别对应三个分位点。全连接层输出是3刚好匹配tau的向量长度。3.3 预测输出与画区间阴影训练完成后输出dlYPred predict(dlnet, dlXTest); YPred extractdata(dlYPred); % YPred(1,:) 下边界, YPred(2,:) 中位数, YPred(3,:) 上边界如果你的源码在数据进入网络之前做了归一化预测完必须逆变换回原始尺度。常见做法是存下训练集的min和max用YPred * (max - min) min还原。这一步错位画出来的区间宽度和位置全不对但代码不会报错是隐蔽性最强的问题。画区间用Matlab的fill函数对应到matlab如何将区间画成线段这类的实操场景t 1:length(actual); fill([t, fliplr(t)], [lower, fliplr(upper)], [0.8 0.8 0.8], EdgeColor, none); hold on; plot(t, mid, b, LineWidth, 1.5); plot(t, actual, k);fill需要把上下边界首尾相接围成闭合多边形所以x轴是t加翻转的ty轴是upper加翻转的lowerEdgeColor,none去掉边界线灰色半透明用RGB三元组[0.8 0.8 0.8]表示。如果你想带透明度可以用FaceAlpha, 0.3。画出来的图里中间蓝色线是预测中位数黑色线是真实值灰色带就是90%预测区间。4. 参数调节与区间质量检验QRTCN最关键的三组参数4.1 分位点选什么0.05/0.5/0.95不是唯一选择通用做法是0.05、0.5、0.95三档对应90%预测区间。但分位点的选择本质上由业务需求决定金融风险场景经常用0.01、0.5、0.99希望极端情况也被包住设备预警场景用0.1、0.5、0.9就够了区间太宽反而没有告警意义。分位数回归本身不对误差分布做任何假设所以不存在正确的分位点只存在对业务有用的分位点。如果你想同时输出多个区间比如80%和95%两个置信水平输出层神经元数量可以设成5个对应[0.025, 0.1, 0.5, 0.9, 0.975]。训练损失就是这五个分位数损失的均值。我试过这种组合训练时间几乎不增加但能一套模型出两种区间给业务方演示时很有说服力。4.2 膨胀系数和卷积核感受野要覆盖主周期感受野的计算公式前面已经给了这里直接给一组参考值。假设目标序列有明显的24小时周期windowSize设成168小时一周那么感受野至少要有168。用k3dilation按1、2、4、8、16、32、64递增7层卷积的感受野累计是12×(248163264128)509已经覆盖7层窗口长度。如果k5同样7层感受野直接破千。这个例子说明膨胀卷积的深层的感受野增长速度是指数级的不需要把网络堆得很深。卷积核膨胀序列层数感受野适用序列31,2,4,8,16,326127短周期、无强趋势31,2,4,8,16,32,647255日周期周趋势51,2,4,8,165156中等长度平滑序列71,2,4,84127有局部波动但周期短卷积核大小建议控制在3到7之间。kernel太大浅层就能看到很宽的局部窗口但参数量涨得快而且容易把局部噪声当成特征。遇到明显周期比如24小时可以把某一层的kernel设成24或25等于让每个卷积核完整覆盖一个周期这比强行用3×N层的组合更直观。要调参时可以先在训练集上跑一次基线记录训练损失下降曲线。如果训练损失在前期下降很快、后期震荡明显通常是学习率偏大如果损失在某个epoch后几乎不动但区间宽度一直偏大可能是输出层参数初始化或者分位点设置的问题。不要一上来就调膨胀系数。4.3 PICP、PINAW和CWC区间预测的三个硬指标做时间序列区间预测光看均方误差是不够的。三个指标必须一起看。PICPPrediction Interval Coverage Probability衡量真实值落在预测区间内的比例PINAWPrediction Interval Normalized Average Width衡量区间平均宽度与数据范围的比值CWCCoverage Width Criterion把两者合并成一个可比较的单值在覆盖不够时加大惩罚。直接给出计算代码function [picp, pinaw, cwc] calcIntervalMetrics(lower, upper, actual, alpha) % lower、upper: 上下边界列向量 % actual: 真实值列向量 % alpha: 显著性水平, 0.1 表示90%置信区间 coverage (actual lower) (actual upper); picp mean(coverage); dataRange max(actual) - min(actual); pinaw mean(upper - lower) / dataRange; eta double(picp (1 - alpha)); cwc pinaw * (1 eta * exp(-8 * (picp - (1 - alpha)))); endPICP的期望值是1-alpha比如alpha0.1时PICP应该接近0.9。如果PICP远高于0.9比如到了0.98不一定是好事——这说明区间过宽PINAW会变大。反之PICP过低就是区间覆盖不住风险在业务上比区间宽更致命。CWC中的指数惩罚系数我一般取8这个值越大对覆盖不足的惩罚越陡。你可以根据自己的业务容忍度调整没有标准答案。这三个指标会出现在很多源码附带的demo脚本里。拿到源码后先把alpha改成你要的置信水平跑一遍测试集记住PICP和PINAW的基线值。后续每改一次结构都拿新值跟基线比才能判断改动是正向还是负向。另外如果你愿意折腾可以把PINAW作为优化目标、PICP不低于1-alpha作为约束用matlab优化工具箱里的patternsearch搜超参。代价是每评估一次就要完整训练一轮模型时间成本很高我通常只在dilation和kernel尺寸拿不准时才这么干。5. 落地时的三个检查滑窗回测、尺度泄漏与边界一致性拿到一份QRTCN的Matlab源码别急着换数据跑。先检查三件事任何一件出错测试集上的漂亮指标都可能是假象。第一件是切片方式。时间序列的划分必须保持时间顺序前80%训练后20%测试顺手写清楚每段对应的时间区间。随机打乱切分会把未来信息泄漏到训练集里这在曲线预测场景里基本宣判死刑。我用滚动回测时会把测试集的预测点逐个推进先用前windowSize步预测第windowSize1步等到真实值出来再滑动窗口预测下一步。这样贴近线上使用方式但要注意滚动回测的每一步都要重新调用predict。第二件是归一化统计量的计算范围。训练集归一化时只允许用训练集自身的min/max或mean/std保存成.mat文件预测时加载这个.mat对测试集做同样的线性变换。很多源码错误地对完整序列做归一化再切分导致测试集的信息已经从统计量里泄漏进训练过程。这种情况最迷惑人的地方在于PICP指标可能比实际更高——不是模型变好了是测试已经提前看到了全局的上下界。把min/max和mean/std单独存盘这个动作比加深网络更有价值。第三件是边界一致性。三个分位点由同一个网络输出但网络没有内建约束保证lower ≤ mid ≤ upper偶尔会出现下边界比上边界大的情况尤其在序列突变的位置。我的做法是在损失函数里加一个边界惩罚项boundaryPenalty 10 * mean(max(0, lower - upper), all);这个惩罚系数我一般取10到20之间。太大会强行把区间压得过宽太小则起不到约束作用。如果发现加了惩罚后PINAW明显变大先把惩罚系数减半再看PICP有没有相应改善。这三个检查做完源码才算真正跑通——用你不会踩数据泄漏的视觉方法替换掉跑完就是胜利的惯性在区间预测这条路上能少返工半年。本文还有配套的精品资源点击获取
返回列表