HiFoReAd 多层时序预测调和 - 母婴跨境补货分层一致性
Skill-Hierarchical-Demand-Forecasting-Reconciliation · 03-时间序列
1. 解决的问题
Momcozy 60+ SKU × 多仓(上海/香港/海外仓) × 多市场(US/DE/JP),各层独立预测加总 30-50% 不一致;采购按 SKU 下单,但财务按市场聚合,两边数字对不上,月底对账 2-3 PM 天纯人工调和 - 数据要求:历史 SKU/仓/市场 三层销售时序 + 加总矩阵 S - HiFoReAd 配置: - Stage 1: LGBM + AutoETS 三月预测
2. 核心算法逻辑
母婴跨境补货场景下,各 SKU/仓/市场层独立预测后,加总不一致(SKU 求和 ≠ 仓库 ≠ 总量),导致采购计划矛盾、财务对账打架. HiFoReAd 用多阶段调和:① 三模型集成基础预测 → ② TopDown + 谐波对齐保留季节性 → ③ MinTrace 投影使全局误差方差最小 → ④ 末层 stratified scaling 强制叶节点一致. Walmart Ads 已生产部署,各层完全一致.
3. 业务应用场景
- 业务问题:Momcozy 60+ SKU × 多仓(上海/香港/海外仓) × 多市场(US/DE/JP),各层独立预测加总 30-50% 不一致;采购按 SKU 下单,但财务按市场聚合,两边数字对不上,月底对账 2-3 PM 天纯人工调和 - 数据要求:历史 SKU/仓/市场 三层销售时序 + 加总矩阵 S - HiFoReAd 配置: - Stage 1: LGBM + AutoETS 三月预测,各层独立 - Stage 2: MinTrace + mint_shrink(协方差收缩,样本量小时鲁棒) - Stage 3: Harmonic alignment 保留 SKU 级月度季节性
- 业务问题:Momcozy 月度采购计划(大贸海运,提前 60 天)与周度补货触发(海外仓 FBA,提前 7 天)不一致,618/双11 备货期间月度预算与周度爆发严重错配 - 数据要求:月度采购历史 + 周度销售历史 + 大促日历 - HiFoReAd 配置: - Stage 1: 月度层 MSTL+ETS 抽取年度季节性,周度层 Prophet 捕捉短期峰谷 - Stage 2: Temporal MinTrace 保证 $\sum_{w \in \text{month}} \tilde{y}_w = \tilde{y}_{\text{month}}$ - Stage 3: Strati
4. 输入数据要求
请查看原始代码模板获取输入规格。
5. 输出结果
请查看原始代码模板获取输出规格。
6. 业务价值 / ROI
- 极易:Nixtla HierarchicalForecast 开源完整,`pip install` 即可
- 易处:MinTrace 是凸优化问题,有解析解,无需训练
- 难处:加总矩阵 S 需要业务专家确认层级关系
- 难处:误差方差 W 估计依赖足够历史数据(论文用 mint_shrink 协方差收缩)
7. 代码模板
代码块数量:2 · 路径:未检测到
"""
HiFoReAd 多层时序预测调和最小骨架
主论文: arXiv:2412.14718 (Walmart, BigData 2024)
开源参考: Nixtla HierarchicalForecast https://github.com/Nixtla/hierarchicalforecast
依赖: pip install hierarchicalforecast statsforecast pandas numpy
"""
from __future__ import annotations
from typing import Dict, List
import numpy as np
def build_summing_matrix(hierarchy: Dict[str, List[str]]) -> np.ndarray:
"""构造层级加总矩阵 S
hierarchy: {parent: [children]} 描述层级关系
返回 S[i,j] = 1 表示节点 i 包含叶子 j
"""
leaves = []
for parent, children in hierarchy.items():
for c in children:
if c not in hierarchy:
leaves.append(c)
nodes = ["Total"] + sorted(hierarchy.keys() - {"Total"}) + sorted(leaves)
node_idx = {n: i for i, n in enumerate(nodes)}
n, m = len(nodes), len(leaves)
S = np.zeros((n, m))
leaf_idx = {leaf: j for j, leaf in enumerate(leaves)}
for leaf in leaves:
S[node_idx[leaf], leaf_idx[leaf]] = 1.0
def collect_leaves(node):
if node in leaf_idx:
return [node]
result = []
for child in hierarchy.get(node, []):
result.extend(collect_leaves(child))
return result
for node in nodes:
if node not in leaf_idx:
for leaf in collect_leaves(node):
S[node_idx[node], leaf_idx[leaf]] = 1.0
return S, nodes, leaves
def mint_reconcile(y_hat: np.ndarray, S: np.ndarray, W: np.ndarray = None) -> np.ndarray:
"""MinTrace 调和投影(Eq. core)
y_hat: (n,) 各层独立预测
S: (n, m) 加总矩阵
W: (n, n) 误差方差矩阵,默认单位矩阵 (OLS)
"""
n = S.shape[0]
if W is None:
W = np.eye(n)
W_inv = np.linalg.inv(W)
G = np.linalg.inv(S.T @ W_inv @ S) @ S.T @ W_inv
return S @ G @ y_hat
8. 论文来源
- 2302.05650
- 2412.14718