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

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

フィッシャーの線形判別(3)

フィッシャーの線形判別(2)の続きです。

先回の記事のまとめ

2クラスのデータを次元圧縮する方向\mathbf wは、次の式で求められることがわかりました。


\mathbf w \propto \mathbf S_W^{-1}(\mathbf{m}_2 - \mathbf{m}_1 ) \tag{1}

次元圧縮の実験

実際に式(1)を使って、2次元のデータを1次元に射影してみます。早速結果です。

左が元のデータで、右が射影した後の分布。左の図の黒い矢印は、求めた\mathbf wでの射影方向です。*1 ちゃんと分離できていそうですね。

データの傾向を変えてみても同じく分離できました。

今回のコードです。行列やベクトルを、なるべく数式に合わせて書こうとしたら、妙に煩雑になってしまった。多分もっとスマートな書き方があるんだと思うのですが、、、。reshape(D, 1)とかなんかかっこ悪いですよね。列ベクトルを行ベクトルにするのに転置のTだとうまくいかないみたいで。

フィッシャーの線形判別(4):フィッシャーの線形判別の解の別の求め方
フィッシャーの線形判別(5)多クラス:多クラスの場合のフィッシャーの線形判別

# フィッシャーの線形判別

import matplotlib.pyplot as plt
import numpy as np

# 各クラスのデータ数
N = 300

# 入力次元数
D = 2

# クラス数
K = 2

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

# 2クラス分のデータを作成
mean1 = np.array([0, 2])
mean2 = np.array([0, -2])
cov = [[1.0, -0.7], [-0.7, 1.0]]
x1 = np.random.multivariate_normal(mean1, cov, N).T
x2 = np.random.multivariate_normal(mean2, cov, N).T

# 各クラスの平均ベクトルを求める
m1 = np.array([np.average(x1[0, :]), np.average(x1[1, :])]).reshape(D, 1)
m2 = np.array([np.average(x2[0, :]), np.average(x2[1, :])]).reshape(D, 1)

# クラス内共分散行列を求める
Sw = np.zeros([D, D])
for i in range(N):
    Sw = np.dot((x1[:, i].reshape(D, 1)-m1), (x1[:, i].reshape(D, 1)-m1).T) + \
        np.dot((x2[:, i].reshape(D, 1)-m2), (x2[:, i].reshape(D, 1)-m2).T) + Sw

# wを求め、グラフ表示用に長さを調整
w = np.dot(np.linalg.inv(Sw), (m2-m1))
w = w/np.linalg.norm(w)

plt.scatter(x1[0, :], x1[1, :], color="blue", alpha=0.5)
plt.scatter(x2[0, :], x2[1, :], color="red", alpha=0.5)
# 射影の方向は直線wに対して垂直方向
plt.quiver(0, 0, w[1, 0], -w[0, 0], angles="xy", units="xy", color="black", scale=0.5)
plt.show()

y1 = np.dot(w.T, x1)
y2 = np.dot(w.T, x2)

plt.hist(y1[0], bins=30, color="blue", alpha=0.5)
plt.hist(y2[0], bins=30, color="red", alpha=0.5)
plt.show()

*1:\mathbf wと内積をとったときに射影する方向であり、\mathbf wの方向そのものを図示しているわけではありません。