パーセプトロンによる分類を実装します。
コーディングする上で参考にした図書は以下です。
「機械学習を理解するための数学のきほん」 立石賢吾著
分類とは
標本データを2値、あるいは多値に分類する手法。教師あり学習の一つ。
パーセプトロンとは
視覚と脳の機能をモデル化したものであり、パターン認識を行う。シンプルなネットワークでありながら学習能力を持つ。1960年代に爆発的なニューラルネットブームを巻き起こしたが、1969年に人工知能学者マービン・ミンスキーらによって線形分離可能なものしか学習できないことが指摘されたことによって下火となった。(Wikipedia)
パーセプトロンとは、複数の入力を受け取り、それぞれの値に重みを掛けて足しあげたものを出力するというモデル。
x1、x2 という変数が存在した場合に、それぞれに対応した重み w1、w2 を設定し、x1*w1 と x2*w2 を加算したものを出力する。
出力されたモデルから、標本データを分類する直線を生成できる。
パーセプトロンは直線しか表現できないため、線形に分離できない標本データに対しては効力をもたない。
パーセプトロンの実装
目的
パーセプトロンモデルの構築。
与えられた標本データを直線で分類することが最終目標
手段
重みベクトル w を発見し、それに対する法線ベクトルを定義する。
重みベクトルwに対する法線ベクトルが、分類の境界となる。
重みベクトルの発見に必要なもの
識別関数 fw(x)
変数xを受け取った時、パラメータwとの内積から正解データを求める関数。
正解データは分類値、変数は入力値なのでどちらも既に明らかとする。
(分類は教師あり学習であり、教師あり学習は標本に対する正解データの存在を前提とするため)
重みベクトルの更新式
現在の重みベクトルと入力値を掛け合わせた時正解データにたどり着けなければ、重みベクトルを更新する必要が生じる。
重みベクトルの更新は、現在の重みベクトルに入力値と正解値の積であるベクトルを加算することで実現できる。
パーセプトロンの実装
流れ
①標本データの用意及び重みベクトルの初期化(更新するので、初期値はなんでもいい)
②識別関数の実装
③重みベクトル更新式の実装
④識別関数と更新式を使用し、モデルに標本データを学習させる
import numpy as np
from matplotlib import pyplot as plt
# ① -----------
# 別途用意したcsvからデータを読み込む
test = np.loadtxt('0225.csv',delimiter=',',skiprows=1)
# 標本データの抽出。元データの2列を読み込み、以後ベクトルとして扱う
train_x = test[:,0:2]
# 正解値の抽出
train_y = test[:,2]
# 重みベクトルの初期化。ベクトルなので、1行2列のデータを設定。
w = np.random.rand(2)
# 分類境界を示す関数の実装
# 重みベクトル自体は境界とはならず、重みベクトルに対する法線ベクトルが境界となる
x_len = np.arange(-3, 3)
def validator(w):
#重みベクトルに対する法線ベクトルの算出方法は、
# w1x1 + w2x2 = 0 が成立するベクトル (x1,x2) を発見すること。
# 移行して、 w2x2 = -w1x1
# x2 = -w1x1 / w2
# よって、 -w1x1/w2 を (x,y)座標における y とする直線が法線ベクトルとなる
x_2 = -w[0] * x_len / w[1]
return [x_len, x_2]
# 標準化関数の実装
mu = train_x.mean()
sigma = train_x.std()
def standardize(x):
return (x - mu) / sigma
# 標本データの標準化。標準化を行うことで、データの平均を0に、分散を1に慣らすことができる
train_x = standardize(train_x)
# ② --------------------
# 識別関数の実装
def f(x):
#現在の重みベクトルと入力ベクトルの内積が正ならば
if np.dot(w,x) >= 0:
return 1
#負ならば
else:
return -1
# ③ --------------------
# 重みベクトル更新式の実装
def wArange(w,x,y):
#現在の重みベクトルと、入力値と正解値のベクトルを加算する
w = w + y * x
return w
# ④ --------------------
# 指定更新を行う
size = 10
count = 0
print('初期状態の重みベクトルw:{}'.format(w))
for _ in range(size):
#標本データと正解データを結合した上で、それぞれを取り出す
for x, y in zip(train_x, train_y):
#標本データを識別した時、現在の重みベクトルでは正解値を導けなかった場合、重みベクトルの更新を行う
if f(x) != y:
#更新の実行
w = wArange(w,x,y)
count += 1
#ログの出力
print('{}回目の更新 現在の重みベクトルw:{}'.format(count, w))
# 標本データの可視化
## 正解値が1のデータを'o'で可視化
plt.plot(train_x[train_y==1, 0],train_x[train_y==1,1],'o')
## 正解値がー1のデータを'o'で可視化
plt.plot(train_x[train_y==-1, 0],train_x[train_y==-1,1],'x')
# 重みベクトルの可視化
plt.plot(w, linestyle='dashed', label='w_vector')
# 法線ベクトルの可視化
plt.plot(validator(w)[0],validator(w)[1],linestyle='solid', label='normal_vector')
plt.legend(loc='upper right')
plt.xlim(-2,2)
plt.ylim(-2,2)
plt.show()
このように、重みベクトルを適切な形に更新したのちに、それに対する法線ベクトルを描画することでデータを分類することができる。
以上。