Unscented Transformation(アンセンテッド変換,U変換)のpythonプログラム
Unscented Transformation
U変換は,標準正規分布に従う確率変数$x$の平均$\bar{x}$と分散共分散行列$P_x$が既知であるとき,$x$の非線形変換$y=f(x)$で変換される確率変数$y$の$\bar{y}$と$P_y$を推定する方法である.
まず,この場合,モンテカロ的に
$$ \bar{y}\simeq\frac{1}{N}\sum_{i=1}^Nf(x_i) $$
$$ P_y \simeq \frac{1}{N}\sum_{i=1}^N(f(x_i)-\bar{y})(f(x_i)-\bar{y})^T $$
のように計算することが思いつくが,精度をよく計算するにはNを大きくする必要があり,実用上問題がある.
線形近似をすることなく,モンテカロ法のよいところを利用できるように,できるだけ少ないサンプル点を用いて,変換後の確率変数の統計的性質を推定する方法がU変換である.
まず,確率変数$x$からサンプルする値(シグマ点)を決め,シグマ点を非線形変換し,変換した値から$y$の$\bar{y}$と$P_y$を求める.
参考1:UKF (Unscented Kalman Filter)っ て何 ?
import matplotlib.pyplot as plt
import numpy as np
import random
import math
import scipy.linalg
入力には$X=(X_1,X_2)$を用いる.
平均ベクトルは,
$$ \mu=(E[X_1],E[X_2])=(\bar{x}_1,\bar{x}_2) $$ 分散共分散ベクトルは,
$$ P_x = [ \begin{array}{cc} var[X_1] & cov[X_1,X_2] \ cov[X_2,X_1] & var[X_2] \end{array} ] = [ \begin{array}{cc} \sigma_1^2 & \sigma_1\sigma_2 \ \sigma_1\sigma_2 & \sigma_2^2 \end{array} ] $$
で表すことができる.
よって, $X_1,X_2$にそれぞれ平均0分散1,平均1分散2の標準正規分布を用いると考えると,
$$ \bar{x}=[0, 1] $$
$$ P_x = [ \begin{array}{cc} 1 & 2 \ 2 & 4 \end{array} ] $$ となる.
これを入力として用いる.出力は1次元とし$f(x)=x[0]*x[1]$の非線形変換を考える.
# Number of dimensions of x
n = 2
# Number of dimensions of y
m = 1
# Mean and Variance of x
x_mean = np.array([0, 1])
x_P = np.array([[1,2],[2,4]])
print("Mean",x_mean)
print("Variance",x_P)
# Nonlinear transformation of x
def f(x):
return [x[0]*x[1]]
シグマ点の計算
$$ \sigma_0 = \bar{x} \tag{1} $$
$$ \sigma_i = \bar{x}+(\sqrt{(n+\lambda)}P_x) \tag{2} $$
$$ \sigma_i = \bar{x}-(\sqrt{(n+\lambda)}P_x) \tag{3} $$
ここで,$\lambda$は以下のように計算される.
$$ \lambda = \alpha^2*(n+\kappa)-n \tag{4} $$
ここで,$\alpha$と$\kappa$はハイパーパラメータである.
$\alpha$:0-1のスカラー値として指定され,平均の状態値の周りのシグマポイントの広がりを決定する.通常、小さい正の数値をとる.シグマポイントの広がりは$\alpha$に比例し,値が小さいほどシグマポイントは平均の状態に近くなる.
$\kappa$:通常は0に設定される.値が小さいほどシグマポイントは平均の状態に近くなり,広がりは$\kappa$の平方根に比例する.
# Parameter
alpha = 0.5
kappa = 0
# 式4
lambd=alpha**2*(n+kappa)-n
print(lambd)
# Sampling sigma points
sigma = np.zeros((n, 2*n+1))
# 式1
sigma[:,0] = x_mean
gamma=math.sqrt(n+lamda)
# 式2
for i in range(n):
sigma[:,i+1] = x_mean + gamma * x_P[:,i]
# 式3
for i in range(n):
sigma[:,i+n+1] =x_mean - gamma * x_P[:,i]
print("selected sigma point")
for i in range(2*n+1):
print(sigma[:,i])
変換
非線形変換により$y_{\sigma}$をもとめる.
$$ y_{\sigma}=f(\sigma) \tag{5} $$
重み関数$w_i$を求める.
$$ w_0=\frac{\lambda}{n+\lambda} \tag{6} $$
$$ w_i=\frac{1}{2(n+\lambda)} \tag{7} $$
$y$の平均と分散は以下のように計算できる.
$$ \bar{y} \simeq \sum_{i=0}^{2n}w_iy_{\sigma i} \tag{8} $$
$$ P_y \simeq \sum_{i=0}^{2n} w_i (y_{\sigma i}-\bar{y})*(y_{\sigma i}-\bar{y})^T \tag{9} $$
sigma_y = np.zeros((m, 2*n+1))
# Non-linear transformation 式5
for i in range(len(sigma)):
sigma_y[:,i] = f(sigma[:,i])
w = np.zeros((1, 2*n+1))
# Weight function calculation 式6,7
w[:,0] = lambd/(n+lambd)
for i in range(2*n):
w[:,i+1] = 1/(2*(n+lambd))
print("重み関数", w)
y_mean=0
# mean of y 式8
for i in range(2*n+1):
y_mean += w[:,i]*sigma_y[:,i]
y_P = 0
# variance of y 式9
for i in range(2*n+1):
y_P += w[:,i]*(sigma_y[:,i]-y_mean)*(sigma_y[:,i]-y_mean).T
print(y_mean)
print(y_P)