需求预测与计划
预测不准是供应链一切问题的根源——但我们能让它不那么不准。
需求计划架构
graph TD
DATA[历史数据] --> STAT[统计模型]
MARKET[市场信号] --> JUDGE[专家判断]
STAT --> FORECAST[需求预测]
JUDGE --> FORECAST
FORECAST --> SOP[S&OP 产销协同]
SOP --> SUPPLY[供应计划]
SOP --> PRODUCTION[生产计划]
SOP --> INVENTORY[库存计划]
style FORECAST fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style SOP fill:#c8e6c9,stroke:#388e3c,stroke-width:2px
预测方法
"""
需求预测模型
"""
from dataclasses import dataclass
@dataclass
class ForecastResult:
method: str
forecast: list[float]
mae: float # 平均绝对误差
class DemandForecaster:
"""需求预测器"""
@staticmethod
def moving_average(
data: list[float], window: int = 3
) -> ForecastResult:
"""移动平均法"""
forecasts = []
errors = []
for i in range(window, len(data)):
avg = sum(data[i - window : i]) / window
forecasts.append(round(avg))
errors.append(abs(data[i] - avg))
# 下期预测
next_forecast = sum(data[-window:]) / window
forecasts.append(round(next_forecast))
mae = sum(errors) / len(errors) if errors else 0
return ForecastResult(
method=f"移动平均 (窗口={window})",
forecast=forecasts,
mae=round(mae, 1),
)
@staticmethod
def exponential_smoothing(
data: list[float], alpha: float = 0.3
) -> ForecastResult:
"""指数平滑法"""
forecasts = [data[0]]
errors = []
for i in range(1, len(data)):
f = alpha * data[i - 1] + (1 - alpha) * forecasts[-1]
forecasts.append(round(f))
errors.append(abs(data[i] - f))
# 下期预测
next_f = alpha * data[-1] + (1 - alpha) * forecasts[-1]
forecasts.append(round(next_f))
mae = sum(errors) / len(errors) if errors else 0
return ForecastResult(
method=f"指数平滑 (α={alpha})",
forecast=forecasts,
mae=round(mae, 1),
)
@staticmethod
def seasonal_naive(
data: list[float], season_length: int = 12
) -> ForecastResult:
"""季节朴素法"""
forecasts = data[:season_length]
errors = []
for i in range(season_length, len(data)):
f = data[i - season_length]
forecasts.append(round(f))
errors.append(abs(data[i] - f))
# 下期
forecasts.append(data[-season_length])
mae = sum(errors) / len(errors) if errors else 0
return ForecastResult(
method="季节朴素法",
forecast=forecasts,
mae=round(mae, 1),
)
# 演示 — 12个月销量
monthly_sales = [
1200, 1100, 1350, 1500, 1400, 1600,
1450, 1300, 1550, 1700, 1800, 2100,
]
forecaster = DemandForecaster()
print("=== 需求预测对比 ===")
methods = [
forecaster.moving_average(monthly_sales, 3),
forecaster.exponential_smoothing(monthly_sales, 0.3),
forecaster.seasonal_naive(monthly_sales, 12),
]
for result in methods:
next_month = result.forecast[-1]
print(f"\n{result.method}:")
print(f" 下月预测: {next_month}")
print(f" MAE: {result.mae}")
牛鞭效应
需求放大从消费者到供应商逐级扩大:
"""
牛鞭效应模拟
"""
def bullwhip_simulation(
actual_demand: int,
amplification: float = 1.5,
levels: int = 4,
) -> dict:
"""模拟牛鞭效应"""
names = ["消费者", "零售商", "批发商", "制造商"]
result = {}
demand = actual_demand
for i in range(min(levels, len(names))):
result[names[i]] = {
"感知需求": round(demand),
"放大倍数": f"{demand / actual_demand:.1f}x",
}
demand *= amplification
return result
print("=== 牛鞭效应 (实际需求=100) ===")
for level, info in bullwhip_simulation(100).items():
print(f" {level}: 感知 {info['感知需求']} ({info['放大倍数']})")
COUNTERMEASURES = {
"信息共享": "共享 POS 数据、库存数据",
"VMI 供应商管理库存": "供应商直接监控零售库存",
"CPFR 协同计划": "联合预测、联合补货",
"缩短前置期": "减少需求信号延迟",
"减少批量": "小批量高频补货",
"稳定价格": "避免促销导致的需求波动",
}
print("\n=== 缓解策略 ===")
for strategy, detail in COUNTERMEASURES.items():
print(f" {strategy}: {detail}")
S&OP 产销协同
| 步骤 | 参与方 | 输出 |
|---|---|---|
| 1. 数据收集 | 市场/销售 | 销售预测 |
| 2. 需求评审 | 销售/市场 | 共识需求计划 |
| 3. 供应评审 | 供应链/生产 | 供应能力评估 |
| 4. 差距分析 | 全部 | 供需平衡方案 |
| 5. 管理层决策 | 高管 | 最终执行计划 |
预测准确度评估
| 指标 | 公式 | 用途 |
|---|---|---|
| MAE | 平均绝对误差 | 通用精度 |
| MAPE | 平均绝对百分比误差 | 跨品类对比 |
| WMAPE | 加权 MAPE | 高值品更重要 |
| Bias | 预测偏差方向 | 检测系统性偏高/偏低 |
行动清单
- [ ] 选取 3 个主力 SKU 用移动平均法和指数平滑法分别预测,计算 MAE 对比准确率
- [ ] 计算当前预测偏差是否存在系统性偏高或偏低(Bias 指标)
- [ ] 识别公司供应链中是否存在牛鞭效应信号(上游供应商感知需求是否明显高于实际销售)
- [ ] 实施至少一项牛鞭效应缓解措施:向供应商共享 POS 数据,或减小订货批量
- [ ] 建立月度预测准确率追踪(MAPE),设定改善目标(例如 MAPE < 20%)
- [ ] 启动第一次 S&OP 月度会议:销售+供应链+运营三方参与,对齐下季度需求预测
下一节:02-销售与运营计划S&OP — S&OP 月度流程与供需平衡实战。