はじめに
やよいちゃんの画像が欲しかったので、なんとか作ってみました。この記事は、その方法を忘れないようにするための私的メモです。
1.やよいちゃんの画像を用意する
先ず、やよいちゃんの画像を用意します。今回は「星彩ステッパー」を踊るやよいちゃんを405枚用意しました。例↓
かわいい。
2.やよいちゃんのシルエットを作る
画像の中でやよいちゃんが映っている場所のシルエットを作ります。私はGIMPで作りました。例↓
かっこいい!
Google Driveにアップロードします。ディレクトリの名前と階層構造は
data ___ img
|_ silhouete
としました。
4.深層学習でニューラルネットワーク(名前:yayoAI)に「やよいちゃん」を学ばせる
今回深層学習に使用したフレームワークはChainerです。Chainerは訓練についての便利な関数が沢山あって私のような素人でも使えるのが嬉しいですね。
それと、Colaboratoryでは無料でGPUを使わせて貰えるのでありがたいですね。
では、プログラムを書いていきます。コピペミスが無ければColaboratoryに貼り付けて貰えれば動くと思います。“ びゅーてふるらいん ” からは程遠いコードはこちら↓
from google.colab import drive
drive.mount('/content/drive')
4-2.データセットの準備をしよう
必要なモジュールをインポートしよう
import os
import sys
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
問題データ画像のパスリストを用意しよう
def get_img_path_list(data_dir_path):
img_dir_path = os.path.join(data_dir_path, "img")
img_name_list = os.listdir(img_dir_path)
img_name_list = sorted(img_name_list)
img_path_list = []
for img_name in img_name_list:
img_path = os.path.join(img_dir_path, img_name)
img_path_list.append(img_path)
return img_path_list
data_dir_path = input("data directory path >>")
img_path_list = get_img_path_list(data_dir_path)
print("data number of a img_path_list : {}".format(len(img_path_list)))
解答データ画像(シルエット)パスリストを用意しよう
def get_silhouette_path_list(data_dir_path):
silhouette_dir_path = os.path.join(data_dir_path, "silhouette")
silhouette_name_list = os.listdir(silhouette_dir_path)
silhouette_name_list = sorted(silhouette_name_list)
silhouette_path_list = []
for silhouette_name in silhouette_name_list:
silhouette_path = os.path.join(silhouette_dir_path, silhouette_name)
silhouette_path_list.append(silhouette_path)
return silhouette_path_list
silhouette_path_list = get_silhouette_path_list(data_dir_path)
print("data number of a silhouette_path_list : {}".format(len(silhouette_path_list)))
二つのリストを見比べてファイル名が同じものの組をつくろう
def get_path_data_set(img_path_list, silhouette_path_list):
path_data_set = []
for img_path in img_path_list:
img_dir_path, img_name = os.path.split(img_path)
for silhouette_path in silhouette_path_list:
silhouette_dir_path, silhouette_name = os.path.split(silhouette_path)
if img_name == silhouette_name:
path_data = (img_path, silhouette_path)
path_data_set.append(path_data)
return path_data_set
path_data_set = get_path_data_set(img_path_list, silhouette_path_list)
print("data number of a path_data_set : {}".format(len(path_data_set)))
つくったパスの組をもとに画像をピックアップしてデータセットをつくろう
def get_img_data_set(path_data_set):
img_data_set = []
for i, path_data in enumerate(path_data_set):
sys.stdout.write("\r Now Loading : {:.2f}% ".format(1/len(path_data_set)))
sys.stdout.flush()
img_path, silhouette_path = path_data
img = np.asarray(Image.open(img_path))
silhouette = np.asarray(Image.open(silhouette_path))
img_data = (img, silhouette)
img_data_set.append(img_data)
print()
return img_data_set
img_data_set = get_img_data_set(path_data_set)
print("data number of a img_data_set : {}".format(len(img_data_set)))
plt.figure()
plt.imshow(img_data_set[0][0])
plt.title('img example')
plt.figure()
plt.imshow(img_data_set[0][1])
plt.title('silhouette example')
4-3.訓練するNeural Networkを定義しよう
いよいよChainerを使います。
必要なモジュールのインポート
import chainer
import chainer.functions as F
import chainer.links as L
from chainer import Chain
Neural Networkを定義しよう
今回はテストなのでテキトウにNetworkを作りました。
class yayo_ai(Chain):
def __init__(self):
super(yayo_ai, self).__init__()
with self.init_scope():
self.l0 = L.Linear(None, 4096)
self.l1 = L.Linear(4096, 4096)
self.l2 = L.Linear(4096, 2)
def __call__(self, x):
f = F.relu(self.l0(x))
f = F.relu(self.l1(f))
y = self.l2(f)
return y
画像認識なのにMLPって。テキトウにも程があるだろ・・・。
4-4.yayoAIを訓練しよう
yayoAIのお勉強のお時間です。
ここからは大体Chainerのチュートリアル通りです。
必要なモジュールをインポートしよう
from chainer import optimizers, iterators, training, serializers
from chainer.training import Trainer, extensions
from chainer.dataset import concat_examples
import random
import cv2
定義したNetworkのインスタンスをつくろう
gpu_id = 0
model = L.Classifier(yayo_ai())
model.to_gpu(gpu_id)
使いたい最適化手法を選択しよう
optimaizer = optimizers.MomentumSGD()
optimaizer.setup(model)
イテレータ―はかってにイテラブルオブジェクトを作ってくれるとっても便利なやつです。
今回は訓練に使われる画像が全部違うようにするのでvalidation用は無くてもいいかなぁって?
batchsize = 16
train_iter = iterators.SerialIterator(img_data_set, batchsize, repeat=True, shuffle=True)
アップデーターを用意しよう
今回は、メモリ節約の為、訓練にデータセットを直接使うのではなく、アップデーターでごにょごにょしてから訓練します。
・ごにょごにょに使う関数たち
解答ラベルとシルエットの色(RGB)を対応付ける
def get_target_color(label):
if label == 0:
target_color = (255,0,0)
else:
target_color = (0,0,255)
return target_color
シルエット画像の指定された色の場所から適当に一ヶ所選ぶ
def view_pointer(img, target_color):
img = cv2.inRange(img, target_color, target_color)
nonzero_index = np.array(np.nonzero(img))
view_point_list = np.transpose(nonzero_index, (1,0))
i = random.randint(0, len(view_point_list) - 1)
view_point = view_point_list[i]
return view_point
yayoAIの網膜に映る像を作る
retina_size = (64, 64)
focal_lenght = 20
def yayo_ai_eye(img, retina_size, view_point, focal_lenght):
img = np.asarray(img)
h, w = retina_size
img_h, img_w, img_ch = img.shape
view_point = np.asarray(view_point)
view_img = np.zeros((h, w, img_ch)).astype("uint8")
for i in range(h):
for j in range(w):
r = np.asarray([i - (h/2), j - (w/2)])
d = np.sqrt(np.dot(r,r.T))
R = r * np.exp(d/focal_lenght) + view_point
k, l = R.astype("int")
if 0 < k < img_h and 0 < l < img_w:
view_img[i][j] = img[k][l]
return view_img
これを使うと霊長類の網膜に映る像に近い画像が作れるそうですよ 。使った例↓
真ん中はよく見えて、全体を把握できる。
・アップデーターで使うコンバーターを用意しよう
これはとっても有益なQiitaの記事「Chainerで画像を読み込む際のTips - Qiita」を参考にしました。
def my_converter(batch, device=None, padding=None):
train_batch = []
for img_data in batch:
label = random.randint(0,1)
img = img_data[0]
silhouette = img_data[1]
target_color = get_target_color(label)
view_point = view_pointer(silhouette, target_color)
view_img = yayo_ai_eye(img, dsize, view_point, magnification_ratio)
img_array = np.asarray(view_img).transpose(2,0,1).astype(np.float32) / 255.
train_batch.append((img_array,label))
return concat_examples(train_batch, device=device, padding=padding)
・イテラブルオブジェクトをアップデーターにセットしよう
updater = training.StandardUpdater(train_iter, optimaizer, device=gpu_id, converter=my_converter)
アップデーターをトレーナーにセットしよう
max_epoch = 40
result_output_dir_path = input("train result output directory path >>")
output_dir_path = os.path.join(result_output_dir_path, "result")
trainer = Trainer(updater, stop_trigger=(max_epoch, 'epoch'), out=output_path)
トレーナーに機能を追加しよう
validationが無いので、いんたーねっとでよく見るものより寂しくなっています。
trainer.extend(extensions.LogReport())
trainer.extend(extensions.PrintReport(['epoch', 'main/loss', 'main/accuracy', 'elapsed_time']))
trainer.extend(extensions.PlotReport(['main/loss'], x_key='epoch', file_name='loss.png'))
trainer.extend(extensions.PlotReport(['main/accuracy'], x_key='epoch', file_name='accuracy.png'))
trainer.extend(extensions.dump_graph('main/loss'))
trainer.extend(extensions.snapshot(filename='snapshot'))
訓練開始!
trainer.run()
Chainerが勝手にかっこいい表を作ってくれるので、ワクワクしながら終わるのを待ちましょう。
訓練したモデルを保存しよう
model_basename = input("model basename >>")
output_model_path = os.path.join(output_dir_path, model_basename, ".model")
serializers.save_npz(output_model_path, model)
これでyayoAIのお勉強はおしまいです。
5.訓練したNeural Network(yayoAI)で推論しよう
yayoAIに勉強の成果を発揮してもらいましょう!
5-1.推論する画像を準備しよう
必要なモジュールのインポート
import sys
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
推論する画像を取得しよう
img_path = input("Please input test img path >>")
img = np.asarray(Image.open(img_path))
plt.imshow(img)
画像のチェックして欲しい場所を指定しよう
def get_view_point_list(img, check_interval_h, check_interval_w):
img_h, img_w = img.shape[:2]
view_point_list = []
for h in range(int(check_interval_h/2), img_h, check_interval_h):
for w in range(int(check_interval_w/2), img_w, check_interval_w):
view_point = (h, w)
view_point_list.append(view_point)
print("view_point = (height, width)")
return view_point_list
check_interval_h = 10
check_interval_w = 10
view_point_list = get_view_point_list(img, check_interval_h, check_interval_w)
print("point number of a view_point_list : {}".format(len(view_point_list)))
指定した各点をyayoAIが見た時の画像を取得しよう
retina_size = (64,64)
focal_lenght = 20
def yayo_ai_eye(img, retina_size, view_point, focal_lenght):
img = np.asarray(img)
h, w = retina_size
img_h, img_w, img_ch = img.shape
view_point = np.asarray(view_point)
view_img = np.zeros((h, w, img_ch)).astype("uint8")
for i in range(h):
for j in range(w):
r = np.asarray([i - (h/2), j - (w/2)])
d = np.sqrt(np.dot(r,r.T))
R = r * np.exp(d/focal_lenght) + view_point
k, l = R.astype("int")
if 0 < k < img_h and 0 < l < img_w:
view_img[i][j] = img[k][l]
return view_img
def get_view_img_list(img, retina_size, view_point_list, focal_lenght):
view_img_list = []
for i, view_point in enumerate(view_point_list):
view_img = yayo_ai_eye(img, retina_size, view_point, focal_lenght)
view_img_list.append(view_img)
sys.stdout.write("\r Now Loading {:.2f}%".format((i+1) / len(view_point_list) * 100))
sys.stdout.flush()
print()
return view_img_list
view_img_list = get_view_img_list(img, retina_size, view_point_list, focal_lenght)
print("img number of a view_img_list : {}".format(len(view_img_list)))
plt.figure()
plt.imshow(view_img_list[int(len(view_img_list)/2 + (192/2))])
plt.title('view_img example')
plt.imsave('view_img example', view_img_list[int(len(view_img_list)/2 + (192/2))])
各点の画像をChainer用の配列にしよう
def get_img_array_list(view_img_list):
img_array_list = []
for view_img in view_img_list:
img_array = np.asarray(view_img).transpose(2,0,1).astype(np.float32) / 255.
img_array_list.append(img_array)
img_array_list = np.asarray(img_array_list)
return img_array_list
img_array_list = get_img_array_list(view_img_list)
print("data number of a img_array_list : {}".format(len(img_array_list)))
5-2. 推論するNeural Networkを準備しよう
必要なモジュールのインポート
import chainer
import chainer.functions as F
import chainer.links as L
from chainer import Chain
from chainer import serializers
Neural Networkを用意しよう
訓練したものと同じ構造のNetworkにしよう。
class yayo_ai(Chain):
def __init__(self):
super(yayo_ai, self).__init__()
with self.init_scope():
self.l0 = L.Linear(None, 4096)
self.l1 = L.Linear(4096, 4096)
self.l2 = L.Linear(4096, 2)
def __call__(self, x):
f = F.relu(self.l0(x))
f = F.relu(self.l1(f))
y = self.l2(f)
return y
model = L.Classifier(yayo_ai())
infer_NN = model
訓練したモデルを読み込もう
infer_NN_model_path = input("infer neural network model path >>")
serializers.load_npz(infer_NN_model_path, infer_NN)
5-3.推論させよう
必要なモジュールのインポート
import chainer.functions as F
from chainer import iterators
import cv2
テスト画像のリストをイテレーターにセットしよう
便利なのでここでも使いました。
test_batchsize = 2048
test_iter = iterators.SerialIterator(img_array_list, test_batchsize, repeat=False, shuffle=False)
推論させよう!
頑張れyayoAI!
各点における確率を予想させます。
pred_probability_list = []
for i in range(0, len(view_point_list) // test_batchsize + 1):
sys.stdout.write("\r Now Loading {:.2f}%".format((i+1) / (len(view_point_list) // test_batchsize + 1) * 100))
sys.stdout.flush()
pred_result_batch = infer_NN.predictor(test_iter.next())
probability_batch = F.softmax(pred_result_batch)
pred_probability_list += list(probability_batch.data)
print()
print("data number of a pred_probability_list : {}".format(len(pred_probability_list)))
確信度別に分けよう
confidence_factor = 0.7
pred_label_list = []
for pred_probability in pred_probability_list:
if pred_probability[1] > confidence_factor:
pred_label_list.append(1)
else:
pred_label_list.append(0)
print("data number of a pred_label_list : {}".format(len(pred_label_list)))
結果を画像で表現しよう
pred_silhouette = np.zeros(img.shape[:2]).astype("uint8")
for pred_label, point in zip(pred_label_list, view_point_list):
h, w = point
if pred_label == 1:
pred_silhouette[h-int(check_interval_h/2):h+int(check_interval_h/2),w-int(check_interval_w/2):w+int(check_interval_w/2)] = 255
yayo_img = np.zeros_like(img)
yayo_img[:,:,0] = cv2.bitwise_and(img[:,:,0], pred_silhouette)
yayo_img[:,:,1] = cv2.bitwise_and(img[:,:,1], pred_silhouette)
yayo_img[:,:,2] = cv2.bitwise_and(img[:,:,2], pred_silhouette)
plt.imshow(yayo_img)
cv2.imwrite("pred_silhouette.png", pred_silhouette) plt.imsave("yayo_img.png", yayo_img)
結果
賢い!
あんなテキトウなNetworkなのに結構いい感じにやよいちゃんだけを切り取ってくれました。
他の結果
おしい!どうやらやよいちゃんの背後に木があると難しいみたいです。茶色と肌色が分かりずらいのかな?
まだまだ精度は悪いですが、とりあえず今回はプログラムが動いてくれたので満足。少しずつでも改良していけたらと思います。
さいごに
深層学習というものを知ってから、ここまで来るのに一年以上経ってしまいました。インターネットを見ると、これくらいのことは数か月や数週間で出来る人達ばかりで、自分の歩みの遅さにへこんでしまいます。ですが、どんなに小さくとも一歩は一歩と思って、進んでいけたらなぁと思っています。
いつか、やよいちゃんと顔を合わせてお話できたらいいなぁ。