ココアのお勉強ブログ

技術職の一般人です。趣味でコード書いたりソフト触ったり。

chainerで画像分類をする

 

Google Colabolatory上でchainerを用いて画像分類をしました。

ランタイムタイプをGPUにします。

準備

Colaboratoryにインストールされているcudaのバージョンを見て、自動的に適切なCuPyをインストールします。

 

!curl https://colab.chainer.org/install | sh -

 

下図のようなmnist_012フォルダを使いました。0、1、2フォルダ内にはそれぞれサイズが28×28で0、1、2が書かれた画像が入っています。

f:id:hotcocoastudy:20190417002202p:plain

zipにしてColaboratoryにアップロードした後unzipします。

 

!unzip mnist_012

 

他に必要そうなものもインストールします。

 

!pip install -U numpy

 

pip install chainercv

 

画像ファイルをデータセットとして読み込む

%matplotlib inline

import matplotlib.pyplot as plt

import numpy as np

import chainer

from PIL import Image

from chainer import cuda, Function, gradient_check, report, training, utils, Variable

from chainer import datasets, iterators, optimizers, serializers

from chainer import Link, Chain, ChainList

from chainercv import transforms

from chainercv.datasets import DirectoryParsingLabelDataset

from chainer import training

from chainer.training import extensions

from chainer import serializers

from numpy import random

 

# mnist_012フォルダから画像ファイルとラベル(mnist_012内のディレクトリ名)を読み込む

dataset = DirectoryParsingLabelDataset('mnist_012')

 

# 各画像のパスを表示

print(dataset.img_paths)

# 各画像のラベルを表示

print(dataset.labels)

 

それぞれ以下のように表示されると思います。

# 各画像のパスを表示

print(dataset.img_paths)

f:id:hotcocoastudy:20190417002218p:plain

# 各画像のラベルを表示

print(dataset.labels)

f:id:hotcocoastudy:20190417002225p:plain

次にimageという名前のリストに画像をnumpy配列にしたものをアペンド(追加)していきます。

 

image = []

for i in dataset.img_paths:

  image.append(np.array(Image.open(i),dtype=np.float32))

- 並び順: (color,x,y)という並び順。今回は28×28で白黒画像(1チャンネル)なので(28,28)。3色であれば、(3,28,28)となる。

- データ型: 0~1のfloat32型で定義しておかないとChainerがエラーを吐く。

 

chainerは学習の際、( [画像(numpy形式) , 正解ラベル] )のような「画像と正解ラベルのリスト」をタプルで並べた形にする必要があります。

 

from sklearn.model_selection import train_test_split

# train(学習用データ)とtest(検証データ)に分割

X_train, X_test, y_train, y_test = train_test_split(image,dataset.labels)

# trainデータを( [画像(numpy形式) , 正解ラベル] )の形にする

train = [(x,y) for x,y in zip(X_train, y_train)]

# testデータを( [画像(numpy形式) , 正解ラベル] )の形にする

test = [(x,y) for x,y in zip(X_test, y_test)]

 

学習

あとはモデルを書いて実行します。

 

import chainer.functions as F

import chainer.links as L

 

class NN(Chain):

    def __init__(self, n_in, n_units, n_out):

        super(NN, self).__init__()

        with self.init_scope():

            self.l1 = L.Linear(n_in, n_units)

            self.l2 = L.Linear(n_units, n_units)

            self.l3 = L.Linear(n_units, n_out)

 

    def __call__(self, x):

        h1 = F.relu(self.l1(x))

        h2 = F.relu(self.l2(h1))

        return self.l3(h2)

 

#############

gpu_device = 0

epoch = 30

batch_size = 512

frequency = -1

n_in = 784 # 28×28=784なので入力を784にする

n_units = 100

n_out = 3 # 今回正解ラベルの種類は3種類なので3にする

#############

 

model = L.Classifier(NN(n_in, n_units, n_out))

chainer.cuda.get_device_from_id(0)

model.to_gpu()

 

optimizer = chainer.optimizers.Adam()

optimizer.setup(model)

 

train_iter = chainer.iterators.SerialIterator(train, batch_size)

test_iter = chainer.iterators.SerialIterator(test, batch_size, repeat=False, shuffle=False)

 

updater = training.StandardUpdater(train_iter, optimizer, device=gpu_device)

trainer = training.Trainer(updater, (epoch, 'epoch'))

 

trainer.extend(extensions.Evaluator(test_iter, model,device=gpu_device))

trainer.extend(extensions.dump_graph('main/loss'))

 

frequency = epoch if frequency == -1 else max(1, frequency)

trainer.extend(extensions.snapshot(), trigger=(frequency, 'epoch'))

trainer.extend(extensions.LogReport())

trainer.extend(

    extensions.PlotReport(['main/loss', 'validation/main/loss'],

                          'epoch', file_name='loss.png'))

trainer.extend(

    extensions.PlotReport(['main/accuracy', 'validation/main/accuracy'],

                          'epoch', file_name='accuracy.png'))

trainer.extend(extensions.PrintReport(

    ['epoch', 'main/loss', 'validation/main/loss',

     'main/accuracy', 'validation/main/accuracy', 'elapsed_time']))

 

trainer.run()

 

最終的に精度が90%以上いくと思います。

 

結果を用いて分類する

実際に分類してみたいと思います。

このモデルの重みをnpz形式で保存します。

 

serializers.save_npz("mymodel.npz", model)

 

保存した重みnpzを読み込みます。

 

test_model = L.Classifier(NN(n_in, n_units, n_out))

serializers.load_npz("mymodel.npz", test_model)

 

test[30][0]に正解ラベルが1の画像があります。これを予測してみます。

 

# (チャンネル数,Widtth,Height)

test_model.predictor(test[30][0].reshape(1,28,28))

 

f:id:hotcocoastudy:20190417002250p:plain

どうやら1と予測されたようです。