需求预测


首页 » 资源 » 此处

在库存优化方面,概率性需求预测不可或缺。当预测值与交付周期内的总需求相符时,就可以说需求预测在交付周期内是完整的;这与传统预测视角截然不同,在传统预测中进行的是定期预测(通常每天、每周或每月进行一次预测),并且交付周期具有不可知性。Lokad 预测引擎提供的是完整概率的需求预测,并以概率性的交付周期作为输入。Lokad 预测引擎为商业数据中的各种统计模式提供原生支持,例如季节性、趋势和产品生命周期。此外,缺货或促销等需求反常的情形也会予以考量。Lokad 预测表示出了未来需求中每个单位的预期概率。在此页面,我们将详细介绍计算 Lokad 完整需求预测值时所采用的语法。

一般语法

预测引擎有一个专门用于概率预测的函数。其语法如下:

Demand = forecast.demand(
category: C1, C2, C3, C4
hierarchy: H1, H2, H3, H4
label: PlainText
location: Store
demandStartDate: LaunchDate
demandEndDate: EndDate
horizon: Leadtime
offset: 0
present: (max(Orders.Date) by 1) + 1
demandDate: Orders.Date
demandValue: Orders.Quantity
censoredDemandDate: Stockouts.StartDate, Stockouts.EndDate
inflatedDemandDate: TVAds.StartDate, TVAds.EndDate
promotionDate: Promotions.StartDate, Promotions.EndDate
promotionDiscount: Promotions.Discount
promotionCategory: Promotions.Type
covariable: Campaign
covariableObserved: false
covariableName: Ads.Campaign
covariableDate: Ads.Date
covariableValue: Ads.Volume)

与正则函数不同,调用函数中没有位置参数,只有命名参数。这些命名参数更适合用于复杂函数,因为此类参数可以提高源代码的可读性,唯一的不足之处就是会让源代码稍显冗长。这些参数与正则函数参数的行为一样,因此可以用于 Envision 公式。

调用函数返回的是向量 Demand,这是一种分布(另请参阅分布代数)型向量。分布是一种用于表示函数 $p: \mathbb{Z} \to \mathbb{R}$ 的高级数据类型。更准确地说,预测引擎返回随机变量,也即分布是正态的,并且有一个质量等于 1。在示例中,$p(k)$ 表示需要 $k$ 个单位的相关概率。每个项目(从 Envision 的意义上说)关联其自身的需求分布。

完整的 forecast.integral 语法包含多个参数,不过其中只有四个参数为强制性参数:

  • present:日期标量值
  • demandDate:与项目相关的日期向量
  • demandValue:与项目相关的数值向量
  • horizon:分布向量

present 为日期值,用于表示所要预测的第一天,其依据的假设是数据截止于前一天。实际上,有些企业可能在星期日关闭,如果数据集里的最新日期是星期六,那么预测是从星期天还是星期一开始就会显得模糊不清。在上面的示例性语法中,我们使用了 max(Orders.Date) + 1,即假设每天都对订单进行观测,并且输入数据的最后更新日期为前一天。

demandDatedemandValue 应属于同一个具有项目相关性的表,在 Envision 中表示为 [Id, *]。日期表示过去观察到此需求的时间。这些值表示需求范畴,通常按单位每个来计数。不支持小数形式的需求值。此表包含预测引擎预测的需求历史记录。从理论上说,历史记录的长度要尽可能长;但实际上,当需求历史记录超过 5 年时其价值就不大了。预测引擎既适合处理较短的需求历史记录,也适合处理较长的需求历史记录;如果需求历史记录较长,数据点的时间越久,其统计相关性越低。

horizon 表示预测需求时所要使用的概率性交付周期。在计算完整需求预测值时,交付周期被视为输入,但交付周期本身通常也是一个预测值。而预测引擎提供了预测交付周期的可能。交付周期预测与需求预测本身分离开来,因为可以在交付周期值传送至预测引擎之前,对交付周期分布进行特定调整。

除这些强制性参数以外,通过向预测引擎提供更多数据可以大大提高预测准确度。下面的章节对此进行了详细说明。

形式定义

在这一节里,我们将简要说明预测引擎在计算完整需求预测值时所执行的统计操作的形式定义。

令 $y(t)$ 为需求函数, $t$ 为时间。令完整需求 $D$ 关联随机变量 $\Lambda$(用于表示所要定义的交付周期):

$$\text{D} : (y,\Lambda,t_0) \to \int_0^{\infty} \mathbf{P}[\Lambda=\lambda] \left( \int_{t_0}^{t_0+\lambda} y(t) dt \right) d\lambda$$ 其中 $\mathbf{P}[\Lambda=\lambda]$ 表示交付周期随机变量 $\Lambda$ 等于 $\lambda$ 的概率。需求之所以具有完整性,是因为它是对概率性交付周期的积分

如果 $t_0$ 表示当前日期,那么需求就是已知的,因为在时间 $t_0$ 以前的需求已观测到,但自此以后的需求未知。预测引擎的目的是计算 $\hat{D}(y, \Lambda)$,即用随机变量表示的这一未来需求的概率估计值。

类别、层次和标签

从预测引擎的角度来说,类别、层次和纯文本标签的作用非常相似:二者均有助于预测引擎处理稀疏的历史数据。

请参阅利用类别和层次进行预测

新产品预测

从预测角度而言,新产品是指尚未出售过的产品。这涉及到一种非常特殊的预测难题,因为按照定义,新产品没有任何相关的历史数据。Lokad 预测引擎支持通过 demandStartDate 参数来进行新产品预测。在历史起始日期已知的情况下,建议也将此信息提供给预测引擎,因为无论是对于新产品还是旧产品,都有助于提高预测准确度。

demandStartDate 参数的预期输入为各个项目的日期。此日期用于表示该项目的需求生效的第一天。对于已销售的项目,此日期为过去的日期;对于尚未上市的项目,此日期则为将来的日期。

提供 demandStartDate 参数有两点不同的好处。显然,第一个好处就是预测新项目。在此情形中,指定 offset 参数通常也很重要。实际上,如果偏移为 0(默认值),那么预测涵盖的周期可能不会与此项目的活动周期重叠。

示例:假设今天为 7 月 1 日。预测范围为 7 天的狄拉克分布;也即交付周期为恒定值——7 天。产品 A 于 7 月 15 日(也即起始日期)上市。如果是在今天进行的预测,那么产品 A 的预测分布就是为 0 时的狄拉克分布,因为日期范围的终止日期早于产品 A 的起始日期。要想预测产品 A 第一周的需求量,应将产品 A 的偏移值设定为 14 天。

指定 demandStartDate 的第二个好处是可以提高所有项目的预测准确度,而不只是提高尚未上市项目的预测准确度。的确,观察项目起始日期当天售出的第一件产品,和观察上市后六个月内售出的第一件产品是不同的。前一种情况意味着接下来会有稳定的销量,而后一种情况意味着每年只有区区几件的有限需求量。Lokad 预测引擎利用 demandStartDate 参数来改进所有项目的需求预测值。

需求紧缩和需求膨胀

我们的目的是预测需求。但很多时候历史数据只是对实际需求的近似估计,因此形成了反常的情况(不管愿意与否,这种情况都会存在)。举个例子,历史数据可以通过历史销量表示。但在发生缺货时,需求本身可能保持稳定,但销量下降。Lokad 预测引擎进行了原生设计,能够处理这些反常的情形,而这也正是 censoredDemandDateinflatedDemandDate 参数的用意所在。这两个参数的预期输入都是与项目相关的日期向量,在 Envision 中表示为 (Id, Date)

当给定项目的日期通过 censoredDemandDate 标记为紧缩时,预测引擎将假定此需求高于或等于观测值。预测引擎不会做出该需求在特定的一天有多高的假设;因为这个值对于我们而言绝不可能是已知的。但是,通过精确定位偏差,预测引擎可以得出围绕这种情况量身定制的优化类别。在实践中,需求紧缩最常见的情况就是缺货,因为通过销售数据进行的观测无法捕捉到缺货时潜在顾客默默离开时的相关信息。

同样,需求也可能膨胀inflatedDemandDate 参数提供了精确定位需求应视为低于或等于观测到的需求时的日期和项目。再者,实际需求始终是未知的,但精确定位偏差对于预测引擎很有帮助。实际上,当存在临时性的不重复出现的市场推力时,需求就会膨胀。例如,当地的某支体育队伍在一项全国性的锦标赛中意外夺冠,会让本地超市的销量持续繁荣数天。

inflatedDemandDatecensoredDemandDate 这两个参数可以取一个或两个向量作为输入。如果提供两个日期向量,那么 (start, end) 对会作为包含段处理,其中第一个日期作为段的开头,第二个日期作为段的末尾。如果只提供一个日期向量,那么段长将视为 1 天;日期会将特定的天数标记为膨胀或紧缩。

如果需求紧缩或膨胀是重复出现的,例如每年、每周等等,那么不需要对此类需求进行这样的标注,因为预测引擎会自动处理此类情形。

预测促销

预测引擎为促销提供原生支持。我们并不硬性规定提供促销数据。但如果提供促销数据,那么无论是过去还是未来都应当指定促销。最起码可以单独提供参数 promotionDate。参数 promotionDatecensoredDemandDate 的使用模式相同:在只提供一个日期变量时,促销期视为只有 1 天;如果提供两个日期,第一个向量表示包含在内的起始日期,第二个向量表示包含在内的结束日期。

promotionDiscount 参数为可选参数,提供此参数有助于预测引擎获知给定促销的“强度”。针对此参数应提供多个变量,预测引擎将此数据作为“序数”值处理:折扣越大,预期的促销影响越大。实际上,预测引擎会根据过去促销时观察到的销量提升情况来计算预期的需求提升。

promotionCategory 参数同样是可选参数,可以作为促销事件的分类来提供。在提供此参数的情况下,预测引擎将使用此数来测试促销事件之间的相似性,并检测同一类别中标记的事件是否达到了类似的需求提升。该参数在本质上与 category 参数极为相似,只是该参数应用于促销,而不是应用于项目。

读者要擦亮眼睛:即便是有非常不错的历史数据,促销也是出了名的难以预测。Lokad 的经验表明,大部分公司都没有“高度准确”的现成促销数据。这就是说,此类数据可以通过在项目后期进行精心准备来获取。根据经验,收集非常适合用于改善预测准确度的促销数据需要做大量工作。向预测引擎馈送近似的促销数据只会降低得到的准确度。

在提供预测数据的情况下,与促销活动相关的时间段通常不能通过 inflatedDemandDate 来标记。同时通过 promotionDateinflatedDemand 日期标记一个时间段存在微妙的语义:它表明促销提升已发生膨胀,超出了可以从促销中合理预计的范围,促销本身将视为出现偏差。

协变量

协变量代表了一种非常先进的向预测引擎传送信息的机制。实际上,我们不建议使用协变量,因为通常来说,此举对于获得令人满意的结果太过于复杂。直观地说,协变量的目的是充分利用这些能够准确、可靠地预计需求的指标。而大部分企业并不存在这样的指标,因为需求历史记录本身蕴含了用于预期未来需求的最佳信号。但是,在某些垂直领域的确存在此类信号。

Contoso Inc 是一家从事风轮机养护的维修公司。该公司不制造风轮机,但会赢得许多大型维修合同。为了保养风轮机,Contoso 需要保持备用零件库存。所需的备用零件数量与需要维护的风轮机数量成线性关系。由于中标结果在合同起始日期前数月便已知晓,因此 Contoso 将风轮机的数量作为协变量,来帮助改进其备用部件需求。

每个项目最多可以指定一个协变量,这个过程通过参数 covariable 进行。所有协变量应在一个包含 3 列的表中定义:

  • covariableName 用作与 covariable 参数相关的外部键
  • covariableDate 表示与协变量相关的日期,可能为将来的日期
  • covariableValue 表示协变量在指定日期的值

最后一个参数是一个叫做 covariableObserved 的布尔标志,其输入应为布尔标量。默认值为 false。当此参数设置为 true 时,预测引擎会从历史角度假定协变量值在此日期之前是未知的,因此这些值为观测值。如果该字段设置有误,将误导预测引擎假定协变量数据事先已知,而实际情况可能并非如此。