K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

移動平均 (MA) モデル

中級

04.6.3

移動平均 (MA) モデル

最終更新 2020-06-10 読了時間 3 分
まとめ
移動平均(MA)モデルは、今日の値を「今日のノイズ + 直近 \(q\) 日のノイズの重み付き和」で表すモデルで、突発的なショックが数日だけ尾を引いて消える動きを捉えます。AR モデルが過去の「値」を使うのに対し、MA モデルは過去の「予測誤差」を使う点が違い、影響はちょうど \(q\) 期で途切れます。このページでは \(\theta_1 = 0.6, \theta_2 = 0.3\) の MA(2) 過程を 220 日分作り、statsmodels.tsa.arima.model.ARIMA(train, order=(0, 0, 2)) で学習して残り 20 日を予測します。読み終えると、予測線が 2 日で平均に張り付く理由と、ACF がラグ 2 で切れる形から次数 \(q\) を読む方法が分かります。

偏自己相関 (PACF) と ACF の違いを先に見ておくと、「ACF がラグ q で切れたら MA(q)」という判定の根拠がつかめます。

直感 #

ある日 SNS で商品が話題になり、売上が跳ねたとします。翌日はその 6 割、翌々日は 3 割だけ余韻が残り、3 日目には元に戻る。MA(2) モデルはこれを「今日の値 = 今日のショック + 0.6 × 昨日のショック + 0.3 × 一昨日のショック」と書きます。ショックは平均 0 のノイズなので、2 日より前の出来事は今日に一切影響しません。AR モデルのように影響が幾何級数的に薄れながら続くのではなく、\(q\) 日で完全に切れるのが MA の特徴で、ACF がラグ \(q\) を超えると 0 になることにそのまま対応します。

何が起きているか(ステップで見る) #

コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。

見るポイント:MA の影響は 1 期(次数分)で消える。短い揺れの形を作る成分。このページは θ1 = 0.6、θ2 = 0.3 の MA(2) なので、影響は 2 期で消える。

式で書くと何を推定しているか #

MA(q) モデルは、今日の値を定数とノイズの重み付き和で表します。

$$ y_t = \mu + \varepsilon_t + \theta_1 \varepsilon_{t-1} + \cdots + \theta_q \varepsilon_{t-q}, \qquad \varepsilon_t \sim N(0, \sigma^2) $$

このページのデータは \(y_t = \varepsilon_t + 0.6,\varepsilon_{t-1} + 0.3,\varepsilon_{t-2}\) なので、正解は \(\mu = 0,\ \theta_1 = 0.6,\ \theta_2 = 0.3\) です。この系列の分散は \(1 + 0.6^2 + 0.3^2 = 1.45\)、自己相関はラグ 1 で \((0.6 + 0.6 \times 0.3)/1.45 \approx 0.54\)、ラグ 2 で \(0.3/1.45 \approx 0.21\)、ラグ 3 以降は厳密に 0 です。予測では未来のノイズを期待値 0 と置くため、1 日先は \(0.6,\hat\varepsilon_T + 0.3,\hat\varepsilon_{T-1}\)、2 日先は \(0.3,\hat\varepsilon_T\)、3 日先からは \(\mu\) だけになります。

Python で MA(2) を学習して 20 日先を予測する #

次のコードは、\(\theta_1 = 0.6, \theta_2 = 0.3\) の MA(2) 過程を 220 日分シミュレーションし、先頭 200 日で ARIMA(order=(0, 0, 2)) を学習して、残り 20 日の予測を実測の上に重ねます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA

rng = np.random.default_rng(21)
dates = pd.date_range("2020-01-01", periods=220, freq="D")
errors = rng.normal(0, 1, len(dates))
values = []
for t in range(len(dates)):
    prev1 = errors[t - 1] if t - 1 >= 0 else 0
    prev2 = errors[t - 2] if t - 2 >= 0 else 0
    values.append(errors[t] + 0.6 * prev1 + 0.3 * prev2)

series = pd.Series(values, index=dates)

train = series.iloc[:-20]
test_index = series.index[-20:]

model = ARIMA(train, order=(0, 0, 2)).fit()
forecast = model.forecast(steps=20)

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(series.index, series, color="#cbd5f5", label="実測値(全体)")
ax.plot(train.index, train, color="#2563eb", linewidth=1.2, label="学習区間")
ax.plot(test_index, forecast, color="#f97316", linewidth=1.6, label="MA(2) 予測")
ax.set_title("移動平均モデルによる予測")
ax.set_xlabel("日付")
ax.set_ylabel("値")
ax.legend()
ax.grid(alpha=0.3)

fig.tight_layout()
fig.savefig("static/images/timeseries/ma_model.svg")

移動平均 (MA) モデルの図

実行結果の読み方 #

青の系列は平均 0、標準偏差約 1.2(\(\sqrt{1.45}\))で上下し、隣どうしの値がやや似ている(ラグ 1 の相関 0.54)ものの、AR(1) のような長い波は作りません。橙の予測線は学習区間の末尾から 2 日分だけ上下し、3 日目以降は 0 付近の水平線になります。この 2 日が「ショックの余韻が残る長さ」で、order の q=2 に一致します。実測(薄い青)は予測線のまわりで標準偏差 1.2 程度の幅で散るので、3 日目以降の予測誤差は系列そのものの散らばりと同じになります。

使いどころと落とし穴 #

MA モデルは、ACF がラグ \(q\) までは有意でそれ以降が急に 0 へ落ちる系列に向いています。逆に ACF がゆっくり減衰するなら AR 成分が必要で、両方の形が混ざるなら ARIMA(p, 0, q) で AR と MA を同時に推定して AIC・BIC を比べます。MA の予測は \(q\) 日より先では定数になるので、週次や月次の長期予測に単独で使うことはなく、ARIMA の中で「ショックの吸収役」として働きます。ノイズの推定は学習データの端で不安定になりやすく、学習区間が 50 点を下回ると係数の標準誤差が大きくなる点にも注意してください。