LoginSignup
15
16

More than 5 years have passed since last update.

TensorFlowの増大するAPIについて少し考えてみた

Posted at

イントロ

ついに TensorFlow Dev SummitにてTensorFLow 1.0 がアナウンスされた.Graph コンパイラ XLA の試験リリースの話題もあるが,個人的には,TensorFlowのAPI (Application Programming Interface) 構成がどうなるかが気になっていた.

(Google Developers Blog "Announcing TensorFlow 1.0" より抜粋.)

  • Higher-level API modules tf.layers, tf.metrics, and tf.losses - brought over from tf.contrib.learn after incorporating skflow and TF Slim

ここ最近,乱立模様を呈していた High-level API について,どう整理されるかversion 1.0に期待していたので,状況を少し調べてみた.

初期のCNNモデルのコード

以前,High-level API が多くなかった状況にて,CNN(Convolutional Neural Network)モデルを扱うにあたり,以下のように自前のクラスを用意してコーディングを行っていた.

#   my_lib_nn.py
#   例えば...  Convolution 2-D Layer
class Convolution2D(object):
    '''
      constructor's args:
          input     : input image (2D matrix)
          input_siz ; input image size
          in_ch     : number of incoming image channel
          out_ch    : number of outgoing image channel
          patch_siz : filter(patch) size
          weights   : (if input) (weights, bias)
    '''
    def __init__(self, input, input_siz, in_ch, out_ch, patch_siz, activation='relu'):
        self.input = input      
        self.rows = input_siz[0]
        self.cols = input_siz[1]
        self.in_ch = in_ch
        self.activation = activation

        wshape = [patch_siz[0], patch_siz[1], in_ch, out_ch]
                w_cv = tf.Variable(tf.truncated_normal(wshape, stddev=0.1), 
                            trainable=True)
        b_cv = tf.Variable(tf.constant(0.1, shape=[out_ch]), 
                            trainable=True)
        self.w = w_cv
        self.b = b_cv
        self.params = [self.w, self.b]

    def output(self):
        shape4D = [-1, self.rows, self.cols, self.in_ch]

        x_image = tf.reshape(self.input, shape4D)  # reshape to 4D tensor
        linout = tf.nn.conv2d(x_image, self.w, 
                  strides=[1, 1, 1, 1], padding='SAME') + self.b
        if self.activation == 'relu':
            self.output = tf.nn.relu(linout)
        elif self.activation == 'sigmoid':
            self.output = tf.sigmoid(linout)
        else:
            self.output = linout

        return self.output

下請けのライブラリとして tf.nn.xxx() の関数を用いるが,それを使いやすくするwrapperを作って利用するやり方である.自作ライブラリはカスタマイズも容易であるが,それがあだとなることもあって,細かいメンテナンスも自分で行わなければならない.(大したライブラリではありませんが...)

Keras を知ってからは「Kerasを使うべきか」と選択肢に入れていたが,慣れや細かいデバッグのしやすさ,柔軟性を考え,TensorFlowライブラリを直接使うコーディングスタイルが多かった.

TensorFlow Slim vs. tf.layers

「薄いTensorFlow wapper」という視点で注目していたのが,"Slim" である.Qiitaでも何件か取り上げられているようである.これを用いてMNISTを分類するためのコードを書くと以下のようになった.

import numpy as np
import tensorflow as tf
import tensorflow.contrib.slim as slim
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("../MNIST_data/", one_hot=True)

# Create the model
def my_nn(images, keep_prob):
   net = slim.layers.conv2d(images, 32, [5,5], scope='conv1')
   net = slim.layers.max_pool2d(net, [2,2], scope='pool1')
   net = slim.layers.conv2d(net, 64, [5,5], scope='conv2')
   net = slim.layers.max_pool2d(net, [2,2], scope='pool2')
   net = slim.layers.flatten(net, scope='flatten3')
   net = slim.layers.fully_connected(net, 1024, scope='fully_connected4')
   net = slim.layers.dropout(net, keep_prob)
   net = slim.layers.fully_connected(net, 10, activation_fn=None, 
                                        scope='fully_connected5')
   return net

def inference(x, y_, keep_prob):
    x_image = tf.reshape(x, [-1, 28, 28, 1])
    y_pred = my_nn(x_image, keep_prob)

    slim.losses.softmax_cross_entropy(y_pred, y_)
    total_loss = slim.losses.get_total_loss()
    correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    return total_loss, accuracy, y_pred

Nueral network モデルを短いコードでわかりやすく記述できる.また,関数"inference"内で用いたように,損失関数も slim のAPIで書くことができた.かなり使いやすい印象をもった.

次に今回,TensorFlow 1.0 で用意された tf.layers のモジュールを調べてみた.APIのドキュメントにしっかり説明があったので,それを参考にコーディングを行った.

Fig. TensorFlow APIドキュメントより(イメージ抜粋)
Module_tf_layers.png

上記Googleアナウンスの抜粋にもあるが,tf.contrib.layers というのもドキュメントに記載があるが,今回の tf.layers は別物なので要注意である.

以下は,tf.layersを用いたCNNのコードである.

import tensorflow as tf
from tensorflow.python.layers import layers
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("../MNIST_data/", one_hot=True)

# Create the model
def my_nn(images, drop_rate):
   net = tf.layers.conv2d(images, 32, [5,5], padding='same', 
                                activation=tf.nn.relu, name='conv1')
   net = tf.layers.max_pooling2d(net, pool_size=[2,2], strides=[2,2], 
                                name='pool1')
   net = tf.layers.conv2d(net, 64, [5,5], padding='same', 
                                activation=tf.nn.relu, name='conv2')
   net = tf.layers.max_pooling2d(net, pool_size=[2,2], strides=[2,2], 
                                name='pool2')
   net = tf.reshape(net, [-1, 7*7*64])
   net = tf.layers.dense(net, 1024, activation=tf.nn.relu, name='dense1')
   net = tf.layers.dropout(net, rate=drop_rate)
   net = tf.layers.dense(net, 10, activation=None, name='dense2')
   return net

def inference(x, y_, keep_prob):
    x_image = tf.reshape(x, [-1, 28, 28, 1])
    drop_rate = 1.0 - keep_prob
    y_pred = my_nn(x_image, drop_rate)

    loss = tf.losses.softmax_cross_entropy(y_, y_pred)
    correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    return loss, accuracy, y_pred

当初,「slimみたいなものだろう」という予断を持ってslimのコードから始めたのだが,意外に関数のスペックに違いがあり戸惑った.("my_nn()" 関数内のコードに着目下さい.)

関数の名前違い (max_pool2d <-> max_pooling2d,fully_connected <-> dense) は「ありがち」として受け入れるとして,引数のキーワードが違っていたり,引数のディフォルトに相違があったりと,細かい調整が必要であった.特に気になった(許せない)ポイントとして,Dropout のパラメータとして,処理後にユニット影響を残す割合 "keep_prob" を与える仕様となっていたものを,効果を省く(落とす)割合を与える仕様に変更している点である.(対策として drop_rate = 1.0 - keep_prob の一行を入れています.)プログラマの「好み」で分かれる点なのかも知れないが,ここはこれまでとの互換性を考えて欲しかった...

これでは,TensorFlow API の整理,整頓ということにはなっていないなと,今の仕様に,やや期待外れな印象を持った.

さてどうしよう?

以上の状況を考慮して選択肢をあげてみる.

  • 準備中の Keras2 を待つ.ユーザー数も多いので,APIの洗練度も継続的に向上される期待も大きい.
  • まだ登場直後であることを考慮し, tf.layers, tf.metrics, tf.losses の今後の完成度upに期待する.
    (オープンソースなのだから,「こうした方がいい」とGitHubで積極的に発言していくのがベストですが.)
  • さらに別のAPIを調べてみる.(tf.contrib.layers, TFLearn など.)
  • 自分の持っているクラスライブラリを見捨てず,メンテしながら使っていく. (気になるAPIのいいところを取り入れていくという作戦もとれます.)

Dropoutのパラメータ(残す割合 or 捨てる割合)のように詳細については「好み」が反映されることが多いので,「どれがベストなAPIか」であまり悩んでも仕方ない気もしてきた.今回取り上げたのが,画像を扱うCNN用関数が中心であったが,Deep Learning の柔軟なモデリング能力(例えば RNN や生成系モデルなど)を考えると,APIの細かいところにこだわることなく(適宜,使い分けながら),広範囲な技術内容をフォローしていく方が建設的かも知れない.

(ご意見,アドバイス等ありましたら,コメント承ります.)
(執筆時のプログラミング環境は,以下になります: Python 3.5.2, TensorFlow 1.0.0 )

参考文献,Web site

15
16
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
16