CNN~mnistを添えて~
はじめに。
こんにちは。
今回で3回目の投稿なんですけど、全然更新できず、気が付けば年を越して春ですね。。。(´;ω;`)
今回はMNISTを用いて機械学習を学んでみますね。
自分も理解を深めるために書いておきたいと思います。
間違いや、認識が違うところが多分、いや結構あると思うので、全部鵜呑みにするのはだめかも。。。( 一一)
それではMNISTについて触れていこうと思います!
MNISTとは
MNISTとは0~9の手書き数字画像のデータベース。
28*28ピクセルのグレースケール画像データ。
MNISTデータ
訓練データ(mnist.train) : 55,000
テストデータ(mnist.test) : 10,000
検証データ(mnist.valid) : 5,000
の計70,000データが詰まっている。
MNISTデータは、手書き数字画像と該当ラベルの2つのパートを持っています。
画像は28*28=784ピクセルの配列をもち、それが55,000もあることから、訓練イメージ mnist,train,imagesは[55000, 784]の配列をもつテンソルとなります。
ラベルは0~9の間数字で、与えられた画像がどの数字であるかを示します。このラベルは、ほとんどの次元が 0 で1つの次元のみ、 1 である形状を持っています。例えば、5 は [0,0,0,0,0,1,0,0,0,0] になります。
よって、訓練ラベル mnist,.train.labels は[55000, 10]の配列を持ちます。
学習の流れ
学習には、CNNを利用して学習させていきます。
CNNについては少し複雑なので以下のサイトなんか見てみるといいかもしれないですね('ω')
学習の流れはこんな感じ・・・
※全結合層に対して、過学習を防ぐためにdropaotを施し、そのあとにSoftmax関数を適用しています。
使用したコード
import os import numpy as np import tensorflow as tf from PIL import Image from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) # 入力 x = tf.placeholder(tf.float32, [None, 784]) # 引数(入力画像、[テンソル数、高さ、幅、チャネル数]) x_img = tf.reshape(x, [-1, 28, 28, 1]) #---畳み込み1層目--- #5×5,32チャンネルのフィルタ定義 f_conv1 = tf.Variable(tf.truncated_normal([5,5,1,32], stddev = 0.1)) #畳み込み&プーリング処理 conv1 = tf.nn.conv2d(x_img, f_conv1, strides = [1,1,1,1], padding='SAME') b_conv1 = tf.Variable(tf.constant(0.1, shape=[32])) h_conv1 = tf.nn.relu(conv1 + b_conv1) h_pool1 = tf.nn.max_pool(h_conv1, ksize=[1,2,2,1], strides = [1,2,2,1], padding='SAME') #---畳み込み2層目--- #1層目と同様 f_conv2 = tf.Variable(tf.truncated_normal([5,5,32,64], stddev=0.1)) conv2 = tf.nn.conv2d(h_pool1, f_conv2, strides=[1,1,1,1], padding='SAME') b_conv2 = tf.Variable(tf.constant(0.1, shape=[64])) h_conv2 = tf.nn.relu(conv2 + b_conv2) h_pool2 = tf.nn.max_pool(h_conv2, ksize=[1,2,2,1], strides=[1,2,2,1], padding = 'SAME') #---全結合層--- #7×7×64チャンネル画像を1次元配列に変換 h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64]) w_fc1 = tf.Variable(tf.truncated_normal([7 * 7 * 64, 1024], stddev = 0.1)) b_fc1 = tf.Variable(tf.constant(0.1, shape=[1024])) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, w_fc1) + b_fc1) #---ドロップアウト--- #dropout率を定義&適用 keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) #---出力層--- w_fc2 = tf.Variable(tf.truncated_normal([1024, 10], stddev=0.1)) b_fc2 = tf.Variable(tf.constant(0.1, shape=[10])) out = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc2) + b_fc2) #---正解ラベル--- y = tf.placeholder(tf.float32, shape=[None, 10]) #---訓練--- #損失関数 loss = tf.reduce_mean(-tf.reduce_sum(y * tf.log(out + 1e-7), axis=[1])) #誤差の最適化(勾配効果法) train_step = tf.train.AdamOptimizer(1e-4).minimize(loss) #---評価--- correct_prediction = tf.equal(tf.argmax(out, 1), tf.argmax(y,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) #---変数初期化--- init = tf.global_variables_initializer() #---学習--- with tf.Session() as sess: sess.run(init) test_images = mnist.test.images test_labels = mnist.test.labels test_images = test_images[:300] test_labels = test_labels[:300] print(test_images.shape, test_labels.shape) saver = tf.train.Saver() for i in range(1200): train_images, train_labels = mnist.train.next_batch(50) feed_dict = {x:train_images, y:train_labels, keep_prob : 0.5} _, l, acc = sess.run([train_step, loss, accuracy], feed_dict=feed_dict) if (i+1) % 100 == 0: print('step: %3d, loss: %.2f, acc: %.2f' % (i + 1, l, acc)) l, acc = sess.run([loss, accuracy], feed_dict = {x: test_images, y:test_labels, keep_prob: 1.0}) print('evaluation of test data : loss : %.2f, acc : %.2f' % (l, acc)) path = saver.save(sess, os.path.join(os.path.dirname('__file__'), 'data', 'regression.ckpt')) print("Saved:", path)
今回の学習に関してはミニバッチ法を採用しています。少ないデータ群をランダムに選択し、学習させています。
上記で述べたようにMNISTの学習データは60,000(訓練データ + 検証データ)件テストデータは10,000件あります。
今回は1200回の学習に対して、バッチサイズを50としています。
実行結果は以下のようになりました。
lossとaccuracyの結果から、しっかり学習できているのが分かります('ω')
学習したデータはckptファイルに保存しています。
+α 自分の手書き文字を認識させてみよう♪
最後は手書き文字の認識です。
学習したデータが自分の文字を認識してくれるか確かめてみます!
今回「8」を書いてそのデータを[28×28]のデータをmnist.pngとして保存します。 自分の文字が認識されるかドキドキです(>_<)。。。
in_sess = tf.InteractiveSession() saver = tf.train.Saver() # check point ckpt = tf.train.get_checkpoint_state('./data') if ckpt: last_model = ckpt.model_checkpoint_path saver.restore(in_sess, last_model) # 予測画像load predict_img = Image.open('./mnist.png').convert('L') predict_img = 1.0 - np.asarray(predict_img, dtype="float32") / 255 predict_img = np.asarray(predict_img, dtype="float32") predict_img = predict_img.reshape((1,784)) plt.imshow(predict_img.reshape(28, 28)) prediction = tf.argmax(out,1) pred = in_sess.run(prediction, feed_dict={x: predict_img, keep_prob: 1.0}) #予測結果 print('prediction:%d' % (pred))
結果は・・・
prediction : 8 としっかり認識されていますね。
補足
anacondaでPILを扱うときにいくつかエラーが発生したので、メモ程度に。。。
この記事と同じ現象が起こったので、、、やっぱりパッケージの依存関係が原因だったみたい。
最後に
今回はMNISTを扱って実際にCNNを実装してみました。まだまだいろんなデータセットがあるみたいなので触れてみたいですね。
中にはこんなデータセットも(・・)。。。
また触れてみたいと思います♪
Tensorflowに触れてみよう
こんにちは。 前回numpyを用いて複数回学習させましたね。今回はTensolflowに触れて前回と同じように学習させて見ましょう。
動作環境
Mac OSX
pyenv
anaconda5.2(Python 3.6)
anacondaにTensorflowの環境がない方はこちらを参照して見てください。
Tensorflow
まあ、そもそもTensorflowってなんだよって話ですよね笑 以下のサイトとか他にも参考資料いっぱい転がってるので見てみるといいかも・・・
では前回のコードをTensorflowを用いて書き直して見ましょう。
#numpyをimport import numpy #tensorflowをimport import tensorflow as tf #重みを定義 w = numpy.array([ [0, 0.3, -0.8], [1, -1.2, 0.5] ]) # tensorflowで重みを定義する w_ = tf.Variable(w, dtype=tf.float32) # 前の層(入力層)からの出力値xを定義する x = numpy.array([ [5], [2], [7] ]) # xのplaceholderを定義する tf_x = tf.placeholder(tf.float32, shape=x.shape) # バイアスを定義する b = numpy.array([ [1.2], [3.4] ]) # tensorflowでバイアスを定義する tf_b = tf.Variable(b, dtype=tf.float32) # 目標値dを定義する d = numpy.array([ [1], [0] ]) # dのplaceholderを定義する tf_d = tf.placeholder(tf.float32, shape=d.shape) # tf_wとtf_xをかけ、さらにtf_bを足してtf_uを求める tf_u = tf.matmul(tf_w, tf_x) + tf_b # 二乗誤差Lossを求める tf_Loss = tf.reduce_sum(tf.square(tf_u - tf_d)) # 訓練コード(勾配降下法) train_op = tf.train.GradientDescentOptimizer(0.0001).minimize(Loss) #Session開始 sess = tf.Session() #変数の初期化 sess.run(tf.global_variavles_initializer()) #100回学習 for i in range(100): #学習の実行 sess.run(train_op, feed_dict = {tf_x : x, tf_d : d}) #誤差の取得 loss = sess.run(Loss, feed_dict = {tf_x, tf_d : d}) #10回に1度誤差を表示 if i % 10 == 0: print(i,":",loss)
前回とは少し結果が異なりますが、しっかり学習できてますね♪
では、さらに効率よく学習を進めるにはどのようにしたらいいのか・・・? そこで扱われるのが活性化関数ですね。 以下のサイトなんかとても分かりやすく乗っているので見てみると良いかも。
今回はその活性化関数の中のReLU関数を用いて学習させようと思います。
重み、バイアスの値も初期値を変えて見ましょう。
import numpy import tensorflow as tf #重みをランダムな値に変更 tf_w = tf.Variable(tf.truncated_normal([2, 3], stddev=0.1), dtype=tf.float32) x = numpy.array([ [5], [2], [7] ]) tf_x = tf.placeholder(tf.float32, shape=x.shape) #バイアスの初期値を0に変更 tf_b = tf.Variable(tf.constant(0, shape=[2], dtype=tf.float32), dtype=tf.float32) d = numpy.array([ [1], [0] ]) tf_d = tf.placeholder(tf.float32, shape=d.shape) #活性化関数 ReLUを使用 tf_u = tf.nn.relu(tf.matmul(tf_w, tf_x) + tf_b) tf_Loss = tf.reduce_sum(tf.square(tf_u - tf_d)) train_op = tf.train.GradientDescentOptimizer(0.0001).minimize(tf_Loss) sess = tf.Session() sess.run(tf.global_variables_initializer()) for i in range(100): sess.run(train_op, feed_dict={tf_x: x, tf_d: d}) loss = sess.run(tf_Loss, feed_dict={tf_x: x, tf_d: d}) if i % 10 == 0: print(i,":",loss)
さっきの結果に比べて、かなりの精度で学習できている✌︎('ω'✌︎ )
ReLU関数を使わないで学習させてみると・・・
学習率は圧倒的に違いますね(-_-)
活性化関数を用いる違いがよく分かりました。。。活性化関数すご・・・
pythonで学ぶ深層学習
初めまして
はじめまして。これが初投稿です。 これから学んだ物を備忘録として残していければな・・・なんて笑
てことで今回はpythonでの深層学習を学んだので載せていこうと思います。 一応Tensorflowまで扱ったので何とかモノになるはず・・・汗
環境設定はanacondaで整えました。真似したい方はこちらを参考にしてください。
ニューラルネットワークの構造について知りたい方はこちらなどを参照してみてください。 ★今回はパーセプトロンと呼ばれるニューラルネットワークの1番小さい単位を取り上げます。
最小モデルの構造
下のような図で表される、小さめの構造のニューラルネットワークの動きを 今回はnumpyを使って確かめてみようと思います。 入力層ユニット数は3,出力層ユニット数は2とします bはバイアス
最小モデルの定義
#numpyをimportする import numpy #重みを定義する w = numpy.array([ [0, 0.3, -0.8], [1, -1.2, 0.5] ]) #前の層(入力層)からの出力値xを定義する x = numpy.array([ [5], [2], [7] ]) #バイアスを定義する b = numpy.array([ [1.2], [3.4] ]) #目標値dを定義する d = numpy.array([ [1], [0] ])
最小モデルを計算して誤差Lossを求める
#前の層からの出力値xに重みwをかけてyを求める y = w.dot(x) #yにバイアスbを足してuとする u = y + b #2乗誤差Lossを計算する Loss = numpy.sum((u - d) ** 2) print("Loss:", Loss)
Lossが 113.29 と出ていたら大丈夫・・・
計算の考え方については上のニューラルネットワークの構造のリンクを見ておいてください🙇♂️
学習させよう♪
学習の流れ
①入力層にデータを入力する
②重み、バイアス、活性化関数などで計算しながら値が伝わる
③出力層から値が出力される
④出力層から出た値(出力値)と目標値から、ある関数を使って誤差を求める
⑤誤差を小さくするように重みやバイアスの値を更新する
⑥以上の操作を繰り返すことにより、出力値を目標値に近づける
学習の基本的な概念
・学習とは →誤差が減るように重みをちょっとだけ増やす、もしくは減らすこと。
・じゃあどうやって、減らすか増やすかを判断するの? 勾配降下法という手法で学習を進めよう!
横軸に重み、縦軸に誤差をとる。 重みを調整することで縦軸の誤差を減らしたい( ・∇・)
1.重みが Wm の場合、「傾き」は正。よって、重みから傾きを引けば左に移動し誤差は減少する。 2.重みが Wn の場合、「傾き」は負。よって、重みから傾きを引けば右に移動し、誤差は減少する。 結局どちらの場合でも、重みから「傾き」を引けば誤差は変動する。また、学習率を用いて学習のスピードを調整し、地道に学習させていく。よって計算は以下のようになる。
w' = w - α*「傾き」 (α:学習率) ※傾き(重みに対する誤差の微分を勾配と呼ぶ)
・学習のゴール →誤差を最小にすること。
1回学習させる
上で定義した最小モデルを1回学習させて見ましょう!
#勾配を定義 gradient = 2 * (u - d) * x.T #学習率を定義 learning_rate = 0.01 #新しく重みを定義 new_w = w - learning_rate * gradient y = new_w.dot(x) u = y + b new_Loss = numpy.sum((u - d) ** 2) print("new_Loss:", new_Loss)
new_Loss が35.527…...となっていればOK! 1回目の時よりもLossの値が減っている=学習している
複数回学習させる
今度は100回学習させて見ましょう♪
learning_rate = 0.0001 #100回学習させる for i in range(100): y = w.dot(x) u = y + b Loss = numpy.sum((u - d) ** 2) #10回に1度表示 if i % 10 == 0: print(i,"回目:",Loss) gradient = 2 * (u - d) * x.T w = w - learning_rate * gradient
しっかりLossが下がっているのが確認できますね。(上の一回分はこの学習に含めていない...)
今回はnumpyを用いて実際に学習の方法と実装について取り上げました。次回は実際にTensorflowを使って学習させようと思います♪