この記事の対象読者
TensorFlowを用いて学習をするとき、最初にぱっと思い浮かぶ方法は以下ではないでしょうか。
- めんどくさいからKeras APIを用いる
- がっつり生のTensorFlowを書く(tf.Sessionとか使う書き方)
なのですが、実はもう一つ選択肢があります。
それが Estimator というAPIを用いる方法で、主にtf.data.Dataset を用いた学習と相性が良いようです。
つまり、TFReocrd形式のファイルからデータを読み出すときはEstimatorを用いるという選択肢もアリということになります(もちろんほかの形式でもできます)。
また、分散学習をやる場合にはその設定が比較的容易というメリットもあります。
さらに、AWSの機械学習マネージドサービスであるSageMakerを使用する場合には、この書き方の方が親和性が良いようです。
Estimator自体は、Keras APIで作成したモデルを変換しても得られます。この記事では、あまり情報も多くないということもあり、まずはKeras APIを変換するコードを紹介したいと思います。
さて、ということで前置きはこれくらいにして、実際の書き方について触れていきます。
tf.estimator.Estimatorの使い方
これ自体を使ったサンプルコードは検索すると割と見つかります。しかし、同じくtf.estimatorに属する「DNNClassifier」などの既に構築されているモデルに対してのものが多く、自作のモデルをカスタムして使用しているものはなかなか見かけない印象です。
ということで、この記事では自作のモデル(と言ってもかなり単純なものにしますが)でこのEstimatorを使用するとどうなるかについてまとめていきます。前置きでも軽く述べましたが、Keras APIで作成したモデルを変換するスタイルで紹介します。では早速サンプルコードです。例によって実行環境は以下になります。
- OS:Windows10
- Pythonのバージョン:3.7.9
- tensorflowのバージョン:2.1.0
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Flatten, Lambda, Conv2D
from tensorflow.keras.optimizers import RMSprop
# 保存しているSparseTensorの形状 ※ Conv2Dでは3階テンソルを要求されるのでこの形状
sparse_shape = [12, 12, 1]
# デシリアライズ用コールバック関数
def input_fn(batch_size, num_epochs, filepath_list):
features = {"train": tf.io.SparseFeature(index_key=["rows", "cols", "channels"],
value_key="values",
dtype=tf.float32,
size=sparse_shape),
"label": tf.io.VarLenFeature(dtype=tf.float32)
}
def parse_example(example):
train_data = tf.io.parse_single_example(example, features=features)
x = train_data["train"]
y = tf.sparse.to_dense(train_data["label"])
return x, y
ds = tf.data.TFRecordDataset(filepath_list) \
.map(parse_example) \
.batch(batch_size) \
.repeat(num_epochs)
return ds
def sample_keras_model_fn():
# Constants
kernel_size = (4, 4)
num_filters = 4
inputs = Input(batch_size=batch_size, shape=sparse_shape, sparse=True)
X = Lambda(tf.sparse.to_dense)(inputs)
X = Conv2D(filters=num_filters, kernel_size=kernel_size)(X)
X = Flatten()(X)
X = Dense(1, activation="relu")(X)
model = tf.keras.models.Model(inputs=inputs, outputs=X)
model.compile(loss="MSE", optimizer=RMSprop())
return model
# メイン
if __name__ == "__main__":
#コンソールにログを表示するために必要
tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.INFO)
# 各種パラメータ
batch_size = 2
epochs = 3
# TFRecordsファイルへのパス & ログ保存パス
filepath = "./sequence_conv.tfrecords"
model_dir = "./models/"
# Kerasモデル構築
model = sample_keras_model_fn()
# ログの出力先・ログ取得タイミングの設定
config = tf.estimator.RunConfig(model_dir=model_dir, log_step_count_steps=5)
# keras model ⇒ Estimator の変換
estimator = tf.keras.estimator.model_to_estimator(model, model_dir=model_dir, config=config)
# keras model ⇒ Estimator の変換
train_spec = tf.estimator.TrainSpec(input_fn=lambda: input_fn(batch_size, epochs, [filepath]))
eval_spec = tf.estimator.EvalSpec(input_fn=lambda: input_fn(batch_size, epochs, [filepath]))
# 学習実行
tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)
"""
【出力結果(一部)】
INFO:tensorflow:Saving checkpoints for 0 into ./models/model.ckpt.
INFO:tensorflow:loss = 0.28118426, step = 0
INFO:tensorflow:global_step/sec: 48.9893
INFO:tensorflow:loss = 0.27649117, step = 5 (0.102 sec)
INFO:tensorflow:global_step/sec: 559.42
INFO:tensorflow:loss = 0.21459994, step = 10 (0.010 sec)
INFO:tensorflow:Saving checkpoints for 15 into ./models/model.ckpt.
"""
以前書いた記事のモデルを流用しました。
tf.estimator.Estimator を使用するうえで重要なのは以下の点です。
- kerasで作成したモデルをEstimatorに変換するにはtf.keras.estimator.model_to_estimator()を用いる
- 学習を行うときは 作成した estimator の train メソッドを使うか、tf.estimator.train_and_evaluate()を用いる
- 上記 train_and_evaluate を用いる場合には、train_specとeval_spec (=学習用データ・評価用データを設定する)を引数に与える必要がある
- tf.estimator.TrainSpec, tf.estimator.EvalSpec の設定のために、tf.data.Dataset を返す関数 input_fn を用意する必要がある
- 学習中のLoss等をコンソール(標準出力)で監視するためには、ログのレベルをINFOレベルにする必要がある
- やり方は「tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.INFO) 」を記載すればOK
最低限のやり方については上記を押さえておけば問題ないと思います。地味に注意なのがログのレベル変更です。これを設定していないと、正常に動いている限りにおいて標準出力には何も表示されません。したがって、今学習がうまく動いているのかイマイチ分からないなんてことになりかねません。
注意点はもう一つあり、tf.estimator.TrainSpec・tf.estimator.EvalSpecの二つで引数にしている「input_fn」は、引数を持たず、tf.data.Dataset を返す必要があるということです。本サンプルコードでは、input_fn でバッチサイズ・エポック数・データ読み出しファイルを指定しているので、それを引数にとる形になっています。これを解決するために、lambda関数を用いているので、そこはご注意ください。
※ちなみに、model_to_estimator・RunConfig で指定している “model_dir” は、Tensorboard用のデータを出力する先のフォルダを指します。ログはテキスト形式で吐き出されるわけでないことに注意です。
まとめ
今回の記事では、TensorFlowの高レベルAPIのひとつであるEstimatorについて、Kerasで作成したモデルを変換して動作させるためにどうすればよいかをまとめました。
大規模データを扱うようになるとGPUを使用したり、複数GPUにデータを並列で流す分散学習を検討することになります。その際に、分散学習の設定がEstimatorの形式ではやりやすいというメリットがあるので、この書き方は覚えておいて損はないはずです。
それでは、今回は以上とします。最後までお読みいただきありがとうございました!
コメント