TensorFlowで畳み込みニューラルネットワークの認識を可視化する
ディープラーニングというのは非常に精度が高くて素晴らしい技術なんですが、学習結果がブラックボックス化されていて何が行われているのかわからない、という声もあるのは事実。
ある意味どうしようもないんですけど、せめてちょっとくらいは可視化できないものか?
以下の記事を参考に、トライしてみました。
畳み込み層とプーリング層を画像表示してみるてすと - Qiita
ここで紹介するコードは、以下の記事で作ったコードで学習させたものを使っています。
”けものフレンズ”動画顔検出器をディープラーニング+OpenCV物体検出で作る(転移学習&メモリ不足対策): EeePCの軌跡
ただ、教師データは「けもフレ」ではなくて、以下の記事で書いた「お城」を使ってます。
TensorFlowで”日本のお城”を識別させてみた: EeePCの軌跡
では、まずコードを紹介しておきます。
◆cnn_png_56.py
#!/usr/bin/env python
import glob
import os
import sys
import numpy as np
import tensorflow as tf
import cv2
import math
from PIL import Image
path=os.getcwd()+'/analysis/'
file_list=os.listdir(path)
i = 0
label_name = []
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_string('label','label.txt','File name of label')
f = open(FLAGS.label,'r')
for line in f:
line = line.rstrip()
l = line.rstrip()
label_name.append(l)
i = i + 1
NUM_CLASSES = i
IMAGE_SIZE = 56
IMAGE_PIXELS = IMAGE_SIZE*IMAGE_SIZE*3
test_image = []
test_filenm = []
for file in file_list:
test_filenm.append(file)
img = cv2.imread('./analysis/' + file )
img = cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE))
test_image.append(img.flatten().astype(np.float32)/255.0)
test_image = np.asarray(test_image)
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
def save_image(file_name, image_ndarray, cols=8):
# 画像数, 幅, 高さ
count, w, h = image_ndarray.shape
# 縦に画像を配置する数
rows = int((count - 1) / cols) + 1
# 復数の画像を大きな画像に配置し直す
canvas = Image.new("RGB", (w * cols + (cols - 1), h * rows + (rows - 1)), (0x80, 0x80, 0x80))
for i, image in enumerate(image_ndarray):
# 横の配置座標
x_i = int(i % cols)
x = int(x_i * w + x_i * 1)
# 縦の配置座標
y_i = int(i / cols)
y = int(y_i * h + y_i * 1)
out_image = Image.fromarray(np.uint8(image))
canvas.paste(out_image, (x, y))
canvas.save('images/' + file_name, "PNG")
def channels_to_images(channels):
count = channels.shape[2]
images = []
for i in range(count):
image = []
for line in channels:
out_line = [pix[i] for pix in line]
image.append(out_line)
images.append(image)
return np.array(images) * 255
images_placeholder = tf.placeholder("float", shape=(None, IMAGE_PIXELS))
labels_placeholder = tf.placeholder("float", shape=(None, NUM_CLASSES))
keep_prob = tf.placeholder("float")
x_image = tf.reshape(images_placeholder, [-1, 56, 56, 3])
with tf.name_scope('A_conv1') as scope:
W_conv1 = weight_variable([3, 3, 3, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
with tf.name_scope('A_pool1') as scope:
h_pool1 = max_pool_2x2(h_conv1)
with tf.name_scope('A_conv2') as scope:
W_conv2 = weight_variable([3, 3, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
with tf.name_scope('A_pool2') as scope:
h_pool2 = max_pool_2x2(h_conv2)
with tf.name_scope('A_conv3') as scope:
W_conv3 = weight_variable([3, 3, 64, 128])
b_conv3 = bias_variable([128])
h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3)
with tf.name_scope('A_pool3') as scope:
h_pool3 = max_pool_2x2(h_conv3)
with tf.name_scope('fc1') as scope:
W_fc1 = weight_variable([7*7*128, 1024])
b_fc1 = bias_variable([1024])
h_pool3_flat = tf.reshape(h_pool3, [-1, 7*7*128])
h_fc1 = tf.nn.relu(tf.matmul(h_pool3_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
with tf.name_scope('fc2') as scope:
W_fc2 = weight_variable([1024, NUM_CLASSES])
b_fc2 = bias_variable([NUM_CLASSES])
with tf.name_scope('softmax') as scope:
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
with tf.Session() as sess:
saver = tf.train.Saver()
sess.run(tf.global_variables_initializer())
saver.restore(sess, "./model.ckpt")
feed_dict = {images_placeholder : test_image, keep_prob: 1.0}
tag = "test"
h_conv1_result = h_conv1.eval(feed_dict=feed_dict)
for i, result in enumerate(h_conv1_result):
images = channels_to_images(result)
save_image("1_%s_h_conv1_%02d.png" % (tag, i), images)
# プーリング1層
h_pool1_result = h_pool1.eval(feed_dict=feed_dict)
for i, result in enumerate(h_pool1_result):
images = channels_to_images(result)
save_image("2_%s_h_pool1_%02d.png" % (tag, i), images)
h_conv2_result = h_conv2.eval(feed_dict=feed_dict)
for i, result in enumerate(h_conv2_result):
images = channels_to_images(result)
save_image("3_%s_h_conv2_%02d.png" % (tag, i), images)
# プーリング2層
h_pool2_result = h_pool2.eval(feed_dict=feed_dict)
for i, result in enumerate(h_pool2_result):
images = channels_to_images(result)
save_image("3_%s_h_pool1_%02d.png" % (tag, i), images)
h_conv3_result = h_conv3.eval(feed_dict=feed_dict)
for i, result in enumerate(h_conv3_result):
images = channels_to_images(result)
save_image("4_%s_h_conv2_%02d.png" % (tag, i), images)
# プーリング3層
h_pool3_result = h_pool3.eval(feed_dict=feed_dict)
for i, result in enumerate(h_pool3_result):
images = channels_to_images(result)
save_image("5_%s_h_pool3_%02d.png" % (tag, i), images)
実行方法は単純に、
> python cnn_png_56.py
です。
このコードと同じ階層に”analysis”というフォルダを作り、その中の画像を読み込んで可視化を行ってくれます。この辺りの動作は過去の”cnn_app_56.py”と同じ。
ただし、上記コード中の”saver.restore(sess, "./model.ckpt")”の中の”model.ckpt”は、各自の学習済データのファイル名に合わせて変えてください。
これを実行した結果ですが、7種類のお城を判別する学習済データ(56×56版)を読み込み、以下の画像ファイルを読み込ませました。
大坂城ですね。このお城を学習済みデータに通すとどうなるのか?
このコードはConv層+Pool層が3つに結合層2つ、Softmaxが一つという階層になってます。
このうち、最初のConv層を通ってきた画像がこちら。
・・・わかりにくいですけど、なんとなくお城って感じの形が見えます。
Conv層の後の画像とは、要するに3×3のフィルターを通した後の画像が出てきているようです。
ここで浮き出てくるものが、この画像のどの部分の特徴をとらえようとしてるのか・・・という解釈でいいみたいです。
ここが真っ黒だと何も見ていないのと同じことになってしまうみたいですが、一応真っ黒という部分はありませんね(限りなく黒はたくさんありますけど)。
これが最後の3つ目のPool層を通るとどうなるのか?
・・・ますますなんだかわかりませんね。
この層では7×7の画像×128個になってます。なんとなくお城っぽい形をマクロにとらえているようですが・・・
可視化したものの、どう解釈すればいいのかわかりづらいところです。
さて、この岡崎城の画像は誤認識されやすい画像です。
これを通してみると、どうなるんでしょうか?
1層目のConv層ですが、なんというかノイズのような画像が多いですね。あまりお城には見えません。
もしかして、岡崎城の認識率が悪いのは、そのせいかもしれません。
わかったような、わからないような結果ですが、会社ではこのコードを改良して使っています。どのあたりの特徴をとらえて判別しているかが大体わかるため、学習の進度を知るために一応参考で使っています。
もうちょっと見やすくする方法があるといいのですが、今のところこれがましな方のようです。
ディープラーニングを可視化したいと思っている方は、参考になさってください。
![]() |
« AI特集のInterface 12月号を買いました! | トップページ | Windows 10でスタートメニューも通知もタスクマネージャーも”管理者権限で実行”も開かなくなった・・・時にやったこと »
「数値解析系」カテゴリの記事
- 「DXに挑む!はじめの一歩 ~デジタルリテラシーを身につけよう~」にて登壇(2022.12.16)
- Interface 2023年1月号を買った(2022.12.04)
- 2022年まとめ(2022.12.31)
- ロビ君再始動!等(2022.11.13)
- 話題のAI迷彩セーターは本当に物体検出をだませているのか!?(2022.10.29)
« AI特集のInterface 12月号を買いました! | トップページ | Windows 10でスタートメニューも通知もタスクマネージャーも”管理者権限で実行”も開かなくなった・・・時にやったこと »
コメント