Qstairs

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

広告

【画像認識・AI】Deep Learning「Chainer」で中間特徴量を画像化する方法

f:id:qstairs:20160601221351j:plain

はじめに

中間特徴量を画像化してみたくなったので、前に紹介した学習モデルを改良しました。

考え方

ネットワーク内の各層の入力と出力は配列になっています。
つまり、適当なサイズで二次元にしてあげれば画像になります。
以降に載せている処理を参考にしていただければ画像化はできます。
OpenCVを使用しています。

学習モデル

self.model = FunctionSetのl3の出力を画像化し、Intermediateフォルダに出力します。
そして、その画像と入力画像のmean_squared_errorをloss値として学習しています。
#この学習方法が良いのかはわかりません。

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

class CNN():
    cuda.get_device(0).use()    # GPUを使用することを前提とする
    def __init__(self):
        if os.path.isdir("Intermediate") != True:
            os.mkdir("Intermediate")

        self.model = FunctionSet(
            conv1 = F.Convolution2D(1,32,3,pad=1),  # 100x100x1 -> 50x50x32
            conv2 = F.Convolution2D(32,32,3,pad=1), # 50x50x32 -> 25x25x32
            conv3 = F.Convolution2D(32,32,3,pad=1), # 25x25x32 -> 13x13x32
            conv4 = F.Convolution2D(32,32,3,pad=1), # 13x13x32 -> 7x7x32 = 1568
            l1 = F.Linear(1568,1000),
            l2 = F.Linear(1000,700),
            l3 = F.Linear(700,400),
            l4 = F.Linear(400,50),
            l5 = F.Linear(50,3),
        ).to_gpu()

    def forward(self, x_data, y_data, resize_data, epoch, train=True):
        x, t, r = Variable(x_data), Variable(y_data), Variable(resize_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)
        h = F.dropout(F.relu(self.model.l2(h)), train=train)
        h = F.relu(self.model.l3(h))

        # 中間特徴出力
        img = cuda.to_cpu(h.data.reshape(5,len(h.data)/5, 20, 20))
        resize_img = cuda.to_cpu(r.data.reshape(5,len(r.data)/5, 20, 20))
        for i in range(len(img)):
            yoko_img =  cv2.hconcat(img[i])
            yoko_resize = cv2.hconcat(resize_img[i])
            com_one_line = cv2.vconcat([yoko_img, yoko_resize])
            if i == 0:
                com_img = com_one_line
            else:
                com_img = cv2.vconcat([com_img, com_one_line])
        #cv2.imshow("Intermediate layer", com_img)
        #cv2.waitKey(1)
        cv2.imwrite("Intermediate/%06d.png"%epoch, com_img*255)
        loss1 = F.mean_squared_error(h, r)

        h = F.relu(self.model.l4(h))
        
        y = self.model.l5(h)

        if train:   # 学習時
            return F.softmax_cross_entropy(y, t)+loss1
        else:
            return y       # 評価時

学習処理

loss = model.forwardで誤差を算出する際に
入力画像をリサイズしたデータを引数に与えています。

# -*- 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 = []    # ラベルデータ格納
resize_train = []   # 画像リサイズデータ格納
for filepath, label in train_list:
    img = cv2.imread(filepath, 0)   # グレースケールで読み込む
    x_train.append(img)
    y_train.append(label)
    resize_img = cv2.resize(img,(20,20))
    resize_train.append(resize_img)

# 学習で使用するsoftmax_cross_entropyは
# 学習データはfloat32,ラベルはint32にする必要がある。
x_train = np.array(x_train).astype(np.float32)
y_train = np.array(y_train).astype(np.int32)
resize_train = np.array(resize_train).astype(np.float32)
# 画像を(学習枚数、チャンネル数、高さ、幅)の4次元に変換する
x_train = x_train.reshape(len(x_train), 1, 100, 100) / 255
resize_train = resize_train.reshape(len(resize_train), 400) / 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]])
        resize_batch = cuda.to_gpu(resize_train[perm[i:i+batchsize]])
        # 初期化
        optimizer.zero_grads()
        # 誤差伝播
        loss = model.forward(x_batch, y_batch, resize_batch, epoch, 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)

画像化イメージ

www.youtube.com



参考記事
qstairs.hatenablog.com

広告