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

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

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

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

先回求めた以下の式を使って、実際に2次元のデータを1次元に射影してみます。


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

背景の理屈は先回までに書いたので、早速結果です。

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

f:id:opabinia2:20180522000455p:plain データの傾向を変えてみても同じく分離できました。

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

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

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()