Edited at

TensorflowでCNNしたった_v1


最近こっちに統合してます Qiita >> https://qiita.com/yoyoyo_/items/cd5b859341106c3b52f9 Github >> https://github.com/yoyoyo-yo/DeepLearningMugenKnock

改良版→[ https://qiita.com/nagayosi/items/8845271ddacabac38507 ]

tensorflowで自分のデータセットをCNNに学習させるまでの流れです。

tensorflow-1.2.1 でUbuntu16.04環境(Docker)で試しました。


インストール

導入は簡単。pipでインストールするだけです。ただしtensorflowはCPUのみかGPUありかでインストールするものも変わります。


CPUのみ

pip install tensorflow



GPUあり

pip install tensorflow-gpu


ただしGPUバージョンではCUDAとCUDNNのバージョンに敏感です。つまり、tensorflowがバージョンにあっているものでないとimportしてもエラー吐きまくります。

【tensorflowのバージョンを指定してインストールする方法】

公式 「 https://www.tensorflow.org/install/ 」 によると、こんな感じでできます。

例えばpython2.7でtensorflow1.5をインストールする時です。


CPUのみ

pip install --upgrade https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.5.0-cp27-none-linux_x86_64.whl

^^^^^^^ ^^^


GPUあり

pip install --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.5.0-cp27-none-linux_x86_64.whl

^^^^^^^ ^^^

もしくは、pipの機能を使って、


CPU

## For Python 2.X

pip install tensorflow==1.5
## For Python 3.X
pip3 install tensorflow==1.5


GPU

## For Python 2.X

pip install tensorflow_gpu==1.5
## For Python 3.X
pip3 install tensorflow_gpu==1.5

とやってもバージョン指定できます。

【注意】

- cp27 の数字はpython2.7の2.7に当たります。これをpython3.5にしたい時はcp35にすればいいだけです。(python3でやる時はpipもpip3になります。)

- -1.5.0- という数字はtensorflowのバージョンです。tensorflow1.3がほしい時はここを-1.3.0にするだけです。


チュートリアル


MNIST (Estimator)

まずはmnistデータを取ります。

MNIST_data はネットからダウンロードしたMNISTのデータを保存するディレクリになります。

from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST_data/', one_hot=False)

そんでtensorflowをインポートします。

import tensorflow as tf

次にネットワーク定義します。これはchainerとかkerasと似た匂いがします。convの特徴マップからdenseにつなげる直前にflatするのはkerasと似てます(kerasがtensorflowのラッパーだから当たり前ですけど…)。

def conv_net(x_dict, n_classes, reuse, is_training):

with tf.variable_scope('ConvNet', reuse=reuse):
x = x_dict['images']
x = tf.reshape(x, shape=[-1, 28, 28, 1])

conv1 = tf.layers.conv2d(x, 32, 5, activation=tf.nn.relu)
pool1 = tf.layers.max_pooling2d(conv1, 2, 2)
conv2 = tf.layers.conv2d(pool1, 64, 3, activation=tf.nn.relu)
pool2 = tf.layers.max_pooling2d(conv2, 2, 2)

flat = tf.contrib.layers.flatten(pool2)

fc1 = tf.layers.dense(flat, 1024)
fc1 = tf.layers.dropout(fc1, rate=0.5, training=is_training)

out = tf.layers.dense(fc1, n_classes)

return out

ここから学習の準備をしていきます。ここでは公式チュートリアルに習ってestimatorというものを使います。これを使うためには学習モデル・テストモデルからoptimizerまで1つの関数でやっています。

def model_fn(features, labels, mode):

logits_train = conv_net(x_dict=features, n_classes=10, reuse=False, is_training=True)
logits_test = conv_net(x_dict=features, n_classes=10, reuse=True, is_training=False)

pred_classes = tf.argmax(logits_test, axis=1)
pred_probs = tf.nn.softmax(logits_test)

if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode, predictions=pred_classes)

loss_op = tf.reduce_mean(
tf.nn.sparse_softmax_cross_entropy_with_logits(
logits=logits_train, labels=tf.cast(labels, dtype=tf.int32)))

optimizer = tf.train.AdamOptimizer(learning_rate=0.001)

acc_op = tf.metrics.accuracy(labels=labels, predictions=pred_classes)

train_op = optimizer.minimize(loss_op, global_step=tf.train.get_global_step())

acc_op = tf.metrics.accuracy(labels=labels, predictions=pred_classes)

estim_specs = tf.estimator.EstimatorSpec(
mode=mode,
predictions=pred_classes,
loss=loss_op,
train_op=train_op,
eval_metric_ops={'accuracy': acc_op}
)

return estim_specs

これでEstimatorインスタンスを作ります。

model = tf.estimator.Estimator(model_fn)

あとは学習の実際のデータとバッチサイズなどを準備します。

input_fn = tf.estimator.inputs.numpy_input_fn(

x={'images': mnist.train.images},
y=mnist.train.labels,
batch_size=32,
num_epochs=None,
shuffle=True
)

これであとは学習させるだけです。

model.train(input_fn, steps=100)

学習が終わったらテストします。

まずはテストデータの用意から。

input_fn = tf.estimator.inputs.numpy_input_fn(

x={'images': mnist.test.images},
y=mnist.test.labels,
batch_size=32,
shuffle=False
)

あとは評価して表示して終わりです。

e = model.evaluate(input_fn)

print('Test Accuracy: {}'.format(e['accuracy']))

以上でチュートリアル終了です。おつです。


自分のデータを学習させる

Estimatorを使わないで、自分で学習させる方法です。


作業ディレクトリを作る

今回はtf-workというディレクトリで作業していきます。

$HOME --- tf-work


import

まずはtensorflowをインポートします。

import tensorflow as tf


placeholderの用意

次にネットワークに使うためのplaceholerを用意します。これは簡単にいえばtensor、つまり行列で、tensorflowを使うための特殊な配列型と思って下さい。

height, width = 28, 28

CLASS = 2

X = tf.placeholder(tf.float32, [None, height * width])
Y = tf.placeholder(tf.float32, [None, CLASS])
keep_prob = tf.placeholder(tf.float32)


データの読み込み

自分で用意した学習データを用意します。

ディレクトリ構造はこんな感じ。

$HOME --- tf-work --- Images --- Train --- 0_img1.jpg

|- 0_img2.jpg
|- 1_img1.jpg
...

画像のファイル名はclass_img.jpgという用に最初にクラスのインデックスを指定して下さい。

次に画像データセットを読み込む関数を作ります。


  • 画像はOpenCVで読み込みます。これはshapeが(height, width, channel)となっていてtensorflowのネットワークのshape(minibatch, height, width, channel)と相性がいいからです。

  • 正解ラベルはファイル名のインデックスをとって作ります。この時、正解ラベルは1-hotベクトルとして作ります。

  • 関数の引数は画像があるディレクトリです。今回は'./Images/Train/'です。

  • 関数から返ってくるのは画像と正解ラベルが入ってリストです。こんな構造になります。[画像のnumpy, 正解のnumpy]

import glob, random, cv2

def load_images(dataset_path, shuffle=True):
filepaths_jpg = glob.glob(dataset_path + '/*.jp*g')
filepaths_png = glob.glob(dataset_path + '/*.png')
filepaths = filepaths_jpg + filepaths_png
filepaths.sort()
dataset = []
imgs = np.zeros((len(filepaths), height, width, 3), dtype=np.float32)
gts = np.zeros((len(filepaths), CLASS), dtype=np.float32)

for i, filepath in enumerate(filepaths):

img = cv2.imread(filepath)
img = cv2.resize(img, (width, height))

x = x / 255.

label = int(filepath.split('/')[-1].split('_')[0])

imgs[i] = x
gts[i, label] = 1.

inds = np.arange(len(filepaths))

if shuffle:
random.shuffle(inds)

imgs = imgs[inds]
gts = gts[inds]

dataset = [imgs, gts]

return dataset

んで、この関数を使ってデータセットを読み取ります。

train_data = load_images('Images/Train/')


ネットワークの定義

ここからネットワークのモデルを定義していきます。まずは簡単に使えるように各層に関する関数を定義します。


Convolution

もともとはVariableなどを別で用意してconv2dに入れる流れですが、ここではその一連を関数化します。

def conv2d(x, ksize=3, in_num=1, out_num=32, strides=1, bias=True):

W = tf.Variable(tf.random_normal([ksize, ksize, in_num, out_num]))
x = tf.nn.conv2d(x, W, strides=[1, strides, strides, 1], padding='SAME')

if bias:
b = tf.Variable(tf.random_normal([out_num]))
x = tf.nn.bias_add(x, b)

return tf.nn.relu(x)


Pooling

def maxpool2d(x, k=2):

return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, k, k, 1], padding='SAME')


Fully-Connected

def fc(x, in_num=100, out_num=100, bias=True):

x = tf.matmul(x, tf.Variable(tf.random_normal([in_num, out_num])))

if bias:
b = tf.Variable(tf.random_normal([out_num]))
x = tf.add(x, b)

return x


ネットワーク全体

次にネットワーク全体を定義します。

def conv_net(x):

x = tf.reshape(x, shape=[-1, height, width, 3])

conv1 = conv2d(x, ksize=5, in_num=1, out_num=32)
pool1 = maxpool2d(conv1, k=2)
conv2 = conv2d(pool1, ksize=5, in_num=32, out_num=64)
pool2 = maxpool2d(conv2, k=2)

mb, h, w, c = pool2.get_shape().as_list()
feature_shape = h * w * c
flat = tf.reshape(pool2, [-1, feature_shape])

fc1 = fc(flat, in_num=feature_shape, out_num=1024)
fc1 = tf.nn.relu(fc1)
fc1 = tf.nn.dropout(fc1, keep_prob=0.5)

out = fc(fc1, in_num=1024, out_num=CLASS)

return out


学習の準備

ここから学習するまでの準備をしていきます。まずはネットワークを作ります。出力にsoftmaxをかけます。

logits = conv_net(X)

prediction = tf.nn.softmax(logits)


lossの定義

loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=Y))


optimizerの定義

optimizer = tf.train.AdamOptimizer(learning_rate=0.001)

train_op = optimizer.minimize(loss_op)

correct_pred = tf.equal(tf.argmax(prediction, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

init = tf.global_variables_initializer()


ミニバッチを作る関数

ミニバッチを作る関数を作成しなければなりません。こんな感じで作ります。

dataはload_images()で読み込んだデータ、batchはこちらで指定するミニバッチサイズです。lastは前回のミニバッチ

def get_batch(data, batch, last):

imgs, gts = data

data_num = len(imgs)
ind = last + batch

if ind < data_num:
img = imgs[last : ind]
gt = gts[last : ind]
last = ind
else:
resi = ind - data_num
img1, gt1 = imgs[last:], gts[last:]
img2, gt2 = imgs[:resi], gts[:resi]
img = np.vstack((img1, img2))
print(gt1.shape, gt2.shape)
gt = np.vstack((gt1, gt2))
last = resi

return img, gt, last


学習させるお

ここから学習を始めます。

Tensorflowではsessionというものを使います。

まずinitをrunにいれてパラメータの初期化を実行します。

ただし、GPUの場合は下のように書きます。

これはGPUのメモリを全部確保しないようにする記述です。絶対に忘れないでください。


CPU

with tf.Session() as tf:

sess.run(init)


GPU使用時の記述


GPU

config = tf.ConfigProto()

config.gpu_options.allow_growth = True
config.gpu_options.visible_device_list="0"
with tf.Session(config=config) as sess:
sess.run(init)

next_minibatchで学習データのミニバッチを取り、sess.run()でfeed-forwardさせて学習させます。今回は1000イテレーション、64ミニバッチにします。

with tf.Session() as sess:

sess.run(init)

last = 0

for step in range(1000):
step += 1
batch_x, batch_y, last = get_batch(train_data, 32, last)

print('Training finished!!')

print(sess.run(accuracy,
feed_dict={X: mnist.test.images[:256], Y: mnist.test.labels[:256], keep_prob: 1.0}))

10ステップ毎に評価する場合は次のようにします。

with tf.Session() as sess:

sess.run(init)
last = 0
for step in range(1000):
step += 1
batch_x, batch_y, last = get_batch(train_data, 64, last)

sess.run(train_op, feed_dict={X: batch_x, Y: batch_y, keep_prob: 0.8})

if step % 10 == 0 or step == 1:
loss, acc = sess.run([loss_op, accuracy],
feed_dict={X: batch_x, Y: batch_y, keep_prob: 1.0})

print('Step: {}, Loss: {}, Accuracy: {}'.format(step, loss, acc))

print('Training finished!!')

print(sess.run(accuracy,
feed_dict={X: mnist.test.images[:256], Y: mnist.test.labels[:256], keep_prob: 1.0}))

以上です。おつかれさまでした。


エラー&警告

1.

I tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA

これをコードに記述

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'