TensorflowでMNISTの次に学ぶと良さそうなこと(その1)

投稿者: | 2017年9月19日

tensorflowで遊びはじめました

世間で流行っているtensorflowで遊び始めました。記録がてらに遊んだことをまとめてみます。

世の中の記事はどうもtensorflowのチュートリアルの「MNISTをやってみた」とか、「CNNを実行してみた。」などの記事が多い印象があります。
一方で「手書き文字を認識するアプリを作ろう」といった具体的にアプリを作ると必要になってくる情報が少ない印象があるのでまとめてみます。

  • Tensorflowの学習のさせかた(Estimator)
  • Tensorflowの学習結果の利用方法(SavedModel & tensorflow serving)

を中心にまとめてみます。

自分の tensorflow や DeepLearning との付き合い方

自分は数学の天才でも無いのでニューラルネットワークや深層学習の専門家を目指すつもりはないので

  • ネットワークは誰か(論文など)を参考に作る
  • ネットワークを自分でこねくり回したりはしない
  • 学習は自前のPCで実施したりする
  • 学習結果を利用してアプリは公開する(かも)

くらいの立ち位置です。

あと、tensorflowを使うにあたって決まりは無いのですが、contribの下にあるパッケージは極力使わない方針でやってみようと考えています。
contribの下にあるパッケージはどうも節操なく色々なものが入ってそうで、流行り廃りも速そうなので近寄りたくないのです……。

TensorFlow’s high-level machine learning API (Estimator )

tensorflowは以前はとてもプリミティブなAPIしか提供していない印象があったのですが、現在は割と高水準なAPIも提供しているようです。
特にestimatorは、tensorflowのsessionなどを隠蔽してくれて、モデルの構築、学習、評価のフローを隠蔽してくれるので利用すると色々楽です。

使い方としては、公式のチュートリアルを参考にするのが良いと思います。
この記事に書いてある内容は基本的にこのチュートリアルの抜粋です。

Tensorflowが提供してくれているEstimatorとしては以下のものがあります。

  • tf.estimator.LinearClassifier
  • tf.estimator.LinearRegressor
  • tf.estimator.DNNClassifier
  • tf.estimator.DNNRegressor
  • tf.estimator.DNNLinearCombinedClassifier
  • tf.estimator.DNNRegressor

これらを利用する場合は、それぞれのモジュールの説明を見れば使えるかと思います。

Estimatorを自作する

上記のモジュールを使わずに自前でネットワークを組む場合(この場合が多い?)は、Estimatorを自分で作ることになります。
Estimatortf.estimator.Estimator で作ります。
引数の詳細はマニュアルを読むのが良いと思いますが、とりあえずmodel_fnparamsだけ知っていれば使えるようになると思います。

nn = tf.estimator.Estimator(model_fn=model_fn, params=model_params)
# model_fnは、EstimatorSpecを返す関数を指定します。
# paramsには学習時のハイパーパラメータを指定します。

model_fnの作成

ここで、model_fnは実際どうやって作るのか?ということになると思います。

model_fnはmodeとfeatureとparamsの3つを引数に取って、それらの情報からEstimatorSpecを作成して返す関数です。

引数はそれぞれ

  • modeはこれから実行するものが、予測なのか学習なのかを示す値です( tf.estimator.ModeKeys.PREDICTなど)
  • featureは名前の通り入力データです
  • paramsはtf.estimator.Estimatorで渡したハイパーパラメータが渡って来ます

となります。
返り値のEstimatorSpecの作成方法を次は見てみます。

EstimatorSpecの作成方法

EstimatorSpecは、tf.estimator.EstimatorSpec で作成できます。

tf.estimator.EstimatorSpec(mode=mode,
                                      predictions=predictions,
                                      loss=loss,
                                      train_op=train_op,
                                      eval_metric_ops=eval_metric_ops,
                                      export_outputs=export_outputs,
                                      training_chief_hooks=None,
                                      training_hooks=None,
                                      scaffold=None)

この関数の引数をそれぞれmodel_fn内で設定していくことになります。
具体的には、

  • train_op
  • loss
  • predictions

の三つをとりあえず指定しておけば良いです。

例として

  • 3層の隠れ層からなるネットワーク
  • crossentropyをloss 関数
  • 最適化手法はGradientDescentOptimizer

というものを作成してみます

def model_fn(features, labels, mode, params):
    # ここで渡ってくるparamsというのはEstimator作成時の引数のparamsです

    hidden1 = tf.layers.dense(inputs=feature['x'], units=10, activation=tf.nn.relu)
    hidden2 = tf.layers.dense(inputs=hidden1,units=20, activation=tf.nn.relu)
    hidden3 = tf.layers.dense(inputs=hidden2, units=10, activation=tf.nn.relu)
    y = tf.layers.dense(inputs=hidden3, units=3, activation=tf.nn.softmax)

    loss = None
    train_op = None

   # 予測を実行する場合はmodeがtf.estimator.ModeKeys.PREDICTが指定されています。
   # この時は訓練が実行されないようにしておきます。
   # 要は、lossとtrain_opはNoneのままです。
    if mode != tf.estimator.ModeKeys.PREDICT:
        y_ = tf.one_hot(labels, depth=3)
        loss = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
        train_op = tf.train.GradientDescentOptimizer(0.01).minimize(loss, global_step=tf.train.get_global_step())

    predictions = y

    spec = tf.estimator.EstimatorSpec(mode=mode,
                                      predictions=predictions,
                                      loss=loss,
                                      train_op=train_op)
    return spec

Estimatorの作成

ここまでで、Estimatorを作成する準備が出来たのでEstimatorを作ってみます。
上の関数では特にハイパーパラメータの指定が無い(酷い!)ので、適当に渡してEstimatorを作ります。

model_params = {}
nn = tf.estimator.Estimator(model_fn=model_fn,  params=model_params)

無事Estimatorを作ることが出来たはずです。
それでは、このEstimatorを使って学習、評価、予測を実行してみます。

Estimatorを使った学習、評価、予測

Estimatorを使って学習をする場合は、作成したEstimatorのtrainメソッドを呼べば自動的に学習してくれます。
trainの第一引数は、教師データを返すような関数を指定します。

例えば以下のような関数です。

# 訓練データを返す関数
train_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x":  np.array(train_x, dtype='float32')} ,
    y=np.array(train_y, dtype='int32'),
    num_epochs=None,
    shuffle=True)

上記の関数を入力として、stepsは5000にして学習を行なう場合は以下のようになります。

# Train
nn.train(input_fn=train_input_fn, steps=5000)

評価を実行する場合は、evaluateメソッドを呼ぶことになります。
引数としては、trainメソッドと同様に入力データを返す関数を指定します。

# テストデータを返す関数
test_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": np.array(test_x, dtype='float32')},
    y=np.array(test_y, dtype='int32'),
    num_epochs=1,
    shuffle=False)

ev = nn.evaluate(input_fn=test_input_fn)

# 評価結果を出力してみる
for key in sorted(ev):
  print("%s: %s" % (key, ev[key]))

予測を実行する場合には、predictメソッドを呼べば良いです。
引数としては、trainメソッドと同様に入力データを返す関数を指定します。

# Print out predictions
predict_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": np.array(test_x,dtype='float32')},
    num_epochs=1,
    shuffle=False)
predictions = nn.predict(input_fn=predict_input_fn)
for i, j  in enumerate(predictions):
    print(i, j)

Estimatorのまとめ

  • Estimatorという学習、評価、予測をまとめてくれる便利なフレームワークがあります
  • Estimatorを作成するには、EstimatorSpecを返すmodel関数を定義します
    • model関数には実行モードとデータとハイパーパラメータが渡ってきます
    • model関数はmodeと予測結果、誤差関数、最適化手法を定義し、これらの情報からEstimatorSpecを作成します
  • Estimatorを作成すれば、train, evaluate, predictの関数で学習、評価、予測が実行できます
    • それぞれのメソッドには、入力を与える関数を定義する必要があります
    • tf.estimator.inputs.numpy_input_fnなどを利用すれば手軽に実装できます

savedmodelについて

書くのに疲れたので、明日以降に別記事にします。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です