Hello Wor.log

IT系大学生4人による備忘録のようなもの

slimとkerasの計算グラフ

CPPXのXです。

tensorflowをぬるぬる書くためのライブラリであるslimとkerasをちょこっと比較しようかなと思います。
tensorboardを使いつつ、どのようなグラフが出来上がるのかを見たいと思います。

どっち使っても簡単に記述出来るし、どっちがいいの!?みたいなことです。

今回は、使用頻度が高い畳み込み層、プーリング層、全結合層を見ていこうかと思います。

slim, kerasのついでに生TF, tf.contrib.layersも見てみます。

では目次です。

環境

  • python 3.5.3
  • tensorflow 1.9.0

imports

import tensorflow as tf
import numpy as np

使うモデル

input: 28x28@3ch

conv

max pool

fully connected

こんな感じの適当ぶっこいたモデルを作っています。

入力はこれで統一しています。

inp = tf.placeholder(tf.float32, (None, 28, 28, 3), name="input")

生TF

slimもkerasも使わないで生のtensorflowで書いて見ます。

with tf.variable_scope("tensorflow"):
    with tf.name_scope("conv"):
        w = tf.Variable(tf.random_normal((3, 3, 3, 32)))
        b = tf.Variable(tf.random_normal((32,)))
        x = tf.nn.conv2d(inp, w, strides=[1, 2, 2, 1], padding="SAME")
        x = tf.nn.relu(tf.nn.bias_add(x, b))

    with tf.name_scope("pool"):
        x = tf.nn.max_pool(x, [1, 2, 2, 1], strides=[1, 2, 2, 1], padding="VALID")

    with tf.name_scope("flatten"):
        x = tf.reshape(x, (-1, np.prod(x.shape[1:])))

    with tf.name_scope("fc"):
        w = tf.Variable(tf.random_normal((x.shape[-1].value, 10)))
        b = tf.Variable(tf.random_normal((10,)))
        logits = tf.nn.bias_add(tf.matmul(x, w), b)

んひゃあ
初めて書きました。

グラフは以下のようになりました。

全体像です。
f:id:cppx:20180904004044p:plain:w100

畳み込み層です。
f:id:cppx:20180904004437p:plain

プーリング層です。
f:id:cppx:20180904004442p:plain

全結合層です。
variableとかの中身は畳み込み層のものと変わらないので、展開しません。 f:id:cppx:20180904004446p:plain

コードは複雑ですが、出来上がるグラフは簡素でいいですね。

keras

Sequential使って書くのがkerasっぽいのですが、今回は使わずに書いています。

実際に中身は見ていないのであんまり言えないですが、おそらく出来上がるグラフに大きい影響は無いと思います。

with tf.variable_scope("keras"):
    x = tf.keras.layers.Conv2D(32, 3, strides=2, padding="SAME")(inp)
    x = tf.keras.layers.MaxPool2D(2)(x)
    x = tf.keras.layers.Flatten()(x)
    logits = tf.keras.layers.Dense(10)(x)

いい感じですね。

グラフは以下のようになりました。

全体像です。
f:id:cppx:20180904004040p:plain:w100

畳み込み層です。
isinitializedみたいなのがありますね。
なんでしょうこれ、調べてもよくわかりませんでした。 f:id:cppx:20180904005500p:plain

プーリング層です。
まあ、変わりようが無いですよね。
f:id:cppx:20180904005509p:plain

全結合層です。
f:id:cppx:20180904011401p:plain

見やすいですね、keras。
ただ、中身を開いて見るとほんの少しだけごっちゃりしています。

slim

name_scopeを使うと、畳み込みとか全結合の重みがスコープの外にはみ出しちゃうので注意が必要です。

with tf.variable_scope("slim"):
    x = tf.contrib.slim.conv2d(inp, 32, 3, stride=2)
    x = tf.contrib.slim.max_pool2d(x, 2)
    x = tf.contrib.slim.flatten(x)
    logits = tf.contrib.slim.fully_connected(x, 10)

スリムですね。

グラフは以下のようになりました。

全体像です。
f:id:cppx:20180904004037p:plain:w100

畳み込み層です。 f:id:cppx:20180904011642p:plain

プーリング層は、他と同様なので省きます。

全結合層です。 f:id:cppx:20180904011656p:plain

initializerとか開いて無いですが、kerasと同じようになっていました。

kerasよりもスッキリしてると思うのですが、どうでしょう。

ちなみに、variable_scopeを使わないとこのように中身がはみ出します。
f:id:cppx:20180904011950p:plain

個人的にはちょっとこの仕様がつらいです。

雑にループ回したい時など、name_scopeだと勝手に数値を後ろにくっつけてよしなにしてくれますが、variable_scopeだと名前を変える必要が出てきて、面倒臭っとなります。

誤差みたいな手間ですが、ループから出したり入れたりして試してると面倒臭くなりました。

tf.contrib.layers

これ、slimと全く同じものが出来上がりました。

一応載せますが、全体像です。
f:id:cppx:20180904004033p:plain:w100

中身開いても全く同じものが出てきます。

slimとkerasの共存

今回話に上げたslimとkerasですが、機能的にはkerasの方が色々あります。

ただ、slimの書き方が自分好みだったので、kerasに移るかどうか迷っていました。

kerasを軽く触って見たところ、あっこれslimと共存出来るじゃんと気がつきました。
足りない機能は借りてくればいいのです。

例えばこんな感じです。

with tf.variable_scope("keraslim"):
    x = tf.contrib.slim.conv2d(inp, 32, 3, stride=2, activation_fn=tf.keras.layers.PReLU())
    x = tf.contrib.slim.max_pool2d(x, 2)
    x = tf.keras.layers.GlobalAvgPool2D()(x)
    x = tf.contrib.slim.flatten(x)
    logits = tf.contrib.slim.fully_connected(x, 10)

PReLU使いて。とか

GAP使いて。とか

雰囲気で添えてあげてください。

おわり

slimとkerasで軽くベンチーマークとか取ったらよかったかもしれません。
またの機会にやろうかと思います。

slimとkerasですが、結局のところどっちがいいとか無い気がします。

グラフに関してはslimの方が見やすいかな って思いましたが、このぐらいの差なら人によって違うと思います。

好きな方使って、どうしても ってところを借りてくると良さそうです。

今回はslimベースでkerasを借りるのをちょろっとだけ紹介しましたが、kerasからslimを借りるのもきっと簡単に出来るはずです。