gggggraziegrazie

graizegrazieさんのやったこと、学んだことを記録する雑記帳です

マハラノビス距離

統計学で用いられる、データ点が平均からどれだけ外れているかを判定するための手法。[2]によると、ユークリッド距離を標準偏差で割った値に該当する。

例えば2次元のデータを評価する時、主成分分析と共に用いる。第一主成分をX軸、第二主成分をY軸とした時、マハラノビス距離は主成分上での距離を指す。マハラノビス距離の算出式は、

{
\sqrt{(\vec{x} - \mu)^T \Sigma^{-1} (\vec{x} - \mu)}
}

である。つまり分散が大きいと距離が小さくなり、分散が小さくなると距離が大きくなる。これにより、外れ値の判定に用いられたりする。

参考までに、下記にマハラノビス距離を用いて乱数データの異常値検知をするサンプルコードを作成しました。


Probabilistic Robotics - Chap.3 Gaussian Filters

3.2 The Kalman Filter

カルマンフィルタは線形ガウスシステムでフィルタリング(つまり余計なものを省くプロセス[1])と予測を行うために開発された。カルマンフィルタは、連続状態空間でのbeliefを実装する。離散状態空間やハイブリッドな空間には対応しない。

時刻{t}でのbelief{\  bel(x_t)}は、ベイズフィルタがマルコフ過程に加えて下記3点を満たす時、平均{\mu_t}と共分散{\sigma_t}ガウス分布で表せる。

【1点目】
状態遷移確率{p(x_t | u_t, x_{t-1})}が線形関数で表わせ、状態がガウスノイズを加味した下記の式で表せる。
{x_t = A_t x_{t-1} + B_t u_t + \epsilon_t \tag{1}}
ここで、{x_t、x_{t-1}}状態ベクトル{u_t}は制御ベクトルを、それぞれ表す。また{A_t}{n \times n}の正方行列で、{n}状態ベクトルの次元を示す。そして{B_t}{n \times m}の行列で、{m}は制御ベクトルの次元を示す。このように行列{A_t、B_t}を導入することで、カルマンフィルタは入力である状態と制御の線形関数として状態遷移を表現する。
なお{\epsilon_t}は、状態遷移の不確かさを表現するためのガウスノイズで、平均は{0}、共分散は{R_t}、次元は状態ベクトルと同じ{n}とする。

{(1)}式を多変量ガウス分布の式に当てはめると、平均{A_t x_{t-1} + B_t u_t}で共分散{R_t}である
{
p(x_t | u_t, x_{t-1}) = det(2 \pi R_t)^{- \frac{1}{2}} exp\{- \frac{1}{2} (x_t - A_t x_{t-1} - B_t u_t)^T R^{-1}_t (x_t - A_t x_{t-1} - B_t u_t)\} \tag{2}
}
として表せる。

【2点目】
計測確率{p(z_t | x_t)}も、状態遷移確率と同様で入力に対して線形とする。また計測データは、ガウスノイズを加味した下記の式で表せる。
{z_t = C_t x_t + \delta_t \tag{3}}
ここで{C_t}{k \times n}の行列で、{k}は計測ベクトル{z_t}の次元とする。{\delta_t}は、平均{0}で共分散{Q_t}の計測ノイズである。計測確率は、{(2)}と同様に多変量ガウス分布の形で、
{
p(z_t | x_t) = det(2 \pi Q_t)^{- \frac{1}{2}} exp\{- \frac{1}{2} (z_t - C_t x_t)^T Q^{-1}_t (z_t - C_t x_t)\} \tag{4}
}
として表せる。

【3点目】
最後に、初期belief {bel(x_0)}は、平均{\mu_0}で共分散{\Sigma_0}ガウス分布
{
bel(x_0) = p(x_0) = det(2 \pi \Sigma_0)^{- \frac{1}{2}} exp\{- \frac{1}{2} (x_0 - \mu_0)^T \Sigma^{-1}_0 (x_0 - \mu_0)\} \tag{5}
}
として表せる。

これら3つの仮定は、{bel(x_t)}ガウス分布で表せることを確認するのに十分である。

3.2.2 The Kalman Filter Algorithm

カルマンフィルタの入力と出力は
入力:{平均\mu_{t-1}、共分散\Sigma_{t-1}のbel(x_{t-1})}と制御ベクトル{u_t}、観測ベクトル{z_t}
出力:{平均\mu_t、共分散\Sigma_tのbel(x_t)}
である。アルゴリズムは下記の数式で表せる。

{
Algorithm \  Kalman\_Filter(\mu_{t-1}, \Sigma_{t-1}, u_t, z_t):\\
\  \  \overline{\mu}_t    = A_t \mu_{t-1} + B_t u_t\\
\  \  \overline{\Sigma}_t = A_t \Sigma_{t-1} A^T_t + R_t\\
\\
\  \  K_t = \overline{\Sigma}_t C^T_t (C_t \overline{\Sigma}_t C^T_t + Q_t)^{-1} \\
\  \  \mu_t    = \overline{\mu}_t + K_t (z_t - C_t \overline{\mu}_t)\\
\  \  \Sigma_t = (I - K_t C_t) \overline{\Sigma}_t\\
\  \  return\  \mu_t, \Sigma_t
}
Table3.1 対象が線形な場合のカルマンフィルタアルゴリズム

なお{K_t}はカルマンゲインと呼ばれ、状態遷移時に観測結果をどの程度考慮するかを調節する値である。

Table3.1の2,3行目は制御を加えた結果の予測過程を、4~6行目は観測結果を元に状態を更新する過程を、それぞれ示している。
それぞれの行に対する私の解釈は、

  • 2行目:制御を加えたことによる平均の変化を予測
  • 3行目:共分散{\Sigma_t}は分散の2乗であるから、{A_t}に対する二次形式になっている
  • 5行目:予測した平均に対し、予測からどれだけずれたか、{z_t - C_t \overline{\mu_t}}を考慮して平均を更新
  • 6行目:共分散を求める式なので、{I - K_t C_t}の部分は必ず正になる。よって{0 \  \leq \  (I - K_t C_t)_i \  \leq \  1}とわかる。このことから、共分散を抑えにかかっていると言える。

下記に私がPythonで書いたカルマンフィルタのコードを載せました。参考になれば。


3.3 The Extended Kalman Filter

3.3.1 Why Liearlization?

システムが線形であることは、現実的にはかなり稀である。そのため、本節では非線形なシステムにカルマンフィルタを適用する方法を検討する。今状態{x_t}と観測結果{z_t}がそれぞれ、
{
x_t = g(u_t, x_{t-1}) + \epsilon_t \\
z_t = h(x_t) + \delta_t
}
で表せるものとする。ここで{g、h}非線形関数とする。
上記2式は、線形カルマンフィルタで使った行列{A_t、B_t}{g}で、行列{C_t}{h}にそれぞれ置き換えた式と言える。つまり拡張カルマンフィルタは、基本的には線形カルマンフィルタと同じと言える。ただ一方で、非線形関数を用いることからbeliefはガウス分布の形を成さなくなる。

3.3.2 Linealization Via Taylor Expansion

EKFの重要なアイデアは、線形化である。{g、h}を線形化すると、EKFはLKFと同様にして振る舞う。
線形化には種々の方法があるが、EKFでは1次のテイラー展開[2]を用いる。よって{g、h}はそれぞれ、

{
g(u_t, x_{t-1}) \approx g(u_t, \mu_{t-1}) + g^{'}(u_t, \mu_{t-1})(x_{t-1} - \mu_{t-1})\\
\  \  \  \  \  \  \  \  \  \  \  \  \  \  \  \  \  \  = g(u_t, \mu_{t-1}) + G_t (x_{t-1} - \mu_{t-1})\\
\\
h(x_t) \approx h(\overline{\mu_t}) + h^{'}(\overline{\mu_t})(x_t - \overline{\mu_t})\\
\  \  \  \  \  \  \  \  \  = h(\overline{\mu_t}) + H_t (x_t - \overline{\mu_t})
}

と近似できる。この結果を使い、状態遷移確率{p(x_t | u_t, x_{t-1})}と観測確率{p(z_t | x_t)}はそれぞれ、

{
p(x_t | u_t, x_{t-1}) \approx det(2 \pi R_t)^{- \frac{1}{2}} exp \{ - \frac{1}{2} [x_t - g(u_t, \mu_{t-1}) - G_t (x_{t-1} - \mu_{t-1} ]^T R^{-1}_t [x_t - g(u_t, \mu_{t-1}) - G_t (x_{t-1} - \mu_{t-1} ] \} \\
\\
p(z_t | x_t) = det(2 \pi R_t)^{- \frac{1}{2}} exp \{- \frac{1}{2} [ z_t - h(\overline{\mu}_t - H_t (x_t - \overline{\mu}_t) ]^T Q^{-1}_t [ z_t - h(\overline{\mu}_t - H_t (x_t - \overline{\mu}_t) ] \}
}

と表すことができる。

{
Algorithm \  Extended_Kalman\_Filter(\mu_{t-1}, \Sigma_{t-1}, u_t, z_t):\\
\  \  \overline{\mu}_t    = g(u_t, \mu_{t-1})\\
\  \  \overline{\Sigma}_t = G_t \Sigma_{t-1} G^T_t + R_t\\
\\
\  \  K_t = \overline{\Sigma}_t H^T_t (H_t \overline{\Sigma}_t H^T_t + Q_t)^{-1} \\
\  \  \mu_t    = \overline{\mu}_t + K_t (z_t - h(\overline{\mu}_t))\\
\  \  \Sigma_t = (I - K_t H_t) \overline{\Sigma}_t\\
\  \  return\  \mu_t, \Sigma_t
}

3.4 The Unscented Kalman Filter(UKF)

UKFも非線形なシステムに対応するカルマンフィルタの1手法。線形回帰を使うことで、統計的に線形化する。回帰に使うサンプル点として、Sigma Point[3]を設定する。Sigma Pointは、平均を中心とし、かつ共分散行列の主軸に対して線対称になるように配置する。平均
{\mu}・共分散{\Sigma}{n}次元のガウス分布については、{2n+1}個のSigma Pointを配置する。配置時のルールは下記の通り。

{
\begin{align}
\chi^{[0]} &= \mu\\
\chi^{[i]} &= \mu + (\sqrt{(n + \lambda \Sigma})_i \  \  \  for\  \  i = 1,...,n  \\
\chi^{[i]} &= \mu - (\sqrt{(n + \lambda \Sigma})_{i-n} \  \  \  for\  \  i = n+1,...,2n  \\
\end{align}
}

ここで、{\lambda = \alpha^2(n + \kappa) - n}で、{\alpha}{\kappa}{\mu}からどの程度離れるかを決めるスケールパラメータである。

また、各{\chi^{[i]}}は2種類の重み{w^{[i]}_m}{w^{[i]}_c}を持つ。{w^{[i]}_m}は平均を、{w^{[i]}_c}ガウス分布の共分散を、それぞれ計算する際に用いる。なお{w^{[i]}_m}{w^{[i]}_c}は下記の式で求める。

{
\begin{align}
w^{[0]}_m &= \frac{\lambda}{n + \lambda} \\
w^{[0]}_c &= \frac{\lambda}{n + \lambda} + (1 - \alpha^2 + \beta) \\
w^{[i]}_m &= w^{[i]}_c = \frac{1}{2(n + \lambda)} \  \  \  for\  \  i = 1,...,2n.
\end{align}
}

ここで{\beta}は、分布に関する追加情報をエンコードするためのパラメータであり、分布がガウス分布であれば{\beta = 2}が最良となる。
 Sigma Pointは、関数{g}を使って写像される。写像後の平均{\mu^{'}}と共分散{\Sigma^{'}}はそれぞれ、
{
Y^{[i]}  = g(\chi^{[i]}) \\

\displaystyle \mu^{'}    = \sum^{2n}_{i=0} w^{[i]}_m Y^{[i]}\\

\Sigma^{'} = w^{[i]}_c (Y^{[i]} - \mu^{'} ) (Y^{[i]} - \mu^{'} )^T
}
として表せる。

3.4.2 The UKF Alogorithm

UKFとEKFの入出力は同じだが、計算負荷はUFKの方が少し高い。また線形システムでは、UFKとLKFは同じ結果を示す。UKFの利点は、ヤコビアンの計算が不要な点である。ヤコビアンの計算には微分が必要であり、時には計算が難しい。そのためヤコビアンの計算を回避できることは計算上好ましい。なおUKFはパーティクルフィルタに似ているが、パーティクルフィルタはサンプル点をランダムに取るのに対し、UKFではサンプル点の取り方が固定という違いがある。

{
Algorithm\  \  Unscented\_Kalman\_Filter(\mu_{t-1}, \Sigma_{t-1}, u_t, z_t):\\

\  \  \  \chi_{t-1} = (\mu_{t-1}  \  \  \  \mu_{t-1} + \gamma \sqrt{\Sigma_{t-1}} \  \  \  \mu_{t-1} - \gamma \sqrt{\Sigma_{t-1}})\\

\  \  \  \overline{\chi}^{*}_t = g(u_t, \chi_{t-1})\\

\  \  \  \overline{\mu}_t = \sum^{2n}_{i=0} w^{[i]}_m \overline{\chi}^{*[i]}_t \\

\  \  \  \overline{\Sigma}_t = \sum^{2n}_{i=0} w^{[i]}_c (\overline{\chi}^{*[i]}_t - \overline{\mu}_t) (\overline{\chi}^{*[i]}_t - \overline{\mu}_t)^T + R_t \\

\  \  \  \overline{\chi}_t = (\overline{\mu}_t  \  \  \  \overline{\mu}_t + \gamma \sqrt{\overline{\Sigma}_t} \  \  \  \overline{\mu}_t - \gamma \sqrt{\overline{\Sigma}_t})\\

\  \  \  \overline{Z}_t = h( \overline{\chi}_t)\\

\  \  \  \hat{z}_t = \sum^{2n}_{i=0} w^{[i]}_m \overline{Z}^{[i]}_t \\

\  \  \  S_t = \sum^{2n}_{i=0} w^{[i]}_c (\overline{Z}^{[i]}_t - \hat{z}_t) (\overline{Z}^{[i]}_t - \hat{z}_t)^T + Q_t \\

\  \  \  \sum^{x,z}_t = \sum^{2n}_{i=0} w^{[i]}_c (\overline{\chi}^{[i]}_t - \overline{\mu}_t) (\overline{Z}^{[i]}_t - \overline{\mu}_t)^T \\

\  \  \  K_t = \sum^{x,z}_t S^{-1}_t \\

\  \  \  \mu_t = \overline{\mu}_t + K_t ( z_t - \hat{z}_t )\\

\  \  \  \Sigma_t = \overline{\Sigma}_t - K_t S_t K_t^T \\

\  \  \  return \  \  \mu_t, \Sigma_t
}

3.5 The Information Filter(IF)

 LKFと同様に線形なシステムを仮定している。LKFとの主な違いは、ガウス分布の表現方法である。LKFとその派生は、ガウス分布をモーメント(平均と共分散)を使って表している。それに対しIFは、カノニカルな表現方法を使用する(*注意:僕、カノニカルとはどういうことかよくわかっていません!調べている途中です。。)。

 IFとLKFは、状態遷移確率と観測確率それぞれを、LKFと同じ式で求める。またLKFと同様に、予測と更新の2ステップで構成される。IFの入力は、カノニカルなパラメータ{\xi_{t-1}と\Omega_{t-1}、u_t、z_t}である。そして出力は{\xi_tと\Omega_t}である。なお{\xiと\Omega}の定義は下記の通り。
{
\begin{align}
\Omega &= \Sigma^{-1} \\
\xi    &= \Sigma^{-1} \mu
\end{align}
}

IFとLKF・その派生を比較した時、下記のようなメリット・デメリットがある。

  • メリット

LKF・その派生よりも数値的に安定している(安定が曖昧だが、他のことばへの言い換えがわからない。。)
LKF・その派生よりもロボットの実問題へ当てはめやすい

  • デメリット

更新時に状態推定をリカバーする必要がある
状態空間の次元が大きくなると計算コストが膨大になる

Numpyでの行列の扱い方

Numpyで行列を作る場合、下記の3種類の方法がある。

①numpy.array([1, 2], [3, 4])
②numpy.matrix(numpy.array([1, 2], [3, 4]))
③numpy.mat(numpy.array([1, 2], [3, 4]))

上記の挙動について調べたことを下記に示す。

足し算・引き算

import numpy as np

a = np.array([2, 2])
a.shape -> (2, )

b = np.array([[1, 2], [3, 4]])
b.shape -> (2, 2)

a - b   -> array([[1 , 0],
                  [-1, -2]]) 
# なんと計算できちゃいます。aの1行目が2行目にもコピーされ、
# 2行2列の行列として扱われます。

c = np.matrix([[1, 2, 3], [4, 5, 6]])
c.shape -> (2, 3)

a - c   -> error! # 相手のデータタイプがmatrixだと、aとcのデータタイプが異なるからなのか、aは2行2列とは扱わないです。

掛け算

①の方法を採用する時、行列aとbの掛け算はnumpy.dot(a, b)とする必要がある。もしもa * bとすると、それは各要素毎に掛け算を行うことを意味する。②、③の時は、a * bでもnumpy.dot(a, b)のどちらでもよい。以上をコードで表すと下記の様になる。

import numpy as np

a = np.array([[1, 2], [3, 4]])
b = np.array([[4, 3], [2, 1]])

a * b        -> array([[4 , 6]
                       [6 , 4]]) # a * b[0][0] = a[0] * b[0] となっている
np.dot(a, b) -> array([[8 , 5],
                       [20, 13]])

c = np.matrix(np.array([[1, 2], [3, 4]]))
d = np.matrix(np.array([[4, 3], [2, 1]]))

c * d        -> matrix([[8 , 5],
                        [20, 13]])
np.dot(c * d)-> matrix([[8 , 5],
                        [20, 13]])

e = np.mat(np.array([[1, 2], [3, 4]]))
f = np.mat(np.array([[4, 3], [2, 1]]))

e * f        -> matrix([[8 , 5],
                        [20, 13]])
np.dot(e * f)-> matrix([[8 , 5],
                        [20, 13]])

g = np.array([1, 2])
g * a        -> error!
np.dot(a, a) -> error!
# 掛け算の場合、足し算・引き算と異なり、1行目は2行目にコピーされない

RTAB-Mapについてのメモ

最近3DSLAMに興味があり、ROSにも対応したRTAB-Mapをいじってみました。その過程でわかったことなどをメモします。ある程度纏まった量が出てくるまでは箇条書きになりますのでご容赦を(笑)

・Eigen3.3.3には対応せず。3.2.0はOK(RTAB-Map 0.12.5で)

Huawei P9 Liteでアプリによる通知を有効化する方法

先日Huawei P9 Liteに機種変しました。その後Wuderlist等のアプリをインストール・使ったところ、リマインダーの通知が一向に上がってこないという事態に陥りました。調べたところ、P9 Liteのバッテリーマネージャが原因だということがわかりました。本記事では、どうやって通知を有効化すればよいか、設定方法を記載します。

(少なくともP9 Liteでは)、一部のアプリを除き、画面オフ状態やそのアプリが表示されていない時(バックグラウンドでの実行状態)になるとアプリの動作が無効化されるみたいです。そのため、通知を有効化するためには、アプリの動作有効化が必要となります。有効化の方法は単純で、
  「設定」→「詳細設定」→「バッテリーマネージャ」→「保護されたアプリ」
で有効/無効を、タップで切り替えるだけです。

上記「保護されたアプリ」の一覧でwunderlistを有効化することで、リマインダの通知が上がってくるようになりました。参考になれば幸いです。

apt-getとaptitude

はじめに

 Linuxでは、コンパイラやエディタ、ライブラリ等のパッケージのインストールを、コマンドを使ってできます。そのコマンドは、apt-getとaptitudeというコマンドがあります。下記ではそれぞれの違いについて簡単に説明します。

apt-getとaptitudeの違い

 2つのコマンドの違いは、簡単に言えば推奨パッケージを自動的にインストールするかどうかです。推奨パッケージとは、あるパッケージをインストールした時に、必ずしもインストールする必要はないものの、同時にインストールすることを推奨されるパッケージのことです。

apt-get

 apt-getはパッケージのインストールをするためのコマンドです。インストール可能なパッケージの検索はできません。別のコマンドapt-cacheを使います。推奨パッケージは手動でインストール必要があります。

aptitude

 aptitudeはパッケージのインストールと検索(インストール済みかも同時に表示される)などが出来ます。Ubuntuには標準的には入っていません。なのでUbuntu Community的には推奨してないのかもしれません。なお、aptitude自体は内部でapt-getやapt-cacheを使っています。

update-alternativesの使い方

update-alternativesとは

 Linuxを使う時、シンボリックリンクを使う時があります。例えばgccでは、生成したバイナリを動かす環境に応じて、バージョンを使い分ける必要があります。この時、毎回必要なバージョン以外のgccを削除することもできますが、シンボリックリンクを使ってバージョン管理をするのが便利です。lnコマンドを使ってシンボリックリンクを張ることもできますが、管理対象が別のパッケージに依存しており、かつそのパッケージのバージョンも変更しないといけないときは面倒です。update-alternativesコマンドはそうした時に使う、目的とするパッケージとそれに従属するパッケージのシンボリックリンクを、一括で切り替えることができるコマンドです。

f:id:graziegrazie:20151112051352p:plain
Fig.1 update-alternativesコマンドのイメージ

update-alternativesコマンドの使い方

 update-alternativesの主要なオプションは、
・--install
 シンボリックリンクの作成・登録
・--slave
 installオプションで作成したシンボリックリンクに従属するシンボリックリンクの作成・登録
・--set
 使用するシンボリックリンクの設定(コマンド)
・--config
 使用するシンボリックリンクの設定(一覧から選択)
・--display
 現在選択されているシンボリックリンクの内容を表示
の5つがあります。下記ではそれぞれのオプションについて説明します。

--install

 installオプションを使う時は下記の様に記載します。

 sudo update-alternatives --install <作成するシンボリックのパス> <グループ名> <実体へのパス> <優先度>

例えばgccの管理を行う時は、
 作成するシンボリックのパス = /usr/bin/gcc
 グループ名 = gcc
 実体へのパス = /usr/bin/gcc-4.8(管理したいバージョンのパスを設定)
 優先度 = 10(任意の数字)
とします。

--slave

 slaveオプションを使う時は、下記の様にinstallオプションを登録する時に、関連するシンボリックリンクを全て記載して使います。

 sudo update-alternatives --install <作成するシンボリックのパス> <グループ名> <実体へのパス> <優先度>\
--slave <作成するシンボリックのパス> <グループ名> <実体へのパス>\
            ・・・・・・・・
--slave <作成するシンボリックのパス> <グループ名> <実体へのパス>

例えばgccと連動してg++のバージョンを変更したい場合は、
 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 10\
--slave /usr/bin/gcc g++ /usr/bin/g++-4.8
として実行する。

--set

 コマンドライン上でグループ名をタイプした時、呼び出される実体が、グループの中のどれを指すか、下記の様にコマンドラインで指定する。

 sudo update-alternatives --install <グループ名> <実体へのパス>

例えばgccとタイプした時、gccの4.8を使いたい場合、
 sudo update-alternatives --install gcc /usr/bin/gcc-4.8
として実行する。

--config

 --setと同じことを、下記の様に対話形式で行います。
 sudo update-alternatives --install <グループ名>

例えばgccとして4.8, 4.9を登録している場合、

There are 2 programs which provide 'gcc'.

Selection Command
-----------------------------------------------
*+ 1 /usr/bin/gcc-4.8
2 /usr/bin/gcc-4.9

として、選択したいバージョンの番号を入力します。

--display

例えばgccを管理する場合、

 sudo update-alternatives --display gcc
 ruby - auto mode
  リンクは現在 /usr/bin/gcc-4.8 を指しています
 /usr/bin/gcc-4.8 - 優先度 20
 /usr/bin/gcc-4.9 - 優先度 10
 現在の `最適' バージョンはgcc-4.8です。

となります。