库存管理的核心指标之一是补货点,也就是当库存降到某个数值时触发采购。传统公式通常写成 ROP = 日均需求 × 提前期 + 安全库存,但真正落实时,日均需求和提前期都不是固定数,而是一个随机变量。AI方法的价值不在于给公式换个参数,而是把需求的不确定性显式建模出来,再反推对应服务水准下的补货点。下面从传统方法的局限说起。

一、传统ROP计算为什么经常失真
先看最基础的公式。补货点通常写为 ROP = 日均需求 d × 提前期 L + 安全库存 SS。其中安全库存一般按 SS = z × σdLT 计算,σdLT 表示提前期内需求的标准差,z 表示目标服务水平对应的标准正态分布分位数。这套公式清晰、好解释,所以在ERP和很多库存模块里仍然是默认逻辑。
问题出在假设上。公式默认需求围绕均值上下波动,而且波动幅度稳定,提前期也可以提前准确知道。实际销售数据里,促销活动会让某天销量突然放大三倍,节假日和周末呈现明显周期,不同SKU的需求波动相差几十倍。用一个固定的安全库存系数覆盖全部商品,容易出现高销量商品断货、长尾商品积压并存的情况。更深一层的问题是,缺货本身会扭曲历史需求数据,销量记录并不等于真实需求,导致需求被系统性低估,传统ROP进一步偏移。
二、用分位数预测代替单一均值
AI参与需求预测,最实用的改变不是把均值预测得更准,而是直接输出分位数。给定服务水平95%,模型不去问“平均每天卖多少”,而问“在95%的情况下,每天需求不会超过多少”。这个上界值可以直接作为补货点计算中的需求侧输入。分位数回归的损失函数对低估和高估赋予不同权重,训练出来的模型会主动学习尾部风险,而不是只拟合平均趋势。
例如某SKU平均日销30件,但促销期可能冲到90件。均值模型只看到30,计算出的ROP偏低;95%分位数模型输出78件,提前期7天时补货点可能接近546件,而不是210件。这个差异不是凭空加库存,而是来源于数据里真实的波动。进一步看,需求波动大的SKU会自动获得更宽的安全库存,需求平稳的SKU则不会被过度补货,这比全品类套同一个z系数更有针对性。
三、Python实现:训练分位数模型并计算ROP
下面用梯度提升树实现一个简化版流程。特征包括星期、促销标记、价格、滞后销量和滚动均值,模型使用分位数损失,分别训练50%和95%分位数。用这两个分位数的差值估算日需求标准差,再结合提前期计算安全库存和补货点。
import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingRegressor
# df 需要包含: sku_id, date, demand, promo, price
df = pd.read_csv('sales_data.csv')
df['date'] = pd.to_datetime(df['date'])
df['weekday'] = df['date'].dt.weekday
df['lag_7'] = df.groupby('sku_id')['demand'].shift(7)
df['roll_14'] = df.groupby('sku_id')['demand'].transform(
lambda s: s.rolling(14, min_periods=1).mean()
)
features = ['weekday', 'promo', 'price', 'lag_7', 'roll_14']
df = df.dropna(subset=features)
train = df.iloc[:-30]
test = df.iloc[-30:].copy()
mid_model = GradientBoostingRegressor(loss='quantile', alpha=0.5)
upper_model = GradientBoostingRegressor(loss='quantile', alpha=0.95)
mid_model.fit(train[features], train['demand'])
upper_model.fit(train[features], train['demand'])
test['pred_mid'] = mid_model.predict(test[features])
test['pred_upper'] = upper_model.predict(test[features])
lead_time_days = 7
service_factor = 1.65 # 95% 服务水平在正态分布下的近似值
# 分位数间距转成日需求标准差,再计算提前期内安全库存
test['daily_std'] = (test['pred_upper'] - test['pred_mid']) / 1.96
test['safety_stock'] = service_factor * np.sqrt(lead_time_days * test['daily_std'] ** 2)
test['rop'] = test['pred_mid'] * lead_time_days + test['safety_stock']
print(test[['sku_id', 'pred_mid', 'pred_upper', 'safety_stock', 'rop']].head())
这段代码的核心思路是把预测区间宽度当作需求不确定性的度量。日均需求的95%分位数与50%分位数差距越大,说明尾部波动越强,安全库存也随之增加。如果提前期也存在波动,可以再加入提前期方差项,或者直接把未来提前期总需求作为预测目标。实际系统中还可以按SKU分组训练模型,避免不同规模商品互相干扰。
四、AI补货模型落地时的常见误区
第一个误区是把点预测直接当需求分布。比如模型预测下周每天卖40件,如果不看预测区间,补货点就可能设成280件。但若90%分位数是65件,提前期需求上界就是455件,缺货风险完全不同。只看均值会低估尾部损失,这在缺货成本远高于库存持有成本时尤其危险。
第二个误区是忽视数据截断。当商品断货时,历史销量记录是零或很低,但真实需求可能依然存在。直接用缺货期间的数据训练模型,会学到错误的低需求模式。解决办法包括加入库存可用标志、删除缺货期间样本,或用补缺后的需求量作为标签。第三个误区是模型漂移后不及时重训。SKU生命周期短、季节变化快,最好按月或按周滚动重训,并监控实际需求超过95%分位数的频率,如果远高于5%,说明模型对尾部风险估计不足。
五、从补货点到完整的库存决策
ROP只是触发补货的阈值,完整的补货策略还要考虑每次补多少、是否受包装规格和最小起订量约束。可以把供应商要求的整箱数量、经济订货批量EOQ与预测需求结合,在触发补货后计算目标库存水平,再减去当前库存和在途库存得到实际下单量。AI模型可以继续参与这部分优化,例如预测不同订货量下的缺货概率和周转天数,给出建议区间而不是单一数值。
此外,不同SKU的服务水平不必完全一致。高毛利、高缺货成本的商品可以设置97%或99%分位数,长尾低周转商品则可放宽到85%分位数,释放仓储和资金压力。把分位数预测嵌入规则引擎后,库存经理可以根据业务经验调整兜底规则,例如新品保护库存、季节末清仓策略等。最终效果不是让AI替代人,而是把不确定性量化出来,让每次补货决策都有一个可以讨论和校准的风险依据。