【tensorflow】Irisデータセットで線形分離で二値分類

Irisデータセットは特徴量として「萼片の長さ」「萼片の幅」「花びらの長さ」「花びらの幅」が与えられ
ラベルはそれぞれ0:setosa, 1:versicolor, 3:virginicaである.

f:id:umashika5555:20170907025607p:plain
setosa
f:id:umashika5555:20170907025614p:plain
versicolor
f:id:umashika5555:20170907025640p:plain
verginica

このデータセットに対してsetosaとそれ以外の二値分類を行った.
まず使うモジュールをimportし計算グラフを宣言する.

import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets
import tensorflow as tf
sess = tf.Session()

次にIrisデータセットを取得
setosaに1 , それ以外に0を割り当てる.

iris = datasets.load_iris()
binary_target = np.array([1. if x==0[f:id:umashika5555:20170907030429p:plain] else 0. for x in iris.target])
iris_2d = np.array([[x[2],x[3]] for x in iris.data])# 花びらの長さと花びらの幅の2つの値の組をデータとする

バッチサイズ, プレースホルダ, 変数を宣言

batch_size = 20
x1_data = tf.placeholder(shape=[None,1], dtype=tf.float32)
x2_data = tf.placeholder(shape=[None,1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None,1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))

線形モデルを定義

my_mult = tf.matmul(x2_data,A)
my_add = tf.add(my_mult,b)
my_output = tf.subtract(x1_data,my_add)

損失関数を定義

xentropy = tf.nn.sigmoid_cross_entropy_with_logits(logits=my_output,labels=y_target)

最適化関数を定義

my_opt = tf.train.GradientDescentOptimizer(0.05)
train_step = my_opt.minimize(xentropy)

変数を初期化する

init = tf.global_variables_initializer()
sess.run(init)

線形モデルを1000回のイテレーションでトレーニング

for i in range(1000):
    rand_index = np.random.choice(len(iris_2d),size=batch_size)
    rand_x = iris_2d[rand_index]
    rand_x1 = np.array([[x[0]] for x in rand_x])
    rand_x2 = np.array([[x[1]] for x in rand_x])
    rand_y = np.array([[y] for y in binary_target[rand_index]])
    sess.run(train_step,feed_dict={x1_data:rand_x1,
                                  x2_data:rand_x2,
                                  y_target:rand_y})
    if (i+1)%200==0:
        print("step #", str(i+1) + "A=" + str(sess.run(A)) + ", b= " + str(sess.run(b)))

実行結果

step # 200A=[[-2.14318705]], b= [[ 8.48896122]]
step # 400A=[[-3.59071422]], b= [[ 10.84847546]]
step # 600A=[[-4.73809004]], b= [[ 12.31665611]]
step # 800A=[[-5.16311646]], b= [[ 13.66902447]]
step # 1000A=[[-5.86469412]], b= [[ 14.52292442]]

結果をグラフにプロットする.

[[slope]] = sess.run(A)
[[intercept]] = sess.run(b)

x = np.linspace(0,3,num=50)
ablineValues = []
for i in x:
    ablineValues.append(slope*i+intercept)

setosa_x = [a[1] for i,a in enumerate(iris_2d) if binary_target[i] == 1]
setosa_y = [a[0] for i,a in enumerate(iris_2d) if binary_target[i] == 1]
non_setosa_x = [a[1] for i,a in enumerate(iris_2d) if binary_target[i] == 0]
non_setosa_y = [a[0] for i,a in enumerate(iris_2d) if binary_target[i] == 0]
plt.plot(setosa_x,setosa_y,"rx",ms=10, mew=2, label="setosa")
plt.plot(non_setosa_x,non_setosa_y,"ro",label="NOn setosa")
plt.plot(x,ablineValues)
plt.xlim([0.0,2.7])
plt.ylim([0.0,7.1])
plt.suptitle("Linear Separator For I.setosa")
plt.xlabel("Petal Length")
plt.ylabel("Petal Width")
plt.legend(loc="lower right")
plt.show()

f:id:umashika5555:20170907030429p:plain
これは線形分離でx1[None*1] - (x2[None*1]*A[1*1] + b[1*1]) = 0[1*1]という直線の下か上かで種類を判別する方法である.
この場合線形分離可能なモデルなので綺麗に分離できたがsetosa以外を分類しようとすると上手くいかない.
f:id:umashika5555:20170907030811p:plain
versicolorを分類しようとした場合
f:id:umashika5555:20170907030907p:plain
versinicaを分類しようとした場合

線形分離可能なデータに対しては上手く働くが, それ以外のものに関しては上手く行かないことが分かる.
むしろ現実世界では線形分離不可能なもののほうが多いのでそれらに対処していく必要がある.
今後SVMやNNの記事を書いていく.