tensorflowによる3色分類の実施

ソフトマックス関数で3色の分類を実施してみました。


 

お決まりのモジュールのインポートです。

In [1]:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from numpy.random import multivariate_normal, permutation
import pandas as pd
from pandas import DataFrame, Series

np.random.seed(20170426)
 

平均値-1.5、分散1.3の正規乱数を100個発生させて青のデータセットとします。train_tは[1,0,0]となっています。

平均値0.5、分散0.8の正規乱数を100個発生させて赤のデータセットとします。train_tは[0,1,0]となっています。

平均値3.5、分散1.2の正規乱数を100個発生させて緑のデータセットとします。train_tは[0,0,1]となっています。

permutationを使って順番を入れ替えています。

In [2]:
#青のデータセット
num_train0=100
x0=np.random.normal(-1.5,1.3,num_train0)
t0=np.zeros((num_train0,3))
df0=DataFrame(np.c_[x0,t0],columns=['x','blue','red','green'])
df0['blue']=1

#赤のデータセット
num_train1=100
x1=np.random.normal(0.5,0.8,num_train1)
t1=np.zeros((num_train1,3))
df1=DataFrame(np.c_[x1,t1],columns=['x','blue','red','green'])
df1['red']=1

#緑のデータセット
num_train2=100
x2=np.random.normal(3.5,1.2,num_train2)
t2=np.zeros((num_train2,3))
df2=DataFrame(np.c_[x2,t2],columns=['x','blue','red','green'])
df2['green']=1
#繋ぎあわせて一つのデータフレームにします
df=pd.concat([df0,df1,df2],ignore_index=True)
df=df.reindex(permutation(df.index)).reset_index(drop=True)
#データフレームから行列変換します。train_tはOne-Hot表現にしています。
train_x=df['x'].as_matrix().reshape([len(df),1])
train_t=df[['blue','red','green']].as_matrix().reshape([len(df),3])
 

冗長ですが散布図を描いてみました。縦軸に意味はありません。

値の小さい方から青→赤→緑となっていますが、その境目は渾然としています。

この境界をソフトマックス関数で決定しようというのが狙いです。

In [3]:
fig = plt.figure(figsize=(3,3))
subplot = fig.add_subplot(1,1,1)
subplot.set_xlim([-5,5])
subplot.set_ylim([-1,1.5])
red=df[df.red==1]
subplot.scatter(red.x,-0.5*red.red+0.05, color='r', marker='o')
blue=df[df.blue==1]
subplot.scatter(blue.x,-0.5*blue.blue-0.05, color='b', marker='o')
green=df[df.green==1]
subplot.scatter(green.x,-0.5*green.green, color='g', marker='o')
Out[3]:
<matplotlib.collections.PathCollection at 0x6d28150>
 
 

xは300×1の行列です。300はデータセットの標本数です。

青、赤、緑の3つに分類するので、wとw0は3列となります。これは未知数です。

wとw0に適当な数値を仮定してf=x*w+w0という一次関数を計算します。

これにソフトマックス関数を適用することで、仮定したw,w0に対応する確率pを求めます。

プレースホルダーtにはtrain_tが代入されることとなります。

例えば実際に「青」であった場合t=[1 0 0]です。

t*log(p)を計算することにより、仮定したw,w0により「青」と推定された確率p[0]だけが意味を持つこととなります。

これの総和(reduce_sum)が最も良い値となるようにトレーニングを実施します。

In [4]:
x=tf.placeholder(tf.float32,[None,1])
w=tf.Variable(tf.zeros([1,3]))
w0=tf.Variable(tf.zeros([3]))
f=tf.matmul(x,w)+w0
p=tf.nn.softmax(f)

t=tf.placeholder(tf.float32,[None,3])
loss=-tf.reduce_sum(t*tf.log(p))
train_step=tf.train.AdamOptimizer().minimize(loss)
In [5]:
sess=tf.Session()
sess.run(tf.initialize_all_variables())
 

10000回トレーニングを実施します。

テストデータとして-5から5を50分割したtest_xを作成しました。

このテストデータで、青赤緑それぞれの確率を計算して描画します

In [6]:
#10000回トレーニングを実施します。
for _ in range(10000):
    sess.run(train_step,feed_dict={x:train_x, t:train_t})
    
# テストデータとして-5から5を50分割したtest_xを作成します
# このテストデータで、青赤緑それぞれの確率を計算して描画します
test_x=np.linspace(-5,5,50).reshape([50,1])
p_val=sess.run(p,feed_dict={x:test_x})
line=DataFrame(np.c_[test_x,p_val],columns=['x','blue','red','green'])

fig = plt.figure(figsize=(3,3))
subplot = fig.add_subplot(1,1,1)
subplot.set_xlim([-5,5])
subplot.set_ylim([-1,1.5])
subplot.plot(line.x,line.blue,color='b')
subplot.plot(line.x,line.red,color='r')
subplot.plot(line.x,line.green,color='g')

red=df[df.red==1]
subplot.scatter(red.x,-0.5*red.red+0.05, color='r', marker='o')
blue=df[df.blue==1]
subplot.scatter(blue.x,-0.5*blue.blue-0.05, color='b', marker='o')
green=df[df.green==1]
subplot.scatter(green.x,-0.5*green.green, color='g', marker='o')
Out[6]:
<matplotlib.collections.PathCollection at 0x6d22710>
 
 

値の小さいうち、例えば-5から-3位までは青がほぼ100%で、赤や緑は0%です。

実際そのような値の赤点や緑点は存在しません。

赤点が存在し始める(より、やや小さな)-2位から赤の確率が上昇してきます。

同時に青の確率が下降し、概ね-0.2で両者は交差します

値の大きな青もあれば値の小さな赤もあるのですが、その境界は-0.2であったと断言することができるわけです。

同様に赤と緑の境界は+1.8であったと言い切れます。

このようにソフトマックス関数を適用することで渾然としたデータの境界を決定することが可能となります。