Qstairs

現役AIベンチャーCTOの知見、画像認識(人工知能、Deep Learning)を中心とした技術ノウハウをアップしていきます

広告

【画像認識】Deep Learningフレームワーク「Chainer」で自前データで学習する方法

f:id:qstairs:20160601221351j:plain

はじめに

前回は「Chainer」で学習するための下準備として、
学習データのリストファイルを作成する方法について紹介させていただきました。

qstairs.hatenablog.com

ということで、準備は完了したのでいよいよ今回は学習について紹介します。

学習の流れ

学習の流れは大きく分けて以下のようになります。

  1. 学習モデルの設定
  2. 学習データリスト読み込み
  3. 学習データ格納
  4. 学習データ形式変換
  5. 学習
  6. 学習したモデルの出力



ソースコード

学習に必要なソースコードとして
学習モデルと学習処理を載せています。
学習モデルが精度の肝になっていて、
数値や処理を変えることで結果が変わってきます。

GPUを使用することを前提としています。
GPUを使用しない場合は、ソースコード内の「cuda」、「gpu」を削除すれば動くはずです。

  • 学習モデル

※14行目の「784」は入力画像サイズによって変わってきます。今後計算方法を紹介できればと思います。

# -*- coding:utf-8 -*-
from chainer import cuda, Function, FunctionSet, gradient_check, Variable, optimizers, serializers
from chainer.training import extensions
import chainer.functions as F

class CNN():
    cuda.get_device(0).use()    # GPUを使用することを前提とする
    def __init__(self):
        self.model = FunctionSet(
            conv1 = F.Convolution2D(1,32,3,pad=1),
            conv2 = F.Convolution2D(32,32,3,pad=1),
            conv3 = F.Convolution2D(32,16,3,pad=1),
            conv4 = F.Convolution2D(16,16,3,pad=1),
            l1 = F.Linear(784,50),
            l2 = F.Linear(50,3),
        ).to_gpu()

    def forward(self, x_data, y_data, train=True):
        x, t = Variable(x_data), Variable(y_data)
        c = F.max_pooling_2d(F.relu(self.model.conv1(x)), 2)
        c = F.max_pooling_2d(F.relu(self.model.conv2(c)), 2)
        c = F.max_pooling_2d(F.relu(self.model.conv3(c)), 2)
        c = F.max_pooling_2d(F.relu(self.model.conv4(c)), 2)
        h = F.dropout(F.relu(self.model.l1(c)), train=train)
        y = self.model.l2(h)

        if train:   # 学習時
            return F.softmax_cross_entropy(y, t)
        else:
            return y       # 評価時
  • 学習処理

※46行目あたりの画像サイズは用意した画像のサイズに合わせてください。

# -*- coding:utf-8 -*-
import numpy as np
import chainer
from chainer import cuda, Function, FunctionSet, gradient_check, Variable, optimizers, serializers
from chainer.training import extensions
import argparse
import random
import chainer.functions as F
import cv2
import CNN_model as cnn
import time

cuda.get_device(0).use()    # GPUを使用することを前提とする

# 引数指定
parser = argparse.ArgumentParser(description='Train Sample')
parser.add_argument('--train_list', '-train', default='train.txt', type=str, help='Train data list')
parser.add_argument('--test_list', '-test', default='test.txt', type=str, help='Test data list')
parser.add_argument('--epoch', '-e', type=int, default=100, help='Number of epochs to train')
args = parser.parse_args()

# 学習モデル設定
model = cnn.CNN()
optimizer = optimizers.Adam()
optimizer.setup(model.model)

# 学習データリストファイルから一行ずつ読み込む
train_list = []
for line in open(args.train_list):
    pair = line.strip().split()
    train_list.append((pair[0], np.float32(pair[1])))

# 画像データとラベルデータを取得する
x_train = []    # 画像データ格納
y_train = []    # ラベルデータ格納
for filepath, label in train_list:
    img = cv2.imread(filepath, 0)   # グレースケールで読み込む
    x_train.append(img)
    y_train.append(label)

# 学習で使用するsoftmax_cross_entropyは
# 学習データはfloat32,ラベルはint32にする必要がある。
x_train = np.array(x_train).astype(np.float32)
y_train = np.array(y_train).astype(np.int32)
# 画像を(学習枚数、チャンネル数、高さ、幅)の4次元に変換する
x_train = x_train.reshape(len(x_train), 1, 100, 100) / 255

N = len(y_train)

batchsize = 100
datasize = len(x_train)
n_epoch =  args.epoch   # 繰り返し学習回数
# 学習開始
train_start = time.time()
for epoch in range(1, n_epoch+1):
    sum_loss = 0
    perm = np.random.permutation(N) # データセットの順番をシャッフル
    train_start_one = time.time()
    for i in range(0,datasize, batchsize):
        x_batch = cuda.to_gpu(x_train[perm[i:i+batchsize]]) # バッチサイズ分のデータを取り出す
        y_batch = cuda.to_gpu(y_train[perm[i:i+batchsize]])
        # 初期化
        optimizer.zero_grads()
        # 誤差伝播
        loss = model.forward(x_batch, y_batch, train=True)
        # 誤差逆伝播
        loss.backward()
        # ネットワークパラメータ更新
        optimizer.update()

        sum_loss += loss.data
    print "epoch:{} loss={} time:{}".format(epoch, sum_loss / (datasize/batchsize), time.time()-train_start_one)

print "train time:{}".format(time.time()-train_start)
# 学習したモデルを出力する
serializers.save_npz("model_{}".format(n_epoch), model.model)
serializers.save_npz("state_{}".format(n_epoch), optimizer)

実行結果

上記のソースコードを使用し、
自前で用意した以下のような図形データ(円、三角、四角)を学習しました。
f:id:qstairs:20160806114452p:plainf:id:qstairs:20160806114458p:plainf:id:qstairs:20160806114504p:plain

データ数はそれぞれ10,000枚用意しています。

~\chainer>python train.py -train .\chainer_data\train.txt
epoch:1 loss=0.234141930938 time:47.6890001297
epoch:2 loss=0.0120183927938 time:44.8639998436
epoch:3 loss=0.00538328383118 time:45.0660002232
epoch:4 loss=0.00240588164888 time:44.8359999657
epoch:5 loss=0.00354681606404 time:44.9549999237
epoch:6 loss=0.00137812725734 time:44.9100000858
epoch:7 loss=0.0240326672792 time:45.123000145
epoch:8 loss=0.0008278505411 time:44.8499999046
epoch:9 loss=0.00111551373266 time:44.9399998188
epoch:10 loss=0.000555399165023 time:44.9600000381
~略~
epoch:90 loss=0.000580499123316 time:44.5870001316
epoch:91 loss=2.5021530746e-05 time:44.6370000839
epoch:92 loss=5.85371271882e-05 time:44.6289999485
epoch:93 loss=6.34381358395e-05 time:44.7200000286
epoch:94 loss=7.72891962697e-06 time:44.9309999943
epoch:95 loss=0.000185455748579 time:44.9500000477
epoch:96 loss=0.00284633412957 time:44.7589998245
epoch:97 loss=6.4926345658e-05 time:44.8150000572
epoch:98 loss=2.26454394578e-05 time:44.7060000896
epoch:99 loss=0.000476426939713 time:44.9259998798
epoch:100 loss=0.00179152819328 time:44.8580000401
train time:4524.86300015

最後に

今回は学習方法について紹介しました。
次回は学習したモデルを使用して識別する方法を紹介します。

P.S.
実は今日からなんと10連休!
だが、予定はないという...(-_-;
こうなったらDeep Learningとアプリ開発とことんやってやります!



【関連記事】

  • 「Chainer」の構築

qstairs.hatenablog.com

  • 「Chainer」での学習の下準備

qstairs.hatenablog.com

  • 「Chainer」での自前データによる識別

qstairs.hatenablog.com

広告