有限数据下如何利用贝叶斯分层模型开展非线性时间序列预测
问题答复
分层模型是否支持非线性数据预测
当然可以。你看到的公开示例大多用线性回归,只是因为线性形式代码最短、推理最快,最适合演示分层模型「部分池化」的核心逻辑,根本不是分层模型的能力上限。
分层模型的本质是一套参数共享的统计框架:上层先验学习群体共性,下层个体参数在群体分布的约束下结合自身数据调整,数据多的个体保留更多个性化特征,数据少的个体自动向群体均值收缩,这套逻辑和底层用线性还是非线性函数完全解耦。
实际落地中常用的非线性嵌入方式有三类:
- 特征非线性变换:对时间、价格这类连续特征做样条变换、傅里叶周期展开、对数变换,把非线性关系转化为变换后特征的线性关系,改造成本极低,是工业界最常用的方案。
- 非线性函数参数分层:如果业务规律明确(比如比率类指标天然有0-100%的上下界、符合饱和增长规律),可以直接用逻辑斯蒂、指数衰减、龚珀兹这类非线性函数拟合趋势,把函数的上限、增长速率、拐点都设成分层参数——全局设超先验,品类层设中间分布,产品层参数从所属品类的分布中采样,天然适配新品数据少的冷启动场景。
- 嵌套高斯过程:如果非线性关系完全没有明确的业务规律可循,可以在分层结构里嵌入高斯过程做非参数非线性拟合,上层给高斯过程的核函数超参设全局/品类先验,下层每个产品的时序映射共享核超参、保留个体偏移,适合规律极复杂的场景,缺点是算力要求高,产品量级大的时候不适合全量用。
时间序列场景落地经验(适配高频上下架/冷启动场景)
我在电商商品转化率预测场景落地Numpyro分层时序模型有3年多,和你现在的场景高度重合:商品迭代速度快,超过30%的商品生命周期不足2周,大量新品历史数据不足7天,最终上线的分层模型比之前用的XGBoost、单品ARIMA模型的MAE低27%左右,几个踩过坑之后总结的关键经验:
- 层级不要只设到产品层,要按你的字段搭
全局 -> 品类 -> 产品的嵌套结构。完全没有历史数据的新品,直接用所属品类的参数分布做预测,不会出现冷启动时输出离谱值的问题。如果有更多商品属性(比如价格带、上新批次、供应商),可以继续加层级,部分池化的效果会更好。 - 时序部分不要硬套线性趋势。比率类指标有天然的取值上下界,我自己用下来效果最好的组合是分层逻辑斯蒂长期趋势+分层傅里叶季节项:逻辑斯蒂曲线拟合趋势不会出现线性外推超出0-100%合理范围的问题;傅里叶基拟合周度、月度的季节性波动,每个品类、产品的季节系数单独设分层先验,对周末、大促的周期性波动拟合效果远好于线性趋势。
- 不要扔掉已下架商品的历史数据。所有商品(包括生命周期极短的已下架商品)的全量数据都要放进训练集,模型会自动学习短生命周期商品“上新后前N天的比率变化规律”,给新上架的商品提供参考。
- 大数量级下做效率优化。如果你的产品量级在万级以上,全量跑MCMC速度会很慢,可以先在抽样的小样本数据集上跑MCMC得到全局、品类级的超参数,再用MAP估计单个产品的参数,推理速度能提两个数量级,精度损失不到3%,完全满足线上批量预测的要求。
下面是适配你场景的极简非线性分层模型示例,对应你之前看到的线性模型结构,把线性趋势替换成了逻辑斯蒂非线性趋势,加了品类层级:
import numpy as np import numpyro import numpyro.distributions as dist from numpyro.infer import MCMC, NUTS from jax import random def nonlinear_hierarchical_model(category_id, product_id, days_since_launch, rate_obs=None): # 全局超先验 μ_cap = numpyro.sample("μ_cap", dist.Normal(0.5, 0.2)) # 全局比率上限均值 σ_cap = numpyro.sample("σ_cap", dist.HalfNormal(0.1)) μ_growth = numpyro.sample("μ_growth", dist.Normal(0.1, 0.05)) # 全局增长速率均值 σ_growth = numpyro.sample("σ_growth", dist.HalfNormal(0.03)) # 品类层参数 n_cats = len(np.unique(category_id)) with numpyro.plate("category_plate", n_cats): cat_cap = numpyro.sample("cat_cap", dist.Normal(μ_cap, σ_cap)) cat_growth = numpyro.sample("cat_growth", dist.Normal(μ_growth, σ_growth)) # 产品层参数,从所属品类的分布中采样 n_products = len(np.unique(product_id)) with numpyro.plate("product_plate", n_products): prod_cap = numpyro.sample("prod_cap", dist.Normal(cat_cap[category_id], 0.05)) prod_growth = numpyro.sample("prod_growth", dist.Normal(cat_growth[category_id], 0.02)) # 非线性逻辑斯蒂趋势,替换原线性形式 rate_est = prod_cap[product_id] / (1 + np.exp(-prod_growth[product_id] * (days_since_launch - 7))) σ = numpyro.sample("σ", dist.HalfNormal(0.05)) with numpyro.plate("data", len(days_since_launch)): numpyro.sample("obs", dist.StudentT(5, rate_est, σ), obs=rate_obs) # 用StudentT似然抗异常值
提示:上面只是最小可运行示例,实际落地时需要补充傅里叶季节项、节假日/大促特征、价格特征,预测精度会有明显提升。
内容的提问来源于stack exchange,提问作者import_numpy
相关产品推荐
相关产品推荐

