MXNet で iris を分類

MXNet を使って、階層型ニューラルネットによる iris の分類を試してみました。

ソースは http://github.com/fits/try_samples/tree/master/blog/20170821/

準備

MXNet は様々なプログラミング言語用の Docker イメージを提供しているので、今回は Python 用の mxnet/python をそのまま使います。

Docker イメージの pull
$ docker pull mxnet/python

(1) iris データセット

Keras で iris を分類」 では sklearn の iris データセットを使いましたが、今回は numpy を使って iris.data ファイルを処理してデータセットを作成します。

iris データセット (iris.data) は https://archive.ics.uci.edu/ml/datasets/iris からダウンロードし配置(今回は /vagrant/work)しておきます。

品種(Iris-setosa 等)は質的データ(カテゴリカルデータ)ですが、このままだと MXNet では扱えなさそうだったので数値化するようにしました。

numpy で該当するような関数が見当たらなかったので、品種をリスト化して該当する要素のインデックスを取得する方法で数値化するようにしてみました。 (map 処理も無さそうだったので vectorize で代用しています)

iris データセットの作成処理
import numpy as np

# 品種の数値化(Iris-setosa -> 0, Iris-versicolor -> 1, Iris-virginica -> 2)
def categorical(ds):
    ct = np.unique(ds).tolist()
    return np.vectorize(lambda x: ct.index(x))(ds)

iris = np.loadtxt('iris.data', delimiter = ',', dtype = [
    ('sepal-length', 'f4'), ('sepal-width', 'f4'), 
    ('petal-length', 'f4'), ('petal-width', 'f4'), 
    ('species', 'S15')
])

iris_data = np.c_[
    iris['sepal-length'], 
    iris['sepal-width'], 
    iris['petal-length'], 
    iris['petal-width']
]

iris_label = categorical(iris['species'])

これで iris_data と iris_label は以下のような内容になります。

iris_data の内容
[[ 5.0999999   3.5         1.39999998  0.2       ]
 [ 4.9000001   3.          1.39999998  0.2       ]
 [ 4.69999981  3.20000005  1.29999995  0.2       ]
 [ 4.5999999   3.0999999   1.5         0.2       ]
 ・・・
 ・・・]
iris_label の内容
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]

(2) 学習と評価

それでは iris データセットを学習用と評価用に分割して学習と評価をそれぞれ実行してみます。

MXNet では学習や評価に使うデータは mxnet.io.DataIter で表現し、numpy の ndarray を使う場合は mx.io.NDArrayIter を使えば良さそうです。

学習・評価用のデータ分割してくれるような mxnet.io.DataIter は見当たらなかったので、「Keras で iris を分類」 と同様に random.permutation で 0 ~ 149 の数値をランダムに配置した配列を作成して、学習・評価用にデータ分割しました。

また、MXNet には以下のような API が用意されていますが、今回は Module API の方を使う事にします。

Module API では mx.symFullyConnected 等を使ってニューラルネットの構成を定義して mx.mod.Module でモジュールを作成、fit で学習、score で評価を実施できます。

/vagrant/work/iris_sample.py
import mxnet as mx
import numpy as np

train_test_rate = 0.7

def categorical(ds):
    ct = np.unique(ds).tolist()
    return np.vectorize(lambda x: ct.index(x))(ds)

iris = np.loadtxt('iris.data', delimiter = ',', dtype = [
    ('sepal-length', 'f4'), ('sepal-width', 'f4'), 
    ('petal-length', 'f4'), ('petal-width', 'f4'), 
    ('species', 'S15')
])

iris_data = np.c_[
    iris['sepal-length'], 
    iris['sepal-width'], 
    iris['petal-length'], 
    iris['petal-width']
]

iris_label = categorical(iris['species'])

data_size = len(iris)
train_size = int(data_size * train_test_rate)

perm = np.random.permutation(data_size)
train_perm = perm[0:train_size]
test_perm = perm[train_size:]

# 学習用データセット
train_iter = mx.io.NDArrayIter(iris_data[train_perm], iris_label[train_perm])
# 評価用データセット
test_iter = mx.io.NDArrayIter(iris_data[test_perm], iris_label[test_perm])

# ニューラルネットの構成定義
data = mx.sym.Variable('data')

net = mx.sym.FullyConnected(data = data, name = 'fc1', num_hidden = 5)
net = mx.sym.Activation(data = net, name = 'relu1', act_type = 'relu')

net = mx.sym.FullyConnected(data = net, name = 'fc2', num_hidden = 3)
net = mx.sym.SoftmaxOutput(data = net, name = 'softmax')

# モジュールの作成
mod = mx.mod.Module(net)

# 学習
mod.fit(train_iter, num_epoch = 20)

# 評価
res = mod.score(test_iter, mx.metric.Accuracy())

print(res)

mxnet/python の Docker コンテナを起動して実行します。

実行例
$ docker run -it --rm -v /vagrant/work:/work mxnet/python

・・・# cd /work
・・・# python iris_sample.py
[('accuracy', 0.9555555555555556)]