特異スペクトル変換 (SST) とは

= Singular Spectrum Transformation

行列の特異値分解を利用した、時系列データの異常検知の手法。

手順

時系列データ

\[\boldsymbol{x} = \left( x_1, x_2, \cdots, x_T \right)\]

に対して、注目する時刻 $t$ から、ウインドウ幅 $w$ 個の要素を取り出し、$\boldsymbol{y_t}$ とする:

\[\left( x_1, \cdots, \underbrace{x_t, x_{t+1}, x_{t+2}, \cdots, x_{t+(w-1)}}_{\boldsymbol{y_t}}, x_{t+(w-1)+1}, x_{t+(w-1)+2}, \cdots, x_T \right)\]

ウインドウを1ずつずらし、同様の操作をして $\boldsymbol{y_{t+1}}, \cdots, \boldsymbol{y_{t+M-1}}$ を抽出する:

\[\begin{eqnarray} &\left( x_1, \cdots, x_t, \underbrace{x_{t+1}, x_{t+2}, \cdots, x_{t+(w-1)}, x_{t+(w-1)+1}}_{\boldsymbol{y}_{t+1}}, x_{t+(w-1)+2}, \cdots, x_T \right) \\ &\left( x_1, \cdots, x_t, x_{t+1}, \underbrace{x_{t+2}, \cdots, x_{t+(w-1)}, x_{t+(w-1)+1}, x_{t+(w-1)+2}}_{\boldsymbol{y}_{t+2}}, \cdots, x_T \right) \\ &\vdots \\ &\left( x_1, \cdots, x_{t+(M-1)-2}, x_{t+(M-1)-1}, \underbrace{x_{t+(M-1)}, \cdots, x_{t+(w-1)+(M-1)}}_{\boldsymbol{y}_{t+(M-1)}}, \cdots, x_T \right) \end{eqnarray}\]

これら $\boldsymbol{y}t , \cdots, \boldsymbol{y}{t+(M-1)}$ を転置した列ベクトルを並べた行列

\[H_T \equiv \left(\boldsymbol{y}_t^T , \cdots, \boldsymbol{y}_{t+(M-1)}^T \right) = \begin{pmatrix} x_t & x_{t+1} & \cdots & x_{t+(M-1)} \\ x_{t+1} & x_{t+2} & & x_{t+(M-1)+1} \\ \vdots & & \ddots & \vdots \\ x_{t+(w-1)} & x_{t+(w-1)+1} & \cdots & x_{t+(w-1)+(M-1)} \end{pmatrix}\]

を定義する。

実装例

結果が期待通りにならないのでバグが有りそう。要確認

import numpy as np
from matplotlib import pyplot as plt

T = 5000
w = 10
M = 500
L = 300

ts = np.arange(T) / 100
y_noise = np.random.normal(loc=0, scale=0.1, size=T)
y1 = y_noise + np.where(np.logical_and(4*np.pi < ts, ts < 9*np.pi), np.sin(ts), np.sin(3*ts))
y2 = y_noise + np.sin(ts) + np.where(np.logical_and(np.pi < ts, ts < np.pi*101/100), 5.0, 0)

#plt.plot(ts, y1)
#plt.show()
#plt.plot(ts, y2)
#plt.show()

def get_matrix(i_start, y):
    ret = []
    for i in range(i_start, i_start+M):
        ret.append(y[i:i+w])
    return np.array(ret).T

def calc_svd(matrix):
    U, s, V = np.linalg.svd(matrix)
    return U, s, V

def step(i_start, y):
    H = get_matrix(i_start, y)
    H_history = get_matrix(i_start-L, y)
    U, _, _ = calc_svd(H)
    U_history, _, _ = calc_svd(H_history)
    _, s, _ = calc_svd((U.T).dot(U_history))
    return 1 - s[0]

a_y1 = []
a_y2 = []
for i_start in range(L, T-w-M):
    if i_start % 100 == 0:
        print(i_start)
    a_y1.append(step(i_start, y1))
    a_y2.append(step(i_start, y2))

plt.plot(ts[L:T-w-M], a_y1)
plt.show()
plt.plot(ts[L:T-w-M], a_y2)
plt.show()