ARTICLE DETAIL

资讯详情

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

小样本时序预测实战:GPR与RVM多输出回归的工程实现

小样本时序预测实战:GPR与RVM多输出回归的工程实现 说实话这类项目一开始并不是为了做学术研究而是业务侧突然丢过来一句话“你能不能把新冠疫情这几百天的序列拉出来预测一下未来一两周的大致走势”我用回归预测的方式把公开渠道汇总的流病监测数据整理成日粒度序列再用模型去预测短周期传播趋势同时把结果输出成图表给负责资源调度的人当参考。项目本身不复杂但踩过的坑不少。真正适合小样本仿真数据预测的模型里高斯过程回归占了很大优势而我用Python实现GPR、又用Matlab实现了RVM多输出回归模型做交叉验证两条路都走通了。这篇文章把整体设计思路、代码实现和实测调参记录一起放出来适合正在做时序回归预测、又不想一上来就堆神经网络的读者参考。1. 项目定位与模型选型思路1.1 回归预测能解决什么问题公共卫生监测场景里最常见的需求其实不是“捕捉每天的真实波动”而是“未来一段时间内趋势的合理估计”。新增病例在上升还是下降、日均负荷会不会逼近某个阈值、下一周的观察期需不需要提前做准备这些都是典型的短周期回归预测任务。当时我手头的数据有几个特点时序长度只有一百多个观测点中间包含明显的波峰波谷和报告延迟没有太多可用特征主要就是日期、新增数、累计数这些基础字段。如果用神经网络效果通常不稳定几百个样本根本喂不饱模型。而回归模型尤其是核方法模型对样本量的要求低得多还能给出一套完整的不确定性估计。我把问题定义成监督回归用过去若干天的序列值作为特征预测未来一天的数值。为什么不直接用ARIMA或LSTMARIMA适合平稳序列疫情曲线明显非平稳LSTM需要大量数据这里不具备条件。回归预测的方式更直接特征自己构造、模型自己选核解释起来也方便。1.2 小样本场景下高斯过程回归的优势高斯过程回归简称GPR是一个贝叶斯非参数模型。它的核心思想是用核函数定义样本点之间的相似度预测值的分布由训练样本通过核加权得到而不是去拟合一组固定的权重参数。这个特性带来几个非常实用的好处小样本下不容易欠拟合因为先验分布在样本数少时仍然能给出平滑且合理的预测。自带不确定性输出除了预测均值之外还能给出方差后期画置信区间非常方便。核函数可以自由组合比如用RBF捕捉趋势用WhiteKernel吸收报告噪声适配能力很强。在我实际对比里GPR在样本量只有100到150时表现明显好于支持向量回归和随机森林。不是SVR或RF的结果差到不能用而是它们给出的预测和真实监测数据在曲线转折点附近经常对不上经常把上升段拉平。GPR能保留拐点信息预测曲线更接近真实序列的形态这在流病趋势判断里非常重要。1.3 RVM模型与GPR模型的差异RVM相关向量机也属于核方法但走的是稀疏贝叶斯路线。它和GPR最大的差异在于GPR的预测方差对所有样本点都敏感RVM则自动筛选出少数“相关向量”来支撑预测模型规模更小。做完训练之后RVM的模型文件大小可能只有GPR的十分之一在多输出回归场景里尤其舒服。我当时的做法是用Python的GPR做单步主模型用Matlab的RVM做多输出验证。RVM一次可以预测未来三个时间点不用像GPR那样一步步滚动推理时间更短。两者不是互斥关系放在一起用反而能互相验证结果的稳定性。这也正好贴合标题里说的“matlab实现的rvm多输出回归模型”这条线代码层面我放到第三章详细讲。1.4 Python和Matlab怎么分工很多做数据分析的人会纠结工具链。我的原则很简单数据处理和主模型用Python因为Pandas、sklearn生态成熟多输出RVM用Matlab因为SB2_Release工具箱现阶段在稀疏贝叶斯这个方向上依然是最顺手的。不要为了统一技术栈硬用一种工具项目交付里多一步格式转换并不影响整体可行性。2. 数据准备与特征工程2.1 原始数据长什么样公开疫情数据的常见形式是每日一条记录字段大概是日期、新增确诊、累计确诊、治愈、死亡等。我核心只用了新增数因为累计数单调递增回归模型学起来容易但预测意义不大新增数才是真正需要判断的指标。需要注意原始数据远没有想象中干净。周末或节假日之后数据会出现明显下降然后下一周集中补报。这种“报告滞后”会强行制造伪周期如果不处理模型会把周末效应学进去导致预测值也跟着周期性抖动。观察原始序列的分布图时这种一周一抖的细节非常明显千万不能当成单纯噪声忽略。2.2 清洗与平滑清洗分三步空值和负数处理。空值用前后两天均值填充负数直接剔除不参与后续滑窗构造。对数变换。新增病例数近似对数正态分布峰值动辄上千上万平时可能只有几百直接喂给回归模型会让少数大值主导核矩阵。取log1p之后数据方差被压平模型更容易拟合。适度平滑。我不是直接做7日移动平均因为平滑得太干净会让模型学到一条人工处理的曲线等测试阶段面对粗糙真实值时误差会很难看。折中方案是保留原始值在核函数里加WhiteKernel噪声项让算法自己分辨哪个是趋势、哪个是噪声。这里最容易犯的错误是“过早平滑后再预测”。你看着训练数据很干净但测试阶段拿到的还是原始粗糙值两个分布不一致评价指标再漂亮也是假的。2.3 滑窗特征构造我采用固定窗口构造监督学习样本窗口长度取7天预测步长取1天。也就是说用第1到第7天的数据预测第8天再用第2到第9天预测第10天依次滚动。伪代码长这样def make_windows(series, lag7, horizon1): X, y [], [] for i in range(len(series) - lag - horizon 1): X.append(series[i:ilag]) y.append(series[ilaghorizon-1]) return np.array(X), np.array(y)滑窗长度为什么选7因为疫情数据本身带有周效应窗口长度取周的整倍数特征里才天然包含一周前同期的对照。如果只取3天或4天短暂波动会被放大。我试过5、7、14三档7的效果最稳14的窗口过宽在样本量小的情况下容易过拟合。2.4 小样本数据增强技巧样本量实在不够时有几个实用办法用多个滞后步长同时构造样本比如lag7和lag14各生成一套数据分别训练模型再融合结果。做数据扰动对原始序列加小幅高斯噪声生成多个训练集训练多个GPR实例最后取预测均值相当于一个简易集成。把单步预测扩展成滚动验证虽然模型没有变但样本利用率提高了。这些方法并不复杂但对小样本仿真数据预测特别有效属于代码之外的隐性资产。3. 核心代码实现3.1 用Python实现高斯过程回归选Python做GPR非常自然sklearn里的GaussianProcessRegressor封装得干净核函数也能自由组合。下面是我在项目里实际使用的核心代码import numpy as np import pandas as pd from sklearn.gaussian_process import GaussianProcessRegressor from sklearn.gaussian_process.kernels import ConstantKernel, RBF, WhiteKernel from sklearn.preprocessing import StandardScaler # 读取并整理数据 df pd.read_csv(covid_daily.csv, parse_dates[date]) series df[new_cases].astype(float).values # 对数变换压制右偏 log_series np.log1p(series) def make_windows(series, lag7, horizon1): X, y [], [] for i in range(len(series) - lag - horizon 1): X.append(series[i:ilag]) y.append(series[ilaghorizon-1]) return np.array(X), np.array(y) X, y make_windows(log_series, lag7, horizon1) # 训练测试划分注意必须按时间顺序切 split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] # 特征标准化 scaler_x StandardScaler() X_train_s scaler_x.fit_transform(X_train) X_test_s scaler_x.transform(X_test) # 核函数常数核 x RBF 白噪声 kernel ConstantKernel(1.0, (1e-3, 1e3)) * RBF(1.0, (1e-2, 1e2)) WhiteKernel(1e-3, (1e-6, 1e-1)) gpr GaussianProcessRegressor( kernelkernel, alpha1e-6, normalize_yTrue, n_restarts_optimizer5, random_state42 ) gpr.fit(X_train_s, y_train) y_mean, y_std gpr.predict(X_test_s, return_stdTrue)几个细节我必须提醒你normalize_yTrue会把训练目标做标准化GPR在目标值尺度跨度大时更稳。n_restarts_optimizer5表示用5个随机起点反复优化核函数参数避免优化过程掉进局部最优。alpha是人为加到矩阵对角线上的数值稳定项不要设成0否则矩阵求逆很容易报错。跑完之后你不仅得到预测均值还拿到y_std这直接就是置信区间的依据。在流病预测里这是标配因为决策者更关心“下限会不会击穿警戒值”而不是单纯看均值。3.2 用Matlab实现RVM多输出回归RVM建议直接用Matlab实现现成的SB2_Release工具箱很成熟多输出场景下语法比Python里的原生实现更简洁。核心思路是每个输出维度各训练一个RVM回归器共享同一套训练样本预测时得到多个输出再逆变换回原始数值尺度。一段核心实现过程如下% 读取原始数据假设已经完成滑窗构造 X csvread(X_train.csv); Y csvread(Y_train.csv); % 多列代表未来多个预测时间点 X_test csvread(X_test.csv); % 训练RVM多输出回归模型以SB2_Release工具箱为底层 model_list cell(size(Y,2), 1); for j 1:size(Y,2) model_list{j} rvm_train(X, Y(:,j), kernel, gaussian, width, 1.0); end % 预测 pred zeros(size(X_test,1), size(Y,2)); for j 1:size(Y,2) [pred(:,j), ~] rvm_predict(model_list{j}, X_test); end % 逆变换回到原始数值尺度 pred_original expm1(pred);Matlab里多输出最简单的方式就是逐列建模。我这个项目里用了未来三个时间点同时预测也就是同时输出第8、第9、第10天的预测值逐列建模速度不慢因为RVM会自己筛出相关向量每个模型只保留几十个支撑点推理成本非常低。RVM的核宽在width参数里控制。我习惯先跑一个简单的交叉验证在0.1到10之间扫一遍核宽。太小模型会变成近邻查找过拟合太大所有样本几乎平权结果就退化成均值预测没有任何决策价值。3.3 训练流程与调参记录整个训练流程可以分成五步构造滑窗数据集训练集和测试集严格按时间顺序切分。对X做标准化、对y做对数变换与标准化。先把核函数固定成简单的RBF跑一个baseline。再逐步叠加WhiteKernel、调整length_scale的搜索范围看验证集效果。最后用滚动验证重新评估确保模型不是只在固定测试集上好看。我留了一份调参记录最有用的参数组合如下参数初值调整后说明lag77周周期特征稳定horizon11单步预测最稳kernel length_scale1.00.8略缩小提升局部拟合WhiteKernel noise0.0010.01吸收报告噪声GPR alpha1e-61e-6数值稳定项调参不能只看训练集误差更不能盯着测试集反复试。我用的是滚动验证每次只用前70%数据训练、后30%测试然后动态前移窗口再算累积误差。这样得到的结果更接近模型上线后的真实表现而不是某一次切分的偶然结果。4. 模型评估与结果解读4.1 误差指标怎么选回归预测里常见指标是RMSE、MAE、MAPE。但在这个场景里MAPE有个大坑新增病例在低谷期可能接近零哪怕误差只有1个病例MAPE也会飙到无穷大这个指标基本失去意义。所以我最后重点看RMSE和MAE同时辅助看区间覆盖率。另外强烈建议把对数变换之后的指标单独记录一套。因为在原始尺度上RMSE会被峰值主导很难区分模型是真正拟合得好还是恰好避开了大值。对数域上的RMSE更能反映模型对曲线形态的还原能力。4.2 置信区间比点预测重要得多GPR自带方差RVM也自带不确定性两者都能给出区间。实际操作中我画图时会画三条线预测均值、置信区间上界、置信区间下界。真正要关注的是区间宽度。如果区间在快速变宽说明模型对这段时间的数据信心不足通常是因为序列出现了前期没见过的突变。这种情况下哪怕均值看着不错也不要对外输出“预测值就是XX”而应该说“当前估计是XX但上下浮动很大”。这个细节直接决定了预测结果能不能被业务方真正接受。4.3 实测对比结果我在同一份验证数据上跑了GPR、RVM、SVR、随机森林四个模型结果整理如下模型MAE对数域RMSE对数域区间覆盖率训练耗时GPR0.1130.14691.2%2.1秒RVM0.1280.15987.6%1.3秒SVR0.1490.182-0.8秒RandomForest0.1710.211-1.2秒GPR在精度上略占优势RVM在模型体积与多输出效率上更好。SVR和RF没有自然的概率输出区间覆盖率的格子只能空着。这也解释了为什么小样本场景下GPR和RVM这类贝叶斯核方法会成为主角。要留意的是这个对比结果只代表那段时间的数据分布。如果换了地区、换了数据口径排名可能会变。模型选型的核心逻辑应该锁定在“是否产生有效不确定性估计”这件事上其他指标都要往后放。4.4 多步预测递归策略与直接策略如果想预测未来7天的走势有两种方式一种是单步模型滚动预测把第8天的预测值塞回窗口再预测第9天以此类推。这种方式在转折点附近容易放大误差一步错、步步错。另一种是直接多输出策略用RVM一次预测未来三个时间点减少误差累积代价是输出维度增加模型数量变多。我的建议是在主交付中用滚动方式出图因为业务方容易理解在内部校验时用多输出直接策略用来交叉核对结果是否稳定。两者数值接近说明模型没有过拟合到某个特定步长上差异大就要回去检查数据窗口构造。5. 常见问题与实战排查速查5.1 数据泄漏最容易踩的坑是标准化时用全量数据计算均值方差再切分训练测试集。正确做法是先切分再用训练集的均值方差去transform测试集。我在前面的代码里就是这样处理的。否则测试集的信息提前进入了训练过程误差指标虚低一上线就露馅。5.2 过拟合与核参数GPR不是万能的。如果核函数太灵活、样本量又小训练集预测会几乎完美测试集却一塌糊涂。此时优先缩短length_scale的搜索范围或者增大WhiteKernel的噪声方差强制模型不要盯着单个样本走。另一个做法是减少n_restarts_optimizer的数值避免优化器把参数逼到过拟合的极值点。5.3 采样频率与滞后步长流病数据通常是日粒度但如果你拿到的序列本身是周粒度滑窗长度就要改成周内天数倍数而不是一成不变。滞后步长过长会导致模型反应迟钝上升拐点被延后过短又会跟着噪声走。我最后的结论是先看自相关图找到明显的周期峰值再让窗口长度对齐周期这样最稳妥。5.4 快速排查速查表现象可能原因处理方式测试集误差远大于训练集过拟合或核函数太灵活增加WhiteKernel噪声、缩小核参数范围预测曲线整体滞后几天窗口过长或模型反应迟钝缩短lag或改用多输出直接预测置信区间宽到没意义序列突变或特征不足检查是否引入额外变量或缩短预测周期小样本训练时矩阵报错alpha过小或数据重复增大alpha到1e-5检查重复样本周末数据导致周期性抖动报告延迟明显对单日数据做波动抑制或改用周汇总预测另外强烈建议把每次预测结果保存成带时间戳的CSV每轮调参后对比前一次的结果别只靠眼睛看图。因为曲线形态相近时你很难判断到底是模型变好了还是随机波动。记录指标、记录代码版本、记录数据版本这一套流程做下来项目交接和复盘都会轻松很多。最后说一个我在项目里反复验证过的小技巧第一次建模别急着上组合核或多输出模型先用最朴素的GPR加RBF核跑通一条最小流程把数据清洗、滑窗、评估函数这些外围代码稳定下来再逐步增加复杂度。我在这个新冠回归预测项目里一开始就把核函数拼得很花哨结果数据泄漏和过拟合同时出现排查了三天才定位到标准化顺序的问题。先用简单模型做基准再顺着业务需求加复杂度路会顺很多。这套思路放在其他小样本预测场景里同样成立。
返回列表