🎓 レベル:標準 | 重要度:A(必須)
📎 前提:指数平滑と季節指数による需要予測 | モデル選択・交差検証:バックテストと予測の評価(時系列分析) | 応用:第3章 安全在庫
要点(BLUF)
- 予測誤差は (実績 − 予測)。大きさの指標は MAD(平均絶対偏差)・MSE/RMSE・MAPE(平均絶対%誤差)、偏りの指標が MFE(平均誤差=バイアス)です。
- モデル選択(どの予測式を採用するか)に MAD/RMSE/MAPE を使う話は バックテストと予測の評価(時系列CV)に譲ります。OMの主役は、運用に入った予測の「監視」——いま動いている予測が偏っていないかを見張ることです。
- そのための道具が追跡信号 (累積誤差 ÷ MAD)。これは予測の管理図で、管理限界 ±4 を超えたら「予測が系統的にずれた」と判断します。
- 予測誤差は安全在庫に直結します。正規近似で 、安全在庫 (リード1期)。予測誤差のばらつきが在庫を決める——詳細は第3章。
1. 誤差指標:大きさと偏りは別物
予測誤差 から、性格の違う指標を作ります。
- MAD:絶対誤差の平均。外れ値に頑健で、後述の安全在庫に直結します。
- RMSE:2乗するので大きな誤差を強く罰する。正規誤差なら (第5節で導出)。
- MAPE:%表示で単位によらず比較できるが、実績 が小さいと暴れ、過大予測と過小予測で非対称という弱点があります。
- MFE(バイアス):誤差の符号つき平均。0 近傍なら無バイアス、正なら過小予測が続いている(在庫が枯れる方向)、負なら過大予測。MAD/RMSE/MAPE は大きさだけを見て、偏りを区別しません。同じ MAD でも、誤差が左右対称か一方向かは別問題で、後者は在庫を系統的に過少/過剰にします。
モデル選択 vs 運用監視:「どのモデルが良いか」を過去データで選ぶのは交差検証=バックテストと予測の評価 の仕事。本稿は「採用後の予測が偏っていないかを見張る」運用監視で、目的が違います。
2. 誤差指標を計算する(コード)
ある予測の実績/予測ペア(擬似・無バイアス)から、5つの指標を計算します。
import numpy as np
rng = np.random.default_rng(29)
# 実績 vs 予測(擬似・無バイアス)。誤差指標 MAD/MSE/RMSE/MAPE/MFE を計算
n = 60
actual = 100 + 5 * np.sin(2 * np.pi * np.arange(n) / 12) + rng.normal(0, 6, n)
forecast = actual + rng.normal(0, 8, n) # 平均0の予測誤差を上乗せ(無バイアス)
e = actual - forecast # 予測誤差 e_t = A_t - F_t
MAD = np.mean(np.abs(e)) # 平均絶対偏差
MSE = np.mean(e ** 2)
RMSE = np.sqrt(MSE)
MAPE = np.mean(np.abs(e / actual)) * 100 # 平均絶対パーセント誤差(%)
MFE = np.mean(e) # 平均誤差(バイアス)
print(f"観測数 n = {n}")
print(f"MAD = {MAD:.3f}")
print(f"MSE = {MSE:.3f}")
print(f"RMSE = {RMSE:.3f}")
print(f"MAPE(%) = {MAPE:.3f}")
print(f"MFE(bias) = {MFE:.3f} (0 近傍なら無バイアス)")
print(f"RMSE/MAD = {RMSE / MAD:.3f} (正規誤差なら理論値 約1.25)")
出力:
観測数 n = 60
MAD = 6.564
MSE = 67.745
RMSE = 8.231
MAPE(%) = 6.625
MFE(bias) = -0.013 (0 近傍なら無バイアス)
RMSE/MAD = 1.254 (正規誤差なら理論値 約1.25)
出力の意味:MAD 6.56・RMSE 8.23・MAPE 6.6%。MFE は −0.013 とほぼ0——この予測は無バイアス(過大も過小も続いていない)と判断できます。一方 RMSE/MAD は 1.254 で、正規誤差の理論値 をほぼ言い当てています。この比は第5節の と同じ関係で、MAD さえ測れば誤差の標準偏差が出る → 安全在庫が計算できるという橋になります。
3. 追跡信号:予測の管理図
問題は、運用中に予測がだんだん偏っていくことです(市場が構造変化したのにモデルが古いまま、など)。単発の MAD では気づきにくい。そこで累積予測誤差 RSFE(Running Sum of Forecast Errors)を MAD で割った追跡信号を、管理図のように監視します。
無バイアスなら誤差は正負に散り、RSFE は0近傍をふらつくので も0付近。ところが予測が一方向にずれ始めると、同符号の誤差が累積して RSFE が一方向に伸び、 が0から離れます。経験則として管理限界 ±4(おおむね「6ヶ月連続で同じ符号」に相当)を超えたら、系統的バイアスありと見て予測を見直します。 は、誤差が独立・無バイアスなら滅多に超えない水準として使われます(要最新確認:限界値は運用方針で ±3〜±6 と幅があります)。
4. バイアスを検知する(コード)
予測を需要水準=100 と見て据え置いたところ、第18期に需要の真の水準が +12 シフトした状況を作ります。予測がこれを取り逃がし、誤差が正に偏り続けます。追跡信号がいつ管理限界を破るかを見ます。
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
rng = np.random.default_rng(24)
# 36期。予測は需要水準=100 とみて固定。ところが第18期に需要の真の水準が +12 シフト
# (構造変化)。予測がこれを捉え損ね、実績-予測 が正に偏り続ける
n, bias_start = 36, 18
mu = np.full(n, 100.0)
mu[bias_start:] = 112.0 # 第18期に需要水準が +12
demand = mu + rng.normal(0, 5, n) # 実績需要
forecast = np.full(n, 100.0) # 予測は100に据え置き
e = demand - forecast # 予測誤差 e_t = A_t - F_t
rsfe = np.cumsum(e) # 累積予測誤差 RSFE
mad_run = np.array([np.mean(np.abs(e[:i + 1])) for i in range(n)]) # 逐次MAD
ts = rsfe / mad_run # 追跡信号 TS_t = RSFE/MAD
limit = 4.0
breach = np.where(np.abs(ts) > limit)[0]
first_breach = int(breach[0]) if breach.size else None
print(f"需要水準シフト = 第{bias_start}期に +12(予測は100で据え置き)")
print(f"追跡信号が +-{limit:.0f} を最初に超えた時点 = 第{first_breach}期")
print(f"その時点の TS = {ts[first_breach]:.2f}")
# 誤差 -> 安全在庫の橋渡し(無バイアスな前半=安定期のMADを使う)
mad_stable = np.mean(np.abs(e[:bias_start]))
sigma_e = 1.25 * mad_stable # 正規近似 sigma_e approx 1.25*MAD
z = 1.65 # 95%サービス水準(片側)
SS = z * sigma_e # 安全在庫(リード1期)
print(f"\n[誤差 -> 安全在庫] 安定期(前半)のMAD = {mad_stable:.2f}")
print(f" sigma_e approx 1.25*MAD = {sigma_e:.2f}")
print(f" 安全在庫 SS = z*sigma_e = {z} * {sigma_e:.2f} = {SS:.2f} (z=1.65, 95%)")
# 図:追跡信号と ±4 の管理限界
fig, ax = plt.subplots(figsize=(11, 5))
ax.plot(np.arange(n), ts, "o-", color="#1f77b4", label="追跡信号 TS = RSFE/MAD")
ax.axhline(limit, ls="--", color="#d62728", label="管理限界 ±4")
ax.axhline(-limit, ls="--", color="#d62728")
ax.axhline(0, color="gray", lw=0.8)
ax.axvline(bias_start, ls=":", color="green", label=f"第{bias_start}期:水準シフト")
if first_breach is not None:
ax.plot(first_breach, ts[first_breach], "*", color="black", ms=16,
label=f"検知(第{first_breach}期)")
ax.set_xlabel("期"); ax.set_ylabel("追跡信号 TS")
ax.set_title("追跡信号:累積誤差/MAD が ±4 を超えたら予測バイアスを検知")
ax.legend(); plt.tight_layout(); plt.show()
出力:
需要水準シフト = 第18期に +12(予測は100で据え置き)
追跡信号が +-4 を最初に超えた時点 = 第19期
その時点の TS = 5.96
[誤差 -> 安全在庫] 安定期(前半)のMAD = 3.65
sigma_e approx 1.25*MAD = 4.56
安全在庫 SS = z*sigma_e = 1.65 * 4.56 = 7.53 (z=1.65, 95%)
出力の意味:第18期まで追跡信号は±4の内側をふらつき(無バイアス)、需要が +12 シフトした直後の第19期に TS が 5.96 と管理限界を突破しました——構造変化をほぼ1期で検知できています。図では、バイアス注入(緑の点線)後に青い追跡信号が急上昇し、★印(第19期)で赤い限界線を超え、その後も登り続けます。これは「予測が外れ続けているのに気づかず在庫を切らす」事態を防ぐ早期警報です。検知したら予測の水準を上方修正します(あるいは を上げて適応を速める → 指数平滑と季節指数による需要予測)。
5. 誤差 → 安全在庫:σ_e ≈ 1.25 MAD(第3章への橋)
予測は必ず外れる以上、OM はその誤差を安全在庫で吸収します。誤差 が平均0・標準偏差 の正規分布なら、絶対値の期待値は
つまりMAD を1.25倍すれば誤差の標準偏差が得られます(第2節で RMSE/MAD≈1.25 を確認した通り)。リードタイム1期の安全在庫は、欲しいサービス水準に対応する正規分位 を使って
上のコードでは、安定期の MAD 3.65 から 、95%サービス水準()で安全在庫 SS ≈ 7.53。予測誤差のばらつきがそのまま在庫量を決めるわけです。リードタイムが 期に伸びると誤差は で効く——この一般化と発注点の設計は第3章で扱います。
⚠️ よくある誤解
- 「MAPE が小さければ常に良い予測」ではない:MAPE は実績 が小さい期で暴れ、過大予測(上限なし)と過小予測(最大100%)で非対称です。低需要・断続需要では MAD やスケール調整済み指標を併用します。
- 「MAD(RMSE)が小さい=バイアスもない」ではない:大きさ指標は誤差の偏りを区別しません。同じ MAD でも誤差が一方向なら在庫は系統的にずれます。偏りは MFE・追跡信号で別に監視します。
- 「交差検証で良かったモデルは運用後も大丈夫」ではない:モデル選択(バックテストと予測の評価)と運用監視は目的が違います。採用後も市場は動くので、追跡信号で偏りを見張り続ける必要があります。
- 「追跡信号の±4は絶対」ではない:±4 は経験則で、実務では ±3〜±6 と幅があります(要最新確認)。許容できる見逃し/誤検知のバランスで決めます。
関連ノート
- 指数平滑と季節指数による需要予測(前提・監視対象となる予測を作る側)
- 需要予測の枠組みと移動平均(予測の3原則・誤差を持つ前提)
- バックテストと予測の評価(時系列分析・モデル選択の交差検証/予測区間の評価)
- 第3章 在庫管理(予測誤差 σ_e → 安全在庫・発注点。)
- オペレーションズ・マネジメント 全体目次