こんにちは!
消費財メーカーでデジタルマーケ&データサイエンティスト的なお仕事をしているウマたん(@statistics1012)です。
最近はどっぷりディープラーニングに浸かっています。
僕自身、前はディープラーニングについてそれほど詳しい知識を有しておらずアルゴリズムに関しても曖昧な理解でした。
ディープラーニングのアルゴリズムをブラックボックス的なモノだと考えている方も多いでしょう。
しかし!ディープラーニングの仕組み・考え方は非常にシンプルなのです。この記事では入門と題していますが、ディープラーニングの基礎的な仕組みからPythonでの実装まで見ていきます。
目次
ディープラーニングとは
実は、ディープラーニングの原型は1940年代から存在していたと言われています。
1940年~1950年にかけて人間の神経を模した仕組み「ニューラルネットワーク」が確立されました。
人間の神経が信号を伝播させていくようにある入力を次の層へと重み付けをして伝播させていき出力を求めます。

この層を多層にしていくのがディープラーニングなのですが、当時のマシンパワーでは計算量が膨大過ぎて実現不可能でした。
計算負荷を軽減する誤差逆伝播法(バックプロパゲーション)などの計算手法の登場やマシンパワーの増強により現在に至ります。
そんな計算負荷の問題が解消され深層ニューラルネットワーク(ディープラーニング)が日の目を浴びたのは2006年。
このブレークスルーによって再びAIのブームが巻き起こり、第3次AIブームへと突入していくのです。
AIの歴史については以下の記事でまとめていますのでよければご覧ください!

ディープラーニングの仕組み
さて、そんなディープラーニングですがどのような仕組みで成り立っているのでしょうか?
先ほど人間の脳を模した仕組みと述べましたが実際にどのようなカタチで信号が伝播していくのか、そしてその伝播をどのようにチューニングしているのか簡単に見ていきたいと思います。
ディープラーニングの基礎を学ぶ上では以下の書籍が非常に参考になります!あまりにも有名ですが、是非目を通してみてください。こちらの記事でもこの書籍を参考にしていきます。
ディープラーニングの基本を理解する上で押さえておかなくはいけないのは以下の3点。それぞれについて見ていきます。
・パーセプトロン
・活性化関数
・損失関数と勾配法
パーセプトロン
パーセプトロンとは1957年にアメリカの研究者であるローゼンブラットによって考案されたアルゴリズムです。
パーセプトロンがニューラルネットワークに発展し、そこから近年のディープラーニングへと繋がっているのです。
ニューラルネットワークそしてディープラーニングについて学ぶためには、まずはパーセプトロンについて押さえておく必要があります。
パーセプトロンは以下のようなイメージ。入力\(x_1\)と\(x_2\)に対して重み\(w_1\)と\(w_2\)が掛け合わされ足されます。


そして、足しあわされた値がある閾値を超えれば\(y\)の出力値が変わるモデルです。
閾値を\({\theta}\)と置き数式で書くと以下のようなモデルになります。
\begin{eqnarray} y= \left\{ \begin{array}{l} 0 (w_1x_1+w_2x_2<=\theta) \\ 1 (w_1x_1+w_2x_2>\theta) \end{array} \right.\end{eqnarray}
この数式をグラフにすると以下のようになりステップ関数と呼びます。


このパーセプトロンに関して真理表を用いた例をPythonでの実装例を含めて「ゼロから作るDeep Leaning」で分かりやすく教えてくれるので興味のある人は是非読んでみてください!
活性化関数
パーセプトロンでは、ある閾値を超えた場合に1を出力し超えなければ0であるというステップ関数を用いていました。
これらのインプットからアウトプットに変換する関数を活性化関数と呼び、ディープラーニングを学ぶ上では非常に重要になります。
ディープラーニングでは、活性化関数に様々な関数を用います。
代表的な活性化関数について、それぞれどのような特徴があるのか見ていきましょう!
シグモイド関数
シグモイド関数は以下のロジスティック回帰分析に登場する関数であり出力を線形的に捉えることが可能です。以下のような関数になります。
$$ y= \frac{1}{1+exp(-x)} $$
シグモイド関数の意味は、ある入力に対する出力を0~1の範囲に抑えること。グラフは以下のようになります。


ステップ関数では一定の閾値を超えないと計算結果の大小に限らず同じ値が出力されてしまいますが、シグモイド関数を用いれば微妙な大小を出力の段階で捉えることが可能です。ロジスティック回帰分析と通常の判別分析の違いと同じですね。
シグモイド関数と似た関数で、Tangent関数があります。Tangent関数はx,y=(0,0)であるのに対して、シグモイド関数はx,y=(0,0.5)であることが特徴です。
RELU(ランプ)関数
RELU関数は0より小さい場合は0を出力し、0より大きい場合はそのまま計算結果を出力するという特殊な関数です。
\begin{eqnarray} y= \left\{ \begin{array}{l} x (x>0) \\ 0 (x<=0) \end{array} \right.\end{eqnarray}
単純なため計算速度が速いことと、マイナスの値をノイズと捉えたい場合は有用な活性化関数です。画像認識の場面ではマイナスはノイズになるのでRELU関数を用いられることが多いです。
出力層の活性化関数としては非常に貧弱なので、出力層では用いられることはほぼありません。隠れ層の活性化関数として用いられます。
グラフは以下のようになります。


どの活性化関数を用いるかは状況によって違いますが、活性化関数によっては結果が大きく変わってくるので慎重に選ぶことが必要です。
損失関数と勾配法
ディープラーニングでは、基本的にパーセプトロンの考え方で層を積み重ね、活性化関数を用いて出力を変化させます。言ってしまえばそれだけです。
構造は分かりましたが、最適な重みを見つけるためにはどうすればよいでしょう?ここで登場するのが「損失関数と勾配法」という考え方です。
損失関数とはその名の通り損失を定義した関数。ここで言う損失とは何か。
ディープラーニングの層の最終出力と実測値の差
これが損失になります。損失には二乗誤差やエントロピー誤差が使用されますが基本的には出力と実測値の差を示していると考えて問題ございません。
出力と実測値の差が0なら損失は0です。(ちなみに二乗誤差は回帰問題に、エントロピー誤差は分類問題に使用されます)
出力値を\(y_i\)、実測値を\(t_i\)とすると二乗誤差は以下のようにあらわされます。統計学・機械学習ではお馴染みの式ですね!
$$ \displaystyle \sum_{ i = 1 }^{ n } (y_i-t_i) $$
この時、\(t_i\)は定数であり、変数なのは(\(y_i\)。
どれだけ隠れ層があったとしても、(\(y_i\)は入力層からの入力と重み(\(w_i\)で表すことができます。つまり(\(y_i\)は(\(w_i\)の関数で表せるということ。
損失関数を最小限にとどめる(\(w_i\)を求めるには(\(y_i\)を(\(w_i\)で偏微分すれば良い!!高校数学の知識があれば分かると思います。
このようにしてディープラーニングの各層の重みが最適解に決まっていくのです。
この時、偏微分で得られた値をそのまま反映してしまうと局所最適になってしまうので、学習率を設けて徐々にパラメータ(\(w_i\)を更新していきます。
これを勾配法と呼ぶのです。
ちなみに偏微分は解析的に数値微分で解く方法もありますが複雑なネットワークだと計算負荷が膨大になるためそれを回避するために誤差逆伝播法が考案されており、現在のディープラーニングにはほぼ誤差逆伝播法が使用されています。
興味のある人は「ゼロから作るDeep Leaning」を読んでみると良いでしょう!
ディープラーニングをPythonで実装してみよう!
今まで見てきたディープラーニングを実際に0から実装してみることも可能ですが、Pythonには便利なライブラリが備わっていてライブラリを読み込むことで誰でも簡単にディープラーニングを実装することが可能です。
隠れ層の数や学習効率などパラメータを調整することは必要ですが、ディープラーニングのフォーマットが提供されておりそれを無料で自由に使用できるのは素晴らしい!
今回は定番のMnistという手書き文字のデータセットを用いて、Kerasというライブラリに入ったディープラーニングを使用して画像認識問題を解いていきます!
KerasはTensorFlowや、CNTK、Theanoなどの、より基本的な機械学習ライブラリを「バックエンド」として呼び出して使うようになっており、ディープラーニングのモデルを、層を重ねるイメージで簡単に構築することができるようになっています。
Mnistは「Gradient-based learning applied to document recognition」で用いられたデータセットであり、現在でも多くの論文で用いられています。
Modified National Institute of Standards and Technologyの略であり、0~9の数字が手書き文字として格納されているデータセットです。学習用に60000枚、検証用に10000枚のデータセットが格納されています。
まずは、必要なライブラリをインストールしていきましょう!
1 2 3 4 5 6 7 8 |
import numpy as np from sklearn.model_selection import train_test_split import tensorflow as tf from tensorflow.keras.datasets import mnist from tensorflow import keras from tensorflow.keras.layers import Dense, Activation from tensorflow.keras.models import Sequential from tensorflow.keras.utils import to_categorical |
tensorflowなどのライブラリはあらかじめpip installしておいてくださいね!
続いてMnistのデータを学習データとテストデータに分けます。そしてさらに学習データからパラメータチューニングのための検証データを取り出します。
1 2 3 4 5 6 |
# Kerasに付属の手書き数字画像データをダウンロード np.random.seed(0) (X_train_base, labels_train_base), (X_test, labels_test) = mnist.load_data() # Training set を学習データ(X_train, labels_train)と検証データ(X_validation, labels_validation)に8:2で分割する X_train,X_validation,labels_train,labels_validation = train_test_split(X_train_base,labels_train_base,test_size = 0.2) |
この時画像データは、描画がしやすいように28×28の行列になっているのですが、学習するために1×784に直しましょう!さらに0~255のスケールを正規化しましょう!
1 2 3 4 5 6 7 8 9 10 11 12 |
# 各画像は行列なので1次元に変換→X_train,X_validation,X_testを上書き X_train = X_train.reshape(-1,784) X_validation = X_validation.reshape(-1,784) X_test = X_test.reshape(-1,784) #正規化 X_train = X_train.astype('float32') X_validation = X_validation.astype('float32') X_test = X_test.astype('float32') X_train /= 255 X_validation /= 255 X_test /= 255 |
続いてラベルをダミー変数化します。
1 2 3 4 |
# labels_train, labels_validation, labels_test をダミー変数化して y_train, y_validation, y_test に格納する y_train = to_categorical(labels_train) y_validation = to_categorical(labels_validation) y_test = to_categorical(labels_test) |
ここでデータの成型が終了したので、ディープラーニングのネットワーク構築に入ります。
隠れ層では、RELU(ランプ)関数を用いて出力層ではソフトマックス関数を用いています。
Model.addを使うことで隠れ層をいくつも積み重ねることが可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# パラメータの設定 n_features = 784 n_hidden = 100 bias_init = 0.1 # 学習率 rate = 0.01 # Sequentialクラスを使ってモデルを準備する model = Sequential() # 隠れ層を追加 model.add(Dense(n_hidden,activation='relu',input_shape=(n_features,))) model.add(Dense(n_hidden,activation='relu')) model.add(Dense(n_hidden,activation='relu')) # 出力層を追加 model.add(Dense(10,activation='softmax')) |
ネットワークの構築が終了した後は、最適な重みを見つけていきます。AdamOptimizerは最近よく使われている最適化手法です。
Early stoppingとはもう精度が改善しないようなら学習を止めてしまう条件です。これによってムダな学習を省くことが可能です。最後のvalidation_dataで過学習が起こらないように検証を行っています。
1 2 3 4 5 6 7 8 9 10 |
# TensorFlowのモデルを構築 model.compile(optimizer=tf.train.AdamOptimizer(rate), loss='categorical_crossentropy', metrics=['mae', 'accuracy']) # Early stoppingを適用してフィッティング log = model.fit(X_train, y_train, epochs=3000, batch_size=100, verbose=True, callbacks=[keras.callbacks.EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)], validation_data=(X_validation, y_validation)) |
最後にテストデータで予測を実行して実測値と予測値の正解率を求めます!
1 2 3 4 5 6 7 8 |
# Test dataで予測を実行。 pred_test = model.predict_classes(X_test) validation = (pred_test == labels_test) size = validation.size size correct = np.count_nonzero(validation) print(f"{correct}/{size} correct ({correct*100/size:.3f}%)") |
最終的な結果は・・・・96.96%!!
そこそこな精度をたたき出すことができました。パラメータをいじることで精度を99%まで伸ばしてみてください!
最後にまとめてコードを載せておきます。
ディープラーニング まとめ
ディープラーニングについて見てきましたが、非常にシンプルな仕組みでありかつPythonを使えば簡単に実装できることが理解いただけたでしょうか?
意外とちゃんと見ていくとシンプルなディープラーニングなのですが、効果は絶大!簡単な例ですが画像認識を相当な精度で実現できたのはディープラーニングの素晴らしさをひしひしと感じますねー!
とは言え、まだまだここで紹介したのはディープラーニングの入門の入門です。ディープラーニングの領域は研究が盛んで日々様々な手法が生み出されています。
この記事で何度も紹介してきましたが、以下の書籍がディープラーニングの理論を理解するにはうってつけなので興味のある人は是非読んでみてください!
またディープラーニングを勉強する上でオススメな書籍や勉強方法について手前味噌ながら以下の記事でまとめていますのでよければご覧ください!


