機械学習に詳しくなりたいブログ

機械学習や数学について勉強した内容を中心に書きます。100%趣味です。記事は数学的に厳密でなかったり誤りを含んでいるかもしれません。ご指摘頂ければ幸いです。

KL展開 分散最大基準

次元を削減する手法であるKL展開を実験してみました。似た手法で、フィッシャーの線形判別がありますが、目的が違います。フィッシャーの線形判別は、いくつかのクラスに分類されるデータを、分離度を保ちながら次元を減らす手法でした。一方KL展開は、データ全体に対してその分布をよく近似する部分空間を求める手法です。イメージは以下のアニメーション。 f:id:opabinia2:20180616143400g:plain 2次元のデータを1次元に減らすとき、どういう部分空間なら元のデータをよりよく表すか。1次元データになったとき、データが密集してしまうと次元削減によって失われた情報が大きく、逆に適度にばらついていれば失われた情報が少ないと判断できそうです。つまり削減後のばらつきが大きいほどよい部分空間であるといえます。(フィッシャーの線形判別(1)で載せたアニメーションと比べるとよりわかりやすいかもしれません。)

名前のままですが、分散最大基準のKL展開では、データ削減後の分散を最大にするような射影を求めることが目的です。削減対象のデータを\mathbf x、このデータの平均ベクトルを\mathbf mとすると、このデータの共分散行列は


\displaystyle \Sigma = \frac{1}{n} \sum_{\mathbf x \in C}(\mathbf x - \mathbf m)(\mathbf x - \mathbf m)^{T} \tag{1}

と書けます。(参考:多変量正規分布に書いた分散共分散行列の定義) ここでCはデータ\mathbf xの集合を表します。求める射影\mathbf Aは、この\Sigmaの固有ベクトルによって定まります。(参考:KL展開 分散最大基準 解の導出D次元データをD^{\prime}次元に削減するなら、固有値の大きいものからD^{\prime}個選び、それに対応する固有ベクトルを並べたものが求める\mathbf Aです。

実験してみた結果です。 f:id:opabinia2:20180616145132p:plain 図中の矢印が求められた射影の方向です。確かに、この方向で射影すれば、削減後の分散は大きくなりそうです。

3次元のデータでも試してみました。 f:id:opabinia2:20180616145311p:plain f:id:opabinia2:20180616145314p:plain 上のグラフが、元のデータです。ちょっといびつな分布にしてみたので、2方向から描いてみました。下のグラフが、2次元、1次元に削減したものです。こちらも良さそうですね。

最後に、小さい固有値に対応する固有ベクトルを採用してみるとどうなるか試してみました。 f:id:opabinia2:20180616145940p:plain 分散が小さくなってしまうことが確認できました。

今回のコードです。

# 分散最大基準のKL展開

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

# データ数
N = 300

# 入力次元数
D = 3

# 削減後の次元数
D2 = 2

# ランダムシードを固定
np.random.seed(0)

# データを作成
mean = np.array([0, 0, 0])
cov = [[1, 0.9, 0.9], [0.9, 1.0, 0.9], [0.9, 0.9, 2.0]]
x = np.random.multivariate_normal(mean, cov, N)

# 平均ベクトルを求める
m = np.array([np.average(x[:, i]) for i in range(D)])

# 共分散行列を求める 理論解は変数cov
s = np.dot((x-m).T, x-m)/N

# 固有値と固有ベクトルを求める
# lam, v = np.linalg.eig(cov) #この記事のグラフは、covの固有ベクトルを使っていた。結果はほとんど変わらない
lam, v = np.linalg.eig(s)

# 最大固有に対応する固有ベクトルが求めるw
v = v[:, np.argsort(lam)[::-1]]
w = v[:, 0:D2]

# 元のデータを表示
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x[:, 0], x[:, 1], x[:, 2])
ax.set_xlim(-5, 5)
ax.set_ylim(-5, 5)
ax.set_zlim(-5, 5)
ax.set_aspect('equal')
plt.show()

# 次元削減
x2 = np.dot(w.T, x.T)

# 削減したデータを表示
plt.xlim(-5, 5)
plt.ylim(-5, 5)
plt.axes().set_aspect('equal')
plt.scatter(x2.T[:, 0], x2.T[:, 1], color="blue", alpha=0.5)
plt.show()