barus's diary

とても真面目なblogですにゃ

深層畳み込みニューラルネットワーク(その①)Pythonで動かす~network3_test.py

今回から、ニューラルネットワークと深層学習(著:Michael Nielsen )で

紹介されているPythonで書かれたコードnetwork3.pyを元にVC++にアレンジしていくニャー。

今まで過去記事で「network1.py」をPythonで実行させ、VC++に書き換えてきた前回の記事で一区切りついた。

 

VC++に書き換え前に、これまでと同様にまずはサンプルをPython上で動かしてみる。

 

 

 

 

Pythonの開発環境準備(windows10)

Pythonの開発環境はAnaconda3をインストールしている。インストール詳細はPython(Anaconda3)をインストールしscikit-learnでニューラルネットワーク - barus's diaryに書きましたので、まだインストールされていない方はこちらを参照して下さい。

 

DeepLearningPython35-master.zipをダウンロードする

こちらのサイトの「Clone or Download」をクリックしてファイルを取得する。私の場合、2017年6月頃にダウンロードしたファイルを基に以下説明しています。もしかしたら、現在あなたが落としたファイルと中身が微妙に異なっているかもしれないのであしからず。

 

MNISTの訓練データをダウンロードする

MNISTの訓練データを使用します。 
https://github.com/mnielsen/neural-networks-and-deep-learning/archive/master.zip
より取得する。 

 

Theanoのバージョン

s:\plog\Python\python3\DeepLearningPython35-master>python -c "import theano; print(theano.__version__);"
WARNING (theano.configdefaults): g++ not available, if using conda: `conda install m2w64-toolchain`
WARNING (theano.configdefaults): g++ not detected ! Theano will be unable to execute optimized C-implementations (for both CPU and GPU) and will default to Python implementations. Performance will be severely degraded. To remove this warning, set Theano flags cxx to an empty string.
0.9.0

 

 

 

畳み込みニューラルネットワークとは

  畳み込みネットワークは、空間的構造性を考慮した設計となっており、画像分類に非常に適しています。 この特性により、畳込みネットワークは効率的に学習できるのです。 つまり、画像を分類するのに優れた、深くて多層のネットワークを訓練できると言えます。 今日、深層の畳み込みネットワークもしくは類似のネットワークが、画像認識を目的とするニューラルネットワークの大半に使われています。

 

 畳み込みニューラルネットワークは次の3つの重要なアイデアを使っています。 それは、①局所受容野、②重みとバイアスの共有、③プーリングです。 各アイデアを順に見ていきましょう。

 

①局所受容野

畳み込みネットワークでは、入力を 28×28 の正方形のニューロンと考えます。各ニューロンの値は、入力として用いる 28×28 の入力ピクセルの強さに対応します。

1つ目の隠れ層の各ニューロンは、入力ニューロンの小さい領域と結合します。例えば、入力の 25 ピクセルに対応する 5×5 の領域に、隠れ層の各ニューロンが結合します。ある隠れニューロン結合を示すと次のようになります。

 

入力画像内のそのような領域は、隠れニューロン局所受容野と呼ばれます。

これを繰り返して、最初の隠れ層の全体に対して値を設定します。
入力画像のサイズが 28×28 で、局所受容野のサイズが 5×5 の場合、1つ目の隠れ層のニューロンは 24×24 個となります。その理由は、入力画像の右側(もしくは下側)にぶつかるまでに、23 個のニューロン分だけ局所受容野をスライドできるからです。

 

ここまで、1 ピクセルずつ局所受容野が移動する例を見てきました。
ストライドの長さに 1 以外の値が使われることがしばしばあります。
たとえば、2 ピクセルずつ局所受容野を右へ(もしくは下へ)動かすこともあります。

 

②重みとバイアスの共有

各隠れニューロンはバイアスと局所受容野に結合された 5×5 の重みを持つことを上で述べました。しかし、24×24 の全ての隠れニューロンに対して、同じ重みとバイアスを適用することをまだ伝えていませんでした。これはつまり、j,k 個目の隠れニューロンへの出力は、下記のようになることを示しています。

 

f:id:hatakeka:20171021151712p:plain

σ は活性化関数の一種であり、以前の章で使用したシグモイド関数です。 b はバイアスの共有値です。f:id:hatakeka:20171021151851p:plainは共有重みであり、そのサイズは 5×5 です。そして、f:id:hatakeka:20171021151921p:plainは x,y における活性化された入力を示します。

 

 このことが意味するのは、入力画像の異なる位置の全く同じ特徴を、1層目の隠れ層が検知するということです。畳込みニューラルネットワークは画像に対して並進不変性があると言います。 並進不変性とは例えば、猫の絵を少し並進移動しても、それはまだ猫の絵と言えるような性質のことです

 この理由から、入力層から隠れ層への写像特徴マップと呼ぶこともあります。特徴マップを定義する重みを共有重みと呼びます。また、特徴マップを同じように定義するバイアスを、同じように共有バイアスと呼びます。 共有重みと共有バイアスはしばしば、カーネル、もしくはフィルタと呼ばれます。文献では、これらの用語を少し異なる使い方をする場合があります。

 

 これまで扱ってきたネットワーク構造は、一種類の局所的特徴のみ検知できるものでした。 画像認識のためには、二つ以上の特徴マップが必要となります。したがって、完全な畳込み層は幾つかの異なる特徴マップから構成されるのです。

 

 
上の例には 三つの特徴マップがあります。 それぞれの特徴マップ(カーネル)は 5×5 の共有重みと共有バイアス(フィルタ)で定義されています。その結果、三種の異なる特徴を、画像全体に渡って検知できるのです。

図をシンプルにするために、 3 つの特徴マップだけを見せました。
しかし、実際の畳み込みネットワークは(たぶん遥かに)多くの特徴マップを使っています。 

 

以下は、20 の特徴マップを持つ畳み込み層を使っています。その特徴マップを少し見てみましょう

f:id:hatakeka:20171021153239p:plain

 

20 個の画像は 20 個の異なる特徴マップ(もしくはフィルタかカーネル)に対応しています。各マップは、 5×5 ブロック画像で表され、局所受容野の 5×5 の重みに対応しています。


白いブロックは小さい(概してマイナスの)重みを意味し、その特徴マップは入力ピクセルに反応しにくいという性質を持ちます。一方、黒いブロックは大きい重みを意味し、その特徴マップは入力ピクセルによく反応します。


大まかに言うと、畳み込み層がどのような種類の特徴に反応するかを、上の画像群は示しています。

 

これらの特徴マップからどんな結論を導けるでしょうか? 何らかの空間的構造が特徴マップには現れているようです。 特徴マップの多くは明るい領域と暗い領域の両方を含んでいます。 これにより、空間的構造に関する特徴をネットワークが学習していることが分かります。

 

しかし、これらの特徴検出器が何を学んでいるのかを、それ以上に深く把握するのは難しいです。明らかに、この特徴は画像認識の伝統的なアプローチでたくさん採用されてきたガボールフィルタではありません。
畳み込みネットワークの学習した特徴を、突き詰めて理解しようとする研究が、現在多数進行中です。


もし興味があるのなら、2013年のMatthew ZeilerとRob FergusによるVisualizing and Understanding Convolutional Networksを読むのを薦めます。

 

重みとバイアスを共有する大きな利点は、畳込みネットワークのパラメータ数を大きく減らせる点です。


上記の畳み込みネットワークのパラメータを数えてみます。 特徴マップごとに 25=5×5 の共有重みと、1つの共有バイアスを必要とします。 つまり、各特徴マップは 26 のパラメータが必要です。20 個の特徴マップがある場合には、畳込み層を定義するための全パラメータは 20×26=520 個となります。

 

それと比較するために、全結合層のパラメータ数を数えてみます。
これまで本書で使ってきた例と同様に 784=28×28 個のニューロンからなる入力層と、30 個の隠れニューロンからなる全結合層で構成されるネットワークを仮定してみてください。その場合、 784×30 個(=23520)の重みとさらに 30 個のバイアスで、全部で 23550 個のパラメータからなります。

 

つまり、全結合層は畳み込み層と比べて 40 倍のパラメータを保持することになるのです。(全結合層は「network1.py」、畳み込みは「network3.py」)

 

もちろん、根本的に2つのモデルは異なっているため、パラメータ数を直接比較することは本当はできません。しかし直感的に考えてみても、畳み込み層には並進不変の性質があるので、全結合層のモデルと同じパフォーマンスを得るのに要するパラメータ数は少なくなると思えます。パラメータ数が小さいおかげで、畳込みモデルは高速に訓練でき、層を深くできるのです。

 

ところで、畳み込みという呼び名は(125)の、畳み込みという名で知られた操作に由来しています。

f:id:hatakeka:20171021151712p:plain
正確に式を記述すると、f:id:hatakeka:20171021155329p:plain となります。
ここで、 a1 はある特徴マップからの活性化された出力、a0は活性化された入力、「∗」 は畳み込みと呼ばれる操作を示します。 私たちは、いわゆる数学における畳込み操作を深く追い求めません。なので、数学との結びつきを心配する必要はありません。 しかし、由来が何なのかは少なくとも知っておいて損はありません。

 

 ③プーリング

通常の畳み込みニューラルネットワークは、先ほどの畳み込み層に加えて、プーリング層も含みます。このプーリング層は通常、畳込み層の直後に置かれます。 この層の役割は畳込み層の出力を単純化することです。少し詳しく説明すると、プーリング層は畳み込み層から各特徴マップを取得し、特徴マップを濃縮させています。


つまり、プーリング層の各ユニットは前層の 2×2 の領域のニューロンをまとめます。具体的な手法を紹介すると、プーリングのよく知られた例としてMaxプーリングがあります。 Maxプーリングでは、プーリングのユニットは 2×2 の入力領域のうちで最大の値を単純に出力します。 次の図を見てください。

 

 

畳み込み層の出力ニューロンは 24×24 (入力画像のサイズは28×28)なので、プーリング処理後は 12×12 のサイズのニューロンとなります。上で述べた通り、畳み込み層は通常1つ以上の特徴マップを持ちます。それらの特徴マップに対して個別にMaxプーリングを適用します。したがって、3つ特徴マップがある場合には、畳込み層とMaxプーリング層の様子は次のようになります。

Maxプーリングは、画像のある領域内のどこかに指定の特徴があるかをネットワークが確認する手段とみなせます。つまり正確な位置の情報は棄てているのです。 直感的に解釈すると、一度特徴が見つかればその正確な位置は重要でなく、他の特徴に対するおおよその位置さえ分かればよいということなのです。この手法の大きな利点は、特徴はプーリングされると少なくなるため、後方の層で必要なパラメータを減らすことができる点です。

 

プーリングの手法はMaxプーリングだけではありません。 他のよく知られたアプローチとしてL2 プーリングがあります。L2プーリングの手法では、 2×2 領域のニューロンの活性化出力の最大値をとるのではなく、2×2 領域の活性化出力の和の平方根をとります。 L2プーリングは、畳み込み層からの情報を圧縮する方法とも言えます。


詳細な手続きは異なるものの、直感的にはMaxプーリングに近いはたらきをします。実際、どちらの手法も広く使われてきました。 そして、場合によってはさらに別のプーリング手法も使われることもあります。パフォーマンスを本気で良くしようと思ったら、検証データを使って異なるプーリング手法を試すのが良いでしょう。そして、一番良い手法を選択するのです。 しかし私たちは、細かい最適化の種類を気にかけるつもりはありません。

 

これまでのアイデアを使う

 さあ、これまでのアイデア①局所受容野、②重みとバイアスの共有、③プーリング)を全て使って、畳み込みニューラルネットワークを完成させましょう。 

 

 これから作るものは、上で見てきた構造と似ていますが、10 のニューロンを持つ出力層が追加されています。
この層の各ニューロンはMNISTの 10 種の手書き数字 ('0', '1', '2', etc) に対応するものです。

 

このネットワークは、MNIST画像のピクセル強度を符号化するのに使われる 28×28 の入力ニューロンから始まります。 そして、5×5 の局所受容野 3 の特徴マップを使う畳み込み層が続きます。この畳み込み層は 3×24×24 の隠れ特徴ニューロンから構成されます。次にMaxプーリング層が続きます。 この層では 2×2 の領域を 3 の特徴マップごとに処理します。つまりプーリング層は 3×12×12 の隠れ特徴ニューロンからなります。

 

ネットワークの最後の層は全結合層です。 Maxプーリング層の全てのニューロンとこの層の 10 の出力ニューロンが個別に結合します。 この全結合の構造は以前の記事(network.py)で扱ったものと同じです。しかし、上図では表記をシンプルにするため、全ての結合を表示する代わりに1つの矢印で表現しています。 結合の様子は容易に想像できるでしょう。

 

 以前の記事と同じようにネットワークの訓練には、確率的勾配降下法と逆伝播を用います。以前の記事と殆ど同じです。 しかし、逆伝播の手続きには、少し修正を加える必要があります。 以前の記事の逆伝播による偏微分導出では、全結合層を対象とした手続きを扱っていたためです。幸運なことに、畳込み層とMaxプーリング層の偏微分の式を導出するには、修正を少し加えるだけで済みます。

 

 

 

network3_test.pyのサンプルを動かす

 

準備運動として、 100のニューロンを含む隠れ層を1つだけを持つ浅いネットワークから始める。 訓練のエポック数は 3 、学習率は η=0.1、ミニバッチサイズは 10、正規化なしの条件で実行してみます。

 

 PythonのAnaconda3をインストール(インストール方法)し、こちらのサイトの「Clone or Download」をクリックしてDeepLearningPython35-master.zipを取得し解凍する。以下のソースコードを「network3_test.py」名で、「network3.py」がある同じフォルダに作成する。

 MNISTの訓練データを解凍して、フォルダdataをDeepLearningPython35-master.zipを解凍した同じディレクトリに配置する。

フォルダ構成は以下のような感じになります。

f:id:hatakeka:20171104123228p:plain

 

ファイル名「network3_test.py」

import random
import mnist_loader
import numpy as np

import matplotlib.pyplot as plt
import network3
from network3 import Network
from network3 import ConvPoolLayer, FullyConnectedLayer, SoftmaxLayer


def main():
    training_data, validation_data, test_data = network3.load_data_shared()
    mini_batch_size = 10
    print ("network3\n");

    net = Network([
          FullyConnectedLayer(n_in=784, n_out=100),
          SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
    
    net.SGD(training_data, 3, mini_batch_size, 0.1,
              validation_data, test_data)
    return 0


if __name__ == "__main__":
    main()

 

すると、恐らくエラーがでると思います。

 

(注)後日追記。theanoのバージョンが0.9で、DeepLearningPython35-master.zipのコミット(Latest commit a8da42f on 28 Aug)以前にダウンロードしていた場合にエラーとなる。theanoバージョン0.9とDeepLearningPython35-master.zip(Latest commit a8da42f on 28 Aug)であれば相性がよい。こちらを参照。 

 

theanoのバージョンを0.9で、新たにDeepLearningPython35-master.zip(コミットバージョンa8da42f )をダウンロードしなおして、改めて実行してみます。

 

f:id:hatakeka:20171021144553p:plain

 よかった。うまく動いてくれました( ^ω^)・・・。

 

 

とりあえず、今回はここまで 

 

 

以上。

 

 参考URL

ニューラルネットワークと深層学習