【AWS】知識ゼロから理解するRDS超入門
AWSのデータベースサービス「Amazon RDS」を初心者にもわかるように解説します。未経験には難しいMultiAZ構成やレプリケーションは、マスター/スレ...
2018-05-02 16:49 2018-05-29 21:03
ゆざ(@yuzajo_plan)です。
「MNIST実行環境の準備から手書き文字識別までを徹底解説」では、TensorFlowのチュートリアルであるMNIST beginnerを使用して、実際の手書き文字識別を行うプログラムを解説しました。
今回は、そのモデルを少し応用して、隠れ層と活性化関数を加えたニューラルネットワークで手書き文字識別を行っていきます。
「知識ゼロで機械学習・AIを理解するために必要なニューラルネットワークの基礎知識」で解説したモデルをそのままの形で実装していくので、これで基本的なニューラルネットワークをマスターすることができると思います。
「MNIST実行環境の準備から手書き文字識別までを徹底解説」で解説したMNIST beginnerは、下の図のような隠れ層と活性化関数がなく、入力層から処理をされ、直接出力層につながるモデルでした。
このモデルでも手書き文字の簡単な識別を行うには十分でしたが、もっと複雑なデータや画像を予測するためにはやはり隠れ層と活性化関数は必要不可欠です。
そのため、今回は、MNIST beginnerに隠れ層と活性化関数を加えた3層構造のニューラルネットワークのモデルを使用することで、どのような違いが生まれるのか、またはどのように3層構造をプログラムで実現するのかを学べると思います。
また、AnacondaでのTensorFlow環境構築と基礎的な使い方を先に読んでおくと理解が早いかもしれません。
それでは、さっそく見ていきましょう。
今回、使用するニューラルネットワークは、「知識ゼロで機械学習・AIを理解するために必要なニューラルネットワークの基礎知識」で解説した入力層・隠れ層・出力層の3層からなるモデルです。
入力層には、28*28pixelの手書き文字データをピクセルごとにバラバラに分けた計784コのデータが入ります。
そのデータは、それぞれ隠れ層のノード(ニューロン)に入っていき、そこに入ってきた値に重みWをかけ、それらを全て足し合わせ、バイアスbを足した値を隠れ層の値hとして計算します。
そして、hを活性化関数であるシグモイド関数に代入することで隠れ層での処理が終わります。
隠れ層から出力層の処理には、入力層から隠れ層の処理と同様に行われ、最後に出力する値を0-9の確率として出すためにソフトマックス関数に代入して、予測値を算出します。
この予測値と正解ラベルとの誤差を小さくするために、誤差逆伝播法を用いて重みW,バイアスbを更新していきます。
それでは、実際に隠れ層を含む3層のモデルを用いた学習結果を見てもらいましょう。
縦軸は、トレーニング時の予測精度を表し、横軸は試行回数を表しています。
そして、次の図がトレーニング時の誤差を表しています。
前回の2層構造のモデルを使用した結果と比較しても一見あまり違いがみられません。それは、前回のモデルでも十分な精度を実現することができていたからです。
しかし、前回との違いが2点あります。
この2点です。まずは、処理時間です。
前回は、30,000回のトレーニングを終えるまでに約44秒かかったのですが、今回は、すべて終えるまで8分23秒かかりました。(使用したスペックは、MacBook Air (13-inch, Early 2014))
2層と3層で約11倍の処理時間の違いが生じました。これは、ひとえに計算量の違いによるものです。
2層の場合は、予測値を算出する際は行列計算を1回のみ行い、W,bを更新する際も1層分のみの更新(微分計算のこと:「知識ゼロで機械学習・AIを理解するために必要なニューラルネットワークの基礎知識」参照)を行うだけでした。
ですが、3層では予測値を算出する際は行列計算を2回とその間に活性化関数での処理行い、さらに最も計算の負荷が大きいのはW,bの更新で、2層分での微分計算を行う必要があり、ただ2層だから処理も倍ではなく、層が増える毎に指数演算的に増えていくのです。
これが、処理時間の違いが生じる理由であり、10層・100層と階層があるディープラーニングがスペックの高いGPUを用いて処理を行わせる理由です。近年のAIブームも、スペックの高いPCが誕生したことが大きな要因の1つです。
これでは、層を増やすメリットが今の所ありませんよね。
そこで、2つめの違いである30,000回トレーニングをした後の予測精度です。
これが、実際のテスト用データセットを使用して検証した予測精度です。
前回の2層の構造での結果がコチラです。
これで、なぜ処理時間が長くなってしまうのに隠れ層を増やすメリットがあるのかお分かりいただけたのではないでしょうか。
トレーニング時・テスト時ともに隠れ層ありの3像モデルのほうが高い予測成功率を出すことができたのです。
処理時間 | トレーニング時の予測成功確率 | テスト時の予測成功確率 | |
---|---|---|---|
2層モデル/隠れ層なし | 0:44 | 94.0% | 92.2% |
3層モデル/隠れ層あり | 8:23 | 97.0% | 96.6% |
隠れ層と活性化関数が必要な理由は、処理時間は長くなりますがより高い予測精度を叩き出すことができるからです。
下の図を見てください。入力層と出力層のみのモデルで表せる領域には限界があり、本当にほしいモデル、今の場合は、テストデータに対しても100回中100回予測を成功させるモデルです。
これを実現するには、より階層を深くしたり、ノード数を変更する必要があります。そのことにより、よりニューラルネットワークで表現できる領域が広くなり、モデルの精度を高めることができます。
活性化関数が必要な理由も同様で、活性化関数がないと、y=Wx+bの線形表現のみしかできず、表現できるモデルの範囲が狭くなってしまうからです。
今回のケースにおいては、隠れ層のない2層のモデルでも高い精度で予測を行えたため、そこまでの恩恵は受けませんでした。
しかし、これがより複雑なデータ予測、例えば、自動運転や自動翻訳システムの開発には2層では全く太刀打ちできません。
もちろん、隠れ層の3層であれば、自動翻訳ができるモデルが作れるわけではないし、層を増やせば全ての複雑な予測ができるというわけではないですが、より難しくて複雑性の高い予測においてはこの隠れ層がとても大きな役割を果たすのです。
ココでニューラルネットワークを扱う上で、注意して置かなければいけないことがあります。
それは、とにかく試行回数を多く学習させれば、どんどん予測の精度が上がっていくというわけでもないということです。
例えば、今回、バッチを試行回数30,000回で学習を行いましたが、その100倍の300万回の試行を行えばもっと精度の高いモデルができるというわけではありません。
確かにトレーニング用のデータでは、精度が高くなっていくのですが、逆にトレーニング用のデータに特化し過ぎたモデルになってしまい、トレーニングに使用していないテスト用のデータに対しては正しい予測ができなくなってしまうのです。
これを過学習といいます。わかりやすい例としては、正面を向いたある人の顔の写真のみを使ってめちゃくちゃ学習させたモデルがあるとして、そのモデルに少しでも横を向いた写真をインプットすると別人と判断されてしまうのです。
では、次に実際に隠れ層ありの3層ニューラルネットワークを実装したプログラムを解説していきます。
以下のプログラムが、全体のコードです。
# ===========import===========
import tensorflow as tf
import os
from tensorflow.examples.tutorials.mnist import input_data
# GPUの無効化
os.environ['CUDA_VISIBLE_DEVICES'] = ""
def main():
# ===========init===========
image_size = 28*28
output_num = 10
learning_rate = 0.001
loop_num = 30000
batch_size = 100
hidden_size = 625
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
# ===========model===========
with tf.device("/cpu:0"):
x = tf.placeholder(tf.float32, [None, image_size])
y_ = tf.placeholder(tf.float32, [None, output_num])
# ⬇ココだけ違う!(隠れ層を追加)
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
W1 = tf.Variable(tf.random_normal([image_size, hidden_size], mean=0.0, stddev=0.05))
b1 = tf.Variable(tf.zeros([hidden_size]))
h = tf.sigmoid(tf.matmul(x, W1) + b1)
W2 = tf.Variable(tf.random_normal([hidden_size, output_num], mean=0.0, stddev=0.05))
b2 = tf.Variable(tf.zeros([output_num]))
y = tf.nn.softmax(tf.matmul(h, W2) + b2)
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
init = tf.global_variables_initializer()
sess = tf.InteractiveSession()
sess.run(init)
# ===========training===========
for i in range(loop_num):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
if i % 100 == 0:
print("step", i, "train_accuracy:", sess.run(accuracy, feed_dict={x: batch_xs, y_: batch_ys}))
# ===========test===========
print("test_accuracy:", sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
if __name__ == "__main__":
main()
前回の記事を読んでいただいた人には、もうお分かりではないでしょうか。
前回、使用したMNIST beginnerのコードとの違いは破線で囲まれたたった6行だけです。
TensorFlowを使う場合は、層を増やすことはすごく簡単にできます。
まずは、変更する前の前回のコードを見ていきましょう。
# 前回のMNIST beginnerでのコード
W = tf.Variable(tf.zeros([image_size, output_num]))
b = tf.Variable(tf.zeros([output_num]))
y = tf.nn.softmax(tf.matmul(x, W) + b)
image_size=28*28, output_num=10と定義しています。
よって、ノード間の繋がりの強さである重みWは、28*28個の入力層ノードと10個の出力層ノードの間にあるので、合計で重みWは(28*28)*10個必要です。そのため、Wは、(28*28)*10のゼロの入った行列として初期値を定義されています。
バイアスbは、出力層の数だけ必要なので、10個です。そして、予測値yは、入力した値xとWを掛け、全てを足したものにbを足して計算されます。
次に、隠れ層を追加したコードを見ていきましょう。
# ⬇ココだけ違う!(隠れ層を追加)
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
W1 = tf.Variable(tf.random_normal([image_size, hidden_size], mean=0.0, stddev=0.05))
b1 = tf.Variable(tf.zeros([hidden_size]))
h = tf.sigmoid(tf.matmul(x, W1) + b1)
W2 = tf.Variable(tf.random_normal([hidden_size, output_num], mean=0.0, stddev=0.05))
b2 = tf.Variable(tf.zeros([output_num]))
y = tf.nn.softmax(tf.matmul(h, W2) + b2)
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
hidden_size=625としているので、隠れ層のノード数は625個です。これは任意に変更してOKです。
よって、重みW1は、28*28個の入力層ノードと625個の隠れ層ノードの間にあるので、合計で重みW1は(28*28)*625個必要です。そのため、W1は、(28*28)*625の行列として初期値を定義されています。
配列の中には、重みにばらつきを生じされて線形性をなくすために0から1の数字がランダムに入っています。
バイアスb1は、隠れ層の数だけ必要なので、625個です。そして、hは、入力した値xとW1を掛けて、全てを足したものにb1を足してた値をシグモイド関数に代入して計算します。
そして、このhを次は先程のxのように扱います。重みW2は、625個の隠れ層ノードと10個の出力層ノードの間にあるので、625*10の行列として定義されます。同様にb2は、10個です。
よって、yは、hとW2を掛けて、全てを足したものにb1を足しすことで計算できます。
これで、入力層と出力層しかなかった前回のモデルを入力層と隠れ層(+活性化関数)と出力層がある3層構造のニューラルネットワークとなりました。
ここまで、いろいろ解説してきた私ですが、これらをしっかり自分の言葉で説明できるようになったのは記事を書き始めてからでした。
そんな中でプログラミング未経験の私がぶち当たった壁についてまとめておきたいと思います。
きっと、これから機械学習をやろうと思っている人やプログラミングをやろうしている人の助けになるのではないかと期待しています。
これは、はじめてプログラミングを勉強している人がTensorFlowを使って機械学習をやろうとして挫折するポイントです。
TensorFlowについては詳しくは別の記事で解説するつもりですが、ここで少しだけ触れると、例えば、Pythonで1+1を計算して「答えは2です」と表示させるには、
a = 1
b = 1
sum = a + b
print("答えは%sです。"%sum) => 答えは2です。
これだけで済みます。しかし、TensorFlowの場合は、
import tensorflow as tf
a = tf.constant(1)
b = tf.constant(1)
sum = tf.add(a, b)
init = tf.global_variables_initializer()
sess = tf.InteractiveSession()
sess.run(init)
print("答えは%sです。"%sess.run(sum)) => 答えは2です。
こんなに書かなければいけません。ただの1+1で一苦労です。
この壁を乗り越えるには、TensorFlowがどんな処理をやっているのかということを理解するのと実際の動作を一つ一つ表示させて可視化することが大切です。
ニューラルネットワークの要になる誤差逆伝播法による最適化はTensorFlowの最適化関数でびっくりするくらい簡単に実装することができます。
上手くいっている時はそれでも構わないのですが、自分が望むように学習してくれなかった時には手詰まりになってしまいます。
しかも、最適化関数(今回は「GradientDescentOptimizer」)でどんな処理が行われているかはすごく分かりづらくブラックボックスになっています。
そのため、この最適化関数はどんな動きをしているのかを原理に理解して、結果で可視化していってください。
それでも、最適化関数を変えたら上手くいったけど理屈では理解できないといったときもあります。
この「ハイパーパラメータ」は、現役のデータアナリストの人も経験による判断をすることが多いようです。
なので、深く考えすぎで泥沼にはまらないようにも心がけてください!(私はここですごく泥沼にハマりました)
やはり、わかったつもりでも100%原理を理解するのはとても大変です。
しかし、その地盤を固めていないとせっかく積み上げていっても崩れ落ちとします。
コレに関しては、「知識ゼロで機械学習・AIを理解するために必要なニューラルネットワークの基礎知識」にほとんど全てのエッセンスを詰め込んであると思います。
もし、少しでもココで躓いてモヤモヤした部分があるようでしたら、いつでも私(@yuzajo_plan)に連絡してください。どんなに初歩的な質問にも答えます!
ココ乗り越えれば、かなりのチカラがつくと思うので、とにかく妥協せずに頑張ってみてください。
これは、Anacondaにせよ、Pythonにせよ、TensorFlowにせよ、どこに何を入れてどの環境で何を動かすのかが難しいです。
プログラミングができる人からすれば、造作もない事かもしれないですが、プログラミング初心者にはすごく理解しにくいところです。
一番いいのは、近くのプログラミングができる人に教えてもらうことですが、なかなかそう居ないと思います。
なので、とにかく「Python インストール」等と調べて出てきたサイトの解説やコードの意味をしっかりと噛み砕いて少しずつでも理解していくしかありません。
困った人は自分で調べるか、人に聞くかの2択しかないので、出来るだけ早くどちらかの行動を実行しましょう。
これは、プログラミングあるあるかもしれません。世の中には沢山のコードがネット上に転がっています。しかし、この大半はただコピペしても正常に動きません。
だから、コピペして動いてもなんで上手く動いているかの意味がわからないと次に使えるツールにはなりません。
プログラミングとは、本当に勉強しなければいけないことが沢山でしかも常に新しい技術が生まれています。
だから、「自分で学び続けることができるチカラ」はとても大きな武器になるはずです。大変なことも多いですが、その先に楽しさもすごく多いです。一緒に頑張りましょう!
今回は、MNIST beginnerを応用した隠れ層と活性化関数を含むニューラルネットワークを用いたモデルについて解説しました。
まとめると、
正直、今までの記事「プログラミング未経験の私がPythonの機械学習で手書き文字の識別を行うまで」(前半・後半)を理解していれば、すんなり理解することができる内容だったと思います。
ここまで、できれば機械学習の基本を習得したと言っても過言ではありません。
実際にこの技術を使って色んなことを試してみるとより今までの知識が自分の血となり肉となると思うので、頑張ってみて下さい。
学生Webエンジニア PLANインターン生 PHP Laravel Python HTML CSS JS
【名前】 "ゆざ"
【関連】 株式会社PLAN / MIYABI Lab / Tmeet(twitterユーザーマッチングサービス) /
【MIYABI Lab運営】23歳/同期がト◯タやMicr◯softに就職する中、ベンチャーに未経験でWebエンジニアになるのを選んだ脳科学専攻の理系院生◆人見知り日本縦断◆機械学習/Web歴5ヶ月
【AWS】知識ゼロから理解するRDS超入門
AWSのデータベースサービス「Amazon RDS」を初心者にもわかるように解説します。未経験には難しいMultiAZ構成やレプリケーションは、マスター/スレ...
【AWS】Auto Scalingする前に知っておくべき7つのこと
Amazon EC2 Auto Scaling(オートスケール)を使用すると、CPU使用率等に応じてEC2の台数を自動的に増減できます。ここでは、初心者の持つ疑問を通し...
WordPressの基本構造を理解してオリジナルテーマを作ろう(後半)
WordPressのテーマを自作するために必要なテンプレートファイル(functions.phpやstyle.css)の役割やファイル構造を理解して、どのようにオリジナ...
WordPressの基本構造を理解してオリジナルテーマを作ろう(前半)
WordPressのテーマを自作するために必要なテンプレートファイル(functions.phpやfront-page.php)の役割やファイル構造を理解して、どのようにオ...
【Heroku入門】無料枠サーバーを24時間スリープさせない方法
フリープランのHerokuサーバーでは、30分以上アクセスがないと自動的にスリープしてしまいます。ここでは、Herokuサーバーを寝かせない方法につい...
【入門編】Laravelのディレクトリ構造とMVCの処理の流れを理解する
Laravel初心者が学習する際にわかりにくいLaravelのディレクトリ構造を具体的な例を交えて解説します。MVCの基本であるビュー、モデル、コントロー...
【初心者向け】PythonによるHeroku環境で簡単LINEBot開発
誰でも簡単にLINEBotをpythonを使ってHeroku環境で開発できる方法を解説します。ここでは、LINE Messaging APIを用いることでおうむ返しをするBot...
AWSでWebサーバー構築!踏み台サーバーでセキュアなネットワークを構築する(第5回)
連載の第5回です。メインEC2に対して直接SSH接続できる状態というのは、セキュリティの観点からあまり望ましくありません。MySQLやEBSが紐づいたメ...
AnacondaでのTensorFlow環境構築と基礎的な使い方
Anaconda(アナコンダ)のインストールからJupyter notebook(ジュピターノートブック)とTensorFlow(テンサーフロー)の基本的な使い方を初心者...
脱初心者!MNIST beginnerに隠れ層を加えたニューラルネット解説
TensorFlowのチュートリアルであるMNIST beginnerの応用して、隠れ層と活性化関数を加えたニューラルネットワークで手書き文字識別を解説します。...
AWSでWebサーバー構築!Apache2.4, PHP7, MySQLの導入と初期設定(第4回)
連載の第4回です。今回は作成したEC2インスタンスにWebサーバーとしての機能を持たせるため、Apache2.4のインストールおよびhttpd.conf等の各種設...
AWSでWebサーバー構築!EC2を作成してSSH接続する(第3回)
連載の第3回です。前回作成したVPC・サブネットにおいて、セキュリティーグループに保護されたEC2インスタンスの作成・設定およびSSH接続の確立ま...
初心者必読!MNIST実行環境の準備から手書き文字識別までを徹底解説!
Pythonによる機械学習をプログラミング初心者にもわかりやすいように、TensorFlowチュートリアルのMNIST beginnerを使って、手書き文字(MNIST)識別...
AWSでWebサーバー構築!専門用語の解説とVPC環境を構築する手順(第2回)
連載の第2回です。AWSにてVPCネットワークを構築してWebサーバーを設置・運用するためには、AWS内で利用される重要単語について正しく理解しておく...
知識ゼロで機械学習・AIを理解するために必要なニューラルネットワークの基礎知識
機械学習・AIを理解するために必要なニューラルネットワークの基礎について、これから機械学習を勉強したい人、プログラミング未経験の人にもわか...
Canvaで簡単におしゃれなアイキャッチ画像を作ろう!使い方を徹底解説!
PhotoshopやIllustratorを使えなくてもCanvaなら誰でも簡単におしゃれなアイキャッチ画像が作れます。豊富なデザインテンプレートを組み合わせるだ...
プレビュー機能付きの記事編集画面の作り方(Laravel5)
記事編集フォームにはプレビュー機能の実装が必須です。記事を保存する処理とプレビューを表示する処理を共存させるにあたり、ボタンをクリックし...
AWSでWebサーバー構築!VPC設計に必要なIPアドレスとサブネットの基礎知識(第1回)
連載の第1回です。AWSのVPCネットワークを設計して実際に構築するためには、IPアドレスの基礎を理解することが非常に大切です。EC2 Webサーバーを...
MIYABI Labホームページを製作しました
様々な理由でプログラミングの勉強を困難だと感じてしまっている方のお役に立てれば嬉しいです。これからも小さなWebサービスを作り続けていき、技...
ゆざ、株式会社PLANを卒業します。
2年間インターンとしてお世話になった株式会社PLANを卒業します。AWS,Laravel,Pythonなどの技術的なことだけではなく、エンジニアとして、社会人と...