今日のローカルTensorFlowEverywhereイベントの出欠確認!

事前作成された Estimator

TensorFlow.org で表示 Google Colab で実行 GitHub でソースを表示 ノートブックをダウンロード

このチュートリアルでは、Estimator を使用して、TensorFlow でアヤメの分類問題を解決する方法を示します。Estimator は TensorFlow における完全なモデルの高レベルの表現で、スケーリングと非同期トレーニングを行いやすいように設計されています。詳細については、「Estimators」をご覧ください。

TensorFlow 2.0 では、Keras API を使ってこれらのタスクを達成できます。学習しやすいと考えられている API であるため、新しく始める場合は、Keras から始めることをお勧めします。TensorFlow 2.0 で提供されている高レベル API に関する詳細は、「Standardizing on Keras」をご覧ください。

まず最初に

始めるには、最初に TensorFlow と必要となる多数のライブラリをインポートします。

import tensorflow as tf

import pandas as pd

データセット

このドキュメントのサンプルプログラムは、アヤメの花を、萼片花弁のサイズに基づいて、3 つの品種に分類するモデルを構築してテストします。

モデルのトレーニングには、Iris データセットを使用します。Iris データセットには 4 つの特徴量と 1 つのラベルが含まれます。4 つの特徴量は、次に示す各アヤメの植物学的特性を識別します。

  • 萼片の長さ
  • 萼片の幅
  • 花弁の長さ
  • 花弁の幅

この情報に基づき、データを解析する上で役立ついくつかの定数を定義できます。

CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']
SPECIES = ['Setosa', 'Versicolor', 'Virginica']

次に、Keras と Pandas を使用して、Iris データセットをダウンロードして解析します。トレーニング用とテスト用に別々のデータセットを維持することに注意してください。

train_path = tf.keras.utils.get_file(
    "iris_training.csv", "https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv")
test_path = tf.keras.utils.get_file(
    "iris_test.csv", "https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv")

train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)
test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)

データを検査し、4 つの浮動小数型の特徴量カラムと 1 つの int32 ラベルがあることを確認します。

train.head()

各データセットに対し、モデルが予測するようにトレーニングされるラベルを分割します。

train_y = train.pop('Species')
test_y = test.pop('Species')

# The label column has now been removed from the features.
train.head()

Estimator を使ったプログラミングの概要

データのセットアップが完了したので、TensorFlow Estimator を使ってモデルを定義できます。Estimator は、tf.estimator.Estimator から派生したクラスです。TensorFlow は、一群の tf.estimatorLinearRegressor など)を提供しており、一般的な ML アルゴリズムを実装することができます。このほか、独自のカスタム Estimator を作成することもできますが、使用し始めには、事前作成済みの Estimator を使用することをお勧めします。

事前作成済みの Estimator に基づいて TensorFlow プログラムを記述するには、次のタスクを実行する必要があります。

  • 1 つ以上の入力関数を作成する。
  • モデルの特徴量カラムを定義する。
  • Estimator をインスタンス化する。特徴量カラムとさまざまなハイパーパラメータを指定します。
  • Estimator オブジェクトに 1 つ以上のメソッドを呼び出す。データのソースとして適切な入力関数を渡します。

では、アヤメの分類において、これらのタスクをどのように実装するのか見てみましょう。

入力関数を作成する

トレーニング、評価、および予測を行うためのデータを提供する入力関数を作成する必要があります。

入力関数とは、次の要素タプルを出力する tf.data.Dataset オブジェクトを返す関数です。

  • features - 次のような Python ディクショナリ。
    • 各キーが特徴量の名前である。
    • 各値が、特徴量の値のすべてを含む配列である。
  • label - 各サンプルの label の値を含む配列。

入力関数の書式を示すために、単純な実装を次に示します。

def input_evaluation_set():
    features = {'SepalLength': np.array([6.4, 5.0]),
                'SepalWidth':  np.array([2.8, 2.3]),
                'PetalLength': np.array([5.6, 3.3]),
                'PetalWidth':  np.array([2.2, 1.0])}
    labels = np.array([2, 1])
    return features, labels

入力関数を自分で作成すれば、features ディクショナリと label リストを好みに合わせて生成できるようにすることができますが、あらゆる種類のデータを解析できる TensorFlow の Dataset API を使用することをお勧めします。

Dataset API は、多数の一般的な事例を処理することができます。たとえば、Dataset API を使用すると、大量のファイルのレコードを並列して読み取り、単一のストリームに結合することが簡単に行えます。

この例では事を単純にするために、pandas でデータを読み込み、このメモリ内のデータから入力パイプラインを構築します。

def input_fn(features, labels, training=True, batch_size=256):
    """An input function for training or evaluating"""
    # Convert the inputs to a Dataset.
    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

    # Shuffle and repeat if you are in training mode.
    if training:
        dataset = dataset.shuffle(1000).repeat()

    return dataset.batch(batch_size)

特徴量カラムを定義する

特徴量カラムは、特徴量ディクショナリの生の入力データを、モデルがどのように使用すべきかを説明するオブジェクトです。Estimator モデルを作成する際に、モデルが使用する各特徴量を説明する特徴量カラムをモデルに渡します。tf.feature_column モジュールには、モデルに対してデータを表現するためのオプションが多数含まれています。

Iris については、4 つの生の特徴量は数値であるため、Estimator に対して、これら 4 つの各特徴量を 32 ビットの浮動小数点数型の値として表現するように命令する特徴量カラムを構築します。したがって、特徴カラムを作成するためのコードは、次のようになります。

# Feature columns describe how to use the input.
my_feature_columns = []
for key in train.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))

特徴量カラムは、ここに示すものよりもはるかに高度なものに構築することができます。特徴量カラムの詳細については、こちらのガイドをご覧ください。

モデルが生の特徴量をどのように表現するかに関する記述を準備できたので、Estimator を構築することができます。

Estimator をインスタンス化する

アヤメの問題はよく知られた分類問題です。幸いにも、TensorFlow は、次のような事前作成済みの分類子 Estimator を複数用意しています。

アヤメの問題に関しては、tf.estimator.DNNClassifier が最適な選択肢と言えます。この Estimator をインスタンス化する方法を次に示します。

# Build a DNN with 2 hidden layers with 30 and 10 hidden nodes each.
classifier = tf.estimator.DNNClassifier(
    feature_columns=my_feature_columns,
    # Two hidden layers of 30 and 10 nodes respectively.
    hidden_units=[30, 10],
    # The model must choose between 3 classes.
    n_classes=3)
INFO:tensorflow:Using default config.
WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpuejqzijz
INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmpuejqzijz', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true
graph_options {
  rewrite_options {
    meta_optimizer_iterations: ONE
  }
}
, '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}

トレーニングして評価して予測する

Estimator オブジェクトを準備したので、次の項目を行うメソッド呼び出すことができます。

  • モデルをトレーニングする。
  • トレーニングされたモデルを評価する。
  • トレーニングされたモデルを使用して、予測を立てる。

モデルをトレーニングする

次のように、Estimator の train メソッドを呼び出して、モデルをトレーニングします。

# Train the Model.
classifier.train(
    input_fn=lambda: input_fn(train, train_y, training=True),
    steps=5000)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.
Instructions for updating:
Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.
INFO:tensorflow:Calling model_fn.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/adagrad.py:83: calling Constant.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Create CheckpointSaverHook.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...
INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmpuejqzijz/model.ckpt.
INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...
INFO:tensorflow:loss = 1.6864259, step = 0
INFO:tensorflow:global_step/sec: 286.436
INFO:tensorflow:loss = 1.1709123, step = 100 (0.350 sec)
INFO:tensorflow:global_step/sec: 329.127
INFO:tensorflow:loss = 1.0677161, step = 200 (0.304 sec)
INFO:tensorflow:global_step/sec: 332.085
INFO:tensorflow:loss = 1.0121634, step = 300 (0.301 sec)
INFO:tensorflow:global_step/sec: 330.44
INFO:tensorflow:loss = 0.9632655, step = 400 (0.303 sec)
INFO:tensorflow:global_step/sec: 338.518
INFO:tensorflow:loss = 0.9426998, step = 500 (0.295 sec)
INFO:tensorflow:global_step/sec: 336.266
INFO:tensorflow:loss = 0.8951733, step = 600 (0.298 sec)
INFO:tensorflow:global_step/sec: 336.513
INFO:tensorflow:loss = 0.880264, step = 700 (0.297 sec)
INFO:tensorflow:global_step/sec: 342.129
INFO:tensorflow:loss = 0.845543, step = 800 (0.292 sec)
INFO:tensorflow:global_step/sec: 337.993
INFO:tensorflow:loss = 0.826259, step = 900 (0.296 sec)
INFO:tensorflow:global_step/sec: 340.412
INFO:tensorflow:loss = 0.80110097, step = 1000 (0.294 sec)
INFO:tensorflow:global_step/sec: 342.571
INFO:tensorflow:loss = 0.77482253, step = 1100 (0.292 sec)
INFO:tensorflow:global_step/sec: 340.719
INFO:tensorflow:loss = 0.7655463, step = 1200 (0.293 sec)
INFO:tensorflow:global_step/sec: 341.745
INFO:tensorflow:loss = 0.73839176, step = 1300 (0.293 sec)
INFO:tensorflow:global_step/sec: 343.341
INFO:tensorflow:loss = 0.7230158, step = 1400 (0.291 sec)
INFO:tensorflow:global_step/sec: 331.79
INFO:tensorflow:loss = 0.7079378, step = 1500 (0.303 sec)
INFO:tensorflow:global_step/sec: 327.361
INFO:tensorflow:loss = 0.69259924, step = 1600 (0.304 sec)
INFO:tensorflow:global_step/sec: 335.377
INFO:tensorflow:loss = 0.67400044, step = 1700 (0.298 sec)
INFO:tensorflow:global_step/sec: 333.262
INFO:tensorflow:loss = 0.65713406, step = 1800 (0.300 sec)
INFO:tensorflow:global_step/sec: 336.637
INFO:tensorflow:loss = 0.6539304, step = 1900 (0.297 sec)
INFO:tensorflow:global_step/sec: 334.042
INFO:tensorflow:loss = 0.63001454, step = 2000 (0.299 sec)
INFO:tensorflow:global_step/sec: 336.917
INFO:tensorflow:loss = 0.6210363, step = 2100 (0.297 sec)
INFO:tensorflow:global_step/sec: 335.522
INFO:tensorflow:loss = 0.60008836, step = 2200 (0.298 sec)
INFO:tensorflow:global_step/sec: 331.155
INFO:tensorflow:loss = 0.5919484, step = 2300 (0.302 sec)
INFO:tensorflow:global_step/sec: 335.825
INFO:tensorflow:loss = 0.5834999, step = 2400 (0.298 sec)
INFO:tensorflow:global_step/sec: 339.199
INFO:tensorflow:loss = 0.5771369, step = 2500 (0.295 sec)
INFO:tensorflow:global_step/sec: 337.84
INFO:tensorflow:loss = 0.5600395, step = 2600 (0.296 sec)
INFO:tensorflow:global_step/sec: 334.79
INFO:tensorflow:loss = 0.55171585, step = 2700 (0.299 sec)
INFO:tensorflow:global_step/sec: 340.656
INFO:tensorflow:loss = 0.5352994, step = 2800 (0.294 sec)
INFO:tensorflow:global_step/sec: 339.104
INFO:tensorflow:loss = 0.5179188, step = 2900 (0.294 sec)
INFO:tensorflow:global_step/sec: 332.337
INFO:tensorflow:loss = 0.51709044, step = 3000 (0.301 sec)
INFO:tensorflow:global_step/sec: 342.315
INFO:tensorflow:loss = 0.51084745, step = 3100 (0.292 sec)
INFO:tensorflow:global_step/sec: 334.568
INFO:tensorflow:loss = 0.5085169, step = 3200 (0.299 sec)
INFO:tensorflow:global_step/sec: 334.552
INFO:tensorflow:loss = 0.49221683, step = 3300 (0.299 sec)
INFO:tensorflow:global_step/sec: 332.739
INFO:tensorflow:loss = 0.47726077, step = 3400 (0.301 sec)
INFO:tensorflow:global_step/sec: 339.435
INFO:tensorflow:loss = 0.47995022, step = 3500 (0.295 sec)
INFO:tensorflow:global_step/sec: 334.135
INFO:tensorflow:loss = 0.47088915, step = 3600 (0.299 sec)
INFO:tensorflow:global_step/sec: 335.298
INFO:tensorflow:loss = 0.46575528, step = 3700 (0.298 sec)
INFO:tensorflow:global_step/sec: 338.876
INFO:tensorflow:loss = 0.45248568, step = 3800 (0.295 sec)
INFO:tensorflow:global_step/sec: 336.07
INFO:tensorflow:loss = 0.45413518, step = 3900 (0.299 sec)
INFO:tensorflow:global_step/sec: 329.776
INFO:tensorflow:loss = 0.449424, step = 4000 (0.302 sec)
INFO:tensorflow:global_step/sec: 336.618
INFO:tensorflow:loss = 0.44910705, step = 4100 (0.297 sec)
INFO:tensorflow:global_step/sec: 331.476
INFO:tensorflow:loss = 0.43339106, step = 4200 (0.302 sec)
INFO:tensorflow:global_step/sec: 334.667
INFO:tensorflow:loss = 0.42327073, step = 4300 (0.299 sec)
INFO:tensorflow:global_step/sec: 341.304
INFO:tensorflow:loss = 0.42902696, step = 4400 (0.293 sec)
INFO:tensorflow:global_step/sec: 337.535
INFO:tensorflow:loss = 0.4141013, step = 4500 (0.296 sec)
INFO:tensorflow:global_step/sec: 334.259
INFO:tensorflow:loss = 0.40708044, step = 4600 (0.299 sec)
INFO:tensorflow:global_step/sec: 338.034
INFO:tensorflow:loss = 0.40659776, step = 4700 (0.296 sec)
INFO:tensorflow:global_step/sec: 335.131
INFO:tensorflow:loss = 0.40055385, step = 4800 (0.299 sec)
INFO:tensorflow:global_step/sec: 333.393
INFO:tensorflow:loss = 0.3965168, step = 4900 (0.300 sec)
INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 5000...
INFO:tensorflow:Saving checkpoints for 5000 into /tmp/tmpuejqzijz/model.ckpt.
INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 5000...
INFO:tensorflow:Loss for final step: 0.3866651.

<tensorflow_estimator.python.estimator.canned.dnn.DNNClassifierV2 at 0x7f027bc2dac8>

Estimator が期待するとおり、引数を取らない入力関数を指定しながら、input_fn 呼び出しを lambda にラッピングして引数をキャプチャするところに注意してください。steps 引数はメソッドに対して、あるトレーニングステップ数を完了した後にトレーニングを停止するように指定しています。

トレーニングされたモデルを評価する

モデルのトレーニングが完了したので、そのパフォーマンスに関する統計を得ることができます。次のコードブロックは、テストデータに対してトレーニングされたモデルの精度を評価します。

eval_result = classifier.evaluate(
    input_fn=lambda: input_fn(test, test_y, training=False))

print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Starting evaluation at 2021-02-12T23:04:18Z
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Restoring parameters from /tmp/tmpuejqzijz/model.ckpt-5000
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Inference Time : 0.20944s
INFO:tensorflow:Finished evaluation at 2021-02-12-23:04:18
INFO:tensorflow:Saving dict for global step 5000: accuracy = 0.93333334, average_loss = 0.45099404, global_step = 5000, loss = 0.45099404
INFO:tensorflow:Saving 'checkpoint_path' summary for global step 5000: /tmp/tmpuejqzijz/model.ckpt-5000

Test set accuracy: 0.933


train メソッドへの呼び出しとは異なり、評価するsteps 引数を渡していません。eval の input_fn データの単一のエポックのみを返します。

eval_result ディクショナリには、average_loss(サンプル当たりの平均損失)、loss(ミニバッチ当たりの平均損失)、および Estimator の global_step の値(実行したトレーニングイテレーションの回数)も含まれます。

トレーニングされたモデルから予測(推論)を立てる

良質の評価結果を生み出すトレーニング済みのモデルを準備できました。これから、このトレーニング済みのモデルを使用し、ラベル付けできない測定に基づいてアヤメの品種を予測します。トレーニングと評価と同様に、単一の関数呼び出して予測を行います。

# Generate predictions from the model
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
    'SepalLength': [5.1, 5.9, 6.9],
    'SepalWidth': [3.3, 3.0, 3.1],
    'PetalLength': [1.7, 4.2, 5.4],
    'PetalWidth': [0.5, 1.5, 2.1],
}

def input_fn(features, batch_size=256):
    """An input function for prediction."""
    # Convert the inputs to a Dataset without labels.
    return tf.data.Dataset.from_tensor_slices(dict(features)).batch(batch_size)

predictions = classifier.predict(
    input_fn=lambda: input_fn(predict_x))

predict メソッドは Python イテラブルを返し、各サンプルの予測結果のディクショナリを生成します。次のコードを使って、予測とその確率を出力します。

for pred_dict, expec in zip(predictions, expected):
    class_id = pred_dict['class_ids'][0]
    probability = pred_dict['probabilities'][class_id]

    print('Prediction is "{}" ({:.1f}%), expected "{}"'.format(
        SPECIES[class_id], 100 * probability, expec))
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Restoring parameters from /tmp/tmpuejqzijz/model.ckpt-5000
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
Prediction is "Setosa" (74.9%), expected "Setosa"
Prediction is "Versicolor" (52.7%), expected "Versicolor"
Prediction is "Virginica" (67.5%), expected "Virginica"