ホーム » 「TensorFlow 2.0 Guide (Alpha)」タグがついた投稿

タグアーカイブ: TensorFlow 2.0 Guide (Alpha)

TensorFlow 2.0 Alpha : ガイド : サービングへの単純なパス

TensorFlow 2.0 Alpha : ガイド : サービングへの単純なパス (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/25/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : サービングへの単純なパス

SavedModel は重みと計算を含む、完全な TensorFlow プログラムを含みます。それは実行するための元のモデル構築コードを必要としません、これはそれを (TFLite, TensorFlow.js や TensorFlow Serving そして (TFHub による) モデル共有で) 配備のために有用にします。

Python のモデルのためのコードを持ち重みをそれにロードすることを望む場合には、訓練チェックポイント・ガイド を見てください。

簡単な紹介のために、このセクションでは事前訓練された Keras モデルをエクスポートしてそれで画像分類リクエストにサービスを提供します。ガイドの残りは詳細を補足して SavedModels を作成する他の方法を議論します。

from __future__ import absolute_import, division, print_function
!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf
from matplotlib import pyplot as plt
import numpy as np

file = tf.keras.utils.get_file(
    "grace_hopper.jpg",
    "https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg")
img = tf.keras.preprocessing.image.load_img(file, target_size=[224, 224])
plt.imshow(img)
plt.axis('off')
x = tf.keras.preprocessing.image.img_to_array(img)
x = tf.keras.applications.mobilenet.preprocess_input(
    np.array(img)[tf.newaxis,...])
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg
65536/61306 [================================] - 0s 0us/step

実行サンプルとしてグレース・ホッパーの画像を、そして Keras 事前訓練された画像分類モデルを使用します、何故ならばそれは簡単に利用できるからです。カスタムモデルも動作します、そして詳細は後でカバーされます。

pretrained_model = tf.keras.applications.MobileNet()
result_before_save = pretrained_model(x)
print("Result before saving", 
      tf.keras.applications.mobilenet.decode_predictions(
          result_before_save.numpy())[0][0])
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.6/mobilenet_1_0_224_tf.h5
17227776/17225924 [==============================] - 1s 0us/step
Downloading data from https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json
40960/35363 [==================================] - 0s 2us/step
Result before saving ('n03763968', 'military_uniform', 0.701243)

この画像のための top 予測は “military uniform” (軍服) です。

tf.saved_model.save(pretrained_model, "/tmp/mobilenet/1/")

最後のディレクトリ (ここでは /1/) は貴方のモデルのこのバージョンに関する数字です – それは TensorFlow Serving のようなツールに同じモデルの異なるバージョンの相対的な新鮮さについて推理することを可能にします。

SavedModels はシグネチャと呼ばれる名前付き関数を持ちます。Keras モデルは serving_default シグネチャ・キーの基で forward パスをエクスポートします。The SavedModel command line interface はディスク上の SavedModels を調査するために有用です :

!saved_model_cli show --dir /tmp/mobilenet/1 --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
  inputs['input_1'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 224, 224, 3)
      name: serving_default_input_1:0
The given SavedModel SignatureDef contains the following output(s):
  outputs['reshape_2'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1000)
      name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict

tf.saved_model.load で SavedModel を python にロードし戻して Admiral ホッパーの画像がどのように分類されるかを見ることができます。

loaded = tf.saved_model.load("/tmp/mobilenet/1/")
print(list(loaded.signatures.keys()))  # ["serving_default"]
['serving_default']

インポートされたシグネチャは常に辞書を返します。

infer = loaded.signatures["serving_default"]
print(infer.structured_outputs)
{'reshape_2': TensorSpec(shape=(None, 1000), dtype=tf.float32, name='reshape_2')}

SavedModel からの推論の実行は元のモデルと同じ結果を与えます。

labeling = infer(tf.constant(x))["reshape_2"]
print("Result after saving and loading", 
      tf.keras.applications.mobilenet.decode_predictions(
          labeling.numpy())[0][0])
Result after saving and loading ('n03763968', 'military_uniform', 0.701243)

 

モデルをサーブする

SavedModels は Python から利用可能ですが、プロダクション環境は典型的には推論のために専用サービスを望みます。これは容易で TensorFlow Serving を使用して SavedModel からセットアップします。

ノートブックやローカルマシンに tensorflow_model_server をインストールするための手順を含む、serving の詳細については TensorFlow Serving REST チュートリアル を見てください。概観としては、上でエクスポートされた mobilenet モデルをサーブするために単に model server に SavedModel ディレクトリをポイントさせます :

nohup tensorflow_model_server \
  --rest_api_port=8501 \
  --model_name=mobilenet \
  --model_base_path="/tmp/mobilenet" >server.log 2>&1
  
  Then send a request.
  
>     ip install -q requests
>     port json
>     port numpy
>     port requests
>     ta = json.dumps({"signature_name": "serving_default", 
>                      "instances": x.tolist()})
>     aders = {"content-type": "application/json"}
>     on_response = requests.post('http://localhost:8501/v1/models/mobilenet:predict', 
>                                 data=data, headers=headers)
>     edictions = numpy.array(json.loads(json_response.text)["predictions"])

結果としての予測は Python からの結果と同一です。

 

SavedModel フォーマット

SavedModel はシリアライズされたシグネチャと変数値や語彙のようなそれらを実行するために必要な状態を含むディレクトリです。

!ls /tmp/mobilenet/1  # assets  saved_model.pb  variables
assets  saved_model.pb  variables

saved_model.pb は名前付けられたシグネチャのセットを含みます、それぞれが関数を識別します。SavedModels はシグネチャの複数のセット (saved_model_cli のために tag_set 引数で識別されるマルチ MetaGraphs) を含むかも知れませんが、これは稀です。シグネチャの複数セットを作成する API は tf.Estimator.experimental_export_all_saved_models と TensorFlow 1.x では tf.saved_model.Builder を含みます。

!saved_model_cli show --dir /tmp/mobilenet/1 --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"
SignatureDef key: "serving_default"

variables ディレクトリは標準的な訓練チェックポイントを含みます (訓練チェックポイント・ガイド を見てください)。

!ls /tmp/mobilenet/1/variables
variables.data-00000-of-00001  variables.index

assets ディレクトリは TensorFlow グラフにより使用されるファイルを含みます、例えば語彙テーブルを初期化するために使用されるテキストファイルです。この例ではそれは未使用です。

SavedModels は TensorFlow グラフで使用されない任意のファイルのための assets.extra ディレクトリを持つかもしれません、例えば SavedModel で何をするかについての消費者のための情報です。TensorFlow 自身はこのディレクトリは使用しません。

 

カスタムモデルをエクスポートする

最初のセクションでは、tf.saved_model.save が tf.keras.Model オブジェクトのためのシグネチャを自動的に決定しました。これは、Keras モデルオブジェクトがエクスポートするための明確なメソッドと既知の入力 shape を持つために動作しました。tf.saved_model.save は単に低位モデル構築 API でも同様に動作しますが、モデルをサーブすることを計画しているのであれば、シグネチャとしてどの関数を使用するかを示す必要があるでしょう。

class CustomModule(tf.Module):

  def __init__(self):
    super(CustomModule, self).__init__()
    self.v = tf.Variable(1.)

  @tf.function
  def __call__(self, x):
    return x * self.v

  @tf.function(input_signature=[tf.TensorSpec([], tf.float32)])
  def mutate(self, new_v):
    self.v.assign(new_v)

module = CustomModule()

このモジュールは tf.function で修飾される 2 つのメソッドを持ちます。SavedModel が tf.saved_model.load 経由で Python プログラムに再ロードされる場合、これらの関数が SAvedModel に含まれて利用可能である一方で、サービング・シグネチャを明示的に宣言することなしに、TensorFlow Serving と saved_model_cli のようなツールはそれらにアクセスできません。

module.mutate は input_signature を持ちますので、その計算グラフを SavedModel にセーブするために十分な情報が既にあります。__call__ はシグネチャを持ちませんのでこのメソッドはセーブする前に呼び出される必要があります。

module(tf.constant(0.))
tf.saved_model.save(module, "/tmp/module_no_signatures")

input_signature がない関数のためには、セーブする前に使用された任意の入力 shape がロードの後で利用可能です。__call__ を単なるスカラーで呼び出しましたので、それはスカラー値だけを受け取ります。

imported = tf.saved_model.load("/tmp/module_no_signatures")
assert 3. == imported(tf.constant(3.)).numpy()
imported.mutate(tf.constant(2.))
assert 6. == imported(tf.constant(3.)).numpy()

関数はベクトルのような新しい shape は受け取りません。

imported(tf.constant([3.]))
ValueError: Could not find matching function to call for canonicalized inputs ((,), {}). Only existing signatures are [((TensorSpec(shape=(), dtype=tf.float32, name=u'x'),), {})].

get_concrete_function は貴方にそれを呼び出すことなしに関数に入力 shape を追加させます。それは Tensor 引数の代わりに tf.TensorSpec オブジェクトを取り、入力の shape と dtype を示します。shape は任意の shape が受け取れることを示す None か、軸 size のリストです。軸 size が None の場合には、その軸に対して任意のサイズが受け取れます。tf.TensorSpecs はまた名前を持つことができて、これは関数の引数キーワードがデフォルトです (ここでは “x”)。

module.__call__.get_concrete_function(x=tf.TensorSpec([None], tf.float32))
tf.saved_model.save(module, "/tmp/module_no_signatures")
imported = tf.saved_model.load("/tmp/module_no_signatures")
assert [3.] == imported(tf.constant([3.])).numpy()

tf.keras.Model and tf.Module のようなオブジェクトにアタッチする関数と変数は imort で利用可能ですが、多くの Python 型と属性は失われます。Python プログラム自身は SavedModel にセーブされません。

シグネチャとしてエクスポートした任意の関数を識別しませんので、それは何も持ちません。

!saved_model_cli show --dir /tmp/module_no_signatures --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"

 

エクスポートするシグネチャを識別する

関数がシグネチャであるべきであることを示すために、セーブするときに signatures 引数を指定します。

call = module.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32))
tf.saved_model.save(module, "/tmp/module_with_signature", signatures=call)

tf.function を最初に get_concrete_function で ConcreteFunction に変換したことが分かるでしょう。これは必要です、何故ならば関数は固定された input_signature なしで作成されましたので、それに関連する Tensor 入力の明確なセットを持たなかったからです。

!saved_model_cli show --dir /tmp/module_with_signature --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
  inputs['x'] tensor_info:
      dtype: DT_FLOAT
      shape: unknown_rank
      name: serving_default_x:0
The given SavedModel SignatureDef contains the following output(s):
  outputs['output_0'] tensor_info:
      dtype: DT_FLOAT
      shape: unknown_rank
      name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
imported = tf.saved_model.load("/tmp/module_with_signature")
signature = imported.signatures["serving_default"]
assert [3.] == signature(x=tf.constant([3.]))["output_0"].numpy()
imported.mutate(tf.constant(2.))
assert [6.] == signature(x=tf.constant([3.]))["output_0"].numpy()
assert 2. == imported.v.numpy()

単一のシグネチャをエクスポートして、そのキーは “serving_default” にデフォルト設定されました。複数シグネチャをエクスポートするためには、辞書を渡します。

@tf.function(input_signature=[tf.TensorSpec([], tf.string)])
def parse_string(string_input):
  return imported(tf.strings.to_number(string_input))

signatures = {"serving_default": parse_string, 
              "from_float": imported.signatures["serving_default"]}

tf.saved_model.save(imported, "/tmp/module_with_multiple_signatures", signatures)
!saved_model_cli show --dir /tmp/module_with_multiple_signatures --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"
SignatureDef key: "from_float"
SignatureDef key: "serving_default"

saved_model_cli はまたコマンドラインから直接 SavedModels を実行できます。

!saved_model_cli run --dir /tmp/module_with_multiple_signatures --tag_set serve\
 --signature_def serving_default --input_exprs="string_input='3.'"
!saved_model_cli run --dir /tmp/module_with_multiple_signatures --tag_set serve\
 --signature_def from_float --input_exprs="x=3."
2019-03-07 18:09:11.694255: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2019-03-07 18:09:11.717052: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 3491755000 Hz
2019-03-07 18:09:11.718329: I tensorflow/compiler/xla/service/service.cc:162] XLA service 0x5a156e0 executing computations on platform Host. Devices:
2019-03-07 18:09:11.718391: I tensorflow/compiler/xla/service/service.cc:169]   StreamExecutor device (0): , 
WARNING: Logging before flag parsing goes to stderr.
W0307 18:09:11.720156 139736676427520 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/tools/saved_model_cli.py:339: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.
W0307 18:09:11.738183 139736676427520 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/saver.py:1276: checkpoint_exists (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version.
Instructions for updating:
Use standard file APIs to check for files with this prefix.
Result for output key output_0:
6.0
2019-03-07 18:09:14.695447: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2019-03-07 18:09:14.716983: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 3491755000 Hz
2019-03-07 18:09:14.718031: I tensorflow/compiler/xla/service/service.cc:162] XLA service 0x470b890 executing computations on platform Host. Devices:
2019-03-07 18:09:14.718085: I tensorflow/compiler/xla/service/service.cc:169]   StreamExecutor device (0): , 
WARNING: Logging before flag parsing goes to stderr.
W0307 18:09:14.719855 140115307210496 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/tools/saved_model_cli.py:339: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.
W0307 18:09:14.737620 140115307210496 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/saver.py:1276: checkpoint_exists (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version.
Instructions for updating:
Use standard file APIs to check for files with this prefix.
Result for output key output_0:
6.0

 

インポートされたモデルを再調整する

変数オブジェクトが利用可能で、インポートされた関数を通して backprop できます。

optimizer = tf.optimizers.SGD(0.05)

def train_step():
  with tf.GradientTape() as tape:
    loss = (10. - imported(tf.constant(2.))) ** 2
  variables = tape.watched_variables()
  grads = tape.gradient(loss, variables)
  optimizer.apply_gradients(zip(grads, variables))
  return loss
for _ in range(10):
  # "v" approaches 5, "loss" approaches 0
  print("loss={:.2f} v={:.2f}".format(train_step(), imported.v.numpy()))
loss=36.00 v=3.20
loss=12.96 v=3.92
loss=4.67 v=4.35
loss=1.68 v=4.61
loss=0.60 v=4.77
loss=0.22 v=4.86
loss=0.08 v=4.92
loss=0.03 v=4.95
loss=0.01 v=4.97
loss=0.00 v=4.98

 

SavedModels の制御フロー

tf.function に入るものは何でも SavedModel に入ることができます。AutoGraph ではこれは通常の Python 制御フローで指定される、Tensor に依拠する条件付きロジックを含みます。

@tf.function(input_signature=[tf.TensorSpec([], tf.int32)])
def control_flow(x):
  if x < 0:
    tf.print("Invalid!")
  else:
    tf.print(x % 3)

to_export = tf.Module()
to_export.control_flow = control_flow
tf.saved_model.save(to_export, "/tmp/control_flow")
imported = tf.saved_model.load("/tmp/control_flow")
imported.control_flow(tf.constant(-1))  # Invalid!
imported.control_flow(tf.constant(2))   # 2
imported.control_flow(tf.constant(3))   # 0
Invalid!
2
0

 

Estimators からの SavedModels

Estimator は tf.Estimator.export_saved_model を通して SavedModels をエクスポートします。詳細は guide to Estimator を見てください。

input_column = tf.feature_column.numeric_column("x")
estimator = tf.estimator.LinearClassifier(feature_columns=[input_column])

def input_fn():
  return tf.data.Dataset.from_tensor_slices(
    ({"x": [1., 2., 3., 4.]}, [1, 1, 0, 0])).repeat(200).shuffle(64).batch(16)
estimator.train(input_fn)

serving_input_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(
  tf.feature_column.make_parse_example_spec([input_column]))
export_path = estimator.export_saved_model(
  "/tmp/from_estimator/", serving_input_fn)
WARNING: Logging before flag parsing goes to stderr.
W0307 18:09:15.471116 140431406638848 estimator.py:1799] Using temporary folder as model directory: /tmp/tmptkhkl2j8
W0307 18:09:15.482192 140431406638848 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/training_util.py:238: 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.
W0307 18:09:15.530143 140431406638848 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/feature_column/feature_column_v2.py:2758: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.cast` instead.
W0307 18:09:15.894593 140431406638848 deprecation.py:506] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/slot_creator.py:187: calling Zeros.__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
W0307 18:09:16.674963 140431406638848 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.
W0307 18:09:16.716552 140431406638848 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/saver.py:1276: checkpoint_exists (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version.
Instructions for updating:
Use standard file APIs to check for files with this prefix.

この SavedModel はシリアライズされた tf.Example protocol buffer を受け取ります、これはサービングのために有用です。しかしそれを tf.saved_model.load でロードしてそれを Python から実行することもできます。

imported = tf.saved_model.load(export_path)

def predict(x):
  example = tf.train.Example()
  example.features.feature["x"].float_list.value.extend([x])
  return imported.signatures["predict"](
    examples=tf.constant([example.SerializeToString()]))
print(predict(1.5))
print(predict(3.5))
{'classes': <tf.Tensor: id=12044, shape=(1, 1), dtype=string, numpy=array([[b'1']], dtype=object)>, '<tf.Tensor: id=12047, shape=(1, 2), dtype=float32, numpy=array([[0.40853757, 0.59146243]], dtype=float32)>, 'class_ids': <tf.Tensor: id=12043, shape=(1, 1), dtype=int64, numpy=array([[1]])>, 'logistic': <tf.Tensor: id=12045, shape=(1, 1), dtype=float32, numpy=array([[0.59146243]], dtype=float32)>, 'logits': <tf.Tensor: id=12046, shape=(1, 1), dtype=float32, numpy=array([[0.3700143]], dtype=float32)>}
{'classes': <tf.Tensor: id=12055, shape=(1, 1), dtype=string, numpy=array([[b'0']], dtype=object)>, 'probabilities': <tf.Tensor: id=12058, shape=(1, 2), dtype=float32, numpy=array([[0.76074064, 0.23925935]], dtype=float32)>, 'class_ids': <tf.Tensor: id=12054, shape=(1, 1), dtype=int64, numpy=array([[0]])>, 'logistic': <tf.Tensor: id=12056, shape=(1, 1), dtype=float32, numpy=array([[0.23925935]], dtype=float32)>, '<tf.Tensor: id=12057, shape=(1, 1), dtype=float32, numpy=array([[-1.1567444]], dtype=float32)>}

tf.estimator.export.build_raw_serving_input_receiver_fn は、tf.train.Examples ではなく生 tensor を取る入力関数を作成することを可能にします。

 

C++ で SavedModel をロードする

SavedModel loader の C++ バージョンは、SessionOptions と RunOptions を可能にしながら、SavedModel をパスからロードする API を提供します。ロードされるグラフに関連するタグを指定しなければなりません。SavedModel のロードされたバージョンは SavedModelBundle として参照されて MetaGraphDef とそれがロードされた session を含みます。

const string export_dir = ...
SavedModelBundle bundle;
...
LoadSavedModel(session_options, run_options, export_dir, {kSavedModelTagTrain},
               &bundle);

 

SavedModel コマンドライン・インターフェイスの詳細

SavedModel を調査して実行するために SavedModel コマンドライン・インターフェイス (CLI) を利用できます。例えば、モデルの SignatureDefs を調べるために CLI を使用できます。CLI は入力 Tensor dtype と shape がモデルにマッチしていることを簡単に確かめることを可能にします。更に、貴方のモデルをテストすることを望むのであれば、サンプル入力を様々なフォーマット (例えば、Python 式) で渡して出力を取得することによりサニティチェックを行なうために CLI を使用できます。

 

SavedModel CLI をインストールする

大まかに言って、TensorFlow を次の 2 つの方法のどちらかでインストールできます :

  • 事前ビルドされた TensorFlow バイナリをインストールすることによって。
  • ソースコードから TensorFlow をビルドすることによって。

事前ビルドされた TensorFlow バイナリを通して TensorFlow をインストールした場合、SavedModel CLI は貴方のシステム上にパス名 bin\saved_model_cli で既にインストールされています。

TensorFlow をソースコードからビルドした場合、saved_model_cli をビルドするために次の追加のコマンドを実行しなければなりません :

$ bazel build tensorflow/python/tools:saved_model_cli

 

コマンドの概要

SavedModel CLI は SavedModel の MetaGraphDef 上で次の 2 つのコマンドをサポートします :

  • show, これは SavedModel の MetaGraphDef 上の計算を示します。
  • run, これは MetaGraphDef 上の計算を実行します。

 

show コマンド

SavedModel は一つまたはそれ以上の MetaGraphDefs を含み、それらのタグセットによって識別されます。モデルをサーブするために、貴方は各モデルにどのような種類の SignatureDefs があり、それらの入力と出力が何かを思案するかもしれません。show コマンドは貴方に SavedModel の内容を階層的順序で調べさせます。ここにシンタックスがあります :

usage: saved_model_cli show [-h] --dir DIR [--all]
[--tag_set TAG_SET] [--signature_def SIGNATURE_DEF_KEY]

例えば、次のコマンドは SavedModel の総ての利用可能な MetaGraphDef タグセットを表示します :

$ saved_model_cli show --dir /tmp/saved_model_dir
The given SavedModel contains the following tag-sets:
serve
serve, gpu

次のコマンドは MetaGraphDef の総ての利用可能な SignatureDef キーを表示します :

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve
The given SavedModel `MetaGraphDef` contains `SignatureDefs` with the
following keys:
SignatureDef key: "classify_x2_to_y3"
SignatureDef key: "classify_x_to_y"
SignatureDef key: "regress_x2_to_y3"
SignatureDef key: "regress_x_to_y"
SignatureDef key: "regress_x_to_y2"
SignatureDef key: "serving_default"

MetaGraphDef がタグセットで複数のタグを持つ場合、総てのタグを指定しなければなりません、各タグはカンマで分離されます。例えば :

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve,gpu

特定の SignatureDef のための総ての入力と出力 TensorInfo を表示するには、signature_def オプションに SignatureDef キーを渡します。貴方が計算グラフを後で実行するために tensor キー値、入力 tensor の dtype と shape を知ることを望むときこれは非常に有用です。例えば :

$ saved_model_cli show --dir \
/tmp/saved_model_dir --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
  inputs['x'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: x:0
The given SavedModel SignatureDef contains the following output(s):
  outputs['y'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: y:0
Method name is: tensorflow/serving/predict

SavedModel の総ての利用可能な情報を表示するには、--all オプションを使用します。例えば :

$ saved_model_cli show --dir /tmp/saved_model_dir --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['classify_x2_to_y3']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x2:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y3:0
  Method name is: tensorflow/serving/classify

...

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['x'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['y'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y:0
  Method name is: tensorflow/serving/predict

 

run コマンド

グラフ計算を実行するために run コマンドを起動します、入力を渡して出力を表示します (そしてオプションでセーブします)。ここにシンタックスがあります :

usage: saved_model_cli run [-h] --dir DIR --tag_set TAG_SET --signature_def
                           SIGNATURE_DEF_KEY [--inputs INPUTS]
                           [--input_exprs INPUT_EXPRS]
                           [--input_examples INPUT_EXAMPLES] [--outdir OUTDIR]
                           [--overwrite] [--tf_debug]

run コマンドは入力をモデルに渡す次の 3 つの方法を提供します。

  • --inputs オプションは ファイルの numpy ndarray を渡すことを可能にします。
  • --input_exprs オプションは Python 式を渡すことを可能にします。
  • --input_examples オプションは tf.train.Example を渡すことを可能にします。

 
--inputs

ファイルの入力データを渡すために、--inputs オプションを指定します、これは次の一般的な形式を取ります :

--inputs <INPUTS>

ここで INPUTS は次の形式のいずれかです :

  • <input_key>=<filename>
  • <input_key>=<filename>[<variable_name>]

複数の INPUTS を渡すかもしれません。複数の入力を渡す場合には、INPUTS の各々を分離するためにセミコロンを使用します。

saved_model_cli は filename をロードするために numpy.load を使用します。filename は次の形式の任意の一つにあります :

  • .npy
  • .npz
  • pickle 形式

.npy ファイルは常に numpy ndarray を含みます。従って、.npy ファイルからロードするとき、内容な指定された入力 tensor に直接割り当てられます。その .npy ファイルで variable_name を指定する場合、variable_name は無視されて警告が発せられます。

.npz (zip) ファイルからロードするとき、入力 tensor キーのためにロードする zip ファイル内の変数を識別するためにオプションで variable_name を指定することもできます。variable_name を指定しない場合は、SavedModel CLI は zip ファイルに一つのファイルだけが含まれていることを確認してそれを指定された入力 tensor キーのためにロードします。

pickle ファイルからロードするときは、variable_name が角括弧で指定されない場合は、pickle ファイルの内側にあるものは何でも指定された入力 tensor キーに渡されます。そうでなければ、SavedModel CLI は pickle ファイルに辞書がストアされていることを想定して variable_name に対応する値が使用されるでしょう。

 
--input_exprs

入力を Python 式を通して渡すためには、--input_exprs オプションを指定します。これは貴方が転がっているデータファイルを持たないけれども、モデルの SignatureDefs の dtype と shape にマッチする何某かの単純な入力でモデルをサニティチェックすることを依然として望むときに有用です。例えば :

`<input_key>=[[1],[2],[3]]`

Python 式に加えて、numpy 関数を渡しても良いです。例えば :

`<input_key>=np.ones((32,32,3))`

(numpy モジュールは np として既に利用可能であることに注意してください。)

 
--input_examples

入力として tf.train.Example を渡すには、--input_examples オプションを指定します。各入力キーについて、それは辞書のリストを取り、そこでは各辞書は tf.train.Example のインスタンスです。辞書キーは特徴で値は各特徴のための値リストです。例えば :

`<input_key>=[{"age":[22,24],"education":["BS","MS"]}]`

 
出力をセーブする

デフォルトでは、SavedModel CLI は出力を stdout に書きます。

ディレクトリが --outdir オプションで渡される場合、出力は出力 tensor キーの名前を取って与えられたディレクトリ下に npy ファイルとしてセーブされます。

既存の出力ファイルに上書きするためには --overwrite を使用します。

 

以上



TensorFlow 2.0 Alpha : ガイド : 訓練チェックポイント

TensorFlow 2.0 Alpha : ガイド : 訓練チェックポイント (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/24/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : 訓練チェックポイント

慣用句「TensorFlow モデルをセーブする」は典型的には 2 つのことの 1 つを意味しています : (1) チェックポイント, OR (2) SavedModel です。

チェックポイントはモデルにより使用される総てのパラメータの正確な値を捕捉します。チェックポイントはモデルで定義された計算のどのような記述も含みません、そのため典型的にはセーブされたパラメータ値を使用するソースコードが利用可能であるときに限り有用です。

他方、SavedModel フォーマットはパラメータ値 (チェックポイント) に加えてモデルで定義された計算のシリアライズされた記述を含みます。このフォーマットのモデルはこのモデルを作成したソースコードからは独立です。そのためそれらは TensorFlow Serving や他のプログラミング言語 (C, C++, Java, Go, Rust, C# etc. TensorFlow API) のプログラムを通した配備に適しています。

このガイドはチェックポイントを書いて読むための API をカバーします。

 

tf.keras 訓練 API からセーブする

tf.keras guide on saving and restoring を見てください。

tf.keras.Model.save_weights はオプションで TensorFlow チェックポイント・フォーマットでセーブします。このガイドはフォーマットを詳細に説明してカスタム訓練ループでチェックポイントを管理するための API を紹介します。

 

チェックポイントを手動で書く

TensorFlow モデルの永続的な状態は tf.Variable オブジェクトにストアされます。これらは直接に構築できますが、しばしば tf.keras.layers のような高位 API を通して作成されます。

変数を管理する最も容易な方法はそれらを Python オブジェクトにアタッチして、それらのオブジェクトを参照することです。tf.train.Checkpoint のサブクラス、tf.keras.layers.Layer と tf.keras.Model はそれらの属性に割り当てられた変数を自動的に追跡します。次のサンプルは単純な線形モデルを構築してから、モデルの総ての変数のための値を含むチェックポイントを書きます。

from __future__ import absolute_import, division, print_function
!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf
class Net(tf.keras.Model):
  """A simple linear model."""

  def __init__(self):
    super(Net, self).__init__()
    self.l1 = tf.keras.layers.Dense(5)

  def call(self, x):
    return self.l1(x)

このガイドの焦点ではありませんが、実行可能であるためにはサンプルはデータと最適化ステップが必要です。モデルは in-メモリのデータセットのスライス上で訓練されます。

def toy_dataset():
  inputs = tf.range(10.)[:, None]
  labels = inputs * 5. + tf.range(5.)[None, :]
  return tf.data.Dataset.from_tensor_slices(
    dict(x=inputs, y=labels)).repeat(10).batch(2)
def train_step(net, example, optimizer):
  """Trains `net` on `example` using `optimizer`."""
  with tf.GradientTape() as tape:
    output = net(example['x'])
    loss = tf.reduce_mean(tf.abs(output - example['y']))
  variables = net.trainable_variables
  gradients = tape.gradient(loss, variables)
  optimizer.apply_gradients(zip(gradients, variables))
  return loss

次の訓練ループはモデルと optimizer のインスタンスを作成して、それらを tf.train.Checkpoint オブジェクトに集めます。それはデータの各バッチ上のループで訓練ステップを呼び出し、定期的にチェックポイントをディスクに書きます。

opt = tf.keras.optimizers.Adam(0.1)
net = Net()
ckpt = tf.train.Checkpoint(step=tf.Variable(1), optimizer=opt, net=net)
manager = tf.train.CheckpointManager(ckpt, './tf_ckpts', max_to_keep=3)
ckpt.restore(manager.latest_checkpoint)
if manager.latest_checkpoint:
  print("Restored from {}".format(manager.latest_checkpoint))
else:
  print("Initializing from scratch.")

for example in toy_dataset():
  loss = train_step(net, example, opt)
  ckpt.step.assign_add(1)
  if int(ckpt.step) % 10 == 0:
    save_path = manager.save()
    print("Saved checkpoint for step {}: {}".format(int(ckpt.step), save_path))
    print("loss {:1.2f}".format(loss.numpy()))
Initializing from scratch.
Saved checkpoint for step 10: ./tf_ckpts/ckpt-1
loss 25.84
Saved checkpoint for step 20: ./tf_ckpts/ckpt-2
loss 19.26
Saved checkpoint for step 30: ./tf_ckpts/ckpt-3
loss 12.70
Saved checkpoint for step 40: ./tf_ckpts/ckpt-4
loss 6.26
Saved checkpoint for step 50: ./tf_ckpts/ckpt-5
loss 2.32

上述のスニペットはそれが最初に実行されるときモデル変数をランダムに初期化します。最初の実行後それは訓練をやめたところから訓練を再開します :

opt = tf.keras.optimizers.Adam(0.1)
net = Net()
ckpt = tf.train.Checkpoint(step=tf.Variable(1), optimizer=opt, net=net)
manager = tf.train.CheckpointManager(ckpt, './tf_ckpts', max_to_keep=3)
ckpt.restore(manager.latest_checkpoint)
if manager.latest_checkpoint:
  print("Restored from {}".format(manager.latest_checkpoint))
else:
  print("Initializing from scratch.")

for example in toy_dataset():
  loss = train_step(net, example, opt)
  ckpt.step.assign_add(1)
  if int(ckpt.step) % 10 == 0:
    save_path = manager.save()
    print("Saved checkpoint for step {}: {}".format(int(ckpt.step), save_path))
    print("loss {:1.2f}".format(loss.numpy()))
Restored from ./tf_ckpts/ckpt-5
Saved checkpoint for step 60: ./tf_ckpts/ckpt-6
loss 1.39
Saved checkpoint for step 70: ./tf_ckpts/ckpt-7
loss 1.51
Saved checkpoint for step 80: ./tf_ckpts/ckpt-8
loss 0.72
Saved checkpoint for step 90: ./tf_ckpts/ckpt-9
loss 0.81
Saved checkpoint for step 100: ./tf_ckpts/ckpt-10
loss 0.46

tf.train.CheckpointManager オブジェクトは古いチェックポイントを削除します。上で 3 つの最近のチェックポイントだけを保持するように configure されています。

print(manager.checkpoints)  # List the three remaining checkpoints
['./tf_ckpts/ckpt-8', './tf_ckpts/ckpt-9', './tf_ckpts/ckpt-10']

これらのパス, e.g. ‘./tf_ckpts/ckpt-10’ はディスク上のファイルではありません。代わりにそれらはインデックスファイルのための prefix で変数値を含む一つまたはそれ以上のデータファイルがあります。これらの prerfix はまとめて単一のチェックポイントファイル (‘./tf_ckpts/checkpoint’) にグループ化されます、そこでは CheckpointManager がその状態をセーブします。

!ls ./tf_ckpts
checkpoint           ckpt-8.data-00000-of-00001  ckpt-9.index
ckpt-10.data-00000-of-00001  ckpt-8.index
ckpt-10.index            ckpt-9.data-00000-of-00001

 

ローディング機構

TensorFlow は変数を、ロードされたオブジェクトから始めて、名前付けられたエッジを持つ有向グラフを辿ることによりチェックポイントされた値に合わせます。エッジ名は典型的にはオブジェクトの属性名に由来します、例えば self.l1 = tf.keras.layers.Dense(5) の “l1” です。tf.train.Checkpoint はそのキーワード引数名を例えば tf.train.Checkpoint(step=…) の “step” のように使用します。

上のサンプルからの依存グラフはこのようなものです :


サンプル訓練ループのための依存グラフの可視化

optimizer は赤色、通常変数は青色そして optimizer スロット変数はオレンジ色です。他のノード、例えば tf.train.Checkpoint を表わすのは黒色です。

スロット変数は optimizer の状態の一部ですが、特定の変数のために作成されます。例えば上の ‘m’ エッジは momentum に対応します、これは Adam optimizer が各変数のために追跡します。スロット変数は変数と optimizer の両者がセーブされる場合に限りチェックポイントにセーブされますので、破線のエッジです。

tf.train.Checkpoint オブジェクト上の restore() 呼び出しは要求された復元 (= restoration) をキューに入れて、Checkpoint オブジェクトから一致するパスがあればすぐに変数値を復元します。例えば上で定義したモデルから単にカーネルをネットワークと層を通してそれへのパスを再構築することによりロードできます。

to_restore = tf.Variable(tf.zeros([5]))
print(to_restore.numpy())  # All zeros
fake_layer = tf.train.Checkpoint(bias=to_restore)
fake_net = tf.train.Checkpoint(l1=fake_layer)
new_root = tf.train.Checkpoint(net=fake_net)
status = new_root.restore(tf.train.latest_checkpoint('./tf_ckpts/'))
print(to_restore.numpy())  # We get the restored value now
[0. 0. 0. 0. 0.]
[3.406768  2.0089386 2.3660657 2.9850907 4.004283 ]

これらの新しいオブジェクトのための依存グラフは上で書いたより大きなチェックポイントの遥かに小さいサブグラフです。それはバイアスと tf.train.Checkpoint がチェックポイントに番号付けするために使用した save_counter だけを含みます。


バイアス変数のためのサブグラフの可視化

restore() は状態オブジェクトを返します、これはオプションのアサーションを持ちます。新しい Checkpoint で作成した総てのオブジェクトは復元されますので、status.assert_existing_objects_matched() が通ります。

status.assert_existing_objects_matched()
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7fce04957c18>

チェックポイントには、層のカーネルと optimizer の変数を含む、マッチしていない多くのオブジェクトがあります。status.assert_consumed() はチェックポイントとプログラムが正確にマッチする場合に限りパスし、そしてここでは例外を投げます。

 

遅延復元 (= Delayed restorations)

TensorFlow の Layer オブジェクトは変数の作成を最初の呼び出しまで遅らせるかもしれません、入力 shape が利用可能であるときです。例えば Dense 層のカーネルの shape は層の入力と出力 shape の両者に依拠しますので、コンストラクタ引数として必要な出力 shape は単独で変数を作成するために十分な情報ではありません。層の呼び出しはまた変数の値を読みますので、restore は変数の作成とその最初の使用の間で起きる必要があります。

この用法をサポートするために、tf.train.Checkpoint はマッチした変数をまだ持たない restore をキューに入れます。

delayed_restore = tf.Variable(tf.zeros([1, 5]))
print(delayed_restore.numpy())  # Not restored; still zeros
fake_layer.kernel = delayed_restore
print(delayed_restore.numpy())  # Restored
[[0. 0. 0. 0. 0.]]
[[4.4939117 4.8438153 4.976974  4.979875  4.999077 ]]

 

チェックポイントを手動で調べる

tf.train.list_variables はチェックポイント・キーとチェックポイントの変数の shape をリストします。チェックポイント・キーは上で示したグラフのパスです。

tf.train.list_variables(tf.train.latest_checkpoint('./tf_ckpts/'))
[('_CHECKPOINTABLE_OBJECT_GRAPH', []),
 ('net/l1/.ATTRIBUTES/OBJECT_CONFIG_JSON', []),
 ('net/l1/bias/.ATTRIBUTES/VARIABLE_VALUE', [5]),
 ('net/l1/bias/.OPTIMIZER_SLOT/optimizer/m/.ATTRIBUTES/VARIABLE_VALUE', [5]),
 ('net/l1/bias/.OPTIMIZER_SLOT/optimizer/v/.ATTRIBUTES/VARIABLE_VALUE', [5]),
 ('net/l1/kernel/.ATTRIBUTES/VARIABLE_VALUE', [1, 5]),
 ('net/l1/kernel/.OPTIMIZER_SLOT/optimizer/m/.ATTRIBUTES/VARIABLE_VALUE',
  [1, 5]),
 ('net/l1/kernel/.OPTIMIZER_SLOT/optimizer/v/.ATTRIBUTES/VARIABLE_VALUE',
  [1, 5]),
 ('optimizer/.ATTRIBUTES/OBJECT_CONFIG_JSON', []),
 ('optimizer/beta_1/.ATTRIBUTES/VARIABLE_VALUE', []),
 ('optimizer/beta_2/.ATTRIBUTES/VARIABLE_VALUE', []),
 ('optimizer/decay/.ATTRIBUTES/VARIABLE_VALUE', []),
 ('optimizer/epsilon/.ATTRIBUTES/VARIABLE_VALUE', []),
 ('optimizer/iter/.ATTRIBUTES/VARIABLE_VALUE', []),
 ('optimizer/learning_rate/.ATTRIBUTES/VARIABLE_VALUE', []),
 ('save_counter/.ATTRIBUTES/VARIABLE_VALUE', []),
 ('step/.ATTRIBUTES/VARIABLE_VALUE', [])]

 

リストと辞書追跡

self.l1 = tf.keras.layers.Dense(5) のような直接の属性割り当てと同様に、リストと辞書の属性への割り当てはそれらの内容を追跡します。

save = tf.train.Checkpoint()
save.listed = [tf.Variable(1.)]
save.listed.append(tf.Variable(2.))
save.mapped = {'one': save.listed[0]}
save.mapped['two'] = save.listed[1]
save_path = save.save('./tf_list_example')

restore = tf.train.Checkpoint()
v2 = tf.Variable(0.)
assert 0. == v2.numpy()  # Not restored yet
restore.mapped = {'two': v2}
restore.restore(save_path)
assert 2. == v2.numpy()

貴方はリストと辞書のためのラッパー・オブジェクトに気がつくかもしれません。これらのラッパーは基礎的なデータ構造のチェックポイント可能なバージョンです。ちょうどローディングに基づく属性のように、これらのラッパーは変数の値をそれがコンテナに追加されるとすぐに復元します。

restore.listed = []
print(restore.listed)  # ListWrapper([])
v1 = tf.Variable(0.)
restore.listed.append(v1)  # Restores v1, from restore() in the previous cell
assert 1. == v1.numpy()
ListWrapper([])

同じ追跡が tf.keras.Model のサブクラスに自動的に適用されて例えば層のリストを追跡するために使用されるかもしれません。

 

Estimator でオブジェクトベースのチェックポイントをセーブする

Estimator のガイド を見てください。

Estimator はデフォルトでチェックポイントを前のセクションで説明されたオブジェクトグラフではなく変数名でセーブします。tf.train.Checkpoint は名前ベースのチェックポイントを受け取りますが、モデルの一部を Estimator の model_fn の外側に移すときには変数名は変わるかもしれません。オブジェクトベースのチェックポイントのセーブは Estimator の内側でのモデルを訓練して外側でそれを使用することを容易にします。

import tensorflow.compat.v1 as tf_compat
def model_fn(features, labels, mode):
  net = Net()
  opt = tf.keras.optimizers.Adam(0.1)
  ckpt = tf.train.Checkpoint(step=tf_compat.train.get_global_step(),
                             optimizer=opt, net=net)
  with tf.GradientTape() as tape:
    output = net(features['x'])
    loss = tf.reduce_mean(tf.abs(output - features['y']))
  variables = net.trainable_variables
  gradients = tape.gradient(loss, variables)
  return tf.estimator.EstimatorSpec(
    mode,
    loss=loss,
    train_op=tf.group(opt.apply_gradients(zip(gradients, variables)),
                      ckpt.step.assign_add(1)),
    # Tell the Estimator to save "ckpt" in an object-based format.
    scaffold=tf_compat.train.Scaffold(saver=ckpt))

tf.keras.backend.clear_session()
est = tf.estimator.Estimator(model_fn, './tf_estimator_example/')
est.train(toy_dataset, steps=10)
WARNING: Logging before flag parsing goes to stderr.
W0307 18:07:17.775649 140524399343360 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/training_util.py:238: 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.

<tensorflow_estimator.python.estimator.estimator.EstimatorV2 at 0x7fcddc691be0>

それから tf.train.Checkpoint は Estimator のチェックポイントをその model_dir からロードできます。

opt = tf.keras.optimizers.Adam(0.1)
net = Net()
ckpt = tf.train.Checkpoint(
  step=tf.Variable(1, dtype=tf.int64), optimizer=opt, net=net)
ckpt.restore(tf.train.latest_checkpoint('./tf_estimator_example/'))
ckpt.step.numpy()  # From est.train(..., steps=10)
10

 

要約

TensorFlow オブジェクトはそれらが使用する変数の値をセーブして復元するための容易で自動的なメカニズムを提供します。

 

以上



TensorFlow 2.0 Alpha : ガイド : tf.data パフォーマンス

TensorFlow 2.0 Alpha : ガイド : tf.data パフォーマンス (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/23/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : tf.data パフォーマンス

概要

GPU と TPU は単一の訓練ステップを実行するために必要な時間を劇的に減少させます。最高点のパフォーマンスの獲得は、現在のステップが終了する前に次のステップのためのデータを引き渡す効率的な入力パイプラインが必要です。tf.data API は柔軟で効率的な入力パイプラインを構築する手助けをします。このドキュメントは様々なモデルとアクセラレータに渡る非常にパフォーマンスが高い TensorFlow 入力パイプラインを構築するための tf.data API の特徴とベストプラクティスを説明します。

このガイドは次を行ないます :

  • TensorFlow 入力パイプラインが本質的には ETL プロセスであることを示す。
  • 高パフォーマンスな TensorFlow 入力パイプラインを設計するための推奨プラクティスを記述する
  • 変換を適用する順序のパフォーマンスの影響を議論する。

 

入力パイプライン構造

典型的な TensorFlow 訓練入力パイプラインは ETL プロセスとして構成できます :

  • Extract (抽出) : メモリ (NumPy) または永続性ストレージ — ローカル (HDD or SSD) またはリモート (e.g. GCS or HDFS) — からデータを読みます。
  • Transform (変換, 変形): CPU を使用して parse してシャッフル、バッチ処理のようなデータ上の前処理演算、そして画像デコンプレッションのようなドメイン固有の変換そして増強、テキストベクトル化、あるい動画の時間的サンプリングを遂行します。
  • Load (ロード): 変換されたデータを機械学習モデルを実行するアクセラレータ・デバイス (e.g. GPU(s) or TPU(s)) 上にロードします。

このパターンはアクセラレータを貴方のモデルを訓練する重労働のために取っておく一方で、CPU を効果的に活用します。更に、入力パイプラインを ETL プロセスとして見ることはパフォーマンス最適化の適用を容易にするフレームワークを提供します。

下のサンプルは、ラベル付けされた画像を含む TFRecord ファイルを読みそしてそれらを訓練に適した画像-ラベル・ペアのバッチに変換する入力パイプラインの素朴な実装を表します。入力パイプラインは tf.data.Dataset として表わされます、これは tf.keras のような高位 TensorFlow APIに渡すことができます。

def parse_fn(example):
  "Parse TFExample records and perform simple data augmentation."
  example_fmt = {
    "image": tf.FixedLengthFeature((), tf.string, ""),
    "label": tf.FixedLengthFeature((), tf.int64, -1)
  }
  parsed = tf.parse_single_example(example, example_fmt)
  image = tf.io.image.decode_image(parsed["image"])
  image = _augment_helper(image)  # augments image using slice, reshape, resize_bilinear
  return image, parsed["label"]

def make_dataset():
  dataset = tf.data.TFRecordDataset("/path/to/dataset/train-*.tfrecord")
  dataset = dataset.shuffle(buffer_size=FLAGS.shuffle_buffer_size)
  dataset = dataset.map(map_func=parse_fn)
  dataset = dataset.batch(batch_size=FLAGS.batch_size)
  return dataset

次のセクションはこの入力パイプラインを利用して、高パフォーマンスな TensorFlow 入力パイプラインを設計するためのベストプラクティスを示します。

 

パフォーマンスを最適化する

(GPU と TPU のような) 新しい計算デバイスがニューラルネットワークをどんどん高速に訓練することを可能にしますので、CPU 処理がボトルネックになりがちです。tf.data API は CPU を効果的に利用して ETL プロセスの各ステップを最適化する入力パイプラインを設計するためのビルディングブロックをユーザに提供します。

 

パイプライン処理

訓練ステップを遂行するためには、最初に訓練データを抽出して変換してそれからそれをアクセラレータで動作するモデルに供給しなければなりません。けれども、素朴な同期実装では、CPU がデータを準備している間、アクセラレータはアイドリングしています。反対に、アクセラレータがモデルを訓練している間、CPU はアイドリングしています。こうして訓練ステップ時間は CPU 前処理時間とアクセラレータ訓練時間の両者の総計です。

パイプライン処理 は前処理と訓練ステップのモデル実行をオーバーラップさせます。アクセラレータが訓練ステップ N を遂行している間、CPU はステップ N+1 のためのデータを準備しています。そのようにすることはステップ時間を訓練とデータを抽出して変換するのにかかる時間の (総計と対照的に) 最大値にまで減じます。

パイプライン処理なしでは、CPU と GPU/TPU は殆どの時間アイドリングします :

パイプライン処理では、アイドル時間は著しく減少します :

tf.data API は tf.data.Dataset.prefetch 変換を通してソフトウェア・パイプライン処理機構を提供します、これはデータが生成される時をデータが消費される時から切り離します。特に、変換は要求される前に入力データセットから要素を先取りする (= prefetch) ためにバックグラウンド・スレッドと内部バッファを使用します。先取りする要素の数は単一の訓練ステップにより消費されるバッチの数と同じ (あるいは多分それ以上) であるべきです。この値を手動で調整するかそれを tf.data.experimental.AUTOTUNE に設定することもできるでしょう、これは tf.data ランタイムに実行時に動的に値を調整することを促します、

この変更を私達の実行サンプルに適用するためには、次を :

dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)

貴方の入力パイプラインの最後の変換として挿入します。

prefetech 変換は “producer” のワークを “consumer” のワークとオーバーラップさせる機会があればいつでも利益をもたらすことに注意してください。

 

データ変換を並列化する

バッチを準備するとき、入力要素は前処理される必要があるかもしれません。この目的のために、tf.data API は tf.data.Dataset.map 変換を提供します、これは入力データセットの各要素にユーザ定義関数 (例えば、実行サンプルからの parse_fn) を適用します。入力要素は互いに無関係なので、前処理はマルチ CPU コアに渡り並列化できます。これを可能にするために、map 変換は並列処理のレベルを指定する num_parallel_calls 引数を提供します。例えば、次の図は map 変換に num_parallel_calls=2 を設定する効果を示します :

num_parallel_calls 引数のための最善の値を選択することはハードウェア、(サイズと shape のような) 訓練データの特性、map 関数のコスト、そして何の他の処理が同時に CPU 上で起きているかに依拠します ; 単純な経験則は利用可能な CPU コアの数を使用します。例えば、上のサンプルを実行するマシンが 4 コアを持つ場合、それは num_parallel_calls=4 を設定することはより効率的でしょう。その一方で、num_parallel_calls を利用可能な CPU の数よりも遥かに大きい値に設定すると非効率的なスケジューリングに繋がり、スローダウンの結果になる可能性があります。prefetch 変換と同様に、map 変換は tf.data.experimental.AUTOTUNE をサポートします、これはどのレベルの並列処理を使用するかについての決定を tf.data ランタイムに委ねます。

実行サンプルにこの変更を適用するには、次を :

dataset = dataset.map(map_func=parse_fn)

次で置き換えます :

dataset = dataset.map(map_func=parse_fn, num_parallel_calls=tf.data.experimental.AUTOTUNE)

 

データ抽出を並列化する

現実世界の設定では、入力データはリモートにストアされるかもしれません (例えば、GCS や HDFS)、何故ならば入力データがローカルに収まらないとか、訓練が分散されていて入力データを総てのマシン上に複製することが無意味であるとかの理由です。ローカルでデータを読む時には上手く動作したデータセット・パイプラインは、ローカルとリモート・ストレージ間の次の違いによりデータをリモートに読む時 I/O 上のボトルネックになるかもしれません。

  • 最初のバイトへの時間: リモートストレージから最初のバイトを読むのはローカルストレージからよりも桁違いに長くかかる可能性があります。
  • 読み込みスループット: リモートストレージが巨大な集合的な帯域を提供する一方で、単一のファイルを読むことはこの帯域の小さな断片を利用できるだけかもしれません。

加えて、ひとたび生バイトがメモリに読み込まれれば、データ (e.g. probobuf) をデシリアライズ and/or 解読する必要もあるかもしれません、これは追加の計算を必要とします。このオーバーヘッドはデータがローカルかリモートにストアされているかに無関係に存在します。

様々なデータ抽出オーバーヘッドのインパクトを migrate するために、データ抽出ステップを並列化するために tf.data.Dataset.interleave 変換が使用できて、(データファイル・リーダーのように) 他のデータセットの内容をさしはさみます。オーバーラップさせるデータセットの数は cycle_length 引数により指定され、一方で並列レベルは num_parallel_calls 引数により指定されます。prefetch と map 変換と同様に、interleave 変換は tf.data.experimental.AUTOTUNE をサポートします、これは並列性のどのレベルを使用するかについての決定を tf.data ランタイムに委ねます。

次の図は interleave 変換に cycle_length=2 と num_parallel_calls=2 を供給する効果を示します :

この変更を訓練サンプルに適用するためには、次を :

dataset = tf.data.TFRecordDataset("/path/to/dataset/train-*.tfrecord")

次で置き換えます :

files = tf.data.Dataset.list_files("/path/to/dataset/train-*.tfrecord")
dataset = files.interleave(
    tf.data.TFRecordDataset, cycle_length=FLAGS.num_parallel_reads,
    num_parallel_calls=tf.data.experimental.AUTOTUNE)

 

パフォーマンス考察

tf.data API は構成可能な変換まわりをユーザに柔軟性を提供するために設計されています。これらの変換の多くは可搬ですが、特定の変換の順序はパフォーマンスとの関係を持ちます。

 

Map と Batch

map 変換に渡されたユーザ定義関数の起動はスケジューリングとユーザ定義関数の実行に関連するオーバーヘッドを持ちます。通常は、このオーバーヘッドは関数により遂行される計算の総量に比較して小さいです。けれども、map が殆ど作業をしない場合には、このオーバーヘッドは総コストを占める可能性があります。そのような場合、ユーザ定義関数のベクトル化を使用します (つまり、それを入力のバッチに渡り一度に演算させます)、そして map 変換の前に batch 変換を適用します。

 

Map と Cache

tf.data.Dataset.cache 変換はデータセットをメモリかローカルストレージにキャッシュできます。map 変換に渡されるユーザ定義関数が高価である場合、map 変換の後に cache 変換を適用します、結果データセットが依然としてメモリかローカルストレージに収まる限りは。ユーザ定義関数がキャッシュ容量を越えてデータセットをストアする必要がある空間を増加させる場合には、リソース使用を減じるために訓練ジョブの前にデータの前処理を考えてください。

 

Map と Interleave / Prefetch / Shuffle

interleave, prefetch と shuffle を含む多くの変換は要素の内部バッファを維持します。map 変換に渡されるユーザ定義関数が要素の数を変更する場合、map 変換と要素をバッファリングする変換の順序はメモリ使用に影響を与えます。一般に、(例えば、map と batch 変換の融合を有効にするために) パフォーマンスのために異なる順序が望ましいのでなければ、より低いメモリフットプリントの結果になる順序を選択することを推奨します。

 

Repeat と Shuffle

tf.data.Dataset.repeat 変換は入力データを有限 (or 無限) 回数繰り返します ; データの各反復は典型的にはエポックとして参照されます。tf.data.Dataset.shuffle 変換はデータセット・サンプルの順序をランダム化します。

repeat 変換が shuffle 変換の前に適用される場合、エポック境界は曖昧になります。つまり、他の要素が一度さえ現れる前に、ある要素が繰り返される可能性があります。他方、repeat 変換の前に shuffle 変換が適用される場合、shuffle 変換の初期状態の初期化に関連して各エポックの最初にスローダウンするかもしれません。換言すれば、前者 (repeat before shuffle) はより良いパフォーマンスを提供し、その一方で後者 (shuffle before repeat) はより強い順序の保証を提供します。

 

ベストプラクティスの要約

高パフォーマンスな TensorFlow 入力パイプラインを設計するためのベストプラクティスの要約がここにあります :

  • producer と consumer のワークをオーバラップさせるために prefetch 変換を使用します。訓練はアクセラレータ上で成されながら、特に、CPU 上で遂行される変換群をオーバーラップさせるために入力パイプラインの最後に prefetch を追加することを勧めます。バッファサイズを手動で調整するか、決定を tf.data ランタイムに委ねるために tf.data.experimental.AUTOTUNE を使用します。
  • num_parallel_calls 引数を設定することにより map 変換を並列化します。並列レベルを手動で調整するか、決定を tf.data ランタイムに委ねるために tf.data.experimental.AUTOTUNE を使用します。
  • リモートにストアされている and/or デシリアライゼーションを必要とするデータで作業している場合、異なるファイルからのデータの読み込み (とデシリアライゼーション) を並列化するために interleave 変換を使用することを勧めます。
  • map 変換に渡される安価なユーザ定義関数を、スケジューリングと関数実行に関連するオーバーヘッドを償却するためにベクトル化します。
  • 貴方のデータがメモリに収まる場合、最初のエポックの間にそれをメモリにキャッシュするために cache 変換を使用してください、その結果続くエポックはそれを読み込み、パーシングして変換することに関連するオーバヘッドを回避できます。
  • 前処理が貴方のデータのサイズを増やす場合には、メモリ使用を減らすために interleave, prefetch と (可能であれば) shuffle を最初に適用することを勧めます。
  • repeat 変換の前に shuffle 変換を適用することを勧めます。
 

以上



TensorFlow 2.0 Alpha : ガイド : GPU を使用する

TensorFlow 2.0 Alpha : ガイド : GPU を使用する (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/22/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : GPU を使用する

サポートされるデバイス

TensorFlow は CPU と GPU を含む、様々なタイプのデバイス上で計算を実行することをサポートします。それらは文字列で表わされます、例えば :

  • “/cpu:0”: 貴方のマシンの CPU。
  • “/device:GPU:0”: TensorFlow に見える、貴方のマシンの最初の GPU
  • “/device:GPU:1”: TensorFlow に見える、貴方のマシンの 2 番目の GPU, etc.

TensorFlow 演算が CPU と GPU 実装の両者を持つ場合、演算がデバイスに割り当てられるときデフォルトでは GPU デバイスに優先権が与えられるでしょう。例えば、matmul は CPU と GPU カーネルの両者を持ちます。デバイス cpu:0 と gpu:0 を持つシステム上では、貴方がそれを他のデバイス上で実行することを明示的に要請しない限りは matmul を実行するために gpu:0 が選択されます。

 

デバイス配置をログ記録する

貴方の演算と tensor がどのデバイスに割り当てられたかを見い出すためには、tf.debugging.set_log_device_placement(True) を貴方のプログラムの最初のステートメントとして置きます。

tf.debugging.set_log_device_placement(True)

# Creates some tensors
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
print(c)

次の出力を見るはずです :

Executing op MatMul in device /job:localhost/replica:0/task:0/device:CPU:0
tf.Tensor(
[[22. 28.]
 [49. 64.]], shape=(2, 2), dtype=float32)

 

手動デバイス配置

貴方のために自動的に選択されたものの代わりに特定の演算に貴方が選択したデバイス上で実行して欲しい場合、デバイスコンテキストを作成するために with tf.device を使用できてそしてそのコンテキスト内の総ての演算は指定された同じデバイス上で実行されます。

tf.debugging.set_log_device_placement(True)

# Place tensors on the CPU
with tf.device('/cpu:0'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
print(c)

今では a と b は cpu:0 に割り当てられたことを見るでしょう。MatMul 演算のためにはデバイスは明示的に指定されていませんので、TensorFlow ランタイムは演算と利用可能なデバイス (この例では cpu:0) に基づいて一つを選択して必要であればデバイス間で tensor を自動的にコピーします。

Executing op MatMul in device /job:localhost/replica:0/task:0/device:CPU:0
tf.Tensor(
[[22. 28.]
 [49. 64.]], shape=(2, 2), dtype=float32)

 

GPU メモリ増加を可能にする

デフォルトでは、TensorFlow は (CUDA_VISIBLE_DEVICES に従って) 可視の総ての GPU の GPU メモリの殆ど総てをプロセスにマップします。これはメモリ断片化を減らしてデバイス上の比較的貴重な GPU メモリリソースをより効率的に使用するために成されます。

ある場合にはプロセスに対して利用可能なメモリの一部だけを割り当てたり、プロセスにより必要とされるメモリ使用量を増やすだけが望ましいです。TensorFlow はこれを制御するために 2 つのメソッドを提供します。

最初のオプションは tf.config.gpu.set_per_process_memory_growth() メソッドを呼ぶことによりメモリ増大を有効にします、これはランタイム割り当てのために必要な GPU メモリだけを割り当てることを試みます : それは非常に小さいメモリを割り当てることから始めて、そしてプログラムが実行されてより多くの GPU メモリが必要になるにつれて、TensorFlow プロセスに割り当てられる GPU メモリ領域を拡張します。メモリを解放しないことに注意してください、何故ならばそれはより悪いメモリ断片化に繋がる可能性さえあるからです。プロセスメモリ増加を有効にするためには、貴方のプログラムの最初のステートメントにこれを置きます :

tf.config.gpu.set_per_process_memory_growth()

2 番目のメソッドは tf.gpu.set_per_process_memory_fraction() です、これは各可視な GPU が割り当てられるべきメモリの全体的な総量の割合を決定します。例えば、次により TensorFlow に各 GPU の総メモリの 40% だけを割り当てることを知らせることができます :

tf.config.gpu.set_per_process_memory_fraction(0.4)

これは TensorFlow プロセスに利用可能な GPU メモリの総量を正確に抑制したい場合に有用です。

 

マルチ GPU システム上で単一 GPU を使用する

貴方のシステムに一つの GPU 以上を持つ場合、デフォルトでは最少の ID を持つ GPU が選択されます。異なる GPU 上で実行したい場合には、その選択を明示的に指定する必要があります :

tf.debugging.set_log_device_placement(True)

# Specify a device
with tf.device('/device:GPU:2'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)

print(c)

貴方が指定したデバイスが存在しない場合には、RuntimeError を得るでしょう :

RuntimeError: Error copying tensor to device: /job:localhost/replica:0/task:0/device:GPU:2. /job:localhost/replica:0/task:0/device:GPU:2 unknown device.

指定されたものが存在しない場合に TensorFlow に演算を実行するための存在しているサポートされるデバイスを自動的に選択させたいのであれば、tf.config.set_soft_device_placement(True) を呼び出すことができます。

tf.config.set_soft_device_placement(True)
tf.debugging.set_log_device_placement(True)

# Creates some tensors
with tf.device('/device:GPU:2'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)

print(c)

 

マルチ GPU を使用する

With tf.distribute.Strategy

マルチ GPU を使用するためのベストプラクティスは tf.distribute.Strategy を使用することです。ここに単純なサンプルがあります :

strategy = tf.distribute.MirroredStrategy()

with strategy.scope():
  inputs = tf.keras.layers.Input(shape=(1,))
  predictions = tf.keras.layers.Dense(1)(inputs)
  model = tf.keras.models.Model(inputs=inputs, outputs=predictions)
  model.compile(loss='mse',
                optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.2))

このプログラムは各 GPU 上で貴方のモデルのコピーを実行します、それらの間で入力データを分割します、「データ並列」としても知られています。

分散ストラテジーについてのより多くの情報については、ここで ガイド を調べてください。

 

Without tf.distribute.Strategy

tf.distribute.Strategy は内部的にはデバイスに渡り計算を複製することにより動作しています。各 GPU 上で貴方のモデルを構築することにより複製を手動で実装することができます。例えば :

tf.debugging.set_log_device_placement(True)

# Replicate your computation on multiple GPUs
c = []
for d in ['/device:GPU:2', '/device:GPU:3']:
  with tf.device(d):
    a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3])
    b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2])
    c.append(tf.matmul(a, b))
with tf.device('/cpu:0'):
  sum = tf.add_n(c)

print(sum)

次の出力を見るでしょう :

Executing op MatMul in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op MatMul in device /job:localhost/replica:0/task:0/device:GPU:1
Executing op AddN in device /job:localhost/replica:0/task:0/device:CPU:0
tf.Tensor(
[[ 44.  56.]
 [ 98. 128.]], shape=(2, 2), dtype=float32)
 

以上



TensorFlow 2.0 Alpha : ガイド : TensorFlow で分散訓練

TensorFlow 2.0 Alpha : ガイド : TensorFlow で分散訓練 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/21/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : TensorFlow で分散訓練

概要

tf.distribute.Strategy はマルチ GPU、マルチマシンや TPU に渡り訓練を分散するための TensorFlow API です。この API を使用して、ユーザは既存のモデルと訓練コードを最小限のコード変更で分散できます。tf.distribute.Strategy はこれらの主要なゴールに留意して設計されています : * 容易に使用できて研究者、ML 技術者, etc. を含むマルチ・ユーザセグメントをサポートします。* 良いパフォーマンスを独創的に提供します。* ストラテジー間の容易な切り替え。

tf.distribute.Strategy は TensorFlow の高位 API、tf.keras と tf.estimator で単に数行のコード変更で利用できます。それはまたカスタム訓練ループ (そして一般に TensorFlow を使用する任意の計算) を分散するために使用できる API も提供します。TensorFlow 2.0 では、ユーザはプログラムを eagerly に、あるいは tf.function を使用してグラフ内で実行できます。tf.distribute.Strategy はこれらの実行モードの両者をサポートすることを意図します。このガイドの時間の殆どで訓練について語るかも知れませんが、この API は異なるプラットフォーム上で評価と予測を分散するためにも使用できることに注意してください。

すぐに見るように、貴方のコードで tf.distribute.Strategy を使用するためには非常に少しの変更が必要とされます。これは strategy-aware とするために TensorFlow の基礎的なコンポーネントを変更したからです。これは変数、層、モデル、optimizer、メトリクス、要約そしてチェックポイントを含みます。

このガイドでは、様々なタイプのストラテジーについてとそれらを異なる状況でどのように使用できるかについて話します。

# Import TensorFlow
from __future__ import absolute_import, division, print_function
!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

 

ストラテジーのタイプ

tf.distribute.Strategy は異なる軸に沿った多くのユースケースをカバーすることを意図しています。これらの組み合わせの幾つかは現在サポートされていて他のものは将来的に追加されます。これらの軸の幾つかは : * 同期 vs 非同期訓練です : これらはデータ並列で訓練を分散する 2 つの一般的な方法です。同期訓練では、総てのワーカーは入力データの異なるスライスに渡り同期的に訓練して、各ステップで勾配を集めます。非同期訓練では、総てのワーカーは入力データに渡り独立的に訓練して変数を非同期的に更新します。典型的には同期訓練は all-reduce を通してそして非同期 (訓練) はパラメータサーバ・アーキテクチャを通してサポートされます。* ハードウェア・プラットフォーム: ユーザは一つのマシンかネットワーク内のマルチマシン (それぞれ 0 かそれ以上の GPU) 上のマルチ GPU、あるいはクラウド TPU 上に彼らの訓練をスケールすることを望むかもしれません。

これらのユースケースをサポートするために、利用可能な 4 つのストラテジーを持ちます。次のセクションでは現時点で TF 2.0-alpha 内でどのシナリオにおいてこれらのどれがサポートされるかについて話しをします。

 

MirroredStrategy

tf.distribute.MirroredStrategy は一つのマシン上のマルチ GPU 上で同期分散訓練をサポートします。それは GPU デバイス毎に一つのレプリカを作成します。モデルの各変数は総てのレプリカに渡りミラーリングされます。一緒に、これらの変数は MirroredVariable と呼ばれる単一の概念的な変数を形成します。これらの変数は同一の更新を適用することにより互いに同期されます。

効率的な all-reduce アルゴリズムはデバイスを通して変数更新を伝達するために使用されます。all-reduce は総てのデバイスに渡り tensor をそれらを合計することにより集めてそれらを各デバイスで利用可能にします。それは融合されたアルゴリズムで非常に効率的で同期のオーバーヘッドを著しく減らせます。デバイス間で利用可能な通信のタイプに依拠して、利用可能な多くの all-reduce アルゴリズムと実装があります。デフォルトでは、それは all-reduce 実装として NVIDIA NCCL を使用します。ユーザはまた提供する 2, 3 の他のオプション間で選択できて、彼ら自身のものを書くこともできます。

ここに MirroredStrategy を作成する最も単純な方法があります :

mirrored_strategy = tf.distribute.MirroredStrategy()
WARNING: Logging before flag parsing goes to stderr.
W0307 18:06:27.818398 139925123892992 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.

これは MirroredStrategy インスタンスを作成します、これは TensorFlow に見える総ての GPU を使用して、そして交差デバイス通信として NCCL を使用します。

貴方のマシン上の GPU の一部だけを利用することを望む場合、それをこのようにできます :

mirrored_strategy = tf.distribute.MirroredStrategy(devices=["/gpu:0", "/gpu:1"])
W0307 18:06:27.835354 139925123892992 cross_device_ops.py:1106] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.

交差デバイス通信をオーバーライドすることを望む場合、cross_device_ops 引数を使用して tf.distribute.CrossDeviceOps のインスタンスを供給することによりそれを行なうことができます。デフォルトの tf.distribute.NcclAllReduce 以外の 2 つの他のオプションとして現在 tf.distribute.HierarchicalCopyAllReduce と tf.distribute.ReductionToOneDevice を提供しています。

mirrored_strategy = tf.distribute.MirroredStrategy(
    cross_device_ops=tf.distribute.HierarchicalCopyAllReduce())
W0307 18:06:27.850420 139925123892992 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.

 

MultiWorkerMirroredStrategy

tf.distribute.experimental.MultiWorkerMirroredStrategy は MirroredStrategy に非常に類似しています。それはマルチ・ワーカーに渡る同期分散訓練を実装し、それぞれ潜在的にマルチ GPU を持ちます。MirroredStrategy と同様に、それはモデルの総ての変数のコピーを総てのワーカーに渡り各デバイス上に作成します。

それは CollectiveOps を変数の同期を取るために使用されるマルチワーカー all-reduce 通信メソッドとして使用します。collective op は TensorFlow グラフの単一 op で、それはハードウェア、ネットワーク・トポロジーそして tensor サイズに従って TensorFlow ランタイムで all-reduce アルゴリズムを自動的に選択できます。

それは追加のパフォーマンス最適化も実装します。例えば、それは小さい tensor 上のマルチ all-reductions をより巨大な tensor 上のより少ない all-reductions に変換する静的最適化を含みます。更に、それをプラグイン・アーキテクチャを持つように設計していますので、将来的にユーザは彼らのハードウェアのためにより良く調整されたアルゴリズムをプラグインできるでしょう。collective ops はまた broadcast と all-gather のような他の collective 演算も実装することに注意してください。

ここに MultiWorkerMirroredStrategy を作成する最も単純な方法があります :

multiworker_strategy = tf.distribute.experimental.MultiWorkerMirroredStrategy()
W0307 18:06:27.865478 139925123892992 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.

MultiWorkerMirroredStrategy は現在 collective ops の 2 つの異なる実装間で選択することを可能にします。CollectiveCommunication.RING は gRPC を通信層として使用して ring-based collectives を実装します。CollectiveCommunication.NCCL は collectives を実装するために Nvidia の NCCL を使用します。CollectiveCommunication.AUTO は選択を実行時まで延期します。collective 実装の最善の選択は GPU の数と種類、そしてクラスタのネットワーク相互接続に依拠します。それらをこのように指定できます :

multiworker_strategy = tf.distribute.experimental.MultiWorkerMirroredStrategy(
    tf.distribute.experimental.CollectiveCommunication.NCCL)
W0307 18:06:27.882048 139925123892992 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.

マルチ GPU 訓練と比較して、マルチワーカー訓練を進めるための主要な違いの一つはマルチワーカー・セットアップです。”TF_CONFIG” 環境変数はクラスタの一部である各ワーカーへのクラスタ構成を指定するための TensorFlow の標準的な方法です。どのようにこれが成されるかのより詳細については下の “TF_CONFIG” セクションを見てください。

Note: このストラテジーは 実験的 です、何故ならば現在それを改良してより多くのシナリオで動作するようにしているからです。その一環として、 将来的に API が変更されることを想定してください。

 

TPUStrategy

tf.distribute.experimental.TPUStrategy はユーザに彼らの TensorFlow 訓練を Tensor 処理ユニット (TPU) 上で実行させます。TPU は機械学習ワークロードを劇的に高速化するように設計された Google の特化された ASIC です。それらは Google Colab、TensorFlow Research Cloud そして Google Compute Engine で利用可能です。

分散訓練アーキテクチャの観点からは、TPUStrategy は同じ MirroredStrategy です – それは同期分散訓練を実装します。TPU はマルチ TPU コアに渡る効率的な all-reduce のそれら自身の実装と他の collective 演算を提供します、それらは TPUStrategy で使用されます。

ここにどのように TPUStrategy をインスタンス化するかがあります。Note: このコードを Colab で実行するには、Colab 実行時に TPU を選択すべきです。実行可能なバージョンについては Using TPUs ガイド (訳注: リンク切れ) を見てください。

resolver = tf.distribute.cluster_resolver.TPUClusterResolver()
tf.tpu.experimental.initialize_tpu_system(resolver)
tpu_strategy = tf.distribute.experimental.TPUStrategy(resolver)

TPUClusterResolver インスタンスは TPU を見つける助けをします。Colab では、それにどのような引数も指定する必要がありません。これを Cloud TPU で使用することを望む場合、tpu 引数で貴方の TPU リソースの名前を指定する必要があります。またプログラムの最初で tpu システムを明示的に初期化する必要もあります。これは TPU が計算のために使用できる前に必要で理想的には最初に成されるべきです、何故ならばそれはまた TPU メモリも拭き取るために総ての状態が失われるからです。

 

ParameterServerStrategy

tf.distribute.experimental.ParameterServerStrategy はパラメータサーバ訓練をサポートします。それはマルチ GPU 同期ローカル訓練か非同期マルチマシン訓練のために使用できます。一つのマシン上のローカルで訓練するために使用されるとき、変数はミラーリングされません、代わりにそれらは CPU に置かれて演算は総てのローカル GPU に渡り複製されます。マルチマシン設定では、幾つかのマシンはワーカーとしてそして幾つかはパラメータサーバとして指定されます。モデルの各変数は一つのパラメータサーバに置かれます。計算は総てのワーカーの総ての GPU に渡り複製されます。

コードの観点からは、それは他のストラテジーと同様に見えます :

ps_strategy = tf.distribute.experimental.ParameterServerStrategy()

マルチワーカー訓練については、”TF_CONFIG” はクラスタのパラメータサーバとワーカーの構成を指定する必要があります、それについて下の “TF_CONFIG” で更に読むことができます。

ここまで利用可能な異なるストラテジーが何であるかそしてそれらをどのようにインスタンス化できるかについて話してきました。次の幾つかのセクションでは、貴方の訓練を分散するためにそれらを使用する異なる方法について話します。このガイドでは短いコードスニペットを示して、end-to-end で実行できる完全なチュートリアルへリンクします。

 

Keras で tf.distribute.Strategy を使用する

私達は tf.distribute.Strategy を tf.keras に統合しました、これは Keras API 仕様の TensorFlow 実装です。tf.keras はモデルを構築して訓練するための高位 API です。

tf.keras バックエンドに統合することで、Keras ユーザに Keras 訓練フレームワークで書かれた彼らの訓練を分散することをシームレスにしました。ユーザのプログラムで変更が必要なものは以下だけです : (1) 適切な tf.distribute.Strategy のインスタンスを作成します。そして (2) Keras モデルの作成とコンパイルを strategy.scope の内側に移す。

ここに一つの dense 層を持つ非常に簡単な Keras モデルのためにこれを行なうコードのスニペットがあります :

mirrored_strategy = tf.distribute.MirroredStrategy()
with mirrored_strategy.scope():
  model = tf.keras.Sequential([tf.keras.layers.Dense(1, input_shape=(1,))])
  model.compile(loss='mse', optimizer='sgd')
W0307 18:06:27.907308 139925123892992 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.

この例では MirroredStrategy を使用していますのでこれをマルチ GPU を持つ (一つの) マシン上で実行できます。strategy.scope() はコードのどの部分を分散して実行するかを示しました。このスコープの内側でモデルを作成すると通常の変数の代わりにミラーリングされた変数を作成することを可能にします。このスコープ下でコンパイルするとユーザはこのストラテジーを使用してモデルを訓練することを意図していることを知ることができます。ひとたびこれがセットアップされれば、モデルを通常するように fit できます。MirroredStrategy は勾配を集める等、利用可能な GPU 上でモデルの訓練を複製する処理をします。

dataset = tf.data.Dataset.from_tensors(([1.], [1.])).repeat(100).batch(10)
model.fit(dataset, epochs=2)
model.evaluate(dataset)
W0307 18:06:28.070022 139925123892992 training_utils.py:1353] Expected a shuffled dataset but input dataset `x` is not shuffled. Please invoke `shuffle()` on input dataset.

Epoch 1/2
10/10 [==============================] - 0s 7ms/step - loss: 2.1846
Epoch 2/2
10/10 [==============================] - 0s 2ms/step - loss: 2.0163
10/10 [==============================] - 0s 5ms/step - loss: 1.9289

1.9288513660430908

ここで訓練と評価入力を提供するために tf.data.Dataset を使用しました。numpy 配列も使用できます :

import numpy as np
inputs, targets = np.ones((100, 1)), np.ones((100, 1))
model.fit(inputs, targets, epochs=2, batch_size=10)
Epoch 1/2
10/10 [==============================] - 0s 2ms/step - loss: 1.8610
Epoch 2/2
10/10 [==============================] - 0s 2ms/step - loss: 1.7176

<tensorflow.python.keras.callbacks.History at 0x7f4274ade400>

両者のケース (dataset か numpy) で、与えられた入力の各バッチはマルチレプリカの中で均等に分割されます。例えば、2 GPU で MirroredStrategy を使用する場合、size 10 の各バッチは 2 GPU の間で分割されて、それぞれが各ステップで 5 入力サンプルを受け取ります。それで貴方がより多くの GPU を追加するにつれて各エポックはより高速に訓練されます。典型的には、貴方がより多くのアクセラレータを追加するとき特別な計算パワーを効果的に利用するためにバッチサイズを増やすことを望むでしょう、またモデルに依拠して、学習率を再調整する必要もあるでしょう。レプリカの数を得るために strategy.num_replicas_in_sync を使用できます。

# Compute global batch size using number of replicas.
BATCH_SIZE_PER_REPLICA = 5
global_batch_size = (BATCH_SIZE_PER_REPLICA * 
                     mirrored_strategy.num_replicas_in_sync)
dataset = tf.data.Dataset.from_tensors(([1.], [1.])).repeat(100)
dataset = dataset.batch(global_batch_size)

LEARNING_RATES_BY_BATCH_SIZE = {5: 0.1, 10: 0.15}
learning_rate = LEARNING_RATES_BY_BATCH_SIZE[global_batch_size]

 

今は何がサポートされますか?

TF 2.0 alpha リリースでは、MirroredStrategy を使用した Keras による訓練と、ParameterServerStrategy を使用した一つのマシンのパラメータサーバをサポートします。他のストラテジーのためのサポートは間もなくです。API とどのように使用するかも上と正確に同じです。TF 2.0 の Keras で TPUStrategy や MultiWorkerMirorredStrategy のような他のストラテジーを使用することを望む場合、現在は eager execution を無効にする (tf.compat.v1.disable_eager_execution()) ことによりそれを行なうことができます。注意すべきもう一つのことは Keras でマルチワーカーのために MultiWorkerMirorredStrategy を使用するとき、現在ユーザは異なるワーカーのためにデータを明示的にシャードするかシャッフルしなければならないことですが、将来的には知的に入力データを自動的にシャードするようにこれを変更します。

 

サンプルとチュートリアル

ここに Keras の end-to-end で上の統合を示すチュートリアルとサンプルのリストがあります : 1. MirroredStrategy で MNIST を訓練する チュートリアル。2. TPUStrategy で Fashion MNIST を訓練する チュートリアル (訳注: リンク切れ) (現在は disable_eager_execution を使用)。3. MirroredStrategy を使用して ImageNet データで公式 ResNet50 訓練。4. TPUStrategy で Cloud TPU 上 Imagenet データで訓練された ResNet50。このサンプルは現在は TensorFlow 1.x で動作のみ動作することに注意してください。

 

Estimator で tf.distribute.Strategy を使用する

tf.estimator は分散訓練 TensorFlow API で、それは非同期パラメータサーバのアプローチを元々サポートしていました。Keras でのように、私達は tf.distribute.Strategy を tf.Estimator に統合しました、その結果 Estimator を訓練に使用しているユーザは彼らのコードへの僅かの変更で訓練を容易に分散に変更できます。これにより、estimater ユーザは今では TPU の使用に加えて、マルチ GPU とマルチワーカー上で同期分散訓練を行なうことができます。

tf.distribute.Strategy の使用方法は Keras の場合と僅かに異なります。strategy.scope を使用する代わりに、今は strategy オブジェクトを Estimator のための RunConfig に渡します。

ここに premade estimator LinearRegressor と MirroredStrategy でこれを示すコードスニペットがあります :

mirrored_strategy = tf.distribute.MirroredStrategy()
config = tf.estimator.RunConfig(
    train_distribute=mirrored_strategy, eval_distribute=mirrored_strategy)
regressor = tf.estimator.LinearRegressor(
    feature_columns=[tf.feature_column.numeric_column('feats')],
    optimizer='SGD',
    config=config)
W0307 18:06:33.035279 139925123892992 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.
W0307 18:06:33.037824 139925123892992 estimator.py:1799] Using temporary folder as model directory: /tmp/tmp9yy5cqbb

ここでは premade Estimator を使用しますが、同じコードがカスタム Estimator でもまた動作します。train_evaluate は訓練がどのように分散されるかを決定し、そして eval_distribute は評価がどのように分散されるかを決定します。これはもう一つの Keras との違いです、そこでは訓練と評価の両者のために同じ strategy を使用します。

今ではこの Estimator を input 関数で訓練して評価することができます :

def input_fn():
  dataset = tf.data.Dataset.from_tensors(({"feats":[1.]}, [1.]))
  return dataset.repeat(1000).batch(10)
regressor.train(input_fn=input_fn, steps=10)
regressor.evaluate(input_fn=input_fn, steps=10)
W0307 18:06:33.136213 139916847593216 distribute_lib.py:830] Partitioned variables are disabled when using current tf.distribute.Strategy.
W0307 18:06:33.151402 139916847593216 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/feature_column/feature_column_v2.py:2758: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.cast` instead.
W0307 18:06:34.565543 139925123892992 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/saver.py:1276: checkpoint_exists (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version.
Instructions for updating:
Use standard file APIs to check for files with this prefix.

{'average_loss': 1.4210855e-14,
 'global_step': 10,
 'label/mean': 1.0,
 'loss': 1.4210855e-14,
 'prediction/mean': 0.99999994}

ここで強調したい Estimator と Keras 間のもう一つの違いは入力処理です。Keras では、dataset の各バッチはマルチ・レプリカに渡り分割されることに言及しました。Estimator では、けれども、ユーザは input_fn を提供して彼らのデータがワーカーとデバイスに渡りどのように分散されることを望むかについて完全な制御を持ちます。私達はバッチの自動的分割を行ないませんし、異なるワーカーに渡りデータを自動的にシャードもしません。提供される input_fn はワーカー毎に一度呼び出され、ワーカー毎に一つの dataset を与えます。それからその dastaset から一つのバッチがそのワーカー上の一つのレプリカに供給され、従って 1 ワーカー上の N レプリカのために N バッチを消費します。換言すれば、input_fn から返される dataset はサイズ PER_REPLICA_BATCH_SIZE のバッチを提供するべきです。そしてステップのためのグローバル・バッチサイズは PER_REPLICA_BATCH_SIZE * strategy.num_replicas_in_sync として得られます。マルチワーカー訓練を行なうとき、ユーザはまたデータをワーカーに渡り分割するかそれぞれの上でランダムシードでシャッフルすることもまた望むでしょう。これをどのように行なうかのサンプルを マルチワーカー・チュートリアル で見ることができます。

Estimator で MirroredStrategy を使用するサンプルを示しました。貴方はまた Estimator で TPUStrategy を使用することもまたできます、正確に同じ方法で :

config = tf.estimator.RunConfig(
    train_distribute=tpu_strategy, eval_distribute=tpu_strategy)

そして同様に、マルチワーカーとパラメータサーバのストラテジーもまた使用できます。コードは同じままですが、tf.estimator.train_and_evaluate を使用してクラスタで動作する各バイナリに対して “TF_CONFIG” 環境変数を設定する必要があります。

 

今は何がサポートされますか?

TF 2.0 alpha リリースでは、総てのストラテジーを使用して Estimater による訓練をサポートします。

 

サンプルとチュートリアル

ここに幾つかのサンプルがあります、これらは Estimator で様々なストラテジーの end-to-end の使用方法を示します :

  1. MultiWorkerMirroredStrategy を使用してマルチワーカーで MNIST を訓練する [Tutorial]((../tutorials/distribute/multi_worker)
  2. Kuberentes テンプレートを使用した tensorflow/ecosystem のマルチワーカー訓練のための end-to-end サンプル。このサンプルは Keras モデルから始めてそれを tf.keras.estimator.model_to_estimator API を使用して Estimator に変換します。
  3. 公式 ResNet50 モデル、これは MirroredStrategy か MultiWorkerMirroredStrategy を使用して訓練できます。
  4. TPUStrategy による ResNet50 サンプル。

 

カスタム訓練ループで tf.distribute.Strategy を使用する

貴方が見たように、高位 API で tf.distrbute.Strategy を使用するのは 2, 3 行のコード変更だけです。もう少しの努力で、tf.distrbute.Strategy はこれらのフレームワークを使用していない他のユーザによっても使用できます。

TensorFlow は様々な種類のユースケースのために使用されて (研究者のような) あるユーザは彼らの訓練ループに渡るより多くの柔軟性と制御を必要とします。これは彼らに Estimator や Keras のような高位フレームワークを使用することを困難にします。例えば、GAN を使用するある人は各ラウンドで異なる数の generator や discriminator ステップを取ることを望むかもしれません。同様に、高位フレームワークは強化学習訓練のためにはそれほど適してはいません。そのためこれらのユーザは通常は彼ら自身の訓練ループを書きます。

これらのユーザのために、tf.distrbute.Strategy クラスを通してメソッドのコアセットを提供します。これらの使用は初期化でコードの少量の再構成が必要かもしれませんが、ひとたびそれが成されれば、strategy インスタンスを単に変更することによりユーザは GPU / TPU / マルチマシンの間で切り替えることができるはずです。

ここで前と同じ Keras モデルを使用して単一の訓練サンプルのためのこのユースケースを表わす簡単なスニペットを示します。Note: これらの API は依然として実験的で TensorFlow 2.0 でそれらをよりユーザフレンドリーにするために改良しています。

最初に、strategy のスコープの内側でモデルと optimizer を作成します。これはこのモデルと optimizer で作成された任意の変数がミラーリングされる変数であることを確かなものにします。

with mirrored_strategy.scope():
  model = tf.keras.Sequential([tf.keras.layers.Dense(1, input_shape=(1,))])
  optimizer = tf.keras.optimizers.SGD()

次に、入力 dataset を作成して strategy に基づいて dataset を分散するために make_dataset_iterator を呼び出します。この API は近い将来に変更されることが予定されています。

with mirrored_strategy.scope():
  dataset = tf.data.Dataset.from_tensors(([1.], [1.])).repeat(1000).batch(
      global_batch_size)
  input_iterator = mirrored_strategy.make_dataset_iterator(dataset)

それから、訓練のワンステップを定義します。勾配を計算するために tf.GradientTape を使用してモデルの変数を更新するためにそれらの勾配を適用するために optimizer を使用します。この訓練ループを分散するために、関数 step_fn を中に入れてそれを前に作成された iterator と一緒に strategy.experimental_run に渡します :

@tf.function
def train_step():
  def step_fn(inputs):
    features, labels = inputs

    with tf.GradientTape() as tape:
      logits = model(features)
      cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
          logits=logits, labels=labels)  
      loss = tf.reduce_sum(cross_entropy) * (1.0 / global_batch_size)

    grads = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(list(zip(grads, model.trainable_variables)))
    return loss

  per_replica_losses = mirrored_strategy.experimental_run(
      step_fn, input_iterator)
  mean_loss = mirrored_strategy.reduce(
      tf.distribute.ReduceOp.MEAN, per_replica_losses)
  return mean_loss

上のコードで注意すべき 2, 3 の他のこと: 1. 損失を計算するために tf.nn.softmax_cross_entropy_with_logits を使用しました。そしてそれからトータル損失をグローバル・バッチサイズでスケールしました。これは重要です、何故ならば総てのレプリカは同期して訓練して訓練の各ステップのサンプル数はグローバル・バッチだからです。もし tf.losses や tf.keras.losses から TensorFlow の標準損失を使用していれば、それらは分散 aware で strategy がスコープにあるときはいつでもレプリカ数によるスケーリングを処理します。2. experimental_run により返される結果を集計するために strategy.reduce API を使用しました。experimental_run は strategy の各ローカル・レプリカからの結果を返して、そしてこれらの結果を消費するための複数の方法があります。集計値を得るためにそれらを reduce できます。ローカル・レプリカ毎に一つ、結果に含まれる値のリストを得るために strategy.unwrap(results)* を行なうこともできます。

* 変更される予定です。

最後に、ひとたび訓練ステップを定義したならば、 iterator を初期化してループで訓練を実行できます :

with mirrored_strategy.scope():
  input_iterator.initialize()
  for _ in range(10):
    print(train_step())
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)

上の例では、入力を貴方の訓練に提供するために make_dataset_iterator を使用しました。私達はまた 2 つの追加の API を提供します : 他の種類の入力をサポートするための make_input_fn_iterator と make_experimental_numpy_iterator。tf.distribute.Strategy のそれらのドキュメントを見てくださいそしてそれらが make_dataset_iterator とどのように違うのかを。

これはカスタム訓練ループを分散するために tf.distribute.Strategy API を使用する最も単純なケースをカバーします。私達はこれらの API を改良する過程にあります。このユースケースはユーザ側のより多くのワークを必要としますので、将来的にこのユースケースのための個別の詳細なガイドを公開します。

 

今は何がサポートされますか?

TF 2.0 alpha リリースでは、上で示されたように MirroredStrategy を使用してカスタム訓練ループを持つ訓練をサポートします。他のストラテジーのサポートは間もなくです。カスタム訓練ループで TF 2.0 で TPUStrategy のような他のストラテジーを使用することを望むのであれば、現在 eager execution を無効にすることによりそれを行なうことができます (tf.compat.v1.disable_eager_execution())。コードは同様のままです、訓練を実行するために TF 1.x グラフとセッションを使用する必要があることを除いて。MultiWorkerMirorredStrategy サポートは将来的にもたらされます。

 

サンプルとチュートリアル

ここにカスタム訓練ループで分散ストラテジーを使用するためのサンプルがあります : 1. MirroredStrategy を使用して MNIST を訓練する チュートリアル。2. MirroredStrategy を使用する DenseNet サンプル。

 

他のトピック

このセクションでは、複数のユースケースに関連する幾つかのトピックをカバーします。

 

TF_CONFIG 環境変数をセットアップする

マルチワーカー訓練のためには、前に言及したように、貴方のクラスタの各バイナリ実行のために “TF_CONFIG” 環境変数を設定する必要があります。”TF_CONFIG” 環境変数は JSON 文字列でこれは何のタスクがクラスタを構成するか、それらのアドレスとクラスタの各タスクの役割りを指定します。tensorflow/ecosystem レポで Kubernetes テンプレートを提供します、これは貴方の訓練タスクのために “TF_CONFIG” を設定します。

“TF_CONFIG” の一つの例は :

os.environ["TF_CONFIG"] = json.dumps({
    "cluster": {
        "worker": ["host1:port", "host2:port", "host3:port"],
        "ps": ["host4:port", "host5:port"]
    },
   "task": {"type": "worker", "index": 1}
})

この “TF_CONFIG” はクラスタに 3 つのワーカーと 2 つの ps タスクがそれらのホストとポートと一緒にあることを指定しています。”task” パートはクラスタの現在のタスクの役割り, ワーカー 1 (2 番目のワーカー) を指定しています。クラスタの有効な役割りは “chief”, “worker”, “ps” と “evaluator” です。tf.distribute.experimental.ParameterServerStrategy を使用するとき以外は “ps” ジョブはないはずです。

 

以上



TensorFlow 2.0 Alpha : ガイド : Keras : TensorFlow Keras でモデルをセーブしてシリアライズする

TensorFlow 2.0 Alpha : ガイド : Keras : TensorFlow Keras でモデルをセーブしてシリアライズする (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/20/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : Keras : TensorFlow Keras でモデルをセーブしてシリアライズする

このガイドの最初のパートは Sequential モデルと Functional API を使用して構築されたモデルのためのセーブとシリアライゼーションをカバーします。セーブとシリアライゼーション API はモデルのこれらのタイプの両者について正確に同じです。

モデルのカスタム・サブクラスのためのセーブはセクション「サブクラス化されたモデルをセーブする」でカバーされます。この場合の API は Sequential や Functional モデルのためのものとは僅かに異なります。

 

セットアップ

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

tf.keras.backend.clear_session()  # For easy reset of notebook state.

 

Part I: Sequential モデルまたは Functional モデルをセーブする

次のモデルを考えましょう :

from tensorflow import keras
from tensorflow.keras import layers

inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)

model = keras.Model(inputs=inputs, outputs=outputs, name='3_layer_mlp')
model.summary()
Model: "3_layer_mlp"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
digits (InputLayer)          [(None, 784)]             0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
_________________________________________________________________
predictions (Dense)          (None, 10)                650       
=================================================================
Total params: 55,050
Trainable params: 55,050
Non-trainable params: 0
_________________________________________________________________

オプションとして、このモデルを訓練してみましょう、そしてセーブするための重み値、そして optimizer 状態を持ちます。もちろん、まだ訓練していないモデルもセーブできますが、明らかにそれは面白くないでしょう。

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop())
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=1)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
60000/60000 [==============================] - 3s 46us/sample - loss: 0.3139
# Save predictions for future checks
predictions = model.predict(x_test)

 

モデル全体のセーブ

Functional API で構築されたモデルを単一のファイルにセーブできます。このファイルから同じモデルを後で再作成できます、モデルを作成したコードへのアクセスをもはや持たない場合でさえも。

ファイルは以下を含みます :

  • モデルのアーキテクチャ
  • モデルの重み値 (それは訓練の間に学習されました)
  • モデルの訓練 config (それは compile に渡したものです)、もしあれば
  • optimizer とその状態、もしあれば (これは貴方がやめたところで訓練を再開することを可能にします)
# Save the model
model.save('path_to_my_model.h5')

# Recreate the exact same model purely from the file
new_model = keras.models.load_model('path_to_my_model.h5')
import numpy as np

# Check that the state is preserved
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, atol=1e-6)

# Note that the optimizer state is preserved as well:
# you can resume training where you left off.

 

SavedModel にエクスポートする

TensorFlow SavedModel 形式にモデル全体をエクスポートすることも可能です。SavedModel は TensorFlow オブジェクトのためのスタンドアロン・シリアライゼーション形式で、TensorFlow serving と Python 以外の TensorFlow 実装によりサポートされます。

# Export the model to a SavedModel
keras.experimental.export_saved_model(model, 'path_to_saved_model')

# Recreate the exact same model
new_model = keras.experimental.load_from_saved_model('path_to_saved_model')

# Check that the state is preserved
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, atol=1e-6)

# Note that the optimizer state is preserved as well:
# you can resume training where you left off.
WARNING: Logging before flag parsing goes to stderr.
W0307 18:14:17.363332 140113924687616 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:253: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.
W0307 18:14:17.365585 140113924687616 tf_logging.py:161] Export includes no default signature!
W0307 18:14:17.814892 140113924687616 tf_logging.py:161] Export includes no default signature!

作成された SavedModel は以下を含みます :

  • モデル重みを含む TensorFlow チェックポイント。
  • 基礎となる TensorFlow グラフを含む SavedModel proto。予測 (サービング)、訓練と評価のために個別のグラフがセーブされます。モデルが前にコンパイルされていない場合は、推論グラフだけがエクスポートされます。
  • モデルのアーキテクチャ config、もし利用可能であれば。

 

アーキテクチャ-only セービング

時に、貴方はモデルのアーキテクチャだけに興味があり、そして重み値や optimizer をセーブする必要がありません。この場合、get_config() メソッドを通してモデルの “config” を取得できます。config は Python 辞書で同じモデルを再作成することを可能にします — スクラッチから初期化され、訓練の間に以前に学習された任意の情報は持ちません。

config = model.get_config()
reinitialized_model = keras.Model.from_config(config)

# Note that the model state is not preserved! We only saved the architecture.
new_predictions = reinitialized_model.predict(x_test)
assert abs(np.sum(predictions - new_predictions)) > 0.

代わりに from_json() から to_json() を使用することができます、これは config をストアするために Python 辞書の代わりに JSON 文字列を使用します。これは config をディスクにセーブするために有用です。

json_config = model.to_json()
reinitialized_model = keras.models.model_from_json(json_config)

 

重み-only セービング

時に、貴方はアーキテクチャではなくモデルの状態 — その重み値 — にだけ興味があります。この場合、get_weights() を通して重み値を Numpy 配列のリストとして取得できて、set_weights を通してモデルの状態を設定できます :

weights = model.get_weights()  # Retrieves the state of the model.
model.set_weights(weights)  # Sets the state of the model.

貴方のモデルを同じ状態で再作成するために get_config()/from_config() と get_weights()/set_weights() を結合できます。けれども、model.save() とは違い、これは訓練 config と optimizer を含みません。モデルを訓練のために使用する前に compile() を再度呼び出さなければならないでしょう。

config = model.get_config()
weights = model.get_weights()

new_model = keras.Model.from_config(config)
new_model.set_weights(weights)

# Check that the state is preserved
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, atol=1e-6)

# Note that the optimizer was not preserved,
# so the model should be compiled anew before training
# (and the optimizer will start from a blank state).

get_weights() と set_weights(weights) への save-to-disk 代用は save_weights(fpath) と load_weights(fpath) です。

ここにディスクにセーブするサンプルがあります :

# Save JSON config to disk
json_config = model.to_json()
with open('model_config.json', 'w') as json_file:
    json_file.write(json_config)
# Save weights to disk
model.save_weights('path_to_my_weights.h5')

# Reload the model from the 2 files we saved
with open('model_config.json') as json_file:
    json_config = json_file.read()
new_model = keras.models.model_from_json(json_config)
new_model.load_weights('path_to_my_weights.h5')

# Check that the state is preserved
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, atol=1e-6)

# Note that the optimizer was not preserved.

しかし覚えておいてください、最も単純な、推奨される方法は単にこれです :

model.save('path_to_my_model.h5')
del model
model = keras.models.load_model('path_to_my_model.h5')

 

SavedModel 形式で重み-only セービング

save_weights は Keras HDF5 形式か、TensorFlow SavedModel 形式でファイルを作成できることに注意してください。このフォーマットは貴方が提供するファイル拡張子から推論されます : それが”.h5″ か “.keras” であれば、フレームワークは Keras HDF5 形式を使用します。他の任意のものは SavedModel をデフォルトとします。

model.save_weights('path_to_my_tf_savedmodel')

総合的な明瞭さのために、フォーマットは save_format 引数を通して明示的に渡すことができます、これは値 “tf” か “h5” を取ることができます :

model.save_weights('path_to_my_tf_savedmodel', save_format='tf')

 

サブクラス化されたモデルをセーブする

Sequential モデルと Functional モデルは層の DAG を表わすデータ構造です。そのようなものとして、それらは安全にシリアライズとデシリアライズされます。

サブクラス化されたモデルはそれがデータ構造ではないという点で異なります、それはコードのピースです。モデルのアーキテクチャは call メソッド本体を通して定義されます。モデルのアーキテクチャは安全にシリアライズ化されないことを意味します。モデルをロードするためには、それを作成したコード (モデル・サブクラスのコード) へのアクセスを持つ必要があるでしょう。代わりに、このコードをバイトコードとしてシリアライズすることもできるでしょうが (e.g. pickling を通して)、それは安全ではなく一般に可搬ではありません。

これらの違いについてのより多くの情報は、記事 “What are Symbolic and Imperative APIs in TensorFlow 2.0?” を見てください。

次のサブクラス化されたモデルを考えましょう、これは最初のセクションからのモデルと同じ構造に従います :

class ThreeLayerMLP(keras.Model):
  
  def __init__(self, name=None):
    super(ThreeLayerMLP, self).__init__(name=name)
    self.dense_1 = layers.Dense(64, activation='relu', name='dense_1')
    self.dense_2 = layers.Dense(64, activation='relu', name='dense_2')
    self.pred_layer = layers.Dense(10, activation='softmax', name='predictions')
    
  def call(self, inputs):
    x = self.dense_1(inputs)
    x = self.dense_2(x)
    return self.pred_layer(x)

def get_model():
  return ThreeLayerMLP(name='3_layer_mlp')

model = get_model()

最初に、決して使用されていないサブクラス化されたモデルはセーブできません。

これはサブクラス化されたモデルはその重みを作成するために何某かのデータの上で呼び出される必要があるからです。

モデルが作成されるまで、それはそれが期待すべき入力データの shape と dtype を知りません、そしてそれ故にその重み変数を作成できません。

最初のセクションから Functional モデルでは、入力の shape と dtype が (keras.Input(…)を通して) 前もって指定されたことを覚えているかもしれません — それが Functional モデルがインスタンス化されてすぐに状態を持つ理由です。

モデルを、それに状態を与えるために、訓練しましょう :

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop())
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=1)
60000/60000 [==============================] - 3s 44us/sample - loss: 0.3129

サブクラス化されたモデルをセーブする推奨される方法は TensorFlow SavedModel チェックポイントを作成するために save_weights を使用することです、これはモデルに関連する総ての変数の値を含みます :- 層の重み – optimizer の状態 – ステートフル・モデル・メトリクスに関連する任意の変数 (もしあれば)

model.save_weights('path_to_my_weights', save_format='tf')
# Save predictions for future checks
predictions = model.predict(x_test)
# Also save the loss on the first batch
# to later assert that the optimizer state was preserved
first_batch_loss = model.train_on_batch(x_train[:64], y_train[:64])

貴方のモデルをリストアするためには、モデル・オブジェクトを作成したコードへのアクセスが必要です。

optimizer 状態と任意のステートフル・メトリックの状態をリストアするためには、モデルを (前と正確に同じ引数で) compile して load_weights を呼び出す前にそれをあるデータ上で呼び出すべきです :

# Recreate the model
new_model = get_model()
new_model.compile(loss='sparse_categorical_crossentropy',
                  optimizer=keras.optimizers.RMSprop())

# This initializes the variables used by the optimizers,
# as well as any stateful metric variables
new_model.train_on_batch(x_train[:1], y_train[:1])

# Load the state of the old model
new_model.load_weights('path_to_my_weights')

# Check that the model state has been preserved
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, atol=1e-6)

# The optimizer state is preserved as well,
# so you can resume training where you left off
new_first_batch_loss = new_model.train_on_batch(x_train[:64], y_train[:64])
assert first_batch_loss == new_first_batch_loss
 

以上



TensorFlow 2.0 Alpha : ガイド : Keras : TensorFlow Keras で層とモデルを書く

TensorFlow 2.0 Alpha : ガイド : Keras : TensorFlow Keras で層とモデルを書く (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/18/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : Keras : TensorFlow Keras で層とモデルを書く

セットアップ

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

tf.keras.backend.clear_session()  # For easy reset of notebook state.

 

Layer クラス

層は状態 (重み) と幾つかの計算をカプセル化します

貴方が作業する主要なデータ構造は Layer です。層は状態 (層の「重み」) と入力から出力への変換 (“call”、層の forward パス) をカプセル化します。

ここに密結合の層があります。それは状態: 変数 w と b を持ちます。

from tensorflow.keras import layers


class Linear(layers.Layer):

  def __init__(self, units=32, input_dim=32):
    super(Linear, self).__init__()
    w_init = tf.random_normal_initializer()
    self.w = tf.Variable(initial_value=w_init(shape=(input_dim, units),
                                              dtype='float32'),
                         trainable=True)
    b_init = tf.zeros_initializer()
    self.b = tf.Variable(initial_value=b_init(shape=(units,),
                                              dtype='float32'),
                         trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.w) + self.b

x = tf.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)
tf.Tensor(
[[ 0.02647757  0.02421902 -0.22342986 -0.08417849]
 [ 0.02647757  0.02421902 -0.22342986 -0.08417849]], shape=(2, 4), dtype=float32)

重み w と b は層の属性として設定されて自動的に追跡されます :

assert linear_layer.weights == [linear_layer.w, linear_layer.b]

重みを層に追加するためのより簡単なショートカットへのアクセスも持つことに注意してください : add_weight メソッドです :

class Linear(layers.Layer):

  def __init__(self, units=32, input_dim=32):
    super(Linear, self).__init__()
    self.w = self.add_weight(shape=(input_dim, units),
                             initializer='random_normal',
                             trainable=True)
    self.b = self.add_weight(shape=(units,),
                             initializer='zeros',
                             trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.w) + self.b

x = tf.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)
tf.Tensor(
[[-0.00899224 -0.22110203  0.00576357 -0.14656523]
 [-0.00899224 -0.22110203  0.00576357 -0.14656523]], shape=(2, 4), dtype=float32)

 

層は非訓練可能な重みを持つことができます

訓練可能な重みの他に、層に非訓練可能な重みを追加することもできます。そのような重みは、層を訓練しているとき、逆伝播の間は考慮されません。

ここに非訓練可能な重みをどのように追加して使用するかがあります :

class ComputeSum(layers.Layer):

  def __init__(self, input_dim):
    super(ComputeSum, self).__init__()
    self.total = tf.Variable(initial_value=tf.zeros((input_dim,)),
                             trainable=False)

  def call(self, inputs):
    self.total.assign_add(tf.reduce_sum(inputs, axis=0))
    return self.total  

x = tf.ones((2, 2))
my_sum = ComputeSum(2)
y = my_sum(x)
print(y.numpy())
y = my_sum(x)
print(y.numpy())
[2. 2.]
[4. 4.]

それは layer.weights の一部ですが、それは非訓練可能な重みとして分類されます :

print('weights:', len(my_sum.weights))
print('non-trainable weights:', len(my_sum.non_trainable_weights))

# It's not included in the trainable weights:
print('trainable_weights:', my_sum.trainable_weights)
weights: 1
non-trainable weights: 1
trainable_weights: []

 

ベストプラクティス: 入力の shape が知れるまで重み作成を遅延する

上のロジスティック回帰のサンプルでは、私達の Linear 層は __init__ で重み w と b の shape を計算するために使用された input_dim 引数を取りました :

class Linear(layers.Layer):

  def __init__(self, units=32, input_dim=32):
      super(Linear, self).__init__()
      self.w = self.add_weight(shape=(input_dim, units),
                               initializer='random_normal',
                               trainable=True)
      self.b = self.add_weight(shape=(units,),
                               initializer='random_normal',
                               trainable=True)

多くの場合、貴方は入力のサイズを前もって知らないかもしれません、そして重みをその値が知れるときに遅れて作成したいでしょう、時に層をインスタンス化した後に。

Keras API では、層重みを貴方の層の build(inputs_shape) メソッドで作成することを推奨します。このようにです :

class Linear(layers.Layer):

  def __init__(self, units=32):
    super(Linear, self).__init__()
    self.units = units

  def build(self, input_shape):
    self.w = self.add_weight(shape=(input_shape[-1], self.units),
                             initializer='random_normal',
                             trainable=True)
    self.b = self.add_weight(shape=(self.units,),
                             initializer='random_normal',
                             trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.w) + self.b

貴方の層の __call__ メソッドは最初にそれが呼び出されたときに build を自動的に実行します。今貴方は lazy で簡単に利用できる層を持ちます :

linear_layer = Linear(32)  # At instantiation, we don't know on what inputs this is going to get called
y = linear_layer(x)  # The layer's weights are created dynamically the first time the layer is called

 

層は再帰的に構成可能です

Layer インスタンスを他の層の属性として割り当てる場合、他の層は内側の層の重みを追跡し始めるでしょう。

そのような副層 (= sublayer) は __init__ メソッドで作成することを勧めます (何故ならば副層は典型的には build メソッドを持つので、それらは外側の層が構築されるときに構築されるからです)。

# Let's assume we are reusing the Linear class
# with a `build` method that we defined above.

class MLPBlock(layers.Layer):

  def __init__(self):
    super(MLPBlock, self).__init__()
    self.linear_1 = Linear(32)
    self.linear_2 = Linear(32)
    self.linear_3 = Linear(1)

  def call(self, inputs):
    x = self.linear_1(inputs)
    x = tf.nn.relu(x)
    x = self.linear_2(x)
    x = tf.nn.relu(x)
    return self.linear_3(x)
      

mlp = MLPBlock()
y = mlp(tf.ones(shape=(3, 64)))  # The first call to the `mlp` will create the weights
print('weights:', len(mlp.weights))
print('trainable weights:', len(mlp.trainable_weights))
weights: 6
trainable weights: 6

 

層は forward パスの間に作成された損失を再帰的に集めます

層の call メソッドを書くとき、貴方の訓練ループを書くときに、後で使用することを望む損失 tensor を作成できます。これは self.add_loss(value) を呼び出すことで行なうことができます :

# A layer that creates an activity regularization loss
class ActivityRegularizationLayer(layers.Layer):
  
  def __init__(self, rate=1e-2):
    super(ActivityRegularizationLayer, self).__init__()
    self.rate = rate
  
  def call(self, inputs):
    self.add_loss(self.rate * tf.reduce_sum(inputs))
    return inputs

(任意の内側の層で作成それたものを含む) これらの損失は layer.losses を通して取得できます。このプロパティは top-level 層への総ての __call__ の開始でリセットされますので、layer.losses は最後の forward パスの間に作成された損失値を常に含みます。

class OuterLayer(layers.Layer):

  def __init__(self):
    super(OuterLayer, self).__init__()
    self.activity_reg = ActivityRegularizationLayer(1e-2)

  def call(self, inputs):
    return self.activity_reg(inputs)


layer = OuterLayer()
assert len(layer.losses) == 0  # No losses yet since the layer has never been called
_ = layer(tf.zeros(1, 1))
assert len(layer.losses) == 1  # We created one loss value

# `layer.losses` gets reset at the start of each __call__
_ = layer(tf.zeros(1, 1))
assert len(layer.losses) == 1  # This is the loss created during the call above

加えて、loss プロパティはまた任意の内側の層の重みのために作成された正則化損失を含みます :

class OuterLayer(layers.Layer):

  def __init__(self):
    super(OuterLayer, self).__init__()
    self.dense = layers.Dense(32, kernel_regularizer=tf.keras.regularizers.l2(1e-3))
    
  def call(self, inputs):
    return self.dense(inputs)
  
  
layer = OuterLayer()
_ = layer(tf.zeros((1, 1)))

# This is `1e-3 * sum(layer.dense.kernel)`,
# created by the `kernel_regularizer` above.
print(layer.losses)
[<tf.Tensor: id=249, shape=(), dtype=float32, numpy=0.002372378>]

これらの損失は訓練ループを書くときに考慮されます、このようにです :

# Instantiate an optimizer.
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-3)
loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# Iterate over the batches of a dataset.
for x_batch_train, y_batch_train in train_dataset:
  with tf.GradientTape() as tape:
    logits = layer(x_batch_train)  # Logits for this minibatch
    # Loss value for this minibatch
    loss_value = loss_fn(y_batch_train, logits))
    # Add extra losses created during this forward pass:
    loss_value += sum(model.losses)

    grads = tape.gradient(loss_value, model.trainable_variables)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))

訓練ループを書くための詳細なガイドについては、訓練 & 評価 へのガイド を見てください。

 

オプションで貴方の層上でシリアライゼーションを有効にできます

貴方のカスタム層が Functional モデル の一部としてシリアライズ可能であることが必要ならば、オプションで get_config メソッドを実装できます :

class Linear(layers.Layer):

  def __init__(self, units=32):
    super(Linear, self).__init__()
    self.units = units

  def build(self, input_shape):
    self.w = self.add_weight(shape=(input_shape[-1], self.units),
                             initializer='random_normal',
                             trainable=True)
    self.b = self.add_weight(shape=(self.units,),
                             initializer='random_normal',
                             trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.w) + self.b

  def get_config(self):
    return {'units': self.units}
    

# Now you can recreate the layer from its config:
layer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)
{'units': 64}

基底 Layer クラスの __init__ メソッドは幾つかのキーワード引数、特に名前と dtype を取ることに注意してください。これらの引数を __init__ で親クラス渡してそれらを層 config に含めることは良い実践です :

class Linear(layers.Layer):

  def __init__(self, units=32, **kwargs):
    super(Linear, self).__init__(**kwargs)
    self.units = units

  def build(self, input_shape):
    self.w = self.add_weight(shape=(input_shape[-1], self.units),
                             initializer='random_normal',
                             trainable=True)
    self.b = self.add_weight(shape=(self.units,),
                             initializer='random_normal',
                             trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.w) + self.b

  def get_config(self):
    config = super(Linear, self).get_config()
    config.update({'units': self.units})
    return config
    

layer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)
{'trainable': True, 'dtype': None, 'name': 'linear_8', 'units': 64}

層をその config からデシリアライズするときにより柔軟性を必要とする場合には、from_config クラス・メソッドをオーバーライドすることもできます。これは from_config の基底実装です :

def from_config(cls, config):
  return cls(**config)

シリアライゼーションとセービングについて更に学習するためには、完全な Guide to Saving and Serializing Models を見てください。

 

call メソッドの特権 training 引数

幾つかの層、特にBatchNormalization 層と Dropout 層は訓練と推論の間に異なる挙動を持ちます。そのような層のために、training (ブーリアン) 引数を晒すのは標準的な実践です。

call でこの引数を晒すことで、組み込み訓練と評価ループ (e.g. fit) に訓練と推論で層を正しく使用することを可能にします。

class CustomDropout(layers.Layer):
  
  def __init__(self, rate, **kwargs):
    super(CustomDropout, self).__init__(**kwargs)
    self.rate = rate
  
  def call(self, inputs, training=None):
    return tf.cond(training,
                   lambda: tf.nn.dropout(inputs, rate=self.rate),
                   lambda: inputs)

 

モデルを構築する

Model クラス

一般に、内側の計算ブロックを定義するために Layer クラスを使用し、そして外側のモデル — 貴方が訓練するオブジェクト — を定義するために Model クラスを定義します。

例えば、ResNet 50 モデルで、幾つかの ResNet ブロックのサブクラス化された層と、ResNet 50 ネットワーク全体を取り囲む単一のモデルを持つでしょう。

Model クラスは Layer と同じ API を持ちますが、次の違いがあります :- それは組み込み訓練、評価と予測ループを公開します (model.fit(), model.evaluate(), model.predict())。- それはその内側の層のリストを model.layers で公開します。- それはセービングとシリアライゼーション API を公開します。

実際上、”Layer” クラスは著述では (「畳込み層」や「リカレント層」内の) 「層 (= layer)」や (「ResNet ブロック」や「Inception ブロック」内の) 「ブロック”」として言及するものに対応しています。

一方で、”Model” クラスは著述で (「深層学習モデル」内の)「モデル」や (「深層ニューラルネットワーク」内の)「ネットワーク」として言及されるもに対応しています。

例えば、上の mini-resnet サンプルを取り、それを (fit() で訓練可能で save_weights でセーブできるような) Model を構築するために使用できるでしょう :

class ResNet(tf.keras.Model):

    def __init__(self):
        super(ResNet, self).__init__()
        self.block_1 = ResNetBlock()
        self.block_2 = ResNetBlock()
        self.global_pool = layers.GlobalAveragePooling2D()
        self.classifier = Dense(num_classes)

    def call(self, inputs):
        x = self.block_1(inputs)
        x = self.block_2(x)
        x = self.global_pool(x)
        return self.classifier(x)


resnet = ResNet()
dataset = ...
resnet.fit(dataset, epochs=10)
resnet.save_weights(filepath)

 

総てを一つにまとめる : end-to-end サンプル

ここに貴方がここまでに学習したものがありあす :- 層は (__init__ か build で作成された) 状態と (call での) ある計算をカプセル化します。- 層は新しい、より大きな計算ブロックを作成するために再帰的にネストできます。- 層は損失を作成して追跡できます (典型的には正則化損失)。- 外側のコンテナ、貴方が訓練したいものはモデルです。モデルはちょうど層のようなものですが、追加の訓練とシリアライゼーション・ユティリティを持ちます。

これら総てのものを end-to-end サンプルにまとめましょう : 変分オートエンコーダ (VAE) を実装していきます。それを MNIST 数字の上で訓練します。

私達の VAE は Model のサブクラスで、 Layer をサブクラス化した層のネストされた構成として構築されます。それは正則化損失 (KL ダイバージェンス) をフィーチャーします。

class Sampling(layers.Layer):
  """Uses (z_mean, z_log_var) to sample z, the vector encoding a digit."""

  def call(self, inputs):
    z_mean, z_log_var = inputs
    batch = tf.shape(z_mean)[0]
    dim = tf.shape(z_mean)[1]
    epsilon = tf.keras.backend.random_normal(shape=(batch, dim))
    return z_mean + tf.exp(0.5 * z_log_var) * epsilon


class Encoder(layers.Layer):
  """Maps MNIST digits to a triplet (z_mean, z_log_var, z)."""
  
  def __init__(self,
               latent_dim=32,
               intermediate_dim=64,
               name='encoder',
               **kwargs):
    super(Encoder, self).__init__(name=name, **kwargs)
    self.dense_proj = layers.Dense(intermediate_dim, activation='relu')
    self.dense_mean = layers.Dense(latent_dim)
    self.dense_log_var = layers.Dense(latent_dim)
    self.sampling = Sampling()

  def call(self, inputs):
    x = self.dense_proj(inputs)
    z_mean = self.dense_mean(x)
    z_log_var = self.dense_log_var(x)
    z = self.sampling((z_mean, z_log_var))
    return z_mean, z_log_var, z

  
class Decoder(layers.Layer):
  """Converts z, the encoded digit vector, back into a readable digit."""
  
  def __init__(self,
               original_dim,
               intermediate_dim=64,
               name='decoder',
               **kwargs):
    super(Decoder, self).__init__(name=name, **kwargs)
    self.dense_proj = layers.Dense(intermediate_dim, activation='relu')
    self.dense_output = layers.Dense(original_dim, activation='sigmoid')
    
  def call(self, inputs):
    x = self.dense_proj(inputs)
    return self.dense_output(x)


class VariationalAutoEncoder(tf.keras.Model):
  """Combines the encoder and decoder into an end-to-end model for training."""
  
  def __init__(self,
               original_dim,
               intermediate_dim=64,
               latent_dim=32,
               name='autoencoder',
               **kwargs):
    super(VariationalAutoEncoder, self).__init__(name=name, **kwargs)
    self.original_dim = original_dim
    self.encoder = Encoder(latent_dim=latent_dim,
                           intermediate_dim=intermediate_dim)
    self.decoder = Decoder(original_dim, intermediate_dim=intermediate_dim)
    
  def call(self, inputs):
    z_mean, z_log_var, z = self.encoder(inputs)
    reconstructed = self.decoder(z)
    # Add KL divergence regularization loss.
    kl_loss = - 0.5 * tf.reduce_sum(
        z_log_var - tf.square(z_mean) - tf.exp(z_log_var) + 1)
    self.add_loss(kl_loss)
    return reconstructed


original_dim = 784
vae = VariationalAutoEncoder(original_dim, 64, 32)

optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
mse_loss_fn = tf.keras.losses.MeanSquaredError()

loss_metric = tf.keras.metrics.Mean()

(x_train, _), _ = tf.keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255

train_dataset = tf.data.Dataset.from_tensor_slices(x_train)
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Iterate over epochs.
for epoch in range(3):
  print('Start of epoch %d' % (epoch,))

  # Iterate over the batches of the dataset.
  for step, x_batch_train in enumerate(train_dataset):
    with tf.GradientTape() as tape:
      reconstructed = vae(x_batch_train)
      # Compute reconstruction loss
      loss = mse_loss_fn(x_batch_train, reconstructed)
      loss += sum(vae.losses)  # Add KLD regularization loss
      
    grads = tape.gradient(loss, vae.trainable_variables)
    optimizer.apply_gradients(zip(grads, vae.trainable_variables))
    
    loss_metric(loss)
    
    if step % 100 == 0:
      print('step %s: mean loss = %s' % (step, loss_metric.result()))
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
Start of epoch 0
step 0: mean loss = tf.Tensor(161.00046, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(5.378642, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(2.7485619, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(1.8636498, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(1.4188905, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(1.1506605, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.9712579, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.84277666, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.7465494, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.6713111, shape=(), dtype=float32)
Start of epoch 1
step 0: mean loss = tf.Tensor(0.6469352, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(0.59141695, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(0.54561764, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(0.50713503, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(0.4744861, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(0.44623637, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.4216811, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.4000843, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.38100395, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.3639242, shape=(), dtype=float32)
Start of epoch 2
step 0: mean loss = tf.Tensor(0.35792458, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(0.34327272, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(0.33004242, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(0.31798688, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(0.3070505, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(0.29695606, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.28770587, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.2791505, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.27125034, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.26388252, shape=(), dtype=float32)

VAE は Model をサブクラス化していますから、それは組み込み訓練ループをフィーチャーします。従ってそれをこのように訓練することもできるでしょう :

vae = VariationalAutoEncoder(784, 64, 32)

optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)

vae.compile(optimizer, loss=tf.keras.losses.MeanSquaredError())
vae.fit(x_train, x_train, epochs=3, batch_size=64)
Epoch 1/3
60000/60000 [==============================] - 4s 67us/sample - loss: 0.7372
Epoch 2/3
60000/60000 [==============================] - 4s 64us/sample - loss: 0.0686
Epoch 3/3
60000/60000 [==============================] - 4s 65us/sample - loss: 0.0678

<tensorflow.python.keras.callbacks.History at 0x7f94e85c0b70>

 

オブジェクト指向開発を越えて : Functional API

このサンプルは貴方にとってオブジェクト指向開発に過ぎたでしょうか?Functional API を使用してモデルを構築することもできます。重要なことは、一つのスタイルか別のものかの選択は他のスタイルで書かれたコンポーネントを活用することを妨げないことです : 貴方は常に上手く組み合わせることができます。

例えば、下の Functional API サンプルは上の例で定義した同じ Sampling 層を再利用しています。

original_dim = 784
intermediate_dim = 64
latent_dim = 32

# Define encoder model.
original_inputs = tf.keras.Input(shape=(original_dim,), name='encoder_input')
x = layers.Dense(intermediate_dim, activation='relu')(original_inputs)
z_mean = layers.Dense(latent_dim, name='z_mean')(x)
z_log_var = layers.Dense(latent_dim, name='z_log_var')(x)
z = Sampling()((z_mean, z_log_var))
encoder = tf.keras.Model(inputs=original_inputs, outputs=z, name='encoder')

# Define decoder model.
latent_inputs = tf.keras.Input(shape=(latent_dim,), name='z_sampling')
x = layers.Dense(intermediate_dim, activation='relu')(latent_inputs)
outputs = layers.Dense(original_dim, activation='sigmoid')(x)
decoder = tf.keras.Model(inputs=latent_inputs, outputs=outputs, name='decoder')

# Define VAE model.
outputs = decoder(z)
vae = tf.keras.Model(inputs=original_inputs, outputs=outputs, name='vae')

# Add KL divergence regularization loss.
kl_loss = - 0.5 * tf.reduce_sum(
    z_log_var - tf.square(z_mean) - tf.exp(z_log_var) + 1)
vae.add_loss(kl_loss)

# Train.
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
vae.compile(optimizer, loss=tf.keras.losses.MeanSquaredError())
vae.fit(x_train, x_train, epochs=3, batch_size=64)
Epoch 1/3
60000/60000 [==============================] - 4s 67us/sample - loss: 0.9678
Epoch 2/3
60000/60000 [==============================] - 4s 64us/sample - loss: 0.0693
Epoch 3/3
60000/60000 [==============================] - 4s 66us/sample - loss: 0.0679

<tensorflow.python.keras.callbacks.History at 0x7f94d413d550>
 

以上



TensorFlow 2.0 Alpha : ガイド : Keras : TensorFlow Keras による訓練と評価

TensorFlow 2.0 Alpha : ガイド : Keras : TensorFlow Keras による訓練と評価 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/18/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : Keras : TensorFlow Keras による訓練と評価

このガイドは 2 つの広範な状況における TensorFlow 2.0 の訓練、評価そして予測 (推論) モデルをカバーします :

  • (model.fit(), model.evaluate(), model.predict() のような) 訓練 & 検証のための組み込み API を使用するとき。これは「Part I: 組み込み訓練 & 評価ループを使用する」のセクションでカバーされます。
  • eager execution と GradientTape オブジェクトを使用してカスタム・ループをスクラッチから書くとき。これは「Part II: スクラッチから貴方自身の訓練 & 評価ループを書く」のセクションでカバーされます。

一般に、組み込みの訓練ループを使用しようが貴方自身のものを書こうが、モデル訓練 & 評価は総ての種類の Keras モデル — Sequential モデル、Functional API で構築されたモデルそしてモデル・サブクラス化でスクラッチから書かれたモデル — に渡り厳密に同じように動作します。

このガイドは分散訓練はカバーしません。

 

セットアップ

!pip install -q pydot
!apt-get install graphviz
Requirement already satisfied: pydot in /usr/local/lib/python3.6/dist-packages (1.3.0)
Requirement already satisfied: pyparsing>=2.1.4 in /usr/local/lib/python3.6/dist-packages (from pydot) (2.3.1)
Reading package lists... Done
Building dependency tree       
Reading state information... Done
graphviz is already the newest version (2.40.1-2).
0 upgraded, 0 newly installed, 0 to remove and 10 not upgraded.
from __future__ import absolute_import, division, print_function

!pip install -q tensorflow-gpu==2.0.0-alpha0
import tensorflow as tf

tf.keras.backend.clear_session()  # For easy reset of notebook state.

 

Part I: 組み込み訓練 & 評価ループを使用する

データをモデルの組み込み訓練ループに渡すとき、(もし貴方のデータが小さくてメモリに収まるのであれば) Numpy 配列か tf.data Dataset オブジェクトを使用するべきです。次の幾つかのパラグラフでは、optimizer、損失そしてメトリクスをどのように使用するかを示すために MNIST データセットを Numpy 配列として使用します。

 

API 概要: 最初の end-to-end サンプル

次のモデルを考えましょう (ここでは、Functional API で構築しますが、それは Sequential モデルやサブクラス化されたモデルでも同様です) :

from tensorflow import keras
from tensorflow.keras import layers

inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)

model = keras.Model(inputs=inputs, outputs=outputs)

ここに典型的な end-to-end ワークフローがどのようなものかがあります、訓練、元の訓練データから生成された取り置いたセット上の検証、そして最後にテストデータ上の評価 :

# Load a toy dataset for the sake of this example
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

# Preprocess the data (these are Numpy arrays)
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

# Reserve 10,000 samples for validation
x_val = x_train[-10000:]
y_val = y_train[-10000:]
x_train = x_train[:-10000]
y_train = y_train[:-10000]

# Specify the training configuration (optimizer, loss, metrics)
model.compile(optimizer=keras.optimizers.RMSprop(),  # Optimizer
              # Loss function to minimize
              loss=keras.losses.SparseCategoricalCrossentropy(),
              # List of metrics to monitor
              metrics=[keras.metrics.SparseCategoricalAccuracy()])

# Train the model by slicing the data into "batches"
# of size "batch_size", and repeatedly iterating over
# the entire dataset for a given number of "epochs"
print('# Fit model on training data')
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=3,
                    # We pass some validation for
                    # monitoring validation loss and metrics
                    # at the end of each epoch
                    validation_data=(x_val, y_val))

# The returned "history" object holds a record
# of the loss values and metric values during training
print('\nhistory dict:', history.history)

# Evaluate the model on the test data using `evaluate`
print('\n# Evaluate on test data')
results = model.evaluate(x_test, y_test, batch_size=128)
print('test loss, test acc:', results)

# Generate predictions (probabilities -- the output of the last layer)
# on new data using `predict`
print('\n# Generate predictions for 3 samples')
predictions = model.predict(x_test[:3])
print('predictions shape:', predictions.shape)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
# Fit model on training data
Train on 50000 samples, validate on 10000 samples
Epoch 1/3
50000/50000 [==============================] - 5s 110us/sample - loss: 0.3377 - sparse_categorical_accuracy: 0.9053 - val_loss: 0.2020 - val_sparse_categorical_accuracy: 0.9402
Epoch 2/3
50000/50000 [==============================] - 5s 107us/sample - loss: 0.1576 - sparse_categorical_accuracy: 0.9528 - val_loss: 0.1379 - val_sparse_categorical_accuracy: 0.9616
Epoch 3/3
50000/50000 [==============================] - 5s 106us/sample - loss: 0.1143 - sparse_categorical_accuracy: 0.9650 - val_loss: 0.1128 - val_sparse_categorical_accuracy: 0.9681

history dict: {'loss': [0.33772996835231783, 0.15758442388363184, 0.11431736122608185], 'sparse_categorical_accuracy': [0.90532, 0.95276, 0.96504], 'val_loss': [0.2019659897595644, 0.13788076196610927, 0.1128087827205658], 'val_sparse_categorical_accuracy': [0.9402, 0.9616, 0.9681]}

# Evaluate on test data
10000/10000 [==============================] - 0s 36us/sample - loss: 0.1238 - sparse_categorical_accuracy: 0.9606
test loss, test acc: [0.12378974738866091, 0.9606]

# Generate predictions for 3 samples
predictions shape: (3, 10)

 

損失、メトリクスと optimizer を指定する

それでモデルを訓練するためには、損失関数、optimizer そしてオプションで監視するための幾つかのメトリクスを指定する必要があります。

これらをモデルに compile() メソッドへの引数として渡します :

model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
              loss=keras.losses.SparseCategoricalCrossentropy(),
              metrics=[keras.metrics.SparseCategoricalAccuracy()])

metrics 引数はリストであるべきです — 貴方のモデルは任意の数のメトリクスを持つことができます。

貴方のモデルがマルチ出力を持つ場合、各出力に対して異なる損失とメトリクスを指定できて、そしてモデルのトータル損失への各出力の寄与を調節できます。これについての更なる詳細をセクション「データをマルチ入力、マルチ出力モデルに渡す」で見つけるでしょう。

多くの場合、損失とメトリクスはショートカットとして文字列識別子で指定されることに注意してください :

model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
              loss='sparse_categorical_crossentropy',
              metrics=['sparse_categorical_accuracy'])

後で再利用するために、モデル定義と compile ステップを関数内に配置しましょう ; それらをこのガイドの異なるサンプルに渡り幾度も呼び出します。

def get_uncompiled_model():
  inputs = keras.Input(shape=(784,), name='digits')
  x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
  x = layers.Dense(64, activation='relu', name='dense_2')(x)
  outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
  model = keras.Model(inputs=inputs, outputs=outputs)
  return model

def get_compiled_model():
  model = get_uncompiled_model()
  model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
              loss='sparse_categorical_crossentropy',
              metrics=['sparse_categorical_accuracy'])
  return model

 
多くの組み込み optimizer、損失とメトリクスが利用可能です

一般に、貴方自身の損失、メトリクスや optimizer をスクラッチから作成する必要はないでしょう、何故ならば貴方が必要なものは既に Keras API の一部でありがちだからです :

Optimizers: – SGD() (with or without momentum) – RMSprop() – Adam() – etc.

損失: – MeanSquaredError() – KLDivergence() – CosineSimilarity() – etc.

メトリクス: – AUC() – Precision() – Recall() – etc.

 
カスタム損失とメトリクスを書く

API の一部ではないメトリックを必要とする場合、Metric クラスをサブクラス化することによりカスタム・メトリクスを容易に作成できます。4 つのメソッドを実装する必要があります :

  • __init__(self), そこでは貴方のメトリックのための状態変数を作成します。
  • update_state(self, y_true, y_pred, sample_weight=None), これは状態変数を更新するためにターゲット y_true と モデル予測 y_pred を使用します。
  • result(self), 最終結果を計算するために状態変数を使用します。
  • reset_states(self), これはメトリックの状態を再初期化します。

状態更新と結果計算は (それぞれ update_state() と result() で) 別々にしておきます、何故ならばある場合には、結果計算は非常に高価であるためで、それは定期のみに行われるでしょう。

ここに CatgoricalTruePositives メトリックをどのように実装するかの単純なサンプルがあります、これは与えられたクラスに属するものとして正しく分類されたサンプルが幾つかを数えます :

class CatgoricalTruePositives(keras.metrics.Metric):
  
    def __init__(self, name='binary_true_positives', **kwargs):
      super(CatgoricalTruePositives, self).__init__(name=name, **kwargs)
      self.true_positives = self.add_weight(name='tp', initializer='zeros')

    def update_state(self, y_true, y_pred, sample_weight=None):
      y_pred = tf.argmax(y_pred)
      values = tf.equal(tf.cast(y_true, 'int32'), tf.cast(y_pred, 'int32'))
      values = tf.cast(values, 'float32')
      if sample_weight is not None:
        sample_weight = tf.cast(sample_weight, 'float32')
        values = tf.multiply(values, sample_weight)
      return self.true_positives.assign_add(tf.reduce_sum(values))  # TODO: fix

    def result(self):
      return tf.identity(self.true_positives)  # TODO: fix
    
    def reset_states(self):
      # The state of the metric will be reset at the start of each epoch.
      self.true_positives.assign(0.)


model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
              loss=keras.losses.SparseCategoricalCrossentropy(),
              metrics=[CatgoricalTruePositives()])
model.fit(x_train, y_train,
          batch_size=64,
          epochs=3)
Epoch 1/3
50000/50000 [==============================] - 4s 76us/sample - loss: 0.0917 - binary_true_positives: 7792.0000
Epoch 2/3
50000/50000 [==============================] - 3s 67us/sample - loss: 0.0756 - binary_true_positives: 8026.0000
Epoch 3/3
50000/50000 [==============================] - 4s 70us/sample - loss: 0.0647 - binary_true_positives: 8459.0000

 
標準的なシグネチャに当てはまらない損失とメトリクスを処理する

損失とメトリクスの圧倒的な多数は y_true と y_pred から計算できます、そこでは y_pred はモデル出力です。しかし総てではありません。例えば、正則化損失は層の活性だけを必要とするかもしれません (この場合ターゲットはありません)、そしてこの活性はモデル出力ではないかもしれません。

この場合、カスタム層の call メソッドの内側から self.add_loss(loss_value) を呼び出すことができます。ここに activity 正則化を追加する単純なサンプルがあります (activity 正則化は総ての Keras 層の組み込みであることに注意してください — この層は具体的な例を提供する目的だけです) :

class ActivityRegularizationLayer(layers.Layer):
  
  def call(self, inputs):
    self.add_loss(tf.reduce_sum(inputs) * 0.1)
    return inputs  # Pass-through layer.
  
inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)

# Insert activity regularization as a layer
x = ActivityRegularizationLayer()(x)

x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)

model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
              loss='sparse_categorical_crossentropy')

# The displayed loss will be much higher than before
# due to the regularization component.
model.fit(x_train, y_train,
          batch_size=64,
          epochs=1)
50000/50000 [==============================] - 4s 75us/sample - loss: 2.5322

<tensorflow.python.keras.callbacks.History at 0x7f6051a72e10>

logging メトリック値に対しても同じことを行うことができます :

class MetricLoggingLayer(layers.Layer):
  
  def call(self, inputs):
    # The `aggregation` argument defines
    # how to aggregate the per-batch values
    # over each epoch:
    # in this case we simply average them.
    self.add_metric(keras.backend.std(inputs),
                    name='std_of_activation',
                    aggregation='mean')
    return inputs  # Pass-through layer.

  
inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)

# Insert std logging as a layer.
x = MetricLoggingLayer()(x)

x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)

model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
              loss='sparse_categorical_crossentropy')
model.fit(x_train, y_train,
          batch_size=64,
          epochs=1)
50000/50000 [==============================] - 4s 76us/sample - loss: 0.3366 - std_of_activation: 0.9773

<tensorflow.python.keras.callbacks.History at 0x7f6051543e48>

Functional API では、model.add_loss(loss_tensor) か model.add_metric(metric_tensor, name, aggregation) を呼び出すこともできます。

ここに単純なサンプルがあります :

inputs = keras.Input(shape=(784,), name='digits')
x1 = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x2 = layers.Dense(64, activation='relu', name='dense_2')(x1)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x2)
model = keras.Model(inputs=inputs, outputs=outputs)

model.add_loss(tf.reduce_sum(x1) * 0.1)

model.add_metric(keras.backend.std(x1),
                 name='std_of_activation',
                 aggregation='mean')

model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
              loss='sparse_categorical_crossentropy')
model.fit(x_train, y_train,
          batch_size=64,
          epochs=1)
50000/50000 [==============================] - 4s 80us/sample - loss: 2.5158 - std_of_activation: 0.0020

<tensorflow.python.keras.callbacks.History at 0x7f605103ad30>

 
自動的に検証取り置き (= holdout) セットを別にする

貴方が見た最初の end-to-end サンプルでは、各エポックの終わりに検証損失と検証メトリクスを評価するために Numpy 配列のタプル (x_val, y_val) をモデルに渡すために validation_data 引数を使用しました。

ここにもう一つのオプションがあります : 引数 validation_split は検証のために訓練データの一部を自動的に取っておくことを可能にします。引数値は検証のために取っておかれるデータの割合を表しますので、それは 0 より高く 1 より低い数字に設定されるべきです。例えば、validation_split=0.2 は「検証のためにデータの 20% を使用する」ことを意味し、そして validation_split=0.6 は「検証のためにデータの 60% を使用する」ことを意味します。

検証が計算される方法は任意のシャッフルの前に、fit コールにより受け取った配列の最後の x% サンプルを取ります。

Numpy データによる訓練のときに validation_split を使用できるだけです。

model = get_compiled_model()
model.fit(x_train, y_train, batch_size=64, validation_split=0.2, epochs=3)
Train on 40000 samples, validate on 10000 samples
Epoch 1/3
40000/40000 [==============================] - 3s 82us/sample - loss: 0.3735 - sparse_categorical_accuracy: 0.8951 - val_loss: 0.2413 - val_sparse_categorical_accuracy: 0.9272
Epoch 2/3
40000/40000 [==============================] - 3s 82us/sample - loss: 0.1688 - sparse_categorical_accuracy: 0.9499 - val_loss: 0.1781 - val_sparse_categorical_accuracy: 0.9468
Epoch 3/3
40000/40000 [==============================] - 3s 79us/sample - loss: 0.1232 - sparse_categorical_accuracy: 0.9638 - val_loss: 0.1518 - val_sparse_categorical_accuracy: 0.9539

<tensorflow.python.keras.callbacks.History at 0x7f6050904f28>

 

tf.data Datasets からの訓練 & 評価

前の 2, 3 のパラグラフでは、損失、メトリクスと optimizer をどのように扱うかを見ました、そしてデータが Numpy 配列として渡されたとき、fit で validation_data と validation_split 引数をどのように使用するかを見ました。

今は貴方のデータが tf.data Dataset の形式でもたらされる場合を見ましょう。

tf.data API はデータを高速でスケーラブルな方法でロードして前処理するための TensorFlow 2.0 のユティリティのセットです。

Datasets の作成についての完全なガイドは、tf.data ドキュメント を見てください。

Dataset インスタンスをメソッド fit(), evaluate() と predict() に直接的に渡すことができます :

model = get_compiled_model()

# First, let's create a training Dataset instance.
# For the sake of our example, we'll use the same MNIST data as before.
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
# Shuffle and slice the dataset.
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Now we get a test dataset.
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))
test_dataset = test_dataset.batch(64)

# Since the dataset already takes care of batching,
# we don't pass a `batch_size` argument.
model.fit(train_dataset, epochs=3)

# You can also evaluate or predict on a dataset.
print('\n# Evaluate')
model.evaluate(test_dataset)
Epoch 1/3
782/782 [==============================] - 5s 7ms/step - loss: 0.3250 - sparse_categorical_accuracy: 0.9074
Epoch 2/3
782/782 [==============================] - 4s 6ms/step - loss: 0.1484 - sparse_categorical_accuracy: 0.9559
Epoch 3/3
782/782 [==============================] - 4s 5ms/step - loss: 0.1074 - sparse_categorical_accuracy: 0.9685

# Evaluate
157/157 [==============================] - 1s 3ms/step - loss: 0.1137 - sparse_categorical_accuracy: 0.9665

[0.11368312350931062, 0.9665]

Dataset は各エポックの最後にリセットされますので、それは次のエポックで再利用できることに注意してください。

このデータセットから特定の数のバッチ上でだけ訓練を実行することを望む場合、steps_per_epoch 引数を渡すことができます、これは次のエポックに移る前にモデルが Dataset を使用して幾つの訓練ステップを実行するべきかを指定します。

これを行なう場合、dataset は各エポックの最後にリセットされません、代わりに次のバッチを単にドローし続けます。dataset は最終的にはデータを使い果たします (それが無限ループ dataset でない限りは)。

model = get_compiled_model()

# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Only use the 100 batches per epoch (that's 64 * 100 samples)
model.fit(train_dataset, epochs=3, steps_per_epoch=100)
Epoch 1/3
100/100 [==============================] - 1s 11ms/step - loss: 0.7733 - sparse_categorical_accuracy: 0.8067
Epoch 2/3
100/100 [==============================] - 0s 5ms/step - loss: 0.3706 - sparse_categorical_accuracy: 0.8922
Epoch 3/3
100/100 [==============================] - 1s 5ms/step - loss: 0.3379 - sparse_categorical_accuracy: 0.9011

<tensorflow.python.keras.callbacks.History at 0x7f60504173c8>

 
検証 dataset を使用する

Dataset インスタンスを fit の validation_data 引数として渡すことができます :

model = get_compiled_model()

# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Prepare the validation dataset
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(64)

model.fit(train_dataset, epochs=3, validation_data=val_dataset)
Epoch 1/3
782/782 [==============================] - 7s 8ms/step - loss: 0.3440 - sparse_categorical_accuracy: 0.9020 - val_loss: 0.1838 - val_sparse_categorical_accuracy: 0.9490
Epoch 2/3
782/782 [==============================] - 7s 9ms/step - loss: 0.1649 - sparse_categorical_accuracy: 0.9515 - val_loss: 0.1391 - val_sparse_categorical_accuracy: 0.9603
Epoch 3/3
782/782 [==============================] - 8s 10ms/step - loss: 0.1216 - sparse_categorical_accuracy: 0.9645 - val_loss: 0.1208 - val_sparse_categorical_accuracy: 0.9672

<tensorflow.python.keras.callbacks.History at 0x7f6050417908>

各エポックの最後に、モデルは検証 Dataset に渡り iterate して検証損失と検証メトリクスを計算します。

この Dataset から特定の数のバッチ上でだけ検証を実行することを望む場合、validation_steps 引数を渡すことができます、これはモデルが検証を中断して次のエポックに進む前に検証 Dataset で幾つの検証ステップを実行するべきかを指定します :

model = get_compiled_model()

# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

# Prepare the validation dataset
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(64)

model.fit(train_dataset, epochs=3,
          # Only run validation using the first 10 batches of the dataset
          # using the `validation_steps` argument
          validation_data=val_dataset, validation_steps=10)
Epoch 1/3
782/782 [==============================] - 9s 12ms/step - loss: 0.3359 - sparse_categorical_accuracy: 0.9053 - val_loss: 0.3095 - val_sparse_categorical_accuracy: 0.9187
Epoch 2/3
782/782 [==============================] - 7s 9ms/step - loss: 0.1593 - sparse_categorical_accuracy: 0.9528 - val_loss: 0.2196 - val_sparse_categorical_accuracy: 0.9438
Epoch 3/3
782/782 [==============================] - 7s 9ms/step - loss: 0.1158 - sparse_categorical_accuracy: 0.9661 - val_loss: 0.1840 - val_sparse_categorical_accuracy: 0.9469

<tensorflow.python.keras.callbacks.History at 0x7f604fe7da90>

検証 Dataset は各使用の後にリセットされることに注意してください (その結果エポックからエポックへ同じサンプル上で常に評価しています)。

Dataset オブジェクトから訓練するときには (訓練データから取り置いたセットを生成する) 引数 validation_split はサポートされません、何故ならばこの機能は (Dataset API では一般に可能ではない) dataset のサンプルをインデックスする能力を必要とするからです。

 

サポートされる他の入力フォーマット

Numpy 配列と TensorFlow Datasets の他にも、Pandas dataframe を使用したりバッチを yield する Python ジェネレータから Keras モデルを訓練することが可能です。

一般に、貴方のデータが小さくてメモリに収まるのであれば Numpy 入力データを使用し、そうでなければ Datsets を使用することを勧めます。

 

サンプル重み付けとクラス重み付けを使用する

入力データとターゲットデータの他にも、fit を使用するときモデルに sample weights や class weights を渡すことも可能です :

  • Numpy データから訓練するとき: sample_weight と class_weight arguments を通して。
  • Datasets から訓練するとき: Dataset にタプル (input_batch, target_batch, sample_weight_batch) を返させることによって。

“sample weights” 配列は、トータル損失を計算するときにバッチの各サンプルがどのくらいの重みを持つべきであるかを指定する数字の配列です。それは一般に不均衡な分類問題で使用されます (滅多に見られないクラスにより大きい重みを与えるアイデアです)。使用される重みが ones か zeros であるとき、配列は損失関数のためのマスクとして使用できます (特定のサンプルのトータル損失への寄与を完全に捨てます)。

“class weights” 辞書は同じ概念のより具体的なインスタンスです : それはクラスインデックスをこのクラスに属するサンプルのために使用されるべきサンプル重みにマップします。例えば、貴方のデータでクラス “0” がクラス “1” よりも 2 倍少なく表わされるのであれば、class_weight={0: 1., 1: 0.5} を使用できるでしょう。

ここに Numpy サンプルがあります、そこではクラス #5 (これは MNIST データセットの数字 “5” です) の正しい分類により多くの重要性を与えるためにクラス重みかサンプル重みを使用します。

import numpy as np

class_weight = {0: 1., 1: 1., 2: 1., 3: 1., 4: 1.,
                # Set weight "2" for class "5",
                # making this class 2x more important
                5: 2.,
                6: 1., 7: 1., 8: 1., 9: 1.}
model.fit(x_train, y_train,
          class_weight=class_weight,
          batch_size=64,
          epochs=4)

# Here's the same example using `sample_weight` instead:
sample_weight = np.ones(shape=(len(y_train),))
sample_weight[y_train == 5] = 2.

model = get_compiled_model()
model.fit(x_train, y_train,
          sample_weight=sample_weight,
          batch_size=64,
          epochs=4)
Epoch 1/4
50000/50000 [==============================] - 4s 89us/sample - loss: 0.1040 - sparse_categorical_accuracy: 0.9715
Epoch 2/4
50000/50000 [==============================] - 4s 83us/sample - loss: 0.0872 - sparse_categorical_accuracy: 0.9751
Epoch 3/4
50000/50000 [==============================] - 4s 85us/sample - loss: 0.0734 - sparse_categorical_accuracy: 0.9789
Epoch 4/4
50000/50000 [==============================] - 4s 81us/sample - loss: 0.0657 - sparse_categorical_accuracy: 0.9818
Epoch 1/4
50000/50000 [==============================] - 4s 87us/sample - loss: 0.3647 - sparse_categorical_accuracy: 0.9063
Epoch 2/4
50000/50000 [==============================] - 5s 91us/sample - loss: 0.1703 - sparse_categorical_accuracy: 0.9525
Epoch 3/4
50000/50000 [==============================] - 4s 81us/sample - loss: 0.1276 - sparse_categorical_accuracy: 0.9647
Epoch 4/4
50000/50000 [==============================] - 4s 83us/sample - loss: 0.1016 - sparse_categorical_accuracy: 0.9719

<tensorflow.python.keras.callbacks.History at 0x7f604e5e2f98>

ここに適合している Dataset サンプルがあります :

sample_weight = np.ones(shape=(len(y_train),))
sample_weight[y_train == 5] = 2.

# Create a Dataset that includes sample weights
# (3rd element in the return tuple).
train_dataset = tf.data.Dataset.from_tensor_slices(
    (x_train, y_train, sample_weight))

# Shuffle and slice the dataset.
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

model = get_compiled_model()
model.fit(train_dataset, epochs=3)
Epoch 1/3
782/782 [==============================] - 9s 11ms/step - loss: 0.3666 - sparse_categorical_accuracy: 0.9046
Epoch 2/3
782/782 [==============================] - 7s 9ms/step - loss: 0.1646 - sparse_categorical_accuracy: 0.9539
Epoch 3/3
782/782 [==============================] - 7s 9ms/step - loss: 0.1178 - sparse_categorical_accuracy: 0.9677

<tensorflow.python.keras.callbacks.History at 0x7f604edb7208>

 

データをマルチ入力、マルチ出力モデルに渡す

前の例では、単一の入力 (shape (764,) の tensor) と単一の出力 (shape (10,) の予測 tensor) を持つモデルを考えていました。しかしマルチ入力か出力を持つモデルについてはどうでしょう?

次のモデルを考えます、それは shape (32, 32, 3) (それは (高さ、幅、チャネル)) の画像入力と shape (None, 10) (それは (時間ステップ, 特徴)) の時系列入力を持ちます。私達のモデルはこれらの入力の連携から計算された 2 つの出力を持ちます : (shape (1,) の) 「スコア」と (shape (10,) の) 5 クラスに渡る確率分布です。

from tensorflow import keras
from tensorflow.keras import layers

image_input = keras.Input(shape=(32, 32, 3), name='img_input')
timeseries_input = keras.Input(shape=(None, 10), name='ts_input')

x1 = layers.Conv2D(3, 3)(image_input)
x1 = layers.GlobalMaxPooling2D()(x1)

x2 = layers.Conv1D(3, 3)(timeseries_input)
x2 = layers.GlobalMaxPooling1D()(x2)

x = layers.concatenate([x1, x2])

score_output = layers.Dense(1, name='score_output')(x)
class_output = layers.Dense(5, activation='softmax', name='class_output')(x)

model = keras.Model(inputs=[image_input, timeseries_input],
                    outputs=[score_output, class_output])

このモデルをプロットしましょう、そうすれば私達がここで何をしているかを明瞭に見ることができます (プロットで示される shape はサンプル毎 shape ではなくバッチ shape であることに注意してください)。

keras.utils.plot_model(model, 'multi_input_and_output_model.png', show_shapes=True)

 
コンパイル時、損失関数をリストとして渡すことにより異なる出力に異なる損失を指定できます :

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[keras.losses.MeanSquaredError(),
          keras.losses.CategoricalCrossentropy()])

モデルに単一の損失関数を渡すだけの場合には、同じ損失関数が総ての出力に適用されるでしょう、ここではそれは適切ではありません。

メトリクスについても同様です :

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[keras.losses.MeanSquaredError(),
          keras.losses.CategoricalCrossentropy()],
    metrics=[[keras.metrics.MeanAbsolutePercentageError(),
              keras.metrics.MeanAbsoluteError()],
             [keras.metrics.CategoricalAccuracy()]])

出力層に名前を与えましたので、辞書を通して出力毎の損失とメトリクスを指定することもできるでしょう :

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={'score_output': keras.losses.MeanSquaredError(),
          'class_output': keras.losses.CategoricalCrossentropy()},
    metrics={'score_output': [keras.metrics.MeanAbsolutePercentageError(),
                              keras.metrics.MeanAbsoluteError()],
             'class_output': [keras.metrics.CategoricalAccuracy()]})

2 出力以上を持つ場合、明示的な名前と辞書の使用を勧めます。

loss_weight 引数を使用して、異なる重みを異なる出力固有の損失に与えることもできます (例えば、ある人は私達の例の「スコア」損失にクラス損失の 2x の重要性を与えることにより、特権を与えることを望むかもしれません) :

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={'score_output': keras.losses.MeanSquaredError(),
          'class_output': keras.losses.CategoricalCrossentropy()},
    metrics={'score_output': [keras.metrics.MeanAbsolutePercentageError(),
                              keras.metrics.MeanAbsoluteError()],
             'class_output': [keras.metrics.CategoricalAccuracy()]},
    loss_weight={'score_output': 2., 'class_output': 1.})

これらの出力が予測のためで訓練のためでなければ、特定の出力のための損失を計算しないことも選択できるでしょう :

# List loss version
model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[None, keras.losses.CategoricalCrossentropy()])

# Or dict loss version
model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={'class_output': keras.losses.CategoricalCrossentropy()})
WARNING: Logging before flag parsing goes to stderr.
W0305 23:50:32.918388 140053718652800 training_utils.py:1152] Output score_output missing from loss dictionary. We assume this was done on purpose. The fit and evaluate APIs will not be expecting any data to be passed to score_output.

マルチ入力やマルチ出力モデルに fit でデータを渡すことは compile で損失関数を指定するのと同様な方法です : (損失関数を受け取った出力に 1:1 マッピングを持つ) Numpy 配列のリストや出力名を訓練データの Numpy 配列にマップする辞書を渡すことができます。

model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[keras.losses.MeanSquaredError(),
          keras.losses.CategoricalCrossentropy()])

# Generate dummy Numpy data
img_data = np.random.random_sample(size=(100, 32, 32, 3))
ts_data = np.random.random_sample(size=(100, 20, 10))
score_targets = np.random.random_sample(size=(100, 1))
class_targets = np.random.random_sample(size=(100, 5))

# Fit on lists
model.fit([img_data, ts_data], [score_targets, class_targets],
          batch_size=32,
          epochs=3)

# Alernatively, fit on dicts
model.fit({'img_input': img_data, 'ts_input': ts_data},
          {'score_output': score_targets, 'class_output': class_targets},
          batch_size=32,
          epochs=3)
Epoch 1/3
100/100 [==============================] - 1s 6ms/sample - loss: 7.6847 - score_output_loss: 0.7406 - class_output_loss: 6.9441
Epoch 2/3
100/100 [==============================] - 0s 1ms/sample - loss: 7.0638 - score_output_loss: 0.3140 - class_output_loss: 6.7499
Epoch 3/3
100/100 [==============================] - 0s 1ms/sample - loss: 6.7368 - score_output_loss: 0.1928 - class_output_loss: 6.5440
Epoch 1/3
100/100 [==============================] - 0s 4ms/sample - loss: 6.4485 - score_output_loss: 0.1420 - class_output_loss: 6.3065
Epoch 2/3
100/100 [==============================] - 0s 4ms/sample - loss: 6.1095 - score_output_loss: 0.1428 - class_output_loss: 5.9667
Epoch 3/3
100/100 [==============================] - 0s 4ms/sample - loss: 5.8362 - score_output_loss: 0.1219 - class_output_loss: 5.7143

<tensorflow.python.keras.callbacks.History at 0x7f6051289128>

ここに Dataset のユースケースがあります : Numpy 配列のために行なったことと同様です、Dataset は辞書のタプルを返すべきです。

train_dataset = tf.data.Dataset.from_tensor_slices(
    ({'img_input': img_data, 'ts_input': ts_data},
     {'score_output': score_targets, 'class_output': class_targets}))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)

model.fit(train_dataset, epochs=3)
Epoch 1/3
2/2 [==============================] - 0s 152ms/step - loss: 5.6598 - score_output_loss: 0.1304 - class_output_loss: 5.5127
Epoch 2/3
2/2 [==============================] - 0s 107ms/step - loss: 5.5597 - score_output_loss: 0.1229 - class_output_loss: 5.4204
Epoch 3/3
2/2 [==============================] - 0s 145ms/step - loss: 5.4660 - score_output_loss: 0.1176 - class_output_loss: 5.3324

<tensorflow.python.keras.callbacks.History at 0x7f6050417fd0>

 

コールバックを使用する

Keras の Callbacks は訓練の間に異なるポイント (エポックの最初、バッチの最後、エポックの最後, etc.) で呼び出されるオブジェクトでそれは次のような挙動を実装するために使用できます :

  • 訓練の間に異なるポイントで検証を行なう (組み込みのエポック毎検証を越えて)
  • 定期の間隔であるいはそれが特定の精度しきい値を超えたときにモデルをチェックポイントする
  • 訓練が頭打ちになったように思われるときにモデルの学習率を変更する
  • 訓練が頭打ちになったように思われるときに top 層の再調整を行なう
  • 訓練が終わるときあるいは特定のパフォーマンスしきい値を超えた場合に電子メールかインスタントメッセージ通知を送る
  • Etc.

Callback は fit への呼び出しにリストとして渡すことができます :

model = get_compiled_model()

callbacks = [
    keras.callbacks.EarlyStopping(
        # Stop training when `val_loss` is no longer improving
        monitor='val_loss',
        # "no longer improving" being defined as "no better than 1e-2 less"
        min_delta=1e-2,
        # "no longer improving" being further defined as "for at least 2 epochs"
        patience=2,
        verbose=1)
]
model.fit(x_train, y_train,
          epochs=20,
          batch_size=64,
          callbacks=callbacks,
          validation_split=0.2)
Train on 40000 samples, validate on 10000 samples
Epoch 1/20
40000/40000 [==============================] - 4s 102us/sample - loss: 0.3712 - sparse_categorical_accuracy: 0.8955 - val_loss: 0.2237 - val_sparse_categorical_accuracy: 0.9325
Epoch 2/20
40000/40000 [==============================] - 4s 93us/sample - loss: 0.1754 - sparse_categorical_accuracy: 0.9483 - val_loss: 0.1784 - val_sparse_categorical_accuracy: 0.9440
Epoch 3/20
40000/40000 [==============================] - 3s 84us/sample - loss: 0.1255 - sparse_categorical_accuracy: 0.9619 - val_loss: 0.1583 - val_sparse_categorical_accuracy: 0.9514
Epoch 4/20
40000/40000 [==============================] - 4s 90us/sample - loss: 0.1003 - sparse_categorical_accuracy: 0.9703 - val_loss: 0.1404 - val_sparse_categorical_accuracy: 0.9587
Epoch 5/20
40000/40000 [==============================] - 4s 88us/sample - loss: 0.0829 - sparse_categorical_accuracy: 0.9757 - val_loss: 0.1332 - val_sparse_categorical_accuracy: 0.9617
Epoch 6/20
40000/40000 [==============================] - 4s 97us/sample - loss: 0.0705 - sparse_categorical_accuracy: 0.9789 - val_loss: 0.1341 - val_sparse_categorical_accuracy: 0.9641
Epoch 00006: early stopping

<tensorflow.python.keras.callbacks.History at 0x7f604c45ff60>

 
多くの組み込み callbacks が利用可能です

  • ModelCheckpoint: 定期的にモデルをセーブする。
  • EarlyStopping: 訓練がもはや検証メトリクスを改良しないときに訓練を停止する。
  • TensorBoard: TensorBoard で可視化できるモデルログを定期的に書く (セクション「可視化」で更なる詳細)。
  • CSVLogger: 損失とメトリクス・データを CSV ファイルにストリームする。
  • etc.

 
貴方自身の callback を書く

基底クラス keras.callbacks.Callback を拡張することでカスタム callback を作成できます。callback はクラス・プロパティ self.model を通してその関連するモデルへのアクセスを持ちます。

ここに訓練の間にバッチ毎損失値のリストをセーブする単純な例があります :

class LossHistory(keras.callbacks.Callback):

    def on_train_begin(self, logs):
        self.losses = []

    def on_batch_end(self, batch, logs):
        self.losses.append(logs.get('loss'))

 

モデルをチェックポイントする

比較的巨大なデータセット上でモデルを訓練しているとき、貴方のモデルのチェックポイントをしばしばセーブすることは重要です。

これを成すための最も容易な方法は ModelCheckpoint callback によるものです :

model = get_compiled_model()

callbacks = [
    keras.callbacks.ModelCheckpoint(
        filepath='mymodel_{epoch}.h5',
        # Path where to save the model
        # The two parameters below mean that we will overwrite
        # the current checkpoint if and only if
        # the `val_loss` score has improved.
        save_best_only=True,
        monitor='val_loss',
        verbose=1)
]
model.fit(x_train, y_train,
          epochs=3,
          batch_size=64,
          callbacks=callbacks,
          validation_split=0.2)
Train on 40000 samples, validate on 10000 samples
Epoch 1/3
39936/40000 [============================>.] - ETA: 0s - loss: 0.3635 - sparse_categorical_accuracy: 0.8971
Epoch 00001: val_loss improved from inf to 0.21655, saving model to mymodel_1.h5
40000/40000 [==============================] - 4s 108us/sample - loss: 0.3631 - sparse_categorical_accuracy: 0.8972 - val_loss: 0.2166 - val_sparse_categorical_accuracy: 0.9347
Epoch 2/3
39360/40000 [============================>.] - ETA: 0s - loss: 0.1669 - sparse_categorical_accuracy: 0.9506
Epoch 00002: val_loss improved from 0.21655 to 0.17676, saving model to mymodel_2.h5
40000/40000 [==============================] - 4s 97us/sample - loss: 0.1669 - sparse_categorical_accuracy: 0.9505 - val_loss: 0.1768 - val_sparse_categorical_accuracy: 0.9456
Epoch 3/3
39424/40000 [============================>.] - ETA: 0s - loss: 0.1232 - sparse_categorical_accuracy: 0.9624
Epoch 00003: val_loss improved from 0.17676 to 0.15663, saving model to mymodel_3.h5
40000/40000 [==============================] - 4s 99us/sample - loss: 0.1236 - sparse_categorical_accuracy: 0.9624 - val_loss: 0.1566 - val_sparse_categorical_accuracy: 0.9536

<tensorflow.python.keras.callbacks.History at 0x7f604bfdfe80>

モデルをセーブしてリストアするための貴方自身の callback を書くこともできます。

シリアライゼーションとセービングについての完全なガイドは、Guide to Saving and Serializing Models を見てください。

 

学習率スケジュールを使用する

深層学習モデルを訓練するときの一般的なパターンは訓練が進むにつれて学習率を徐々に減少することです。これは一般に「学習率減衰 (= decay)」として知られています。

学習率減衰は静的 (現在のエポックか現在のバッチインデックスの関数として、前もって固定) でも動的 (モデルの現在の挙動に呼応、特に検証損失) でもあり得るでしょう。

 
スケジュールを optimizer に渡す

スケジュール・オブジェクトを optimizer の learning_rate 引数として渡すことにより静的学習率減衰スケジュールは容易に使用できます :

initial_learning_rate = 0.1
lr_schedule = keras.optimizers.schedules.ExponentialDecay(
    initial_learning_rate,
    decay_steps=100000,
    decay_rate=0.96,
    staircase=True)

optimizer = keras.optimizers.RMSprop(learning_rate=lr_schedule)

幾つかの組み込みスケジュールが利用可能です : ExponentialDecay, PiecewiseConstantDecay, PolynomialDecay そして InverseTimeDecay。

 
動的学習率スケジュールを実装するために callback を使用する

動的学習率スケジュール (例えば、検証損失がもはや改善しないときに学習率を減少します) はこれらのスケジュール・オブジェクトでは達成できません、何故ならば optimizer は検証メトリクスへのアクセスを持たないからです。

けれども、callback は検証メトリクスを含む、総てのメトリクスへのアクセスを持ちます!こうして optimizer で現在の学習率を変更する callback を使用してこのパターンを達成できます。実際に、これは ReduceLROnPlateau callback として組み込みでさえあります。

 

訓練の間に損失とメトリクスを可視化する

訓練の間に貴方のモデルを注視する最善の方法は TensorBoard を使用することです、これは以下を提供する、貴方がローカルで実行できるブラウザ・ベースのアプリケーションです :

  • 訓練と評価のための損失とメトリクスのライブ・プロット
  • (オプションで) 層活性のヒストグラムの可視化
  • (オプションで) Embedding 層で学習された埋め込み空間の 3D 可視化

TensorFlow を pip でインストールしたのであれば、以下のコマンドラインから TensorBoard を起動できるはずです :

tensorboard --logdir=/full_path_to_your_logs

 
TensorBoard callback を使用する

Keras モデルと fit メソッドで TensorBoard を使用する最も容易な方法は TensorBoard callback です。

最も単純なケースでは、callback にログを書くことを望むところを単に指定します、それで十分です :

tensorboard_cbk = keras.callbacks.TensorBoard(log_dir='/full_path_to_your_logs')
model.fit(dataset, epochs=10, callbacks=[tensorboard_cbk])

TensorBoard callback は、embeddings, histograms をロギングするか否か、そしてどのくらいの頻度でログを書くかを含む、多くの有用なオプションを持ちます :

keras.callbacks.TensorBoard(
  log_dir='/full_path_to_your_logs',
  histogram_freq=0,  # How often to log histogram visualizations
  embeddings_freq=0,  # How often to log embedding visualizations
  update_freq='epoch')  # How often to write logs (default: once per epoch)

 

Part II: スクラッチから貴方自身の訓練 & 評価ループを書く

貴方の訓練 & 評価ループに渡り fit() と evaluate() が提供するものよりもより低位を望む場合には、貴方自身のものを書くべきです。それは実際には非常に単純です!しかし貴方自身のものの上でより多くのデバッグを行なう準備をすべきです。

 

GradientTape を使用する: 最初の end-to-end サンプル

GradientTape スコープの内側でモデルを呼び出すことは損失値に関する層の訓練可能な重みの勾配を取得することを可能にします。optimizer インスタンスを使用して、これらの変数 (それらは model.trainable_variables を使用して取得できます) を更新するためにこれらの勾配を使用できます。

Part I から私達の初期 MNIST モデルを再利用しましょう、そしてそれをカスタム訓練ループでミニバッチ勾配を使用して訓練しましょう。

# モデルを得ます。
inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
model = keras.Model(inputs=inputs, outputs=outputs)

# optimizer をインスタンス化します。
optimizer = keras.optimizers.SGD(learning_rate=1e-3)
# 損失関数をインスタンス化します。
loss_fn = keras.losses.SparseCategoricalCrossentropy()

# 訓練 dataset を準備します。
batch_size = 64
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)

# epochs に渡り iterate します。
for epoch in range(3):
  print('Start of epoch %d' % (epoch,))
  
  # dataset のバッチに渡り iteratre します。
  for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):

    # forward パスの間に実行される演算を記録するために GradientTape をオープンします、
    # これは自動微分を可能にします。
    with tf.GradientTape() as tape:

      # 層の forward パスを実行します。
      # 層がその入力に適用する演算は GradientTape 上で記録されていきます。
      logits = model(x_batch_train)  # Logits for this minibatch

      # このミニバッチに対する損失値を計算します。
      loss_value = loss_fn(y_batch_train, logits)

      # 損失に関する訓練可能な重みの勾配を自動的に取得するために勾配テープを使用します。
      grads = tape.gradient(loss_value, model.trainable_variables)

      # 損失を最小化するために重みの値を更新することにより勾配降下の 1 ステップを実行します。
      optimizer.apply_gradients(zip(grads, model.trainable_variables))

    # 200 バッチ毎にログ記録します。
    if step % 200 == 0:
        print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))
        print('Seen so far: %s samples' % ((step + 1) * 64))
Start of epoch 0
Training loss (for one batch) at step 0: 2.295337200164795
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.267664909362793
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.1268270015716553
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.0609934329986572
Seen so far: 38464 samples
Start of epoch 1
Training loss (for one batch) at step 0: 1.9627395868301392
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.9132888317108154
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.7715450525283813
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.680647611618042
Seen so far: 38464 samples
Start of epoch 2
Training loss (for one batch) at step 0: 1.554194450378418
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.5058209896087646
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.3611259460449219
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.2863078117370605
Seen so far: 38464 samples

 

メトリクスの低位処理

Let’s add metrics to the mix. スクラッチから書かれたそのような訓練ループで組み込みメトリクス (または貴方が書いたカスタムメトリクス) を容易に再利用できます。ここにフローがあります :

  • ループの最初にメトリックをインスタンス化する
  • 各バッチ後に metric.update_state() を呼び出す
  • メトリックの現在の値を表示する必要があるとき metric.result() を呼び出す
  • メトリックの状態をクリアする必要があるとき metric.reset_states() を呼び出す (典型的にはエポックの最後)

各エポックの最後に検証データ上で SparseCategoricalAccuracy を計算するためにこの知識を使用しましょう :

# Get model
inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
model = keras.Model(inputs=inputs, outputs=outputs)

# Instantiate an optimizer to train the model.
optimizer = keras.optimizers.SGD(learning_rate=1e-3)
# Instantiate a loss function.
loss_fn = keras.losses.SparseCategoricalCrossentropy()

# Prepare the metrics.
train_acc_metric = keras.metrics.SparseCategoricalAccuracy() 
val_acc_metric = keras.metrics.SparseCategoricalAccuracy()

# Prepare the training dataset.
batch_size = 64
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)

# Prepare the validation dataset.
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(64)


# Iterate over epochs.
for epoch in range(3):
  print('Start of epoch %d' % (epoch,))
  
  # Iterate over the batches of the dataset.
  for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
    with tf.GradientTape() as tape:
      logits = model(x_batch_train)
      loss_value = loss_fn(y_batch_train, logits)
    grads = tape.gradient(loss_value, model.trainable_variables)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))
      
    # Update training metric.
    train_acc_metric(y_batch_train, logits)

    # Log every 200 batches.
    if step % 200 == 0:
        print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))
        print('Seen so far: %s samples' % ((step + 1) * 64))

  # Display metrics at the end of each epoch.
  train_acc = train_acc_metric.result()
  print('Training acc over epoch: %s' % (float(train_acc),))
  # Reset training metrics at the end of each epoch
  train_acc_metric.reset_states()

  # Run a validation loop at the end of each epoch.
  for x_batch_val, y_batch_val in val_dataset:
    val_logits = model(x_batch_val)
    # Update val metrics
    val_acc_metric(y_batch_val, val_logits)
  val_acc = val_acc_metric.result()
  val_acc_metric.reset_states()
  print('Validation acc: %s' % (float(val_acc),))
Start of epoch 0
Training loss (for one batch) at step 0: 2.3286547660827637
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.297130823135376
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.168592929840088
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.037825107574463
Seen so far: 38464 samples
Training acc over epoch: 0.2502399981021881
Validation acc: 0.4449000060558319
Start of epoch 1
Training loss (for one batch) at step 0: 1.9728939533233643
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.9893989562988281
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.7468760013580322
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.6020689010620117
Seen so far: 38464 samples
Training acc over epoch: 0.5704200267791748
Validation acc: 0.6780999898910522
Start of epoch 2
Training loss (for one batch) at step 0: 1.476192831993103
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.558509349822998
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.267077922821045
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.1969627141952515
Seen so far: 38464 samples
Training acc over epoch: 0.7189000248908997
Validation acc: 0.7734000086784363

 

extra 損失の低位処理

前のセクションで、層により call メソッドで self.add_loss(value) を呼び出して正則化損失を追加できることを見ました。

一般的なケースでは、貴方のカスタム訓練ループでこれらの損失を考慮することを望むでしょう (貴方がモデルを自身で書いてそれがそのような損失を作成しないことを既に知っている場合でなければ)。

前のセクションからのこのサンプルを思い出してください、正則化損失を作成する層にフィーチャーしています :

class ActivityRegularizationLayer(layers.Layer):
  
  def call(self, inputs):
    self.add_loss(1e-2 * tf.reduce_sum(inputs))
    return inputs
  
inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
# Insert activity regularization as a layer
x = ActivityRegularizationLayer()(x)
x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)

model = keras.Model(inputs=inputs, outputs=outputs)

モデルを呼び出すとき、このようにします :

logits = model(x_train)

forward パスでそれが作成した損失は model.losses 属性に追加されます :

logits = model(x_train[:64])
print(model.losses)
[<tf.Tensor: id=999790, shape=(), dtype=float32, numpy=6.8533154>]

追跡された損失はまず model __call__ の最初でクリアされますので、この 1 つの forward パスの間に作成された損失だけを見るでしょう。例えば、model を繰り返し呼び出して、そしてそれから損失の問い合わせは (最後の呼び出しの間に作成された) 最新の損失を表示するだけです :

logits = model(x_train[:64])
logits = model(x_train[64: 128])
logits = model(x_train[128: 192])
print(model.losses)
[<tf.Tensor: id=999851, shape=(), dtype=float32, numpy=6.88884>]

訓練の間にこれらの損失を考慮するには、貴方が行なわなければならない総てのことは貴方の訓練ループを貴方のトータル損失に sum(model.losses) を追加するように変更することです :

optimizer = keras.optimizers.SGD(learning_rate=1e-3)

for epoch in range(3):
  print('Start of epoch %d' % (epoch,))

  for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
    with tf.GradientTape() as tape:
      logits = model(x_batch_train)
      loss_value = loss_fn(y_batch_train, logits)

      # Add extra losses created during this forward pass:
      loss_value += sum(model.losses)
      
    grads = tape.gradient(loss_value, model.trainable_variables)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))

    # Log every 200 batches.
    if step % 200 == 0:
        print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))
        print('Seen so far: %s samples' % ((step + 1) * 64))
Start of epoch 0
Training loss (for one batch) at step 0: 9.747203826904297
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.5395843982696533
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.427178144454956
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.324587821960449
Seen so far: 38464 samples
Start of epoch 1
Training loss (for one batch) at step 0: 2.322904586791992
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.334357976913452
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.3377459049224854
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.3055613040924072
Seen so far: 38464 samples
Start of epoch 2
Training loss (for one batch) at step 0: 2.3104405403137207
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.317152261734009
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.319432020187378
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.303823471069336
Seen so far: 38464 samples
 

以上



TensorFlow 2.0 Alpha : ガイド : Keras : TensorFlow の Keras Functional API

TensorFlow 2.0 Alpha : ガイド : Keras : TensorFlow の Keras Functional API (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/15/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : Keras : TensorFlow の Keras Functional API

セットアップ

!pip install -q pydot
!apt-get install graphviz
Requirement already satisfied: pydot in /usr/local/lib/python3.6/dist-packages (1.3.0)
Requirement already satisfied: pyparsing>=2.1.4 in /usr/local/lib/python3.6/dist-packages (from pydot) (2.3.1)
Reading package lists... Done
Building dependency tree       
Reading state information... Done
graphviz is already the newest version (2.40.1-2).
0 upgraded, 0 newly installed, 0 to remove and 10 not upgraded.
from __future__ import absolute_import, division, print_function

!pip install -q tensorflow-gpu==2.0.0-alpha0
import tensorflow as tf

tf.keras.backend.clear_session()  # For easy reset of notebook state.

 

Introduction

貴方はモデルを作成するために既に keras.Sequential() の使用に慣れているでしょう。Functional API は Sequential よりもより柔軟なモデルを作成する方法です : それは非線形トポロジーのモデル、共有層を持つモデルそしてマルチ入力と出力を持つモデルを扱うことができます。

それは深層学習モデルが通常は層の有向非巡回グラフ (DAG, directed acyclic graph) であるという考えに基づきます。Functional API は層のグラフを構築するためのツールのセットです。

次のモデルを考えてください :

(input: 784-dimensional vectors)
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (10 units, softmax activation)]
       ↧
(output: probability distribution over 10 classes)

それは単純な 3 層のグラフです。

このモデルを functional API で構築するには、入力ノードを作成することから始めるでしょう :

from tensorflow import keras

inputs = keras.Input(shape=(784,))

ここで私達のデータの shape だけを指定します : 784-次元ベクトルです。バッチサイズは常に省略されることに注意してください、各サンプルの shape を指定するだけです。shape (32, 32, 3) の画像のための入力のためであれば、次を使用したでしょう :

img_inputs = keras.Input(shape=(32, 32, 3))

返された、inputs は、貴方のモデルに供給することを想定する入力データの shape と dtype についての情報を含みます :

inputs.shape
TensorShape([None, 784])
inputs.dtype
tf.float32

この入力オブジェクト上で層を呼び出すことにより層のグラフで新しいノードを作成します :

from tensorflow.keras import layers

dense = layers.Dense(64, activation='relu')
x = dense(inputs)

“layer call” アクションは “inputs” からこの作成した層への矢印を描くようなものです。入力を dense 層に “passing” して、そして x を得ます。

層のグラフに 2, 3 のより多くの層を追加しましょう :

x = layers.Dense(64, activation='relu')(x)
outputs = layers.Dense(10, activation='softmax')(x)

この時点で、層のグラフの入力と出力を指定することによりモデルを作成できます :

model = keras.Model(inputs=inputs, outputs=outputs)

おさらいとして、ここに完全なモデル定義過程があります :

inputs = keras.Input(shape=(784,), name='img')
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
outputs = layers.Dense(10, activation='softmax')(x)

model = keras.Model(inputs=inputs, outputs=outputs, name='mnist_model')

モデル要約がどのようなものかを確認しましょう :

model.summary()
Model: "mnist_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
img (InputLayer)             [(None, 784)]             0         
_________________________________________________________________
dense_3 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_4 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_5 (Dense)              (None, 10)                650       
=================================================================
Total params: 55,050
Trainable params: 55,050
Non-trainable params: 0
_________________________________________________________________

モデルをグラフとしてプロットすることもできます :

keras.utils.plot_model(model, 'my_first_model.png')

そしてオプションでプロットされたグラフに各層の入力と出力 shape を表示します :

keras.utils.plot_model(model, 'my_first_model_with_shape_info.png', show_shapes=True)

この図と私達が書いたコードは事実上同じです。コードバージョンでは、接続矢印は call 演算で単純に置き換えられます。

「層のグラフ」は深層学習モデルのための非常に直感的なメンタルイメージで、functional API はこのメンタルイメージを密接に映すモデルを作成する方法です。

 

訓練、評価そして推論

Functional API を使用して構築されたモデルのための訓練、評価と推論は Sequential モデルのためと正確に同じ方法で動作します。

ここに簡単な例があります。

ここで私達は MNIST 画像データをロードし、それをベクトルに reshape し、(検証分割上でパフォーマンスを監視しながら) データ上でモデルを fit させて最後にテストデータ上でモデルを評価します :

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop(),
              metrics=['accuracy'])
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=5,
                    validation_split=0.2)
test_scores = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', test_scores[0])
print('Test accuracy:', test_scores[1])
Train on 48000 samples, validate on 12000 samples
Epoch 1/5
48000/48000 [==============================] - 3s 64us/sample - loss: 0.3414 - accuracy: 0.9016 - val_loss: 0.1719 - val_accuracy: 0.9501
Epoch 2/5
48000/48000 [==============================] - 3s 57us/sample - loss: 0.1568 - accuracy: 0.9526 - val_loss: 0.1365 - val_accuracy: 0.9605
Epoch 3/5
48000/48000 [==============================] - 3s 58us/sample - loss: 0.1144 - accuracy: 0.9660 - val_loss: 0.1262 - val_accuracy: 0.9625
Epoch 4/5
48000/48000 [==============================] - 3s 54us/sample - loss: 0.0929 - accuracy: 0.9716 - val_loss: 0.1100 - val_accuracy: 0.9701
Epoch 5/5
48000/48000 [==============================] - 3s 55us/sample - loss: 0.0759 - accuracy: 0.9770 - val_loss: 0.1139 - val_accuracy: 0.9670
Test loss: 0.100577776569454
Test accuracy: 0.9696

モデル訓練と評価についての完全なガイドは、Guide to Training & Evaluation を見てください。

 

セービングとシリアライゼーション

Functional API を使用して構築されたモデルのためのセービングとシリアライゼーションは Sequential モデルのためと正確に同じ方法で動作します。

Functional モデルをセーブするための標準的な方法はモデル全体を単一のファイルにセーブするために model.save() を呼び出すことです。貴方は後でこのファイルから同じモデルを再作成できます、モデルを作成したコードへのアクセスをもはや持たない場合でさえも。

このファイルは以下を含みます :- モデルのアーキテクチャ – モデルの重み値 (これは訓練の間に学習されました) – もしあれば、(compile に渡した) モデルの訓練 config – もしあれば、optimizer とその状態 (これは貴方がやめたところから訓練を再開することを可能にします)

model.save('path_to_my_model.h5')
del model
# Recreate the exact same model purely from the file:
model = keras.models.load_model('path_to_my_model.h5')

モデル・セービングについての完全なガイドは、Guide to Saving and Serializing Models を見てください。

 

マルチモデルを定義するために層群の同じグラフを使用する

functional API では、モデルは層群のグラフ内のそれらの入力と出力を指定することにより作成されます。それは層群の単一のグラフが複数のモデルを生成するために使用できることを意味しています。

下の例では、2 つのモデルをインスタンス化するために層の同じスタックを使用しています : 画像入力を 16-次元ベクトルに変換するエンコーダモデルと、訓練のための end-to-end autoencoder モデルです。

encoder_input = keras.Input(shape=(28, 28, 1), name='img')
x = layers.Conv2D(16, 3, activation='relu')(encoder_input)
x = layers.Conv2D(32, 3, activation='relu')(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation='relu')(x)
x = layers.Conv2D(16, 3, activation='relu')(x)
encoder_output = layers.GlobalMaxPooling2D()(x)

encoder = keras.Model(encoder_input, encoder_output, name='encoder')
encoder.summary()

x = layers.Reshape((4, 4, 1))(encoder_output)
x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
x = layers.Conv2DTranspose(32, 3, activation='relu')(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x)

autoencoder = keras.Model(encoder_input, decoder_output, name='autoencoder')
autoencoder.summary()
Model: "encoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
img (InputLayer)             [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 16)        160       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 32)        4640      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d (Global (None, 16)                0         
=================================================================
Total params: 18,672
Trainable params: 18,672
Non-trainable params: 0
_________________________________________________________________
Model: "autoencoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
img (InputLayer)             [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 16)        160       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 32)        4640      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d (Global (None, 16)                0         
_________________________________________________________________
reshape (Reshape)            (None, 4, 4, 1)           0         
_________________________________________________________________
conv2d_transpose (Conv2DTran (None, 6, 6, 16)          160       
_________________________________________________________________
conv2d_transpose_1 (Conv2DTr (None, 8, 8, 32)          4640      
_________________________________________________________________
up_sampling2d (UpSampling2D) (None, 24, 24, 32)        0         
_________________________________________________________________
conv2d_transpose_2 (Conv2DTr (None, 26, 26, 16)        4624      
_________________________________________________________________
conv2d_transpose_3 (Conv2DTr (None, 28, 28, 1)         145       
=================================================================
Total params: 28,241
Trainable params: 28,241
Non-trainable params: 0
_________________________________________________________________

デコーディング・アーキテクチャをエンコーディング・アーキテクチャと厳密に対照的に作成しますので、入力 shape (28, 28, 1) と同じ出力 shape を得ることに注意してください。Conv2D 層の反対は Conv2DTranspose 層で、MaxPooling2D 層の反対は UpSampling2D 層です。

 

総てのモデルは、ちょうど層のように callable です

任意のモデルを、それを入力上あるいは他の層の出力上で呼び出すことによって、それが層であるかのように扱うことができます。モデルを呼び出すことによりモデルのアーキテクチャを単に再利用しているのではなく、その重みを再利用していることに注意してください。

これを実際に見てみましょう。ここに autoencoder サンプルの異なるテイクがあります、これはエンコーダ・モデル、デコーダ・モデルを作成し、autoencoder モデルを得るためにそれらを 2 つのコールにチェインします :

encoder_input = keras.Input(shape=(28, 28, 1), name='original_img')
x = layers.Conv2D(16, 3, activation='relu')(encoder_input)
x = layers.Conv2D(32, 3, activation='relu')(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation='relu')(x)
x = layers.Conv2D(16, 3, activation='relu')(x)
encoder_output = layers.GlobalMaxPooling2D()(x)

encoder = keras.Model(encoder_input, encoder_output, name='encoder')
encoder.summary()

decoder_input = keras.Input(shape=(16,), name='encoded_img')
x = layers.Reshape((4, 4, 1))(decoder_input)
x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
x = layers.Conv2DTranspose(32, 3, activation='relu')(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x)

decoder = keras.Model(decoder_input, decoder_output, name='decoder')
decoder.summary()

autoencoder_input = keras.Input(shape=(28, 28, 1), name='img')
encoded_img = encoder(autoencoder_input)
decoded_img = decoder(encoded_img)
autoencoder = keras.Model(autoencoder_input, decoded_img, name='autoencoder')
autoencoder.summary()
Model: "encoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
original_img (InputLayer)    [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 26, 26, 16)        160       
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 24, 24, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d_1 (Glob (None, 16)                0         
=================================================================
Total params: 18,672
Trainable params: 18,672
Non-trainable params: 0
_________________________________________________________________
Model: "decoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
encoded_img (InputLayer)     [(None, 16)]              0         
_________________________________________________________________
reshape_1 (Reshape)          (None, 4, 4, 1)           0         
_________________________________________________________________
conv2d_transpose_4 (Conv2DTr (None, 6, 6, 16)          160       
_________________________________________________________________
conv2d_transpose_5 (Conv2DTr (None, 8, 8, 32)          4640      
_________________________________________________________________
up_sampling2d_1 (UpSampling2 (None, 24, 24, 32)        0         
_________________________________________________________________
conv2d_transpose_6 (Conv2DTr (None, 26, 26, 16)        4624      
_________________________________________________________________
conv2d_transpose_7 (Conv2DTr (None, 28, 28, 1)         145       
=================================================================
Total params: 9,569
Trainable params: 9,569
Non-trainable params: 0
_________________________________________________________________
Model: "autoencoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
img (InputLayer)             [(None, 28, 28, 1)]       0         
_________________________________________________________________
encoder (Model)              (None, 16)                18672     
_________________________________________________________________
decoder (Model)              (None, 28, 28, 1)         9569      
=================================================================
Total params: 28,241
Trainable params: 28,241
Non-trainable params: 0
_________________________________________________________________

見て取れるように、モデルはネストできます : モデルはサブモデルを含むことができます (何故ならば モデルはちょうど層のようなものだからです)。

モデル・ネスティングのための一般的なユースケースはアンサンブルです。例として、ここにモデルのセットを (それらの予測を平均する) 単一のモデルにどのようにアンサンブルするかがあります :

def get_model():
  inputs = keras.Input(shape=(128,))
  outputs = layers.Dense(1, activation='sigmoid')(inputs)
  return keras.Model(inputs, outputs)

model1 = get_model()
model2 = get_model()
model3 = get_model()

inputs = keras.Input(shape=(128,))
y1 = model1(inputs)
y2 = model2(inputs)
y3 = model3(inputs)
outputs = layers.average([y1, y2, y3])
ensemble_model = keras.Model(inputs=inputs, outputs=outputs)

 

複雑なグラフ・トポロジーを操作する

マルチ入力と出力を持つモデル

functional API はマルチ入力と出力を操作することを容易にします。これは Sequential API では処理できません。

ここに単純なサンプルがあります。

貴方はプライオリティによりカスタム課題チケットをランク付けしてそれらを正しい部門に転送するためのシステムを構築しているとします。

貴方のモデルは 3 入力を持ちます :

  • チケットのタイトル (テキスト入力)
  • チケットのテキスト本体 (テキスト入力)
  • ユーザにより付加された任意のタグ (カテゴリカル入力)

それは 2 つの出力を持つでしょう :

  • 0 と 1 の間のプライオリティ・スコア (スカラー sigmoid 出力)
  • チケットを処理すべき部門 (部門集合に渡る softmax 出力)

このモデルを Functional API で数行で構築しましょう。

num_tags = 12  # Number of unique issue tags
num_words = 10000  # Size of vocabulary obtained when preprocessing text data
num_departments = 4  # Number of departments for predictions

title_input = keras.Input(shape=(None,), name='title')  # Variable-length sequence of ints
body_input = keras.Input(shape=(None,), name='body')  # Variable-length sequence of ints
tags_input = keras.Input(shape=(num_tags,), name='tags')  # Binary vectors of size `num_tags`

# Embed each word in the title into a 64-dimensional vector
title_features = layers.Embedding(num_words, 64)(title_input)
# Embed each word in the text into a 64-dimensional vector
body_features = layers.Embedding(num_words, 64)(body_input)

# Reduce sequence of embedded words in the title into a single 128-dimensional vector
title_features = layers.LSTM(128)(title_features)
# Reduce sequence of embedded words in the body into a single 32-dimensional vector
body_features = layers.LSTM(32)(body_features)

# Merge all available features into a single large vector via concatenation
x = layers.concatenate([title_features, body_features, tags_input])

# Stick a logistic regression for priority prediction on top of the features
priority_pred = layers.Dense(1, activation='sigmoid', name='priority')(x)
# Stick a department classifier on top of the features
department_pred = layers.Dense(num_departments, activation='softmax', name='department')(x)

# Instantiate an end-to-end model predicting both priority and department
model = keras.Model(inputs=[title_input, body_input, tags_input],
                    outputs=[priority_pred, department_pred])

モデルをプロットしましょう :

keras.utils.plot_model(model, 'multi_input_and_output_model.png', show_shapes=True)

 
このモデルをコンパイルするとき、各出力に異なる損失を割り当てることができます。トータルの訓練損失へのそれらの寄与ををモジュール化するために、各損失に異なる重みを割り当てることさえできます。

model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
              loss=['binary_crossentropy', 'categorical_crossentropy'],
              loss_weights=[1., 0.2])

出力層に名前を与えましたので、このように損失を指定することもできるでしょう :

model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
              loss={'priority': 'binary_crossentropy',
                    'department': 'categorical_crossentropy'},
              loss_weights=[1., 0.2])

入力とターゲットの NumPy 配列のリストを渡すことでモデルを訓練できます :

import numpy as np

# Dummy input data
title_data = np.random.randint(num_words, size=(1280, 10))
body_data = np.random.randint(num_words, size=(1280, 100))
tags_data = np.random.randint(2, size=(1280, num_tags)).astype('float32')
# Dummy target data
priority_targets = np.random.random(size=(1280, 1))
dept_targets = np.random.randint(2, size=(1280, num_departments))

model.fit({'title': title_data, 'body': body_data, 'tags': tags_data},
          {'priority': priority_targets, 'department': dept_targets},
          epochs=2,
          batch_size=32)
Epoch 1/2
1280/1280 [==============================] - 11s 9ms/sample - loss: 1.2694 - priority_loss: 0.6984 - department_loss: 2.8547
Epoch 2/2
1280/1280 [==============================] - 11s 9ms/sample - loss: 1.2137 - priority_loss: 0.6489 - department_loss: 2.8242


Dataset オブジェクトで fit を呼び出すとき、それは ([title_data, body_data, tags_data], [priority_targets, dept_targets]) のようなリストのタプルか、({‘title’: title_data, ‘body’: body_data, ‘tags’: tags_data}, {‘priority’: priority_targets, ‘department’: dept_targets}) のような辞書のタプルを yield すべきです。

より詳細な説明については、完全なガイド Guide to Training & Evaluation を参照してください。

 

resnet モデル

マルチ入力と出力を持つモデルに加えて、Functional API は非線形接続トポロジー、つまり層がシークエンシャルに接続されないモデルを操作することも容易にします、これもまた (名前が示すように) Sequential API で扱えません。

これの一般的なユースケースは residual 接続です。

これを示すために CIFAR10 のための toy ResNet モデルを構築しましょう。

inputs = keras.Input(shape=(32, 32, 3), name='img')
x = layers.Conv2D(32, 3, activation='relu')(inputs)
x = layers.Conv2D(64, 3, activation='relu')(x)
block_1_output = layers.MaxPooling2D(3)(x)

x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_1_output)
x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
block_2_output = layers.add([x, block_1_output])

x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_2_output)
x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
block_3_output = layers.add([x, block_2_output])

x = layers.Conv2D(64, 3, activation='relu')(block_3_output)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(256, activation='relu')(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(10, activation='softmax')(x)

model = keras.Model(inputs, outputs, name='toy_resnet')
model.summary()
Model: "toy_resnet"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
img (InputLayer)                [(None, 32, 32, 3)]  0                                            
__________________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, 30, 30, 32)   896         img[0][0]                        
__________________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, 28, 28, 64)   18496       conv2d_8[0][0]                   
__________________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D)  (None, 9, 9, 64)     0           conv2d_9[0][0]                   
__________________________________________________________________________________________________
conv2d_10 (Conv2D)              (None, 9, 9, 64)     36928       max_pooling2d_2[0][0]            
__________________________________________________________________________________________________
conv2d_11 (Conv2D)              (None, 9, 9, 64)     36928       conv2d_10[0][0]                  
__________________________________________________________________________________________________
add (Add)                       (None, 9, 9, 64)     0           conv2d_11[0][0]                  
                                                                 max_pooling2d_2[0][0]            
__________________________________________________________________________________________________
conv2d_12 (Conv2D)              (None, 9, 9, 64)     36928       add[0][0]                        
__________________________________________________________________________________________________
conv2d_13 (Conv2D)              (None, 9, 9, 64)     36928       conv2d_12[0][0]                  
__________________________________________________________________________________________________
add_1 (Add)                     (None, 9, 9, 64)     0           conv2d_13[0][0]                  
                                                                 add[0][0]                        
__________________________________________________________________________________________________
conv2d_14 (Conv2D)              (None, 7, 7, 64)     36928       add_1[0][0]                      
__________________________________________________________________________________________________
global_average_pooling2d (Globa (None, 64)           0           conv2d_14[0][0]                  
__________________________________________________________________________________________________
dense_9 (Dense)                 (None, 256)          16640       global_average_pooling2d[0][0]   
__________________________________________________________________________________________________
dropout (Dropout)               (None, 256)          0           dense_9[0][0]                    
__________________________________________________________________________________________________
dense_10 (Dense)                (None, 10)           2570        dropout[0][0]                    
==================================================================================================
Total params: 223,242
Trainable params: 223,242
Non-trainable params: 0
__________________________________________________________________________________________________

モデルをプロットしましょう :

keras.utils.plot_model(model, 'mini_resnet.png', show_shapes=True)

 
それを訓練しましょう :

(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)

model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
              loss='categorical_crossentropy',
              metrics=['acc'])
model.fit(x_train, y_train,
          batch_size=64,
          epochs=1,
          validation_split=0.2)
Train on 40000 samples, validate on 10000 samples
40000/40000 [==============================] - 318s 8ms/sample - loss: 1.9034 - acc: 0.2767 - val_loss: 1.6173 - val_acc: 0.3870

<tensorflow.python.keras.callbacks.History at 0x7f27a93392b0>

 

層を共有する

functional API のためのもう一つの良いユースケースは共有層を使用するモデルです。共有層は同じモデルで複数回再利用される層インスタンスです : それらは層グラフのマルチパスに対応する特徴を学習します。

共有層は類似空間 (例えば、類似語彙を特徴付けるテキストの 2 つの異なるピース) に由来する入力をエンコードするためにしばしば使用されます、何故ならばそれらは異なる入力に渡る情報の共有を可能にし、そしてそれらはより少ないデータ上でそのようなモデルを訓練することを可能にするからです。与えられた単語が入力の一つであれば、それは共有層を通り抜ける総ての入力の処理に役立つでしょう。

Functional API の層を共有するためには、単に同じ層インスタンスを複数回呼び出すだけです。例えば、ここに2 つの異なるテキスト入力に渡り共有された Embedding 層があります :

# Embedding for 1000 unique words mapped to 128-dimensional vectors
shared_embedding = layers.Embedding(1000, 128)

# Variable-length sequence of integers
text_input_a = keras.Input(shape=(None,), dtype='int32')

# Variable-length sequence of integers
text_input_b = keras.Input(shape=(None,), dtype='int32')

# We reuse the same layer to encode both inputs
encoded_input_a = shared_embedding(text_input_a)
encoded_input_b = shared_embedding(text_input_b)

 

層のグラフのノードを抽出して再利用する

Functional API で貴方が操作する層のグラフは静的データ構造ですので、それはアクセスして調査可能です。これは Functional モデルをどのようにプロットできるかです、例えば。

これはまた中間層 (グラフの「ノード」) の活性にアクセスしてそれらを他の場所で再利用できることも意味します。これは特徴抽出のために極めて有用です、例えば!

サンプルを見てみましょう。これは ImageNet 上で事前訓練された重みを持つ VGG19 モデルです :

from tensorflow.keras.applications import VGG19

vgg19 = VGG19()
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg19_weights_tf_dim_ordering_tf_kernels.h5
574717952/574710816 [==============================] - 6s 0us/step

そしてこれらはグラフデータ構造に問い合わせて得られた、モデルの中間的な活性です。

features_list = [layer.output for layer in vgg19.layers]

私達は新しい特徴抽出モデルを作成するためにこれらの特徴を使用できます、それは中間層の活性の値を返します — そしてこの総てを 3 行で行なうことができます。

feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)

img = np.random.random((1, 224, 224, 3)).astype('float32')
extracted_features = feat_extraction_model(img)

他のものの中では、ニューラルスタイルトランスファーを実装する ときこれは役立ちます。

 

カスタム層を書いて API を拡張する

tf.keras は広範囲の組み込み層を持ちます。幾つかの例がここにあります :

  • 畳み込み層: Conv1D, Conv2D, Conv3D, Conv2DTranspose, etc.
  • Pooling 層: MaxPooling1D, MaxPooling2D, MaxPooling3D, AveragePooling1D, etc.
  • RNN 層: GRU, LSTM, ConvLSTM2D, etc.
  • BatchNormalization, Dropout, Embedding, etc.

貴方が必要なものを見つけられないならば、貴方自身の層を作成して API を拡張することは容易です。

総ての層は Layer クラスをサブクラス化して次を実装します :- call メソッド、層により行われる計算を指定します。- build メソッド、層の重みを作成します (これは単にスタイル慣習であることに注意してください ; __init__ で重みを作成しても良いでしょう)。

スクラッチから層を作成することについて更に学習するためには、ガイド Guide to writing layers and models from scratch を調べてください。

ここに Dense 層の単純な実装があります :

class CustomDense(layers.Layer):

  def __init__(self, units=32):
    super(CustomDense, self).__init__()
    self.units = units

  def build(self, input_shape):
    self.w = self.add_weight(shape=(input_shape[-1], self.units),
                             initializer='random_normal',
                             trainable=True)
    self.b = self.add_weight(shape=(self.units,),
                             initializer='random_normal',
                             trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.w) + self.b
      
inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)

model = keras.Model(inputs, outputs)

シリアライゼーションをサポートするカスタム層を作成することを望む場合、get_config メソッドもまた定義するべきです、これは層インスタンスのコンストラクタ引数を返します :

class CustomDense(layers.Layer):

  def __init__(self, units=32):
    super(CustomDense, self).__init__()
    self.units = units

  def build(self, input_shape):
    self.w = self.add_weight(shape=(input_shape[-1], self.units),
                             initializer='random_normal',
                             trainable=True)
    self.b = self.add_weight(shape=(self.units,),
                             initializer='random_normal',
                             trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.w) + self.b

  def get_config(self):
    return {'units': self.units}
    
    
inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)

model = keras.Model(inputs, outputs)
config = model.get_config()

new_model = keras.Model.from_config(
    config, custom_objects={'CustomDense': CustomDense})

オプションで、クラスメソッド from_config(cls, config) を実装することもできるでしょう、これはその config 辞書が与えられたときに層インスタンスを再作成する責任を負います。from_config のデフォルト実装は :

def from_config(cls, config):
  return cls(**config)

 

いつ Functional API を使用するか

新しいモデルを作成するために Functional API を使用するか、単に Model クラスを直接的にサブクラス化するかをどのように決めるのでしょう?

一般に、Functional API は高位で、利用するにより簡単で安全で、そしてサブクラス化されたモデルがサポートしない多くの特徴を持ちます。

けれども、層の有向非巡回グラフのように容易には表現できないモデルを作成するとき、モデルのサブクラス化は貴方により大きな柔軟性を与えます (例えば、貴方は Tree-RNN を Functional API では実装できないでしょう、Model を直接的にサブクラス化しなければならないでしょう)。

 

Functional API の強みがここにあります :

下にリストされる特性は Sequential に対しても総て真ですが (それはまたデータ構造です)、それらはサブクラス化されたモデルについては真ではありません (それは Python バイトコードであり、データ構造ではありません)。

それはより冗長ではありません。(= It is less verbose.)

No super(MyClass, self).__init__(…), no def call(self, …):, etc.

以下を :

inputs = keras.Input(shape=(32,))
x = layers.Dense(64, activation='relu')(inputs)
outputs = layers.Dense(10)(x)
mlp = keras.Model(inputs, outputs)

サブクラス化されたバージョンと比較してください :

class MLP(keras.Model):
  
  def __init__(self, **kwargs):
    super(MLP, self).__init__(**kwargs)
    self.dense_1 = layers.Dense(64, activation='relu')
    self.dense_2 = layers.Dense(10)
    
  def call(self, inputs):
    x = self.dense_1(inputs)
    return self.dense_2(x)
 
# Instantiate the model.
mlp = MLP()
# Necessary to create the model's state.
# The model doesn't have a state until it's called at least once.
_ = mlp(tf.zeros((1, 32)))

 
それは定義する間に貴方のモデルを検証します。

Functional API では、入力仕様 (shape と dtype) が (Input を通して) 前もって作成され、そして層を呼び出すたびに、その層はそれに渡される仕様が仮定に適合するかをチェックして、そうでないならば役立つエラーメッセージをあげます。

これは Functional API で構築できたどのようなモデルも実行されることを保証します。(収束関連のデバッグ以外の) 総てのデバッグはモデル構築時に静的に発生し、実行時ではありません。これはコンパイラの型チェックに類似しています。

 
貴方の Functional モデルはプロット可能で調査可能

モデルをグラフとしてプロットできます、そしてこのグラフの中間ノードに容易にアクセスできます — 例えば、前の例で見たように中間層の活性を抽出して再利用するためにです。

features_list = [layer.output for layer in vgg19.layers]
feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)

 
貴方の Functional モデルはシリアライズとクローン可能

Functional モデルはコードのピースというよりもデータ構造です から、それは安全にシリアライズ可能で (どのような元のコードにもアクセスすることなく正確に同じモデルを再作成することを可能にする) 単一ファイルにセーブできます。より詳細については saving and serialization guide を見てください。

 

Functional API の欠点がここにあります :

それは動的アーキテクチャをサポートしません。

Functional API はモデルを層の DAG として扱います。これは殆どの深層学習アーキテクチャに対しては真ですが、総てではありません : 例えば、再帰 (= recursive) ネットワークや Tree RNNs はこの仮定に従いませんそして Functional API では実装できません。

 
時に、総てを単にスクラッチから書く必要があります。

進んだアーキテクチャを書く時、「層の DAG を定義する」という範囲の外にあることをすることを望むかもしれません : 例えば、貴方のモデル・インスタンス上の複数のカスタム訓練と推論メソッドを公開することを望むかもしれません。これはサブクラス化を必要とします。

 


Functional API とモデルのサブクラス化の間の違いへより深く潜るために、What are Symbolic and Imperative APIs in TensorFlow 2.0? を読むことができます。

 

異なる API スタイルを上手く組み合わせる

重要なことは、Functional API かモデルのサブクラス化の間を選択することはモデルの一つのカテゴリに貴方を制限する二者択一ではありません。tf.keras API の総てのモデルはそれらが Sequential モデルか、Functional モデルか、あるいはスクラッチから書かれたサブクラス化されたモデル/層であろうと、各々と相互作用できます。

サブクラス化されたモデル/層の一部として Functional モデルや Sequential モデルを常に使用できます :

units = 32
timesteps = 10
input_dim = 5

# Define a Functional model
inputs = keras.Input((None, units))
x = layers.GlobalAveragePooling1D()(inputs)
outputs = layers.Dense(1, activation='sigmoid')(x)
model = keras.Model(inputs, outputs)


class CustomRNN(layers.Layer):

  def __init__(self):
    super(CustomRNN, self).__init__()
    self.units = units
    self.projection_1 = layers.Dense(units=units, activation='tanh')
    self.projection_2 = layers.Dense(units=units, activation='tanh')
    # Our previously-defined Functional model
    self.classifier = model

  def call(self, inputs):
    outputs = []
    state = tf.zeros(shape=(inputs.shape[0], self.units))
    for t in range(inputs.shape[1]):
      x = inputs[:, t, :]
      h = self.projection_1(x)
      y = h + self.projection_2(state)
      state = y
      outputs.append(y)
    features = tf.stack(outputs, axis=1)
    print(features.shape)
    return self.classifier(features)

rnn_model = CustomRNN()
_ = rnn_model(tf.zeros((1, timesteps, input_dim)))
(1, 10, 32)

反対に、Functional API でサブクラス化された層やモデルをそれが (次のパターンの一つに従う) call メソッドを実装する限りは使用することもできます :

  • call(self, inputs, **kwargs)、ここで inputs は tensor か tensor のネスト構造 (e.g. tensor のリスト) で、**kwargs は非 tensor 引数 (非 inputs) です。
  • call(self, inputs, training=None, **kwargs)、ここで training は層が訓練モードか推論モードで動作するべきかを示すブーリアンです。
  • call(self, inputs, mask=None, **kwargs)、ここで mask はブーリアンのマスク tensor です (推論のために、RNN に有用です)。
  • call(self, inputs, training=None, mask=None, **kwargs) — もちろんマスキングと訓練固有の動作の両者を同時に持つことができます。

加えて、貴方のカスタム層やモデルで get_config メソッドを実装する場合、貴方がそれで作成した Functional モデルは依然としてシリアライズ可能でクローン可能です。

ここに Functional モデルでスクラッチから書かれたカスタム RNN を使用する簡単なサンプルがあります :

units = 32
timesteps = 10
input_dim = 5
batch_size = 16


class CustomRNN(layers.Layer):

  def __init__(self):
    super(CustomRNN, self).__init__()
    self.units = units
    self.projection_1 = layers.Dense(units=units, activation='tanh')
    self.projection_2 = layers.Dense(units=units, activation='tanh')
    self.classifier = layers.Dense(1, activation='sigmoid')

  def call(self, inputs):
    outputs = []
    state = tf.zeros(shape=(inputs.shape[0], self.units))
    for t in range(inputs.shape[1]):
      x = inputs[:, t, :]
      h = self.projection_1(x)
      y = h + self.projection_2(state)
      state = y
      outputs.append(y)
    features = tf.stack(outputs, axis=1)
    return self.classifier(features)

# Note that we specify a static batch size for the inputs with the `batch_shape`
# arg, because the inner computation of `CustomRNN` requires a static batch size
# (when we create the `state` zeros tensor).
inputs = keras.Input(batch_shape=(batch_size, timesteps, input_dim))
x = layers.Conv1D(32, 3)(inputs)
outputs = CustomRNN()(x)

model = keras.Model(inputs, outputs)

rnn_model = CustomRNN()
_ = rnn_model(tf.zeros((1, 10, 5)))
 

以上



TensorFlow 2.0 Alpha : ガイド : Keras : 簡単な概要

TensorFlow 2.0 Alpha : ガイド : Keras : 簡単な概要 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/14/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ガイド : Keras : 簡単な概要

tf.keras をインポートする

tf.keras は Keras API 仕様 の TensorFlow 実装です。これはモデルを構築して訓練するための高位 API で、eager execution, tf.data パイプラインと Estimator のような TensorFlow-固有の機能のための first-class サポートを含みます。tf.keras は柔軟性やパフォーマンスを犠牲にすることなく TensorFlow を容易に利用できるようにします。

始めるには、貴方の TensorFlow プログラム・セットアップの一部として tf.keras をインポートします :

!pip install -q pyyaml  # pyyaml is optional
from __future__ import absolute_import, division, print_function

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

from tensorflow import keras

tf.keras は任意の Keras 互換コードを実行できますが、以下に留意してください :

  • 最新の TensorFlow リリースの tf.keras バージョンは PyPI からの最新の keras バージョンと同じではないかもしれません。tf.keras.__version__ を確認してください。
  • モデルの重みをセーブするとき、tf.keras はチェックポイント形式をデフォルトにしています。HDF5 を使用するためには save_format=’h5′ を渡してください。

 

単純なモデルを構築する

Sequential モデル

Keras では、モデルを構築するために層を集めます。モデルは (通常は) 層のグラフです。モデルの最も一般的なタイプは層のスタック : tf.keras.Sequential モデルです。

単純な、完全結合ネットワーク (i.e. 多層パーセプトロン) を構築するには :

from tensorflow.keras import layers

model = tf.keras.Sequential()
# Adds a densely-connected layer with 64 units to the model:
model.add(layers.Dense(64, activation='relu'))
# Add another:
model.add(layers.Dense(64, activation='relu'))
# Add a softmax layer with 10 output units:
model.add(layers.Dense(10, activation='softmax'))

Sequential モデルをどのように使用するかの完全な、短いサンプルは ここ で見つけられます。

Sequential モデルよりもより進んだモデルの構築について学習するためには、
Guide to the Keras Functional – Guide to writing layers and models from scratch with subclassing を見てください。

 

層を configure する

幾つかの一般的なコンストラクタ・パラメータとともに利用可能な多くの tf.keras.layers があります。

  • activation: 層のための活性化関数を設定します。このパラメータは組み込み関数の名前か callable オブジェクトとして指定されます。デフォルトでは、activation は適用されません。
  • kernel_initializerbias_initializer: 層の重み (カーネルとバイアス) を作成する初期化スキームです。このパラメータは名前か callable オブジェクトです。これは “Glorot uniform” initializer がデフォルトです。
  • kernel_regularizebias_regularizer: L1 や L2 正則化のような、層の重み (カーネルとバイアス) に適用される正則化スキームです。デフォルトでは、正則化は適用されません。

以下はコンストラクタ引数を使用して tf.keras.layers.Dense 層をインスタンス化します :

# Create a sigmoid layer:
layers.Dense(64, activation='sigmoid')
# Or:
layers.Dense(64, activation=tf.keras.activations.sigmoid)

# A linear layer with L1 regularization of factor 0.01 applied to the kernel matrix:
layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))

# A linear layer with L2 regularization of factor 0.01 applied to the bias vector:
layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))

# A linear layer with a kernel initialized to a random orthogonal matrix:
layers.Dense(64, kernel_initializer='orthogonal')

# A linear layer with a bias vector initialized to 2.0s:
layers.Dense(64, bias_initializer=tf.keras.initializers.Constant(2.0))
<tensorflow.python.keras.layers.core.Dense at 0x7f184c1d4978>

 

訓練と評価

訓練のセットアップ

モデルが構築された後、compile メソッドを呼び出してその学習プロセスを configure します :

model = tf.keras.Sequential([
# Adds a densely-connected layer with 64 units to the model:
layers.Dense(64, activation='relu', input_shape=(32,)),
# Add another:
layers.Dense(64, activation='relu'),
# Add a softmax layer with 10 output units:
layers.Dense(10, activation='softmax')])

model.compile(optimizer=tf.keras.optimizers.Adam(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

tf.keras.Model.compile は 3 つの重要な引数を取ります :

  • optimizer: このオブジェクトは訓練手続きを指定します。tf.keras.optimizers.Adam や tf.keras.optimizers.SGD のような、tf.keras.optimizers モジュールからの optimizer インスタンスをそれに渡します。単にデフォルト・パラメータを使用することを望む場合には、’adam’ や ‘sgd’ のような、文字列を通して optimizer を指定することも可能です。
  • loss: 最適化の間に最小化するための関数です。一般的な選択は mean square error (mse), categorical_crossentropy そして binary_crossentropy を含みます。損失関数は名前か tf.keras.losses モジュールからの callable オブジェクトを渡すことにより指定されます。
  • metrics: 訓練を監視するために使用されます。これらは文字列名か tf.keras.metrics モジュールからの callable です。
  • 追加として、モデルが eagaly に訓練して評価されることを確かなものにするために、compile へのパラメータとして run_eagerly=True を確実に渡すことができます。

次は訓練のためにモデルを configure する幾つかの例を示します :

# Configure a model for mean-squared error regression.
model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss='mse',       # mean squared error
              metrics=['mae'])  # mean absolute error

# Configure a model for categorical classification.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.01),
              loss=tf.keras.losses.CategoricalCrossentropy(),
              metrics=[tf.keras.metrics.CategoricalAccuracy()])

 

NumPy データから訓練する

小さいデータセットについては、モデルを訓練して評価するために in-memory NumPy 配列を使用してください。モデルは fit メソッドを使用して訓練データに “fit” します :

import numpy as np

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

model.fit(data, labels, epochs=10, batch_size=32)
Epoch 1/10
1000/1000 [==============================] - 0s 186us/sample - loss: 163.1455 - categorical_accuracy: 0.0920
Epoch 2/10
1000/1000 [==============================] - 0s 69us/sample - loss: 737.6459 - categorical_accuracy: 0.0980
Epoch 3/10
1000/1000 [==============================] - 0s 71us/sample - loss: 1518.1089 - categorical_accuracy: 0.1010
Epoch 4/10
1000/1000 [==============================] - 0s 69us/sample - loss: 2442.3686 - categorical_accuracy: 0.1050
Epoch 5/10
1000/1000 [==============================] - 0s 73us/sample - loss: 3902.2138 - categorical_accuracy: 0.1110
Epoch 6/10
1000/1000 [==============================] - 0s 72us/sample - loss: 5223.3229 - categorical_accuracy: 0.1100
Epoch 7/10
1000/1000 [==============================] - 0s 75us/sample - loss: 7148.6296 - categorical_accuracy: 0.0910
Epoch 8/10
1000/1000 [==============================] - 0s 76us/sample - loss: 9415.2926 - categorical_accuracy: 0.1000
Epoch 9/10
1000/1000 [==============================] - 0s 71us/sample - loss: 11110.7333 - categorical_accuracy: 0.0970
Epoch 10/10
1000/1000 [==============================] - 0s 68us/sample - loss: 13441.3140 - categorical_accuracy: 0.1050

<tensorflow.python.keras.callbacks.History at 0x7f18284f97f0>

tf.keras.Model.fit は 3 つの重要な引数を取ります :

  • epochs: 訓練はエポックに構造化されます。エポックは入力データ全体に渡る一つの iteration です (これはより小さなバッチで成されます)。
  • batch_size: NumPy データが渡されたとき、モデルはデータをより小さいバッチにスライスして訓練の間これらのバッチに渡り iterate します。この整数は各バッチのサイズを指定します。サンプル総数がバッチサイズで割り切れない場合は最後のバッチはより小さいかもしれないことに留意してください。
  • validation_data: モデルをプロトタイプするとき、幾つかの検証データ上のそのパフォーマンスを簡単に監視することを望むでしょう。この引数 — 入力とラベルのタプル — を渡すと、各エポックの最後に、渡されたデータについて推論モードで損失とメトリクスをモデルに表示することを可能にさせます。

validation_data を使用するサンプルがここにあります :

import numpy as np

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

val_data = np.random.random((100, 32))
val_labels = np.random.random((100, 10))

model.fit(data, labels, epochs=10, batch_size=32,
          validation_data=(val_data, val_labels))
Train on 1000 samples, validate on 100 samples
Epoch 1/10
1000/1000 [==============================] - 0s 200us/sample - loss: 15978.2837 - categorical_accuracy: 0.1100 - val_loss: 14920.7678 - val_categorical_accuracy: 0.1000
Epoch 2/10
1000/1000 [==============================] - 0s 78us/sample - loss: 19036.1090 - categorical_accuracy: 0.0940 - val_loss: 24388.2088 - val_categorical_accuracy: 0.0900
Epoch 3/10
1000/1000 [==============================] - 0s 80us/sample - loss: 22321.0016 - categorical_accuracy: 0.1020 - val_loss: 24739.9762 - val_categorical_accuracy: 0.0700
Epoch 4/10
1000/1000 [==============================] - 0s 72us/sample - loss: 25868.0920 - categorical_accuracy: 0.0990 - val_loss: 18184.7635 - val_categorical_accuracy: 0.1100
Epoch 5/10
1000/1000 [==============================] - 0s 85us/sample - loss: 29230.7407 - categorical_accuracy: 0.1130 - val_loss: 29062.2665 - val_categorical_accuracy: 0.1700
Epoch 6/10
1000/1000 [==============================] - 0s 83us/sample - loss: 33179.0449 - categorical_accuracy: 0.1150 - val_loss: 33837.8114 - val_categorical_accuracy: 0.0300
Epoch 7/10
1000/1000 [==============================] - 0s 72us/sample - loss: 37348.5848 - categorical_accuracy: 0.0890 - val_loss: 37315.6452 - val_categorical_accuracy: 0.0700
Epoch 8/10
1000/1000 [==============================] - 0s 81us/sample - loss: 42159.9027 - categorical_accuracy: 0.1110 - val_loss: 41714.3591 - val_categorical_accuracy: 0.1700
Epoch 9/10
1000/1000 [==============================] - 0s 81us/sample - loss: 45967.8550 - categorical_accuracy: 0.1120 - val_loss: 48806.3112 - val_categorical_accuracy: 0.1700
Epoch 10/10
1000/1000 [==============================] - 0s 76us/sample - loss: 50035.3631 - categorical_accuracy: 0.0850 - val_loss: 60884.2381 - val_categorical_accuracy: 0.0700

<tensorflow.python.keras.callbacks.History at 0x7f18a0748e10>

 

tf.data datasets から訓練する

巨大なデータセットやマルチデバイス訓練にスケールするには Datasets API を使用します。tf.data.Dataset インスタンスを fit メソッドに渡します :

# Instantiates a toy dataset instance:
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)

# Don't forget to specify `steps_per_epoch` when calling `fit` on a dataset.
model.fit(dataset, epochs=10, steps_per_epoch=30)
WARNING: Logging before flag parsing goes to stderr.
W0307 18:13:21.298871 139743906965248 training_utils.py:1353] Expected a shuffled dataset but input dataset `x` is not shuffled. Please invoke `shuffle()` on input dataset.

Epoch 1/10
30/30 [==============================] - 0s 3ms/step - loss: 55095.1980 - categorical_accuracy: 0.1083
Epoch 2/10
 1/30 [>.............................] - ETA: 0s - loss: 66421.8828 - categorical_accuracy: 0.1250
W0307 18:13:21.415224 139743906965248 training_generator.py:228] Your dataset ran out of data; interrupting training. Make sure that your dataset can generate at least `steps_per_epoch * epochs` batches (in this case, 300 batches). You may need to use the repeat() function when building your dataset.

<tensorflow.python.keras.callbacks.History at 0x7f18a598e550>

ここで、fit メソッドは steps_per_epoch 引数を使用します — これはモデルが次のエポックに移行する前に実行する訓練ステップの数です。Dataset はデータのバッチを yield するので、スニペットは batch_size を必要としません。

Datasets はまた検証のためにも使用できます :

dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)

val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))
val_dataset = val_dataset.batch(32)

model.fit(dataset, epochs=10,
          validation_data=val_dataset)
W0307 18:13:21.444809 139743906965248 training_utils.py:1353] Expected a shuffled dataset but input dataset `x` is not shuffled. Please invoke `shuffle()` on input dataset.

Epoch 1/10
32/32 [==============================] - 0s 4ms/step - loss: 63254.3520 - categorical_accuracy: 0.1070 - val_loss: 72984.6680 - val_categorical_accuracy: 0.0900
Epoch 2/10
32/32 [==============================] - 0s 4ms/step - loss: 67955.6909 - categorical_accuracy: 0.0980 - val_loss: 62399.8779 - val_categorical_accuracy: 0.1700
Epoch 3/10
32/32 [==============================] - 0s 4ms/step - loss: 70544.7288 - categorical_accuracy: 0.0840 - val_loss: 93031.5527 - val_categorical_accuracy: 0.1000
Epoch 4/10
32/32 [==============================] - 0s 4ms/step - loss: 77979.4031 - categorical_accuracy: 0.0970 - val_loss: 74229.4023 - val_categorical_accuracy: 0.1100
Epoch 5/10
32/32 [==============================] - 0s 4ms/step - loss: 83574.9339 - categorical_accuracy: 0.1260 - val_loss: 78287.6426 - val_categorical_accuracy: 0.0900
Epoch 6/10
32/32 [==============================] - 0s 4ms/step - loss: 90891.1725 - categorical_accuracy: 0.1060 - val_loss: 94735.4883 - val_categorical_accuracy: 0.1700
Epoch 7/10
32/32 [==============================] - 0s 4ms/step - loss: 94002.2560 - categorical_accuracy: 0.1060 - val_loss: 100048.7207 - val_categorical_accuracy: 0.1000
Epoch 8/10
32/32 [==============================] - 0s 4ms/step - loss: 101943.8282 - categorical_accuracy: 0.1090 - val_loss: 108326.5488 - val_categorical_accuracy: 0.0800
Epoch 9/10
32/32 [==============================] - 0s 4ms/step - loss: 111068.3402 - categorical_accuracy: 0.1160 - val_loss: 145687.4141 - val_categorical_accuracy: 0.1700
Epoch 10/10
32/32 [==============================] - 0s 4ms/step - loss: 119871.4167 - categorical_accuracy: 0.0950 - val_loss: 143137.7383 - val_categorical_accuracy: 0.1000

<tensorflow.python.keras.callbacks.History at 0x7f182817c358>

 

評価と予測

tf.keras.Model.evaluate と tf.keras.Model.predict は NumPy データと tf.data.Dataset を使用できます。

提供されたデータのために推論モードで損失とメトリクスを評価するには :

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

model.evaluate(data, labels, batch_size=32)

model.evaluate(dataset, steps=30)
1000/1000 [==============================] - 0s 45us/sample - loss: 140248.3122 - categorical_accuracy: 0.1150
30/30 [==============================] - 0s 2ms/step - loss: 140829.9484 - categorical_accuracy: 0.1031

[140829.9484375, 0.103125]

そして提供されたデータのために推論で最後の層の出力を予測します、NumPy 配列として :

result = model.predict(data, batch_size=32)
print(result.shape)
(1000, 10)

カスタム訓練ループをスクラッチからどのように書くかを含めて、訓練と評価の完全なガイドについては、訓練 & 評価へのガイド を見てください。

 

上級モデルを構築する

Functional API

tf.keras.Sequential モデルは層の単純なスタックで任意のモデルを表わすことはできません。次のような複雑なモデル・トポロジーを構築するには Keras functional API を使用します :

  • マルチ入力モデル、
  • マルチ出力モデル、
  • 共有層を持つモデル (同じ層が幾度も呼び出されます)、
  • non-sequential データフローを持つモデル (e.g. 残差コネクション)。

funcitonal API によるモデル構築は次のように動作します :

  1. 層インスタンスは callable で tensor を返します。
  2. 入力 tensor と出力 tensor は tf.keras.Model インスタンスを定義するために使用されます。
  3. このモデルは丁度 Sequential モデルのように訓練されます。

次のサンプルは単純な、完全結合ネットワークを構築するために functional API を使用します :

inputs = tf.keras.Input(shape=(32,))  # Returns an input placeholder

# A layer instance is callable on a tensor, and returns a tensor.
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)

与えられた入力と出力でモデルをインスタンス化します。

Instantiate the model given inputs and outputs.

model = tf.keras.Model(inputs=inputs, outputs=predictions)

# The compile step specifies the training configuration.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Trains for 5 epochs
model.fit(data, labels, batch_size=32, epochs=5)
Epoch 1/5
1000/1000 [==============================] - 0s 150us/sample - loss: 13.0385 - accuracy: 0.1030
Epoch 2/5
1000/1000 [==============================] - 0s 67us/sample - loss: 21.5475 - accuracy: 0.1120
Epoch 3/5
1000/1000 [==============================] - 0s 73us/sample - loss: 35.4754 - accuracy: 0.1100
Epoch 4/5
1000/1000 [==============================] - 0s 70us/sample - loss: 53.0951 - accuracy: 0.0990
Epoch 5/5
1000/1000 [==============================] - 0s 72us/sample - loss: 72.9124 - accuracy: 0.1190

<tensorflow.python.keras.callbacks.History at 0x7f181840bcc0>

 

Model サブクラス化

tf.keras.Model のサブクラス化と貴方自身の forward パスを定義することにより完全にカスタマイズ可能なモデルを構築します。__init__ メソッドで層を作成してそれらをクラスインスタンスの属性として設定します。call メソッドで forward パスを定義します。

Model サブクラス化は eager execution が有効なときに特に有用です、何故ならば forward パスが命令的に書けるからです。

Note: forward パスが常に命令的に実行されることを確実にするためには、super コンストラクタを呼び出すときに dynamic=True を設定しなければなりません。

Key Point: ジョブのために正しい API を使用してください。モデルのサブクラス化は柔軟性を提供する一方で、それはより大きな複雑性とユーザエラーのより多くの機会というのコストがかかります。可能であれば、functional API を選択してください。

次のサンプルは、命令的に実行されなくても良いカスタム forward パスを使用するサブクラス化された tf.keras.Model を示します :

class MyModel(tf.keras.Model):

  def __init__(self, num_classes=10):
    super(MyModel, self).__init__(name='my_model')
    self.num_classes = num_classes
    # Define your layers here.
    self.dense_1 = layers.Dense(32, activation='relu')
    self.dense_2 = layers.Dense(num_classes, activation='sigmoid')

  def call(self, inputs):
    # Define your forward pass here,
    # using layers you previously defined (in `__init__`).
    x = self.dense_1(inputs)
    return self.dense_2(x)

新しいモデル・クラスをインスタンス化します :

model = MyModel(num_classes=10)

# The compile step specifies the training configuration.
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Trains for 5 epochs.
model.fit(data, labels, batch_size=32, epochs=5)
Epoch 1/5
1000/1000 [==============================] - 0s 152us/sample - loss: 11.5199 - accuracy: 0.1050
Epoch 2/5
1000/1000 [==============================] - 0s 73us/sample - loss: 11.4946 - accuracy: 0.1010
Epoch 3/5
1000/1000 [==============================] - 0s 69us/sample - loss: 11.4895 - accuracy: 0.1060
Epoch 4/5
1000/1000 [==============================] - 0s 79us/sample - loss: 11.4847 - accuracy: 0.1140
Epoch 5/5
1000/1000 [==============================] - 0s 68us/sample - loss: 11.4819 - accuracy: 0.1100

<tensorflow.python.keras.callbacks.History at 0x7f1818094c50>

 

カスタム層

tf.keras.layers.Layer をサブクラス化して次のメソッドを実装することでカスタム層を作成します :

  • __init__: オプションでこの層により使用されるサブ層を定義します。
  • build: 層の重みを作成します。add_weight メソッドを持つ重みを追加します。
  • call: forward パスを定義します。
  • オプションで、get_config メソッドとfrom_config クラス・メソッドを実装することにより層はシリアライズ化できます。

入力の kernel 行列による matmul を実装するカスタム層のサンプルがここにあります :

class MyLayer(layers.Layer):

  def __init__(self, output_dim, **kwargs):
    self.output_dim = output_dim
    super(MyLayer, self).__init__(**kwargs)

  def build(self, input_shape):
    # Create a trainable weight variable for this layer.
    self.kernel = self.add_weight(name='kernel',
                                  shape=(input_shape[1], self.output_dim),
                                  initializer='uniform',
                                  trainable=True)

  def call(self, inputs):
    return tf.matmul(inputs, self.kernel)

  def get_config(self):
    base_config = super(MyLayer, self).get_config()
    base_config['output_dim'] = self.output_dim
    return base_config

  @classmethod
  def from_config(cls, config):
    return cls(**config)

貴方のカスタム層を使用してモデルを作成します :

model = tf.keras.Sequential([
    MyLayer(10),
    layers.Activation('softmax')])

# The compile step specifies the training configuration
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Trains for 5 epochs.
model.fit(data, labels, batch_size=32, epochs=5)
Epoch 1/5
1000/1000 [==============================] - 0s 119us/sample - loss: 11.4887 - accuracy: 0.0940
Epoch 2/5
1000/1000 [==============================] - 0s 62us/sample - loss: 11.4894 - accuracy: 0.0930
Epoch 3/5
1000/1000 [==============================] - 0s 67us/sample - loss: 11.4894 - accuracy: 0.0940
Epoch 4/5
1000/1000 [==============================] - 0s 61us/sample - loss: 11.4892 - accuracy: 0.0960
Epoch 5/5
1000/1000 [==============================] - 0s 58us/sample - loss: 11.4895 - accuracy: 0.0910

<tensorflow.python.keras.callbacks.History at 0x7f18103a88d0>

Guide to writing layers and models from scratch でサブクラス化による新しい層とモデルのスクラッチからの作成について更に学習してください。

 

コールバック

コールバックは訓練の間にその挙動をカスタマイズして拡張するためにモデルに渡されるオブジェクトです。貴方自身のカスタム・コールバックを書いたり、以下を含む組み込み tf.keras.callbacks を使用できます :

  • tf.keras.callbacks.ModelCheckpoint: 定期的な間隔でモデルのチェックポイントをセーブします。
  • tf.keras.callbacks.LearningRateScheduler: 学習率を動的に変更します。
  • tf.keras.callbacks.EarlyStopping: 検証パフォーマンスが改善しないときに訓練を中止します。
  • tf.keras.callbacks.TensorBoard: TensorBoard を使用してモデルの挙動を監視します。

tf.keras.callbacks.Callback を使用するには、それをモデルの fit メソッドに渡します :

callbacks = [
  # Interrupt training if `val_loss` stops improving for over 2 epochs
  tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
  # Write TensorBoard logs to `./logs` directory
  tf.keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,
          validation_data=(val_data, val_labels))
Train on 1000 samples, validate on 100 samples
Epoch 1/5
1000/1000 [==============================] - 0s 197us/sample - loss: 11.4896 - accuracy: 0.0950 - val_loss: 11.2864 - val_accuracy: 0.1100
Epoch 2/5
1000/1000 [==============================] - 0s 58us/sample - loss: 11.4892 - accuracy: 0.0950 - val_loss: 11.2883 - val_accuracy: 0.1200
Epoch 3/5
1000/1000 [==============================] - 0s 75us/sample - loss: 11.4900 - accuracy: 0.0940 - val_loss: 11.2864 - val_accuracy: 0.1100
Epoch 4/5
1000/1000 [==============================] - 0s 71us/sample - loss: 11.4894 - accuracy: 0.0930 - val_loss: 11.2881 - val_accuracy: 0.1200
Epoch 5/5
1000/1000 [==============================] - 0s 75us/sample - loss: 11.4897 - accuracy: 0.0960 - val_loss: 11.2875 - val_accuracy: 0.1100

<tensorflow.python.keras.callbacks.History at 0x7f18100a9cc0>

 

セーブとリストア

Weights only

tf.keras.Model.save_weights を使用してモデルの重みをセーブしてロードします :

model = tf.keras.Sequential([
layers.Dense(64, activation='relu', input_shape=(32,)),
layers.Dense(10, activation='softmax')])

model.compile(optimizer=tf.keras.optimizers.Adam(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
# Save weights to a TensorFlow Checkpoint file
model.save_weights('./weights/my_model')

# Restore the model's state,
# this requires a model with the same architecture.
model.load_weights('./weights/my_model')
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f17f07120f0>

デフォルトでは、これはモデルの重みを TensorFlow チェックポイント ファイル形式でセーブします。重みはまた Keras HDF5 形式でもセーブできます (Keras の multi-backend 実装のためのデフォルト) :

# Save weights to a HDF5 file
model.save_weights('my_model.h5', save_format='h5')

# Restore the model's state
model.load_weights('my_model.h5')

 

Configuration only

モデルの configuration はセーブできます — これはどのような重みもなしでモデル・アーキテクチャをシリアライズします。セーブされた configuration は、元のモデルを定義したコードなしでさえも同じモデルを再作成して初期化できます。Keras は JSON と YAML シリアライゼーション形式をサポートします :

# Serialize a model to JSON format
json_string = model.to_json()
json_string
'{"class_name": "Sequential", "backend": "tensorflow", "config": {"name": "sequential_3", "layers": [{"class_name": "Dense", "config": {"batch_input_shape": [null, 32], "bias_constraint": null, "activation": "relu", "kernel_constraint": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "name": "dense_17", "kernel_regularizer": null, "bias_regularizer": null, "trainable": true, "dtype": "float32", "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "units": 64, "activity_regularizer": null, "use_bias": true}}, {"class_name": "Dense", "config": {"bias_constraint": null, "activation": "softmax", "kernel_constraint": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "name": "dense_18", "kernel_regularizer": null, "bias_regularizer": null, "trainable": true, "dtype": "float32", "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "units": 10, "activity_regularizer": null, "use_bias": true}}]}, "keras_version": "2.2.4-tf"}'
import json
import pprint
pprint.pprint(json.loads(json_string))
{'backend': 'tensorflow',
 'class_name': 'Sequential',
 'config': {'layers': [{'class_name': 'Dense',
                        'config': {'activation': 'relu',
                                   'activity_regularizer': None,
                                   'batch_input_shape': [None, 32],
                                   'bias_constraint': None,
                                   'bias_initializer': {'class_name': 'Zeros',
                                                        'config': {}},
                                   'bias_regularizer': None,
                                   'dtype': 'float32',
                                   'kernel_constraint': None,
                                   'kernel_initializer': {'class_name': 'GlorotUniform',
                                                          'config': {'seed': None}},
                                   'kernel_regularizer': None,
                                   'name': 'dense_17',
                                   'trainable': True,
                                   'units': 64,
                                   'use_bias': True}},
                       {'class_name': 'Dense',
                        'config': {'activation': 'softmax',
                                   'activity_regularizer': None,
                                   'bias_constraint': None,
                                   'bias_initializer': {'class_name': 'Zeros',
                                                        'config': {}},
                                   'bias_regularizer': None,
                                   'dtype': 'float32',
                                   'kernel_constraint': None,
                                   'kernel_initializer': {'class_name': 'GlorotUniform',
                                                          'config': {'seed': None}},
                                   'kernel_regularizer': None,
                                   'name': 'dense_18',
                                   'trainable': True,
                                   'units': 10,
                                   'use_bias': True}}],
            'name': 'sequential_3'},
 'keras_version': '2.2.4-tf'}

JSON から (新たに初期化された) モデルを再作成します :

fresh_model = tf.keras.models.model_from_json(json_string)

モデルの YAML 形式へのシリアライズは TensorFlow をインポートする前に pyyaml をインストールする必要があります :

yaml_string = model.to_yaml()
print(yaml_string)
backend: tensorflow
class_name: Sequential
config:
  layers:
  - class_name: Dense
    config:
      activation: relu
      activity_regularizer: null
      batch_input_shape: !!python/tuple [null, 32]
      bias_constraint: null
      bias_initializer:
        class_name: Zeros
        config: {}
      bias_regularizer: null
      dtype: float32
      kernel_constraint: null
      kernel_initializer:
        class_name: GlorotUniform
        config: {seed: null}
      kernel_regularizer: null
      name: dense_17
      trainable: true
      units: 64
      use_bias: true
  - class_name: Dense
    config:
      activation: softmax
      activity_regularizer: null
      bias_constraint: null
      bias_initializer:
        class_name: Zeros
        config: {}
      bias_regularizer: null
      dtype: float32
      kernel_constraint: null
      kernel_initializer:
        class_name: GlorotUniform
        config: {seed: null}
      kernel_regularizer: null
      name: dense_18
      trainable: true
      units: 10
      use_bias: true
  name: sequential_3
keras_version: 2.2.4-tf

YAML からモデルを再作成します :

fresh_model = tf.keras.models.model_from_yaml(yaml_string)

Caution: サブクラス化されたモデルはシリアライズ可能ではありません、何故ならばそれらのアーキテクチャは call メソッドの本体で Python コードで定義されているからです。

 

モデル全体

モデル全体は重み値、モデルの configuration そして optimizer の configuration さえも含むファイルにセーブできます。これはモデルをチェックポイントして後で元のコードにアクセスすることなく — 正確に同じ状態から — 訓練を再開することを可能にします

# Create a simple model
model = tf.keras.Sequential([
  layers.Dense(10, activation='softmax', input_shape=(32,)),
  layers.Dense(10, activation='softmax')
])
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(data, labels, batch_size=32, epochs=5)


# Save entire model to a HDF5 file
model.save('my_model.h5')

# Recreate the exact same model, including weights and optimizer.
model = tf.keras.models.load_model('my_model.h5')
Epoch 1/5
1000/1000 [==============================] - 0s 139us/sample - loss: 11.5094 - accuracy: 0.1040
Epoch 2/5
1000/1000 [==============================] - 0s 58us/sample - loss: 11.5235 - accuracy: 0.1120
Epoch 3/5
1000/1000 [==============================] - 0s 70us/sample - loss: 11.5457 - accuracy: 0.1010
Epoch 4/5
1000/1000 [==============================] - 0s 73us/sample - loss: 11.5808 - accuracy: 0.1120
Epoch 5/5
1000/1000 [==============================] - 0s 51us/sample - loss: 11.6115 - accuracy: 0.1130

Keras モデルのためのセーブとシリアライゼーションについて Guide to saving and Serializing Models で更に学習してください。

 

Eager execution

Eager execution は演算を直ちに評価する命令型プログラミング環境です。これは Keras に必要ではありませんが、tf.keras によりサポートされます、そして貴方のプログラムを調べてデバッグするために有用です。

総ての tf.keras モデル構築 API は eager execution と互換です。そして Sequential と functional APIs が使用できる一方で、eager execution は特にモデルのサブクラス化とカスタム層の構築に役立ちます — API は貴方に forward パスを書くことを要求します (既存の層を集めてモデルを作成する API の代わりに)。

カスタム訓練ループと tf.GradientTape を伴う Keras モデルを使用するサンプルについては eager execution ガイド を見てください。貴方はまた ここ で完全な、短いサンプルを見つけられます。

 

分散

マルチ GPU

tf.keras モデルは tf.distribute.Strategy を使用してマルチ GPU 上で実行できます。この API は既存のコードに殆ど変更なしでマルチ GPU 上の分散訓練を提供します。

現在は、tf.distribute.MirroredStrategy が唯一のサポートされる分散ストラテジーです。MirroredStrategy は単一マシン上で all-reduce を使用して同期訓練を伴う in-graph リプリケーションを行ないます。distribute.Strategys を使用するには、Strategy の .scope() で optimizer インスタンス化とモデル構築とコンパイルをネストして、それからモデルを訓練します。

次のサンプルは単一マシン上のマルチ GPU に渡り tf.keras.Model を分散します。

最初に、分散ストラテジー・スコープの内側でモデルを定義します :

strategy = tf.distribute.MirroredStrategy()

with strategy.scope():
  model = tf.keras.Sequential()
  model.add(layers.Dense(16, activation='relu', input_shape=(10,)))
  model.add(layers.Dense(1, activation='sigmoid'))

  optimizer = tf.keras.optimizers.SGD(0.2)

  model.compile(loss='binary_crossentropy', optimizer=optimizer)

model.summary()
W0307 18:13:28.604741 139743906965248 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.

Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_21 (Dense)             (None, 16)                176       
_________________________________________________________________
dense_22 (Dense)             (None, 1)                 17        
=================================================================
Total params: 193
Trainable params: 193
Non-trainable params: 0
_________________________________________________________________

Next, train the model on data as usual:

x = np.random.random((1024, 10))
y = np.random.randint(2, size=(1024, 1))
x = tf.cast(x, tf.float32)
dataset = tf.data.Dataset.from_tensor_slices((x, y))
dataset = dataset.shuffle(buffer_size=1024).batch(32)

model.fit(dataset, epochs=1)
32/32 [==============================] - 0s 5ms/step - loss: 0.7040


より多くの情報については、full guide on Distributed Training in TensorFlow を見てください。

 

以上



AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com