4時間で「ゼロから作るDeep Learning」を読んで自分で動かしてみた

#「ゼロから作るDeep Learning」とは
[Oreilly公式HP](http://www.oreilly.co.jp/books/9784873117584/)

Oreillyの出版している本，と聞くと分厚く，自分の知りたいことがどっかにはあるんだけど難しい，なんというか英語わからないのに英英辞書を調べているような感覚になることが多い気がします．
しかし，この本は違います．ディープラーニングに興味のある一般人にとことん売れているとのこと．
自分で手を動かしながらディープラーニングとは結局何なのかという中身を知ることができる入門書です．


#準備(5分)

まずは[本家参考URL](https://github.com/oreilly-japan/deep-learning-from-scratch)からソースコードをダウンロードし，ローカルに保存します．
回線が遅い…

#第1章　Python入門(P1~P20 : 7分)
Python, numpy, matplotlibといったPythonによる機械学習の実装に不可欠なものをインストールする．
私はこの辺の環境は揃ってしまっているので流し読み後，ソースを実行するのみでパス．
かの有名なレナさんやsic, cosカーブが表示されたりした．

#第2章　パーセプトロン(P21~P37 : 7分)
1つのパーセプトロンは「重み」と「バイアス」を指定して作成する．
andやnand, orといった単純な論理回路が1層のパーセプトロンで実装できるのに対し，xorはこれらを組み合わせた，すなわち2層のパーセプトロンにしないといけないことを確認．
これを積み重ねていくことでディープになっていくんだろう，となんとなくの想像が出来るようになる．

#第3章　ニューラルネットワーク(P39~p82 : 31分)
パーセプトロンとニューラルネットワークの違いは活性化関数の有無であり，活性化関数にはシグモイド関数やステップ関数，ReLU関数を適用する．
それぞれの関数についてソースコードとグラフ形状を確認する．
行列や内積の説明は分かるのでパス．
インデックスの振り方が自分には分かりづらいな，と思いつつ3層ニューラルネットワークのフォワード方向の実装を確認．
ソースコードでは出力層の活性化関数がsoftmax関数になっていることも確認．
ただ，指数関数の計算コストは高いらしく，クラス分類などでは恒等関数を使いことも多いらしい．また，正しく計算するためには実装に工夫が必要なことも確認し，「たしかに，なるほど」と感じる．
よく見るMNISTデータセットを用いた画像認識の例で実際にニューラルネットワークを動かす．
データセットはインターネットからダウンロードするらしく，少し時間がかかりそうなので書籍の方を先に読み進める．
事前に学習済のパラメータもソースコード内に確認．pklファイルというのは今まで知らなかった…
バッチ処理のコードって本当にこれでいいのか？と少し疑問も感じつつ，バッチ処理にしても大きくはソースコードが変わっていないことを確認し，forward propagationを理解する．

#第4章　ニューラルネットワークの学習(P83~P122 : 12分)
損失関数として2乗誤差と交差エントロピーが用いられ，交差エントロピーでは実装に工夫が必要なことを確認．
ミニバッチ学習が学習の効率化につながることを確認．
ニューラルネットワークの学習が確率的勾配降下法によって実装されることを確認．
この章では数値微分を用いるらしい．
この章は機械学習の基本が分かっていれば，ほぼ読まなくても大丈夫そうだと感じた．

#第5章　誤差逆伝播法(P123~P164 : 15分)
第4章では数値微分を用いたが，これは時間がかかるため，実際には誤差逆伝播法を用いる．
ようは合成関数の微分だな，と思いつつ軽く読み飛ばすが，計算グラフによる説明はとても分かりやすい．
合成関数の微分が苦手な高校生に是非教えてあげたい．まぁ本当に苦手な子は「合成関数」を正しく分解できないのが問題なのだが．
各レイヤの実装は第5章のディレクトリではなく，common/layers.pyに実装されていた．

#第6章　学習に関するテクニック(P165~P203 : 51分)
どうやらここからが本番のようだ．
##パラメータ更新手法の選択
確率的勾配法(SGD)により，ニューラルネットワークの学習が行えることは第4章で確認したが，これには欠点もあり，それを改善する手法として，Momentum, AdaGrad, RMSProp, Adamといった手法が有名である．
- Momentum
    - 前の更新を保存し，慣性力が働くようにする
- AdaGrad
    - 学習係数を各パラメータごとにその勾配の大きさに従って減衰させていく
- RMSProp
    - AdaGradにおいて，過去の更新における勾配を徐々に忘れるようにするもの．更新量がいずれ0になってしまうAdaGradの欠点を補う
- Adam
    - MomentumとAdaGradのあわせ技
    - 新しめで最近良く使われる手法らしい
各手法の実装は第6章ではなく，common/optimizer.pyにあり，上記の他にMomentumに似たNesterovというクラスがある．

##初期パラメータの選択
ニューラルネットワークでは，勾配消失と呼ばれる中間層の出力が0と1に偏ってしまう問題がある．各中間層の出力の理想は0から1まで適度な広がりを持つ状態らしい．
初期値もこれに大きく影響し，Xavierの初期値とHeの初期値が提案されている．
- Xavierの初期値
    - 前層のノード数がnのとき，sqrt(1/n)の標準偏差を持つ分布を使用する
- Heの初期値
    - 活性化関数がReLU関数のとき専用
    - 前奏のノード数がnのとき，sqrt(2/n)の標準偏差を持つ分布を使用する

初期だけでなく学習の途中でも中間層の出力の分布を調整し，勾配消失を防ぐための手法としてBatch Normalizationがある．
各ミニバッチごとにデータ分布を平均0，分散1になるように正規化をする．
これにより，学習効率が上がり，，初期値依存性が小さく，かつ過学習を抑制する効果があるらしい．良いこと尽くめ．
back propagationも多少複雑になるが，十分に手で計算できる程度である．

##正則化
モデルの表現力が高すぎたり，訓練データが少なすぎたりすると，訓練データに適合しすぎる過学習が起きる．
その対策として，他の機械学習でも用いられるWeight decayとニューラルネットワークならではのdropoutがある．
- Weight decay
    - パラメータの値が大きくなることにペナルティを与える（例：L2ノルム）
    - 複雑なニューラルネットワークには対応しきれない
- dropout 
    - 訓練時にいくつかのニューロンをランダムに消去しながら学習を行う（テスト時にはすべて用いる）
また，ニューラルネットワークのハイパパラメータの選択にはgrid searchよりもrandom samplingの方がいいとされている．他にベイズ最適化もある．

#第7章　畳み込みニューラルネットワーク(P205~P239 : 58分)
これまでのニューラルネットワークはAffine-ReLU層がN-1層で最後にAffine-softmax層だった．
CNNではこれがConvolution-ReLU-(Pooling)層が大半で最後の方にAffine-ReLU層とAffine-softmax層になる．

##畳み込み（Convolution)層
Affine層のような全結合層では画像などのデータ形状が表現できないが，Convolution層では表現できる．
Convolution層の入出力データを入力特徴マップ，出力特徴マップと言う．
- 畳み込み演算
    - フィルター処理にバイアスを加えたもの
- パディング
    - 出力サイズの調整のため，入力データの周囲を0で埋めること
- ストライド
    - フィルター適用位置の間隔
入力データが3次元の場合はチャンネル方向について足し算を行う．そのため，入力データとフィルターのチャンネル数は一致させなければならない．
また，複数のフィルターを用いることで出力特徴マップのチャンネル数も操作できる．（バイアスも同じ数になる）
バッチ処理では，（データ数，チャネル数，行数，列数）の4次元のデータとして流れる．

Convolution層の実装にはim2col(image to colummn)を用いる．
フィルター適用領域を行ベクトルに展開し，それを縦に並べていくことで4次元データを2次元に展開する．（行ベクトル展開時，複数データ分横につなげていく）
メモリを通常よりも多く消費するという欠点もあるが，計算高速化の恩恵が得られ，実装も簡単になる．
次にフィルターをそれぞれ縦ベクトルに展開し，横に並べることで2次元に展開する．
これによりConvolution層の計算は大きな2つの行列の内積演算になり，演算後reshapeし，出力データとする．

##プーリング（Pooling)層
縦，横方向の空間を小さくするための演算．
領域の最大値をとるmaxプーリングや平均値を取るaverageプーリングなど種類はあるが，画像処理では主にmaxプーリングを用いる．
また，ウィンドウサイズとストライドは同じ値に設定することが多い．
プーリング層は学習パラメータはなく，微小な位置変化に対してロバストという特徴がある．

Pooling層の実装にもim2colを用いるが，チャンネル方向については独立に展開するという点で異なる．
これはプーリング処理をチャンネルごとに独立に行うためである．

##代表的なCNN
- LeNet(1998)
    - 約20年前に提案された初めてのCNN
    - 活性化関数はシグモイド関数で，Pooling層はサブサンプリングという点が現在のCNNとは異なる．
- AlexNet(2012)
    - LeNetにおける活性化関数をReLUとし，LRN(Local Response Normalization)層を用い，Dropoutを使用するという点が進化している．
なお，2015年にCNNの可視化に関する研究も進み，層が深くなるにつれてより高度な情報を検出するニューロンが生まれるようになっていることがわかっている．

#第8章　ディープラーニング(P241~P273 : 48分)
- Data Augmentation(データ拡張）
    - 訓練画像の回転や平行移動，または一部切り出し(crop処理），左右反転（flip処理），輝度変化などにより，人工的に訓練画像枚数を増やすこと
- ネットワークを深くする理由
    - 深いネットワークの方が浅いネットワークより少ないパラメータ数で高精度な表現力を持つ．
    - 深いネットワークのほうが学習が効率的になる．すなわち少ないデータ数でも学習が行える．（各層がより簡単なタスクに取り組むことになる）
- ImageNet(CVPR2009)
    - ILSVRC(ImageNet Large Scale Visual Recognition Challenge)に用いられる100万枚を超えるラベル付き画像データセット
- 転移学習
    - 学習済みのパラメータを別のニューラルネットワークにうつして再学習すること

##様々なディープラーニングのフレームワーク
- VGG(2014)
    - Convolution層とPooling層からなる基本的なCNN．
    - 3×3の小さなフィルターによる畳み込みを2層〜4層連続で行い，プーリング層でサイズを半分にすることを繰り返す
    - 最後は全結合層を経由する
- GooGleNet(2014)
    - CNNの構造をしているが，縦方向のみならず，横方向にも深さを持つインセプション構造を持つ．
    - 横方向にはサイズの異なるフィルターによる畳込みとプーリングが並んでいる．
    - 1×1のフィルターの畳み込み層が多く用いられ，チャンネル方向にサイズを減らすことでパラメータ削減や処理高速化に貢献している．
- ResNet(2015)
    - Convolution層の一部を複数まとめてスキップするスキップ構造（ショートカット，バイパス）を導入し，学習時の逆伝播において勾配をそのまま流すことで，勾配消失の問題を軽減する
    - これにより150層以上をつなげることができるようになった

##ディープラーニングの高速化
ディープラーニングの学習にかかる時間の9割程度はConvolution層の処理である．これを並列して解くのがGPU.
GPUの主な提供元はNVIDIA社とAMD社の2つだが，親しいのはNVIDIA社のものである．
ただし，GPUの高速化にも限界があるため，分散学習も注目されている．TensorFlow(Google)やCNTK(Microsoft)では分散学習を重要視している．
計算量以外にもメモリ容量やバス帯域が問題になる．
メモリ容量では，大量のパラメータや中間データの扱い方，バス帯域ではネットワークを流れるデータのビット数が問題になり，ディープラーニングでは16bitの半精度浮動小数点数でも学習できると言われている．NVIDIA社のPascalアーキテクチャではこの16bit演算に対応している．2016年には重みや中間データを1bitで表現するBinarized Neural Networkも提案された．

##ディープラーニングの実用例
- 物体検出
    - 画像から物体の種類と位置を特定するクラス分類問題
    - CNNをベースとした手法が多く，R-CNN，さらにはFaster R-CNNが有名．
- セグメンテーション
    - 画像に対するピクセルレベルでのクラス分類問題
    - FCN(Fully Convolutional Network)というすべてがConvolution層からなるネットワークが使われる
- 画像キャプション生成
    - 画像を入力すると，その説明文を出力する問題
    - 代表的な方法はNIC(Neural Image Caption)を呼ばれ，画像を理解するでディープなCNNと自然言語を扱うRNNから構成される．
    - CNNが画像から特徴を抽出し，RNNに初期値としてそれを渡す．そこからRNNは再帰的に文章を生成する．
- 画像スタイル変換
    - スタイル画像とコンテンツ画像の2つの画像を入力することで，新しい画像を生成する．
    - ネットワークの中間データがコンテンツ画像の中間データに近くなるように，かつスタイル画像から抽出したスタイル行列のズレも小さくなるように学習を行う
- 画像生成
    - DCGAN(Deep Convolutional Generative Adversarial Network)
    - GeneratorとDiscriminatorという2つのニューラルネットワークを用い，前者が本物そっくりの偽物を作り，後者がそれが本物か否かを判定することを競争的に繰り返し，お互いに学習を進める．
- 自動運転
    - SegNet(CNNベース)
- Deep Q-Network
    - Q学習における最適価値行動関数の近似においてCNNを用いる強化学習


#感想
勉強時間234分！
とてもわかりやすく，最近話題になっているようなトピックまで触れることができました．
今回は実装を「見る」ことによって学習を進めました．時間的には間違いなく早いですが，やはり自分で実装して初めて気づくことも多々あるかと思います．その点を考えて，これから第5章～7章あたりのものを時間外で実装してみようと思います．
それでも4時間で一通りの理解が出来たことはとても良かったと思います．
ソースコードは章ごとに分かれているのはいいが，ファイル名に数字をつけるなどして，各章内での順番も明示すると，書籍との行き来が楽になるように感じました．

4時間というのは社会人でいうと半日にあたりますね．挑戦できてよかったです．