カルマンフィルタを用いた状態空間モデルの推定
はじめに
時系列解析については以前にMCMCを用いた状態空間モデルの推定を行なったのですが、状態空間モデルの推定方法としてカルマンフィルタも知っておいた方が良さそうだったので、今回はカルマンフィルタの実装を行なっていきます。
状態空間モデルの推定方法について
状態空間モデルの推定方法はいくつかあるのですが、 その内訳は下記に詳細に記載されていました。
MCMCとカルマンフィルタについての大枠の特徴はこのような形になります。
推定の速さが肝になる自動車やロボット制御ではカルマンフィルタが使われ、特に時間の制約や逐次処理の必要がない時系列解析ではMCMCが使用されることが多いのはこの特性のためですね。
実験
では実際にカルマンフィルタを用いた状態空間モデルの推定を実施していきます。
今回はイチローの年別安打数を予測するモデルを作成したいと思います。 データソースは以下になります。
データは以下のような形になります。0に収束してしまうと面白くないので最後の五年は削ってモデルに入力します。
カルマンフィルタのコードも下記サイトを参考にさせていただきました。
最も簡単なローカルレベルモデルに対してカルマンフィルタを適用します。 ローカルトレンドモデルで推定が必要なパラメータは状態方程式のノイズの分散(sigmaW)と観測方程式のノイズの分散(sigmaV)の2つで、これらを最尤推定を用いて推定します。
def localLevelModel(y, xPre, pPre, sigmaW, sigmaV): # 状態の予測(ローカルレベルモデルなので、予測値は、前期の値と同じ) xForecast = xPre # 状態の予測誤差の分散 pForecast = pPre + sigmaW # カルマンゲイン kGain = pForecast / (pForecast + sigmaV) # カルマンゲインを使って補正された状態 xFiltered = xForecast + kGain * (y - xForecast) # 補正された状態の予測誤差の分散 pFiltered = (1 - kGain) * pForecast # 観測値の予測誤差 v = y - xForecast # 観測値の予測誤差の分散 F = pForecast + sigmaV return xFiltered,pFiltered,v,F #尤度の計算 def norm_dens(val, s): #val:値 #s:標準偏差 return np.log((1/np.sqrt(2*np.pi*s**2))*np.exp(-0.5*(val)**2/s**2)) def kalmanfilter(df,sigmaW=1000,sigmaV=10000,optimize=False): x=np.zeros(df.shape[0]+1) P=np.zeros(df.shape[0]+1) v=np.zeros(df.shape[0]) F=np.zeros(df.shape[0]) #状態の予測誤差の分散」の初期値 P[0]=10000000 for i in range(df.shape[0]): x[i + 1],P[i + 1],v[i],F[i] = localLevelModel(df[1].values[i], x[i], P[i], sigmaW = sigmaW, sigmaV = sigmaV) #尤度の計算 if optimize==True: return -norm_dens(v, np.sqrt(F)).sum() else: return x,P,v,F #最尤推定の実施 from scipy.optimize import fmin import math yu = lambda X: kalmanfilter(df,X[0],X[1],optimize=True) result=fmin(yu, [1000, 1000])
この結果、sigmaW,sigmaV=[1635.79921897, 594.56116963]と推定できました。
このパラメータを用いて予測した値と実際の値を比較します。
ローカルトレンドモデルなので一期遅れて値が反映されるような形ですね。
一応ライブラリを用いた場合との比較をしておきます。 pythonだとstatsmodelのUnobservedComponentsでできるようです。
import statsmodels.api as sm # Fit a local level model mod_ll = sm.tsa.UnobservedComponents(df[1].values, 'local level') res_ll = mod_ll.fit() print(res_ll.summary()) # Show a plot of the estimated level and trend component series fig_ll = res_ll.plot_components(legend_loc="upper left", figsize=(12,8)) plt.tight_layout()
この結果のパラメータは以下のように最尤推定で算出した値とほぼ同じ値となったので、正しく推定できているようです。 sigmaW,sigmaV=[1636.5036, 594.1718]
メモ
状態空間モデルの推定方法は複数存在し、それぞれ特性が異なるので用途に合わせて使い分けができると良さそう
今回疑問点を調べる度にLogics of Blue | 統計分析や統計的予測・意思決定理論など様のサイトに辿り着いたのでエスパーかと思った。本当にありがとうございます。
使用したコードは以下になります。