Kerasはfunctional APIもきちんと理解しよう

以下の記事は自身のブログ[Data Science Struggle](https://marubon-ds.blogspot.jp/)でも掲載予定。許可なき掲載とかではない。

#概略
Kerasを使えばTensorflowなどに比べて簡単に深層学習のコードを書くことができる。『深層学習、ニューラルネットワークに入門したいけど難しそう』と言う人は積極的にKerasを使っていくべきだと思う。 
Kerasはモデルの表記方法が二種類あり、そのうちの一つであるfunctional APIを利用した記法について紹介する。 

#functional APIとは
>functional APIは，複数の出力があるモデルや有向非巡回グラフ，共有レイヤーを持ったモデルなどの複雑なモデルを定義するためのインターフェースです．

上記の説明はKerasの[公式ドキュメント](https://keras.io/ja/)から抜粋したものになる。要するに、***複雑なモデルを作るにはこれが必要だ*** ってことだ。 

##何故functional APIが必要なのか？
Kerasでモデルを作成していく際に頻繁にfunctional APIの知識が必要になる理由を考えていく。 

###Kerasでどのようなモデルを作るのか？
まず、理解しておかなければならないのは、僕らがKerasを用いて何を作っているのかと言う点だ。無論これはニューラルネットワークモデルということになる。 
では、***なぜ、ニューラルネットワークモデルを作るのか？他の機械学習手法ではダメなのか？*** 
ニューラルネットワークは以下の特性を持つ。 

* 高次元データに対する優れた予測能力
* 柔軟な入力受付と出力操作

基本的に、あらゆる問題に対して『とりあえずニューラルネットワーク』というのは好ましくない。他の機械学習手法で、ライブラリのオプション調整で解決可能であるのならばそちらを採用するべきだ。 
そうすると、僕らがニューラルネットワークの使用を採用するべき場面とは他の機械学習手法ではデータの予測がしにくい高次元データや、入出力データの形がめんどくさいときなどになる。後者については少しわかりにくいので後に簡単に触れる。 
ここで重要なのは『ニューラルネットワークで解決しようとする問題は、高次元データや入出力の形がめんどくさいデータが絡む』ということだ。そうなれば、それを解決するためのモデルというのは割と簡単に***複雑***になりうる。 

###複雑なモデルの作成にはfunctional APIが必要
Kerasはレイヤーをモデルに加えていく形式でモデルを作成する。基本的なモデルはfunctional APIを使用しなくても作成することができるが、別の入力をネットワークの途中から受け付けるものや途中で出力を出すものなど、少しでもモデルが複雑になればfunctional APIを使用する必要がある。 
つまり、そもそも、ニューラルネットワークを用いて解決する問題は高次元データや何らかの複雑性を持つものが多く、それに対応したモデルは複雑なものになりやすい。そして複雑なモデルを作成するにはfunctional APIが必要ということになる。 

##functional APIを見ていく
具体的にfunctional APIを用いたモデルの書き方を見ていく。以下のコードはKerasチュートリアルより抜粋。 

```python
from keras.layers import Input, Dense
from keras.models import Model

inputs = Input(shape=(784,))

x = Dense(64, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)

model = Model(inputs=inputs, outputs=predictions)
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(data, labels)  # starts training
```

書き方の特徴は以下のようになる

* Input関数で入力として受け付けるデータの次元を指定
* 加えたいレイヤー関数の入力それ以前のレイヤー情報を指定して、変数に入れていく
* Model関数で入力と出力を指定

細かい特徴はいくつもあるが、KerasのSequentialモデルと比べたときに意識するのは以上の点だ。 

ここで、試しにfunctional APIを使用して、分岐させたモデルなどを作成してみる。 

```python
from keras.layers import Input, Dense, merge
from keras.models import Model

inputs = Input(shape=(784,))

x = Dense(64, activation='relu')(inputs)

x_1 = Dense(64, activation='relu')(x)
x_2 = Dense(32, activation='relu')(x)

x_m = merge([x_1, x_2], mode='concat')
predictions = Dense(10, activation='softmax')(x_m)

model = Model(inputs=inputs, outputs=predictions)
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(data, labels)  # starts training
```

上記のモデルでは、入力として受け付けたデータを64個のノードからなるレイヤーに流し、そこから64個からなるレイヤーと32個からなるレイヤーに分岐させて流した後、結合させている。 
functional APIでできることは多岐に及び、[Kerasチュートリアルのfunctional APIのページ](https://keras.io/ja/getting-started/functional-api-guide/)にかなり色々な例が載っているので読んでおくべきだ。 

#実際にモデルを作成する時はfunctional APIとどのように付き合うべきか？
Kerasで実際のデータを用いてモデルを作成するとき、functional APIはどのタイミングで使用するべきか？つまり、最初からfunctional APIを用いて書いていくべきなのか、複雑なモデルを描きたいと思った段階でfunctional APIを導入するべきなのかということだ。 
僕が個人的にオススメしたいのは***基本的には最初からfunctional APIを用いてモデルを作っていく***ことだ。 
まず、モデルの複数個所から入力を受け付けるときや、複数個所から出力を行う場合はそもそも、モデルで解決したい問題によって定められることが多いので、その時点で初手からfunctional APIを使用することになる。 
そうでない場合には、無論、functional APIを用いず、シンプルなモデルで解決が可能な可能性もあるが、それは初手ではわからないことだ。ニューラルネットワークモデルは基本的に、実際にデータによって訓練を行う段階までいかないと精度はわからないため、シンプルなモデルで十分な精度が出たとしてもそれは訓練中もしくは後にわかることになる。 
つまり、初手でシンプルなモデルを組んでいる段階で、それでは十分ではなく、モデルを複雑にしていく可能性がある。そうなると、Sequentialモデルからスタートすると、途中で複雑にしようとしたときにfunctional APIを使用する形にするのはめんどくさい（とは言っても、Keras自体がかなり短いコードでニューラルネットワークを書けるため、そこまでの苦労ではないが）。そこで、シンプルなモデルでもとりあえずfunctional APIを用いて書いて行き、複雑にしていくときに、スムーズにその過程を踏めるようにしておく。 
複数のモデルを試して十分な精度が出たものがあり、それを採用するという段階で、シンプルなコードが良いなら、functional APIを使用した形からSequentialモデルの形に書き換えれば良い。 
ニューラルネットワークの作成は基本的には試行錯誤することになるので、試行のしやすい形で書き進め、落ち着いた段階で、思う形に整えれば良い。