【画像認識・AI】Deep Learning「Chainer」で中間特徴量を画像化する方法
はじめに
中間特徴量を画像化してみたくなったので、前に紹介した学習モデルを改良しました。
考え方
ネットワーク内の各層の入力と出力は配列になっています。
つまり、適当なサイズで二次元にしてあげれば画像になります。
以降に載せている処理を参考にしていただければ画像化はできます。
※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)