この投稿は、TensorFlow Advent Calendarの12日目の記事です。

みなさん。TensorFlow使ってますか?

ぼくはと言えば、気がつけば毎日なにがしかのTensorFlowのコードを書いています。

TensorFlowが発表されてからすぐに使い始めて早二年、さまざまな機械学習向けのフレームワークが登場する中、TensorFlowは高い人気を保っていますね。

さて今回の記事は、そんな人気のTensorFlowを使っていて、ぼくが「つらい」と感じる(感じた)ことを書きます。

同じ気持ちの方には共感していただき、「こうすればもっと便利に使えるのに」という方法を知っている方が居られたら是非教えていただきたいと考えています。

多すぎる選択肢

TensorFlowにはさまざまなAPIが用意されています。

たとえば畳み込み層であればtf.nn.conv2dがもっとも基本的なオペレーションとして挙げることができますが、プリミティブすぎて最近はあまり使っていません。

他にもtf.layersモジュールのtf.layers.conv2dがあります。

tf.layers.conv2dの引数は、つぎの通りです。

conv2d(
    inputs,
    filters,
    kernel_size,
    strides=(1, 1),
    padding='valid',
    data_format='channels_last',
    dilation_rate=(1, 1),
    activation=None,
    use_bias=True,
    kernel_initializer=None,
    bias_initializer=tf.zeros_initializer(),
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    trainable=True,
    name=None,
    reuse=None
)

さらにtf.contrib.layersモジュールにもtf.contrib.layers.conv2dがあります。

結局のところ、どれを使えばいいのでしょう?

tf.contribは本来は変更されたり、消えるかもしれない。将来的にTensorFlowに取り込まれるかもしれないという実験的なコードという位置づけになっています。

では、今は安定版であるtf.layers.conv2dを使っておいて、将来、contribが外れたときに対応すれば……と思うのですが、tf.contrib.layers.conv2dからcontribを外すと、ただのtf.layers.conv2dになるので、名前が衝突してしまい、既存のコードに影響が出ます。

現在のtf.contrib.layers.conv2dの引数を見てみましょう。

conv2d(
    inputs,
    num_outputs,
    kernel_size,
    stride=1,
    padding='SAME',
    data_format=None,
    rate=1,
    activation_fn=tf.nn.relu,
    normalizer_fn=None,
    normalizer_params=None,
    weights_initializer=initializers.xavier_initializer(),
    weights_regularizer=None,
    biases_initializer=tf.zeros_initializer(),
    biases_regularizer=None,
    reuse=None,
    variables_collections=None,
    outputs_collections=None,
    trainable=True,
    scope=None
)

活性化関数の指定がactivation_fnとなっています。これではcontribが外れた瞬間にbreaking changeが発生しそうです。

また、活性化関数のデフォルト値もtf.layers.conv2dNoneですが、tf.contrib.layers.conv2dreluがデフォルトです。

将来のことを考えると、contribが外れるタイミングでtf.contrib.layers.conv2dの方を現行のAPIに寄せるのが妥当と思います。そうなると今tf.contrib.layers.conv2dを使ったところは書き直す必要が出てますが、contribは、それがあり得ると説明があるので、しょうがないと言えばしょうがないのでしょう。

そんなわけで、現時点ではtf.contrib.layers.conv2dは怖くて使えないので、tf.layers.conv2dを使っておこうと思っていたら、TensorFlow 1.4.0でtf.layers.Conv2Dクラスが追加されていました。

コンストラクタを見ると引数やデフォルト値はtf.contrib.layers.conv2dに合わせてあるようですが、これは標準のAPIとして提供する必要はあるのでしょうか……?

このようなことが方々であります。

全結合層の名前もtf.layers.denseだったりtf.contrib.layers.fully_connectedだったりと(こちらは名前が違っているのでまだ救いがありますが)、関わっている人たちが自分が慣れている名前や引数をめいめい追加しているだけなんじゃないかと思えてきます。

また、TensorFlowは、Python 2系と3系をサポートしています。

サポートするのは良いのですが、その影響でTensorFlowを利用したサンプルコードがPython 2系に向けたものしか用意されていないときがあります。

Project Magentaなど先進的な取り組みも、コードを見に行くとPython 2.7系でしか動かないと書かれています。

個人的には仕事以外でPython 2を使うことはしたくないので「あーあ」と思いながら画面を閉じます。

直近の事例では、TensorFlow ServingのPython APIがPython 2向けにしか提供されていないということがありました。

なぜPython 2の方が優先されて実装されるのかより、Python 2しか対応していないなら、それならそうとドキュメントに書いておいて欲しいわけです。

https://www.tensorflow.org/serving/setup

Screen Shot 2017-12-13 at 8.49.08

apt-getでTensorFlow Serving Serverをインストールして、TensorFlow Serving向けにモデルを出力したりといろいろした後に、pipでPython用のクライアントAPIをインストールしようとしたら「バージョンが違うよ」と表示されるのは、あまりにも残酷な仕打ちです。

そんなわけで、PRを送ってみるなどしました。

https://github.com/tensorflow/serving/pull/685

この一行で救われる原稿があります。


そんなわけで、TensorFlow Advent Calendar 12日目の記事は終わりです。

明日の担当はLewuatheさんです。