LoginSignup
2
6

More than 5 years have passed since last update.

Cloud9でTensorFlowのチュートリアルで畳み込みニューラルネットワーク(CNN)をやってみた~手書き画像の分類~

Last updated at Posted at 2017-03-27

はじめに

Cloud9でTensorFlowのチュートリアル(初心者のためのMNIST)をやってみた~手書き画像の分類~で、簡単な機械学習を実装してみました。
次はTensorFlowの「Deep MNIST for Experts」をやってみました。専門家向けと書いてあるだけあって、色々な手法が使われていますが、コードを書いていくと理解しやすいです。
ここでは画像を分類する時に実績のある畳み込みニューラルネットワーク(CNN)を実装していきます。

環境

環境は前回と同じです。
Cloud9
Python 2.7.6
Sample Codes : GitHub
環境構築は「クラウド統合開発環境Cloud9でTensorFlowを使う~GetStarted~
TesorFlowの基本的な使い方は「クラウド統合開発環境Cloud9でTensorFlowを使う〜使い方の基本〜
を参照

コード

前回と同じで学習処理のコードと手書きデータの予測コードで2つに分けています。
まずは学習処理のコードを見ていきます。

nist_neural_train.py
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf

# Define method
def weight_variable(shape, name):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial, name=name)

def bias_variable(shape, name):
  initial= tf.constant(0.1, shape=shape)
  return tf.Variable(initial, name=name)

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')

# Download gz files to MNIST_data directory
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

# Initializing
sess = tf.InteractiveSession()

x = tf.placeholder(tf.float32, shape=[None, 28*28])
y_ = tf.placeholder(tf.float32, shape=[None, 10])
x_image = tf.reshape(x, [-1, 28, 28, 1])

W_conv1 = weight_variable([5, 5, 1, 32], name="W_conv1")
b_conv1 = bias_variable([32], name="b_conv1")
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

W_conv2 = weight_variable([5, 5, 32, 64], name="W_conv2")
b_conv2 = bias_variable([64], name="b_conv2")
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

W_fc1 = weight_variable([7 * 7 * 64, 1024], name="W_fc1")
b_fc1 = bias_variable([1024], name="b_fc1")
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

W_fc2 = weight_variable([1024, 10], name="W_fc2")
b_fc2 = bias_variable([10], name="b_fc2")
y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2

# Making model
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_conv, y_))

# Training
train_step = tf.train.GradientDescentOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
sess.run(tf.initialize_all_variables())
for i in range(20000):
  batch = mnist.train.next_batch(50)
  if i%100 == 0:
    train_accuracy = accuracy.eval(feed_dict={x:batch[0], y_:batch[1], keep_prob:1.0})
    print("step %d, training accuracy %g" %(i, train_accuracy))
  train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

# Evaluating
#print("test accuracy %g" %accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

# Save train data
saver = tf.train.Saver()
saver.save(sess, 'param/neural.param')

基本的な流れは前回と同じですが、差分を説明していきます。

def weight_variable(shape, name):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial, name=name)

def bias_variable(shape, name):
  initial= tf.constant(0.1, shape=shape)
  return tf.Variable(initial, name=name)

パラメータの初期化処理の定義です。前回は実は0で初期化していたのですが、0でない方がよいようです。ReLUという関数を使っているのですが、これは正の値の時には、傾きが1、負の値の時には0という特殊な関数で、その関係で小さな正の値を初期値に持っていた方がよいようです。

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')

畳み込みニューラルネットワークの名前のもととなっている畳み込み(conv2d)とプーリング(max_pool_2x2)です。詳細な処理については下記で説明します。

畳み込み層

W_conv1 = weight_variable([5, 5, 1, 32], name="W_conv1")
b_conv1 = bias_variable([32], name="b_conv1")
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

畳み込み層です。W_conv1の[5, 5, 1, 32]の内、5*5がパッチを表しています。画像の特徴をとらえるための層で、画像の一部(5*5)を取り出して、画像に対してその一部との一致状況を計算します。そうすることで画像の特徴をとらえることができます。
1というのは入力で、入力する1枚の画像データを表しています。
32というのは出力で、32種類のパッチで画像データの特徴をとらえることになります。

h_conv1のreluというのがReLU関数です。負の値だったら0、正の値だったら傾きが1、つまり、入力の値をそのまま使う関数になっています。傾きが1のため傾きが小さすぎたり0になったりして学習できなくなる状況を防いでいます。

h_pool1はプーリング層といって、画像の2*2の4マスの最大値を1マスにして集約してしまう処理です。画像は元々28*28だったので、14*14の4分の1になります。
画像データは少しずれていても違うデータとなってしまうのですが、プーリングで集約することで、ずれを吸収するのだと思います。(間違っていたらすみません)

W_conv2 = weight_variable([5, 5, 32, 64], name="W_conv2")
b_conv2 = bias_variable([64], name="b_conv2")
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

W_fc1 = weight_variable([7 * 7 * 64, 1024], name="W_fc1")
b_fc1 = bias_variable([1024], name="b_fc1")
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

conv2は上記で説明した畳み込み層をもう一度実施しているだけです。
fc1は、前回と同じ計算しているだけです。
ただしh_pool2_flatのところで、多次元配列を1次元に変換して、fc1の計算をできるようにしています。

ドロップアウト

keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

ドロップアウトといって、keep_probで設定した確率でデータを一部使用しないようにします。そうすることで過学習(学習データでは精度が高いが、テストデータでは精度が低い状況)を防ぐことができます。
処理中にkeep_probの値を設定していますが、学習中は0.5を、テストデータでの検証では1.0(ドロップアウトを利用しない)を使用しています。

テストデータでの検証

#print("test accuracy %g" %accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

テストデータでの検証部分ですが、コメントアウトしています。cloud9ではメモリが足りないようで、ここでエラーになってしまうためです。
上記で説明したドロップアウトのkeep_probは1.0に設定されています。

学習の実行

その後の処理は、前回説明した内容で理解できると思います。
学習を実行するとcloud9はフリーではCPUもメモリも貧弱なためとても時間がかかりました。数時間~半日ぐらいかかります。
パラメータを保存しているため、下記のデータの予測はすぐにできるのですが、学習のやり直しが必要になると大変でした。

自分の手書きデータの予測

前回と同じように自分の手書きデータの予測をやってみたところ50%でした。
本来は精度が上がるはずなのですが、逆に下がっています。。。
手書きデータが1と0のデータなのが悪いのか、手書きデータが10枚だけなので、たまたま外れたのか分かりませんが、いまいちのような気はします。
⇒ 手書きデータの予測には前処理が必要でした。詳細については自分の手書きデータをTensorFlowで予測するの記事を参考にして下さい。

mnist_neural.py
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
import sys
import numpy as np
import parsebmp as pb

# Define method
def weight_variable(shape, name):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial, name=name)

def bias_variable(shape, name):
  initial= tf.constant(0.1, shape=shape)
  return tf.Variable(initial, name=name)

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 whatisit(file, sess):
  print("File name is %s" % file)
  data = pb.parse_bmp(file)

  # Show bmp data
  for i in range(len(data)):
    sys.stdout.write(str(int(data[i])))
    if (i+1) % 28 == 0:
      print("")

  # Predicting
  d = np.array([data])
  x_image = tf.reshape(d, [-1, 28, 28, 1])
  h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
  h_pool1 = max_pool_2x2(h_conv1)
  h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
  h_pool2 = max_pool_2x2(h_conv2)
  h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
  h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
  h_fc1_drop = tf.nn.dropout(h_fc1, 1.0)
  y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
  result = sess.run(y_conv)

  # Show result
  print(result)
  print(np.argmax(result, 1))

if __name__ == "__main__":
  # Restore parameters
  W = tf.Variable(tf.zeros([28*28, 10]), name="W")
  b = tf.Variable(tf.zeros([10]), name="b")
  W_conv1 = weight_variable([5, 5, 1, 32], name="W_conv1")
  b_conv1 = bias_variable([32], name="b_conv1")
  W_conv2 = weight_variable([5, 5, 32, 64], name="W_conv2")
  b_conv2 = bias_variable([64], name="b_conv2")
  W_fc1 = weight_variable([7 * 7 * 64, 1024], name="W_fc1")
  b_fc1 = bias_variable([1024], name="b_fc1")
  W_fc2 = weight_variable([1024, 10], name="W_fc2")
  b_fc2 = bias_variable([10], name="b_fc2")

  sess = tf.InteractiveSession()
  saver = tf.train.Saver()
  saver.restore(sess, 'param/neural.param')

  # My data
  whatisit("My_data/0.bmp", sess)
  whatisit("My_data/1.bmp", sess)
  whatisit("My_data/2.bmp", sess)
  whatisit("My_data/3.bmp", sess)
  whatisit("My_data/4.bmp", sess)
  whatisit("My_data/5.bmp", sess)
  whatisit("My_data/6.bmp", sess)
  whatisit("My_data/7.bmp", sess)
  whatisit("My_data/8.bmp", sess)
  whatisit("My_data/9.bmp", sess)

おわりに

最初は全然理解できませんでしたが、色んな本やWebで勉強したり、コードを実装することで理解が深まりました。
次は自分でネットワークを考えて実装してみたいと思います。

更新履歴

  • 2018/06/12: 手書きデータの予測について追記
  • 2017/03/28: 新規投稿
2
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6