Edited at

kerasで頭に描いたネットワーク構造を実現するためのTips ~ Lambda 編 ~


追記2019/10/01

消費税が10%に上がりました。

ではなく、Tensorflow2.0が正式リリースされました。少し前からKerasはTensorflowにインクルードされていますが、そのKerasにおいて

Raw TensorFlow functions can now be used in conjunction with the Keras Functional API during model creation. This obviates the need for users to create Lambda layers in most cases when using the Functional API. Like Lambda layers, TensorFlow functions that result in Variable creation or assign ops are not supported.

このように記載があります。

要するに、Lambdaレイヤー使って書いてたけど、生のTensorflowの関数をKerasのFunctionalAPIで繋げるよって事だと思います。

どこまで完全な対応かは追々実験する必要がありますが、この記事でドヤ顔して書いてる内容は時代の流れによって不要と化した可能性があります。

(※あれ、でもそうなるとcoremltools等のコンバータの実装がより一層追い付かなくなるじゃん… https://qiita.com/Mco7777/items/c5df09e8b68988cdf916 )

以下の記事はご認識の上御覧ください。


本編

functionalAPIについて書いた前回の続きです。前回で説明したfunctionalAPIの範囲だけでもこんな落書きみたいなネットワークも割りとすんなり書けたりします。まぁこんなネットワークを作ったところで何の役に立つのかは知りませんが。

IMG_4713.JPG

これが


network.py

from keras.models import Model

from keras.layers import Input, Dense, Activation, Multiply

my_dense = Dense(5)

model_input = Input(shape=(5,))
mid1 = my_dense(model_input)
mid2 = Dense(5)(mid1)
mid3 = Multiply()([mid1, mid2])
loop = my_dense(mid3)
output1 = Activation('relu')(loop)
output2 = Activation('relu')(mid2)
model = Model(inputs=model_input, outputs=[output1, output2])


スクリーンショット 2017-12-02 16.25.23.png

こんな感じで。

今回はさらにもう少しレイヤーの中身に迫って弄りたい時にどうしたら良いかを書いていきたいと思います。掲題の通りLambdaと呼ばれるクラスを使うのですが、このLambda、汎用性が凄いんです。


Lambdaの前に

例えばサイズ5のレイヤー2つがあってそれを合体させて10のレイヤーにしたい場合、どうしたらよいでしょうか。numpyでイメージするならこんな感じ。


concatenate.py

x1 = np.random.rand(3, 5)

x2 = np.random.rand(3, 5)
x = np.concatenate((x1, x2), axis=1)
x.shape
# (3, 10)

ここで1次元目の3はバッチサイズです。サイズ5のデータが3つあるのが x1,x2 でそれを合体させたのが x ですね。前回の記事でMultiplyのように、合算して1つのTensorにする方法は見ましたが、今回はそうではなく横につなげる感じです。

ではkerasのネットワークではどのように実現するかというと、同じような感じで可能です。


concatenate2.py

from keras.models import Model

from keras.layers import Concatenate, Input

x1 = Input(shape=(5,))
x2 = Input(shape=(5,))
x = Concatenate()([x1, x2])
model = Model(inputs=[x1,x2], outputs=x)


スクリーンショット 2017-12-02 17.14.04.png

numpyと大した違いはなく結構直感的にできました。 Concatenate はバッチ次元はNoneだと考えれば基本的にMultiplyなどとは違いサイズが異なっても繋がります。それでは逆にサイズ10のTensorをサイズ5のTensor2つに分離したいときはどうしたらよいでしょうか?


Lambda


Lambdaの使い方その1

普段numpyを使っている方は配列を分離するなんて何でもない事だと思います。例示するまでも無いと思いますが


split.py

x = np.random.rand(3, 10)

x1 = x[:, :5]
x2 = x[:, 5:]

こんな感じですね。ですが、kerasのTensorに対して model_input[:, :5] こんなことができるのかと言うと流石にそんな便利機能はありません。

しかし Lambda という魔法の箱(と表現したくもなります)を使うとこのように書けてしまうのです。


split2.py

from keras.layers.core import Lambda

model_input = Input(shape=(10,))
x1 = Lambda(lambda x: x[:, :5], output_shape=(5,))(model_input)
x2 = Lambda(lambda x: x[:, 5:], output_shape=(5,))(model_input)
model = Model(inputs=model_input, outputs=[x1,x2])


スクリーンショット 2017-12-02 17.26.33.png

Lambda の中は少しややこしいかもしれませんが、xという引数にデータが渡されてきて、そのデータはnumpyのような配列操作ができてしまうのです。

注意点としてはnumpyからの流れで書いてるので不自然に見えないかもしれませんが、


  • lambdaの中ではバッチ次元(x[:, :5]の最初の:,です)を含めないといけない点

  • output_shapeを指定しないといけない点(実は入力と同じサイズなら不要らしいですが…)

の2点です。この操作ができてしまえば後は半分だろうが1つずつだろうが好きなように分割できますね。


Lambdaの使い方その2

Lambdaを使うことによってレイヤー内部の形を自由自在に扱えるようになりました。

ではLambdaの果たす役割はそれだけなのでしょうか。実は(私的に)もう1つ大きな役割があります。それはbackendの関数を利用する時です。

実はkerasではbackendという、バックエンドのライブラリ(ここではTensorflow)が持つレイヤー操作を利用できるAPIがあります。

例えばあるレイヤーのすべてのノードに定数を足して平方根を取りたい、けどネットワークの途中でそれをやりたい、なんて事はよくあります。そう思ってググって辿り着くのはだいたいここでしょう。

しかしこれをみてサクッとモデルを作るとこうしたくなります。


backend.py

from keras import backend as K

model_input = Input(shape=(10,))
calculated = K.sqrt(model_input + 1.0)
model = Model(inputs=model_input, outputs=calculated)


しかしこれではエラーになります。 K.sqrt のようなバックエンドが返す値はkerasのレイヤーではないんです。なんかドキュメントではこの辺明確に説明されてないような気がするんだよなぁ…

ではどうするかというと、割りと知ってしまえば単純です。


backend2.py

from keras import backend as K

model_input = Input(shape=(10,))
calculated = Lambda(lambda x: K.sqrt(x + 1.0), output_shape=(10,))(model_input)
model = Model(inputs=model_input, outputs=calculated)


Lambda でラッピングしてあげれば何でもできるような気がしてきます。


Lambdaの便利さに取り憑かれたが故の悲劇

今回はLambdaの便利さについて書きましたが、実はある特定の目的でモデルを設計していると(現時点では)悲しみを背負うことになります。そうです、私は背負いました。

そう、coreMLでの利用です。

次回はcoreMLに関する辛みを書き連ねたいと思います。それを見て誰かのお役に立てればこれ幸いです。

先に1つだけ書くと、Lambdaを使ったkerasのモデルはcoreMLのモデルに変換できません。では次回。