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

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

TensorFlow 2.0 Beta : ガイド : Keras : カスタム callback

TensorFlow 2.0 Beta : ガイド : Keras : カスタム callback (翻訳/解説)

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

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

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

 

ガイド : Keras : カスタム callback

カスタム callback は Keras モデルを読み/変更することを含む、訓練、評価や推論の間の Keras モデルの挙動をカスタマイズするためのパワフルなツールです。サンプルは訓練進捗と結果が TensorBoard でエクスポートされて可視化できる tf.keras.callbacks.TensorBoard や、モデルが訓練の間に自動的にセーブされる tf.keras.callbacks.ModelCheckpoint 等を含みます。このガイドでは、Keras callback が何か、それはいつ呼び出されるか、それは何ができるか、そして貴方自身のものをどのように構築できるかを学習します。ガイドの終わりに向けて、貴方のカスタム callback を始めるための 2, 3 の単純な callback アプリケーションを作成するデモがあります。

 

Setup

from __future__ import absolute_import, division, print_function, unicode_literals

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

 

Keras callback へのイントロダクション

Keras では、callback は特定の機能を提供するためにサブクラス化されることを意図した python クラスで、(batch/epoch start と end を含む) 訓練、テストと予測の様々な段階で呼び出されるメソッドのセットを持ちます。callback は訓練の間モデルの内部状態と統計上のビューを得るために有用です。tf.keras.Model.fit(), tf.keras.Model.evaluate() と tf.keras.Model.predict() メソッドのいずれかに (キーワード引数 callbacks として) callback のリストを渡すことができます。そして callback のメソッドが訓練/評価/推論の異なるステージで呼び出されます。

始めるために、tensorflow をインポートして単純な Sequential Keras モデルを定義しましょう :

# Define the Keras model to add callbacks to
def get_model():
  model = tf.keras.Sequential()
  model.add(tf.keras.layers.Dense(1, activation = 'linear', input_dim = 784))
  model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=0.1), loss='mean_squared_error', metrics=['mae'])
  return model

それから訓練とテストのために Keras datasets API から MNIST データをロードします :

# Load example MNIST data and pre-process it
(x_train, y_train), (x_test, y_test) = tf.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

今、データの総てのバッチの開始と終わりを追跡するために単純なカスタム callback を定義します。これらの呼び出しの間、それは現在のバッチのインデックスをプリントします。

import datetime

class MyCustomCallback(tf.keras.callbacks.Callback):

  def on_train_batch_begin(self, batch, logs=None):
    print('Training: batch {} begins at {}'.format(batch, datetime.datetime.now().time()))

  def on_train_batch_end(self, batch, logs=None):
    print('Training: batch {} ends at {}'.format(batch, datetime.datetime.now().time()))

  def on_test_batch_begin(self, batch, logs=None):
    print('Evaluating: batch {} begins at {}'.format(batch, datetime.datetime.now().time()))

  def on_test_batch_end(self, batch, logs=None):
    print('Evaluating: batch {} ends at {}'.format(batch, datetime.datetime.now().time()))

tf.keras.Model.fit() のような model メソッドに callback を提供することはメソッドがそれらのステージで呼び出されることを保証します :

model = get_model()
_ = model.fit(x_train, y_train,
          batch_size=64,
          epochs=1,
          steps_per_epoch=5,
          verbose=0,
          callbacks=[MyCustomCallback()])
Training: batch 0 begins at 15:12:18.987617
Training: batch 0 ends at 15:12:19.379915
Training: batch 1 begins at 15:12:19.380453
Training: batch 1 ends at 15:12:19.660801
Training: batch 2 begins at 15:12:19.661332
Training: batch 2 ends at 15:12:19.943575
Training: batch 3 begins at 15:12:19.944135
Training: batch 3 ends at 15:12:20.226410
Training: batch 4 begins at 15:12:20.226943
Training: batch 4 ends at 15:12:20.480595

 

callback を取る Model メソッド

ユーザは次のメソッド tf.keras.Model に callback のリストを供給できます :

fit(), fit_generator()

固定数のエポックのためのモデル訓練する (データセットに渡る iteration、または Python generator によるバッチ毎に yield されたデータ)。

evaluate(), evaluate_generator()

与えられたデータかデータ generator のためのモデルを評価します。評価から損失とメトリック値を出力します。

predict(), predict_generator()

入力データやデータ generator のための出力予測を生成します。

_ = model.evaluate(x_test, y_test, batch_size=128, verbose=0, steps=5,
          callbacks=[MyCustomCallback()])
Evaluating: batch 0 begins at 15:12:20.534213
Evaluating: batch 0 ends at 15:12:20.579315
Evaluating: batch 1 begins at 15:12:20.579683
Evaluating: batch 1 ends at 15:12:20.609873
Evaluating: batch 2 begins at 15:12:20.610312
Evaluating: batch 2 ends at 15:12:20.624019
Evaluating: batch 3 begins at 15:12:20.624337
Evaluating: batch 3 ends at 15:12:20.637601
Evaluating: batch 4 begins at 15:12:20.637857
Evaluating: batch 4 ends at 15:12:20.650800

 

callback メソッドの概要

訓練/テスト/予測のための一般的なメソッド

訓練、テストと予測のために、次のメソッドが override されるために提供されます。

on_(train|test|predict)_begin(self, logs=None)

fit/evaluate/predict の最初に呼び出されます。

on_(train|test|predict)_end(self, logs=None)

fit/evaluate/predict の最後に呼び出されます。

on_(train|test|predict)_batch_begin(self, batch, logs=None)

訓練/テスト/予測の間にバッチを処理する直前に呼び出されます。このメソッド内で、logs は batch と size を利用可能なキーとする辞書で、現在のバッチ数とバッチサイズを表わします。

on_(train|test|predict)_batch_end(self, batch, logs=None)

バッチの訓練/テスト/予測の終わりに呼び出されます。このメソッド内で、logs はステートフルなメトリクス結果を含む辞書です。

 

訓練用の特定のメソッド

加えて、訓練のために、次が提供されます。

on_epoch_begin(self, epoch, logs=None)

訓練の間にエポックの最初に呼び出されます。

on_epoch_end(self, epoch, logs=None)

訓練の間にエポックの最後に呼びされます。

 

logs 辞書の使用方法

logs 辞書はバッチかエポックの最後の loss 値と総てのメトリックを含みます。サンプルは loss と mean absolute error を含みます。

class LossAndErrorPrintingCallback(tf.keras.callbacks.Callback):

  def on_train_batch_end(self, batch, logs=None):
    print('For batch {}, loss is {:7.2f}.'.format(batch, logs['loss']))

  def on_test_batch_end(self, batch, logs=None):
    print('For batch {}, loss is {:7.2f}.'.format(batch, logs['loss']))

  def on_epoch_end(self, epoch, logs=None):
    print('The average loss for epoch {} is {:7.2f} and mean absolute error is {:7.2f}.'.format(epoch, logs['loss'], logs['mae']))

model = get_model()
_ = model.fit(x_train, y_train,
          batch_size=64,
          steps_per_epoch=5,
          epochs=3,
          verbose=0,
          callbacks=[LossAndErrorPrintingCallback()])
For batch 0, loss is   27.82.
For batch 1, loss is  924.20.
For batch 2, loss is   25.03.
For batch 3, loss is    8.72.
For batch 4, loss is    7.15.
The average loss for epoch 0 is  198.58 and mean absolute error is    8.32.
For batch 0, loss is    6.34.
For batch 1, loss is    5.79.
For batch 2, loss is    5.41.
For batch 3, loss is    5.14.
For batch 4, loss is    4.95.
The average loss for epoch 1 is    5.52 and mean absolute error is    1.94.
For batch 0, loss is    4.80.
For batch 1, loss is    4.69.
For batch 2, loss is    4.61.
For batch 3, loss is    4.53.
For batch 4, loss is    4.46.
The average loss for epoch 2 is    4.62 and mean absolute error is    1.74.

同様に、evaluate() 呼び出しで callback を提供できます。

_ = model.evaluate(x_test, y_test, batch_size=128, verbose=0, steps=20,
          callbacks=[LossAndErrorPrintingCallback()])
For batch 0, loss is    4.16.
For batch 1, loss is    4.16.
For batch 2, loss is    4.16.
For batch 3, loss is    4.16.
For batch 4, loss is    4.16.
For batch 5, loss is    4.16.
For batch 6, loss is    4.16.
For batch 7, loss is    4.16.
For batch 8, loss is    4.16.
For batch 9, loss is    4.16.
For batch 10, loss is    4.16.
For batch 11, loss is    4.16.
For batch 12, loss is    4.16.
For batch 13, loss is    4.16.
For batch 14, loss is    4.16.
For batch 15, loss is    4.16.
For batch 16, loss is    4.16.
For batch 17, loss is    4.16.
For batch 18, loss is    4.16.
For batch 19, loss is    4.16.

 

Keras callback アプリケーションのサンプル

次のセクションは貴方に単純な Callback アプリケーションを作成する案内をします。

 

最小損失における Early stopping

最初の例は Callback の作成を紹介します、これは損失の最小に達したとき属性 model.stop_training (boolean) を変えることにより Keras 訓練を停止します。オプションで、訓練がそれが最終的に停止する前に幾つのエポックを待つべきかを指定するための引数 patience をユーザは提供できます。

tf.keras.callbacks.EarlyStopping はより複雑で一般的な実装を提供します。

import numpy as np

class EarlyStoppingAtMinLoss(tf.keras.callbacks.Callback):
  """Stop training when the loss is at its min, i.e. the loss stops decreasing.

  Arguments:
      patience: Number of epochs to wait after min has been hit. After this
      number of no improvement, training stops.
  """

  def __init__(self, patience=0):
    super(EarlyStoppingAtMinLoss, self).__init__()

    self.patience = patience

    # best_weights to store the weights at which the minimum loss occurs.
    self.best_weights = None

  def on_train_begin(self, logs=None):
    # The number of epoch it has waited when loss is no longer minimum.
    self.wait = 0
    # The epoch the training stops at.
    self.stopped_epoch = 0
    # Initialize the best as infinity.
    self.best = np.Inf

  def on_epoch_end(self, epoch, logs=None):
    current = logs.get('loss')
    if np.less(current, self.best):
      self.best = current
      self.wait = 0
      # Record the best weights if current results is better (less).
      self.best_weights = self.model.get_weights()
    else:
      self.wait += 1
      if self.wait >= self.patience:
        self.stopped_epoch = epoch
        self.model.stop_training = True
        print('Restoring model weights from the end of the best epoch.')
        self.model.set_weights(self.best_weights)

  def on_train_end(self, logs=None):
    if self.stopped_epoch > 0:
      print('Epoch %05d: early stopping' % (self.stopped_epoch + 1))
model = get_model()
_ = model.fit(x_train, y_train,
          batch_size=64,
          steps_per_epoch=5,
          epochs=30,
          verbose=0,
          callbacks=[LossAndErrorPrintingCallback(), EarlyStoppingAtMinLoss()])
For batch 0, loss is   27.10.
For batch 1, loss is  923.16.
For batch 2, loss is   24.37.
For batch 3, loss is    8.33.
For batch 4, loss is    6.95.
The average loss for epoch 0 is  197.98 and mean absolute error is    8.29.
For batch 0, loss is    6.27.
For batch 1, loss is    5.80.
For batch 2, loss is    5.47.
For batch 3, loss is    5.23.
For batch 4, loss is    5.05.
The average loss for epoch 1 is    5.56 and mean absolute error is    1.96.
For batch 0, loss is    4.91.
For batch 1, loss is    4.79.
For batch 2, loss is    4.70.
For batch 3, loss is    4.62.
For batch 4, loss is    4.55.
The average loss for epoch 2 is    4.71 and mean absolute error is    1.77.
For batch 0, loss is    4.48.
For batch 1, loss is    4.42.
For batch 2, loss is    4.36.
For batch 3, loss is    4.31.
For batch 4, loss is    4.26.
The average loss for epoch 3 is    4.36 and mean absolute error is    1.68.
For batch 0, loss is    4.21.
For batch 1, loss is    4.17.
For batch 2, loss is    4.13.
For batch 3, loss is    4.13.
For batch 4, loss is    4.35.
The average loss for epoch 4 is    4.20 and mean absolute error is    1.63.
For batch 0, loss is    6.24.
For batch 1, loss is   22.54.
For batch 2, loss is  133.97.
For batch 3, loss is  249.51.
For batch 4, loss is  108.04.
The average loss for epoch 5 is  104.06 and mean absolute error is    8.31.
Restoring model weights from the end of the best epoch.
Epoch 00006: early stopping

 

学習率スケジューリング

モデル訓練で一般に成される一つのことはより多くのエポックが過ぎるにつれて学習率を変更することです。Keras バックエンドは get_value api を公開しています、これは値を設定するために使用できます。この例では、カスタム Callback が学習率を動的に変更するためにどのように使用できるかを示しています。

tf.keras.callbacks.LearningRateScheduler はより一般的な実装を提供します。

class LearningRateScheduler(tf.keras.callbacks.Callback):
  """Learning rate scheduler which sets the learning rate according to schedule.

  Arguments:
      schedule: a function that takes an epoch index
          (integer, indexed from 0) and current learning rate
          as inputs and returns a new learning rate as output (float).
  """

  def __init__(self, schedule):
    super(LearningRateScheduler, self).__init__()
    self.schedule = schedule

  def on_epoch_begin(self, epoch, logs=None):
    if not hasattr(self.model.optimizer, 'lr'):
      raise ValueError('Optimizer must have a "lr" attribute.')
    # Get the current learning rate from model's optimizer.
    lr = float(tf.keras.backend.get_value(self.model.optimizer.lr))
    # Call schedule function to get the scheduled learning rate.
    scheduled_lr = self.schedule(epoch, lr)
    # Set the value back to the optimizer before this epoch starts
    tf.keras.backend.set_value(self.model.optimizer.lr, scheduled_lr)
    print('\nEpoch %05d: Learning rate is %6.4f.' % (epoch, scheduled_lr))
LR_SCHEDULE = [
    # (epoch to start, learning rate) tuples
    (3, 0.05), (6, 0.01), (9, 0.005), (12, 0.001)
]

def lr_schedule(epoch, lr):
  """Helper function to retrieve the scheduled learning rate based on epoch."""
  if epoch < LR_SCHEDULE[0][0] or epoch > LR_SCHEDULE[-1][0]:
    return lr
  for i in range(len(LR_SCHEDULE)):
    if epoch == LR_SCHEDULE[i][0]:
      return LR_SCHEDULE[i][1]
  return lr

model = get_model()
_ = model.fit(x_train, y_train,
          batch_size=64,
          steps_per_epoch=5,
          epochs=15,
          verbose=0,
          callbacks=[LossAndErrorPrintingCallback(), LearningRateScheduler(lr_schedule)])
Epoch 00000: Learning rate is 0.1000.
For batch 0, loss is   30.28.
For batch 1, loss is  905.91.
For batch 2, loss is   26.87.
For batch 3, loss is    8.73.
For batch 4, loss is    7.09.
The average loss for epoch 0 is  195.78 and mean absolute error is    8.35.

Epoch 00001: Learning rate is 0.1000.
For batch 0, loss is    6.30.
For batch 1, loss is    5.76.
For batch 2, loss is    5.39.
For batch 3, loss is    5.13.
For batch 4, loss is    4.93.
The average loss for epoch 1 is    5.50 and mean absolute error is    1.95.

Epoch 00002: Learning rate is 0.1000.
For batch 0, loss is    4.79.
For batch 1, loss is    4.68.
For batch 2, loss is    4.59.
For batch 3, loss is    4.52.
For batch 4, loss is    4.45.
The average loss for epoch 2 is    4.61 and mean absolute error is    1.74.

Epoch 00003: Learning rate is 0.0500.
For batch 0, loss is    4.39.
For batch 1, loss is    4.36.
For batch 2, loss is    4.34.
For batch 3, loss is    4.31.
For batch 4, loss is    4.28.
The average loss for epoch 3 is    4.34 and mean absolute error is    1.67.

Epoch 00004: Learning rate is 0.0500.
For batch 0, loss is    4.26.
For batch 1, loss is    4.23.
For batch 2, loss is    4.21.
For batch 3, loss is    4.18.
For batch 4, loss is    4.16.
The average loss for epoch 4 is    4.21 and mean absolute error is    1.63.

Epoch 00005: Learning rate is 0.0500.
For batch 0, loss is    4.13.
For batch 1, loss is    4.11.
For batch 2, loss is    4.09.
For batch 3, loss is    4.07.
For batch 4, loss is    4.05.
The average loss for epoch 5 is    4.09 and mean absolute error is    1.60.

Epoch 00006: Learning rate is 0.0100.
For batch 0, loss is    4.02.
For batch 1, loss is    4.02.
For batch 2, loss is    4.02.
For batch 3, loss is    4.01.
For batch 4, loss is    4.01.
The average loss for epoch 6 is    4.02 and mean absolute error is    1.58.

Epoch 00007: Learning rate is 0.0100.
For batch 0, loss is    4.00.
For batch 1, loss is    4.00.
For batch 2, loss is    3.99.
For batch 3, loss is    3.99.
For batch 4, loss is    3.98.
The average loss for epoch 7 is    3.99 and mean absolute error is    1.57.

Epoch 00008: Learning rate is 0.0100.
For batch 0, loss is    3.98.
For batch 1, loss is    3.97.
For batch 2, loss is    3.97.
For batch 3, loss is    3.96.
For batch 4, loss is    3.95.
The average loss for epoch 8 is    3.97 and mean absolute error is    1.57.

Epoch 00009: Learning rate is 0.0050.
For batch 0, loss is    3.95.
For batch 1, loss is    3.94.
For batch 2, loss is    3.94.
For batch 3, loss is    3.94.
For batch 4, loss is    3.93.
The average loss for epoch 9 is    3.94 and mean absolute error is    1.56.

Epoch 00010: Learning rate is 0.0050.
For batch 0, loss is    3.93.
For batch 1, loss is    3.93.
For batch 2, loss is    3.92.
For batch 3, loss is    3.92.
For batch 4, loss is    3.91.
The average loss for epoch 10 is    3.92 and mean absolute error is    1.55.

Epoch 00011: Learning rate is 0.0050.
For batch 0, loss is    3.91.
For batch 1, loss is    3.91.
For batch 2, loss is    3.90.
For batch 3, loss is    3.90.
For batch 4, loss is    3.89.
The average loss for epoch 11 is    3.90 and mean absolute error is    1.55.

Epoch 00012: Learning rate is 0.0010.
For batch 0, loss is    3.89.
For batch 1, loss is    3.89.
For batch 2, loss is    3.89.
For batch 3, loss is    3.88.
For batch 4, loss is    3.88.
The average loss for epoch 12 is    3.89 and mean absolute error is    1.54.

Epoch 00013: Learning rate is 0.0010.
For batch 0, loss is    3.88.
For batch 1, loss is    3.88.
For batch 2, loss is    3.88.
For batch 3, loss is    3.88.
For batch 4, loss is    3.88.
The average loss for epoch 13 is    3.88 and mean absolute error is    1.54.

Epoch 00014: Learning rate is 0.0010.
For batch 0, loss is    3.88.
For batch 1, loss is    3.87.
For batch 2, loss is    3.87.
For batch 3, loss is    3.87.
For batch 4, loss is    3.87.
The average loss for epoch 14 is    3.87 and mean absolute error is    1.54.

 

標準的な Keras callback

api doc を訪ねる ことにより既存の Keras callback を確実に調べてください。アプリケーションは CSV へのロギング、モデルのセーブ、TensorBoard 上の可視化等々を含みます。

 

以上



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

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

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

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

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

 

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

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

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

 

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-beta1
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)
WARNING: Logging before flag parsing goes to stderr.
W0614 15:22:16.427572 140456761870080 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 60000 samples
60000/60000 [==============================] - 3s 48us/sample - loss: 0.3014
# 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.
W0614 15:22:21.755359 140456761870080 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-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.
W0614 15:22:21.757266 140456761870080 export_utils.py:182] Export includes no default signature!
W0614 15:22:22.025224 140456761870080 export_utils.py:182] 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.
W0614 15:22:23.880016 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.decay
W0614 15:22:23.881534 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.learning_rate
W0614 15:22:23.882155 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.momentum
W0614 15:22:23.882841 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.rho
W0614 15:22:23.883504 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-0.kernel
W0614 15:22:23.884812 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-0.bias
W0614 15:22:23.885461 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-1.kernel
W0614 15:22:23.886249 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-1.bias
W0614 15:22:23.887130 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-2.kernel
W0614 15:22:23.888468 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-2.bias
W0614 15:22:23.889342 140456761870080 util.py:252] A checkpoint was restored (e.g. tf.train.Checkpoint.restore or tf.keras.Model.load_weights) but not all checkpointed values were used. See above for specific issues. Use expect_partial() on the load status object, e.g. tf.train.Checkpoint.restore(...).expect_partial(), to silence these warnings, or use assert_consumed() to make the check explicit. See https://www.tensorflow.org/alpha/guide/checkpoints#loading_mechanics for details.
W0614 15:22:23.893026 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer
W0614 15:22:23.893844 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.iter
W0614 15:22:23.894460 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.decay
W0614 15:22:23.895506 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.learning_rate
W0614 15:22:23.896186 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.momentum
W0614 15:22:23.897153 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.rho
W0614 15:22:23.897989 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-0.kernel
W0614 15:22:23.898835 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-0.bias
W0614 15:22:23.899448 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-1.kernel
W0614 15:22:23.900100 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-1.bias
W0614 15:22:23.900975 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-2.kernel
W0614 15:22:23.902399 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-2.bias
W0614 15:22:23.902988 140456761870080 util.py:252] A checkpoint was restored (e.g. tf.train.Checkpoint.restore or tf.keras.Model.load_weights) but not all checkpointed values were used. See above for specific issues. Use expect_partial() on the load status object, e.g. tf.train.Checkpoint.restore(...).expect_partial(), to silence these warnings, or use assert_consumed() to make the check explicit. See https://www.tensorflow.org/alpha/guide/checkpoints#loading_mechanics for details.
W0614 15:22:23.904124 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer
W0614 15:22:23.904798 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.iter
W0614 15:22:23.905418 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.decay
W0614 15:22:23.906876 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.learning_rate
W0614 15:22:23.907450 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.momentum
W0614 15:22:23.908407 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer.rho
W0614 15:22:23.909109 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-0.kernel
W0614 15:22:23.909620 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-0.bias
W0614 15:22:23.910654 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-1.kernel
W0614 15:22:23.911258 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-1.bias
W0614 15:22:23.911958 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-2.kernel
W0614 15:22:23.912729 140456761870080 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'rms' for (root).layer_with_weights-2.bias
W0614 15:22:23.914015 140456761870080 util.py:252] A checkpoint was restored (e.g. tf.train.Checkpoint.restore or tf.keras.Model.load_weights) but not all checkpointed values were used. See above for specific issues. Use expect_partial() on the load status object, e.g. tf.train.Checkpoint.restore(...).expect_partial(), to silence these warnings, or use assert_consumed() to make the check explicit. See https://www.tensorflow.org/alpha/guide/checkpoints#loading_mechanics for details.

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

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)
Train on 60000 samples
60000/60000 [==============================] - 3s 46us/sample - loss: 0.3150

サブクラス化されたモデルをセーブする推奨される方法は 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 Beta : ガイド : Keras : TensorFlow Keras で層とモデルを書く

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

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

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

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

 

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

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-beta1
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.00915685  0.12415549 -0.07327418 -0.1369698 ]
 [-0.00915685  0.12415549 -0.07327418 -0.1369698 ]], 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.04197835 -0.08802906  0.08915389 -0.02476374]
 [-0.04197835 -0.08802906  0.08915389 -0.02476374]], 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='zeros',
                               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=251, shape=(), dtype=float32, numpy=0.0017113965>]

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

# 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_weights)
  optimizer.apply_gradients(zip(grads, model.trainable_weights))

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

 

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

貴方のカスタム層が 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__ メソッドは幾つかのキーワード引数、特にnameと 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, 'name': 'linear_8', 'units': 64, 'dtype': None}

層をその 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):
    if training:
        return tf.nn.dropout(inputs, rate=self.rate)
    return 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_mean(
        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_weights)
    optimizer.apply_gradients(zip(grads, vae.trainable_weights))

    loss_metric(loss)

    if step % 100 == 0:
      print('step %s: mean loss = %s' % (step, loss_metric.result()))
Start of epoch 0
step 0: mean loss = tf.Tensor(0.30561835, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(0.12514462, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(0.098986775, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(0.08902271, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(0.08415081, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(0.08079431, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.07869264, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.077106655, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.075943485, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.07491604, shape=(), dtype=float32)
Start of epoch 1
step 0: mean loss = tf.Tensor(0.074626915, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(0.07397244, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(0.073475204, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(0.07300446, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(0.07268, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(0.07227383, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.07198101, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.07169821, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.07145905, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.07119416, shape=(), dtype=float32)
Start of epoch 2
step 0: mean loss = tf.Tensor(0.071120486, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(0.070949756, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(0.070814244, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(0.07066523, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(0.07057433, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(0.07041376, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.070303105, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.070184246, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.07008442, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.0699536, 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)
Train on 60000 samples
Epoch 1/3
60000/60000 [==============================] - 3s 53us/sample - loss: 0.0746
Epoch 2/3
60000/60000 [==============================] - 3s 48us/sample - loss: 0.0676
Epoch 3/3
60000/60000 [==============================] - 3s 48us/sample - loss: 0.0675

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

 

オブジェクト指向開発を越えて : 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_mean(
    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)
Train on 60000 samples
Epoch 1/3
60000/60000 [==============================] - 4s 63us/sample - loss: 0.0746
Epoch 2/3
60000/60000 [==============================] - 3s 58us/sample - loss: 0.0676
Epoch 3/3
60000/60000 [==============================] - 3s 58us/sample - loss: 0.0676

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

以上



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

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

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

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

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、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 で構築されたモデルそしてモデル・サブクラス化でスクラッチから書かれたモデル — に渡り厳密に同じように動作します。

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

 

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-beta0
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)
WARNING: Logging before flag parsing goes to stderr.
W0611 18:50:37.143470 139764873668352 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

# Fit model on training data
Train on 50000 samples, validate on 10000 samples
Epoch 1/3
50000/50000 [==============================] - 3s 59us/sample - loss: 0.3343 - sparse_categorical_accuracy: 0.9061 - val_loss: 0.1900 - val_sparse_categorical_accuracy: 0.9457
Epoch 2/3
50000/50000 [==============================] - 2s 50us/sample - loss: 0.1577 - sparse_categorical_accuracy: 0.9539 - val_loss: 0.1314 - val_sparse_categorical_accuracy: 0.9629
Epoch 3/3
50000/50000 [==============================] - 2s 50us/sample - loss: 0.1152 - sparse_categorical_accuracy: 0.9654 - val_loss: 0.1163 - val_sparse_categorical_accuracy: 0.9650

history dict: {'loss': [0.3343376000738144, 0.1576878201198578, 0.11524733116716146], 'val_loss': [0.18996894600391387, 0.13136221459209918, 0.11630819611549377], 'sparse_categorical_accuracy': [0.90608, 0.9539, 0.96542], 'val_sparse_categorical_accuracy': [0.9457, 0.9629, 0.965]}

# Evaluate on test data
10000/10000 [==============================] - 0s 17us/sample - loss: 0.1126 - sparse_categorical_accuracy: 0.9660
test loss, test acc: [0.11255755196213722, 0.966]

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

 

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

fit でモデルを訓練するためには、損失関数、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='categorical_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)
      self.true_positives.assign_add(tf.reduce_sum(values))

    def result(self):
      return self.true_positives

    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)
Train on 50000 samples
Epoch 1/3
50000/50000 [==============================] - 3s 51us/sample - loss: 0.0930 - categorical_true_positives: 7860.0000
Epoch 2/3
50000/50000 [==============================] - 2s 47us/sample - loss: 0.0775 - categorical_true_positives: 7764.0000
Epoch 3/3
50000/50000 [==============================] - 2s 48us/sample - loss: 0.0666 - categorical_true_positives: 8242.0000

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

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

損失とメトリクスの圧倒的な多数は 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)
Train on 50000 samples
50000/50000 [==============================] - 2s 40us/sample - loss: 2.4795

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

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)
Train on 50000 samples
50000/50000 [==============================] - 2s 43us/sample - loss: 0.3561 - std_of_activation: 0.9541

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

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)
Train on 50000 samples
50000/50000 [==============================] - 3s 53us/sample - loss: 2.4790 - std_of_activation: 0.0018

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

 
自動的に検証取り置き (= 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 [==============================] - 2s 52us/sample - loss: 0.3724 - sparse_categorical_accuracy: 0.8943 - val_loss: 0.2273 - val_sparse_categorical_accuracy: 0.9327
Epoch 2/3
40000/40000 [==============================] - 2s 50us/sample - loss: 0.1716 - sparse_categorical_accuracy: 0.9486 - val_loss: 0.1749 - val_sparse_categorical_accuracy: 0.9457
Epoch 3/3
40000/40000 [==============================] - 2s 50us/sample - loss: 0.1237 - sparse_categorical_accuracy: 0.9633 - val_loss: 0.1500 - val_sparse_categorical_accuracy: 0.9556

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

 

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 [==============================] - 4s 5ms/step - loss: 0.3400 - sparse_categorical_accuracy: 0.9023
Epoch 2/3
782/782 [==============================] - 3s 4ms/step - loss: 0.1644 - sparse_categorical_accuracy: 0.9515
Epoch 3/3
782/782 [==============================] - 3s 4ms/step - loss: 0.1206 - sparse_categorical_accuracy: 0.9645

# Evaluate
157/157 [==============================] - 1s 4ms/step - loss: 0.1214 - sparse_categorical_accuracy: 0.9633

[0.12137069313410243, 0.9633]

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.take(100), epochs=3)
Epoch 1/3
100/100 [==============================] - 1s 9ms/step - loss: 0.8020 - sparse_categorical_accuracy: 0.7997
Epoch 2/3
100/100 [==============================] - 0s 4ms/step - loss: 0.3257 - sparse_categorical_accuracy: 0.9117
Epoch 3/3
100/100 [==============================] - 0s 4ms/step - loss: 0.2444 - sparse_categorical_accuracy: 0.9320

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

 
検証 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 [==============================] - 4s 6ms/step - loss: 0.3349 - sparse_categorical_accuracy: 0.9059 - val_loss: 0.1803 - val_sparse_categorical_accuracy: 0.9458
Epoch 2/3
782/782 [==============================] - 4s 5ms/step - loss: 0.1626 - sparse_categorical_accuracy: 0.9517 - val_loss: 0.1378 - val_sparse_categorical_accuracy: 0.9588
Epoch 3/3
782/782 [==============================] - 4s 5ms/step - loss: 0.1207 - sparse_categorical_accuracy: 0.9645 - val_loss: 0.1209 - val_sparse_categorical_accuracy: 0.9636

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

各エポックの最後に、モデルは検証 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 [==============================] - 4s 5ms/step - loss: 0.3368 - sparse_categorical_accuracy: 0.9046 - val_loss: 0.3341 - val_sparse_categorical_accuracy: 0.9062
Epoch 2/3
782/782 [==============================] - 3s 4ms/step - loss: 0.1596 - sparse_categorical_accuracy: 0.9518 - val_loss: 0.2450 - val_sparse_categorical_accuracy: 0.9344
Epoch 3/3
782/782 [==============================] - 3s 4ms/step - loss: 0.1163 - sparse_categorical_accuracy: 0.9658 - val_loss: 0.1822 - val_sparse_categorical_accuracy: 0.9516

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

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

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

 

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

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

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

 

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

入力データとターゲットデータの他にも、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)
Train on 50000 samples
Epoch 1/4
50000/50000 [==============================] - 3s 57us/sample - loss: 0.1040 - sparse_categorical_accuracy: 0.9710
Epoch 2/4
50000/50000 [==============================] - 3s 53us/sample - loss: 0.0843 - sparse_categorical_accuracy: 0.9763
Epoch 3/4
50000/50000 [==============================] - 3s 52us/sample - loss: 0.0733 - sparse_categorical_accuracy: 0.9794
Epoch 4/4
50000/50000 [==============================] - 3s 54us/sample - loss: 0.0622 - sparse_categorical_accuracy: 0.9825
Train on 50000 samples
Epoch 1/4
50000/50000 [==============================] - 3s 58us/sample - loss: 0.3711 - sparse_categorical_accuracy: 0.9032
Epoch 2/4
50000/50000 [==============================] - 3s 56us/sample - loss: 0.1727 - sparse_categorical_accuracy: 0.9515
Epoch 3/4
50000/50000 [==============================] - 3s 55us/sample - loss: 0.1274 - sparse_categorical_accuracy: 0.9640
Epoch 4/4
50000/50000 [==============================] - 3s 53us/sample - loss: 0.1022 - sparse_categorical_accuracy: 0.9707

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

ここに適合している 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 [==============================] - 4s 5ms/step - loss: 0.3670 - sparse_categorical_accuracy: 0.9041
Epoch 2/3
782/782 [==============================] - 4s 5ms/step - loss: 0.1702 - sparse_categorical_accuracy: 0.9531
Epoch 3/3
782/782 [==============================] - 4s 5ms/step - loss: 0.1235 - sparse_categorical_accuracy: 0.9655

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

 

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

前の例では、単一の入力 (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()})
W0614 15:24:50.742312 140118631720704 training_utils.py:1237] 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)
Train on 100 samples
Epoch 1/3
100/100 [==============================] - 2s 19ms/sample - loss: 5.8944 - score_output_loss: 0.4273 - class_output_loss: 5.2204
Epoch 2/3
100/100 [==============================] - 0s 220us/sample - loss: 5.4873 - score_output_loss: 0.2532 - class_output_loss: 5.3883
Epoch 3/3
100/100 [==============================] - 0s 218us/sample - loss: 5.2755 - score_output_loss: 0.1834 - class_output_loss: 4.9630
Train on 100 samples
Epoch 1/3
100/100 [==============================] - 0s 223us/sample - loss: 5.1277 - score_output_loss: 0.1491 - class_output_loss: 4.7050
Epoch 2/3
100/100 [==============================] - 0s 202us/sample - loss: 5.0121 - score_output_loss: 0.1738 - class_output_loss: 4.9349
Epoch 3/3
100/100 [==============================] - 0s 185us/sample - loss: 4.8906 - score_output_loss: 0.1341 - class_output_loss: 4.5652

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

ここに 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 158ms/step - loss: 4.8489 - score_output_loss: 0.1605 - class_output_loss: 4.7043
Epoch 2/3
2/2 [==============================] - 0s 8ms/step - loss: 4.7991 - score_output_loss: 0.1543 - class_output_loss: 4.6607
Epoch 3/3
2/2 [==============================] - 0s 8ms/step - loss: 4.7527 - score_output_loss: 0.1506 - class_output_loss: 4.6180

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

 

コールバックを使用する

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 [==============================] - 2s 58us/sample - loss: 0.3607 - sparse_categorical_accuracy: 0.8988 - val_loss: 0.2391 - val_sparse_categorical_accuracy: 0.9283
Epoch 2/20
40000/40000 [==============================] - 2s 54us/sample - loss: 0.1734 - sparse_categorical_accuracy: 0.9487 - val_loss: 0.1696 - val_sparse_categorical_accuracy: 0.9481
Epoch 3/20
40000/40000 [==============================] - 2s 52us/sample - loss: 0.1302 - sparse_categorical_accuracy: 0.9609 - val_loss: 0.1531 - val_sparse_categorical_accuracy: 0.9543
Epoch 4/20
40000/40000 [==============================] - 2s 52us/sample - loss: 0.1032 - sparse_categorical_accuracy: 0.9690 - val_loss: 0.1516 - val_sparse_categorical_accuracy: 0.9530
Epoch 5/20
40000/40000 [==============================] - 2s 55us/sample - loss: 0.0856 - sparse_categorical_accuracy: 0.9743 - val_loss: 0.1376 - val_sparse_categorical_accuracy: 0.9587
Epoch 6/20
40000/40000 [==============================] - 2s 57us/sample - loss: 0.0728 - sparse_categorical_accuracy: 0.9778 - val_loss: 0.1300 - val_sparse_categorical_accuracy: 0.9608
Epoch 7/20
40000/40000 [==============================] - 2s 57us/sample - loss: 0.0619 - sparse_categorical_accuracy: 0.9811 - val_loss: 0.1254 - val_sparse_categorical_accuracy: 0.9622
Epoch 8/20
40000/40000 [==============================] - 2s 56us/sample - loss: 0.0532 - sparse_categorical_accuracy: 0.9833 - val_loss: 0.1409 - val_sparse_categorical_accuracy: 0.9611
Epoch 9/20
40000/40000 [==============================] - 2s 56us/sample - loss: 0.0465 - sparse_categorical_accuracy: 0.9856 - val_loss: 0.1430 - val_sparse_categorical_accuracy: 0.9613
Epoch 00009: early stopping

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

 
多くの組み込み 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
39296/40000 [============================>.] - ETA: 0s - loss: 0.3841 - sparse_categorical_accuracy: 0.8929
Epoch 00001: val_loss improved from inf to 0.24046, saving model to mymodel_1.h5
40000/40000 [==============================] - 2s 62us/sample - loss: 0.3820 - sparse_categorical_accuracy: 0.8936 - val_loss: 0.2405 - val_sparse_categorical_accuracy: 0.9275
Epoch 2/3
39168/40000 [============================>.] - ETA: 0s - loss: 0.1763 - sparse_categorical_accuracy: 0.9483
Epoch 00002: val_loss improved from 0.24046 to 0.18944, saving model to mymodel_2.h5
40000/40000 [==============================] - 2s 54us/sample - loss: 0.1757 - sparse_categorical_accuracy: 0.9485 - val_loss: 0.1894 - val_sparse_categorical_accuracy: 0.9416
Epoch 3/3
39168/40000 [============================>.] - ETA: 0s - loss: 0.1276 - sparse_categorical_accuracy: 0.9628
Epoch 00003: val_loss improved from 0.18944 to 0.16058, saving model to mymodel_3.h5
40000/40000 [==============================] - 2s 56us/sample - loss: 0.1279 - sparse_categorical_accuracy: 0.9626 - val_loss: 0.1606 - val_sparse_categorical_accuracy: 0.9525

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

モデルをセーブしてリストアするための貴方自身の 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)

 
動的学習率スケジュールを実装するために 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 モデルを再利用しましょう、そしてそれをカスタム訓練ループでミニバッチ勾配を使用して訓練しましょう。

# Get the 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.
optimizer = keras.optimizers.SGD(learning_rate=1e-3)
# Instantiate a loss function.
loss_fn = keras.losses.SparseCategoricalCrossentropy()

# 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)

# 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):

    # Open a GradientTape to record the operations run
    # during the forward pass, which enables autodifferentiation.
    with tf.GradientTape() as tape:

      # Run the forward pass of the layer.
      # The operations that the layer applies
      # to its inputs are going to be recorded
      # on the GradientTape.
      logits = model(x_batch_train)  # Logits for this minibatch

      # Compute the loss value for this minibatch.
      loss_value = loss_fn(y_batch_train, logits)

    # Use the gradient tape to automatically retrieve
    # the gradients of the trainable variables with respect to the loss.
    grads = tape.gradient(loss_value, model.trainable_weights)

    # Run one step of gradient descent by updating
    # the value of the variables to minimize the loss.
    optimizer.apply_gradients(zip(grads, model.trainable_weights))

    # 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: 2.3184189796447754
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.3199896812438965
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.1944358348846436
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.114534378051758
Seen so far: 38464 samples
Start of epoch 1
Training loss (for one batch) at step 0: 2.0546462535858154
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.9779424667358398
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.8572919368743896
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.7629480361938477
Seen so far: 38464 samples
Start of epoch 2
Training loss (for one batch) at step 0: 1.7149815559387207
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.5529119968414307
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.44472336769104
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.3457978963851929
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_weights)
    optimizer.apply_gradients(zip(grads, model.trainable_weights))

    # 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.274200439453125
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.1641573905944824
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.9997265338897705
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.912804365158081
Seen so far: 38464 samples
Training acc over epoch: 0.3887999951839447
Validation acc: 0.5658000111579895
Start of epoch 1
Training loss (for one batch) at step 0: 1.8960016965866089
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.7283663749694824
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.597784161567688
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.488420009613037
Seen so far: 38464 samples
Training acc over epoch: 0.6281200051307678
Validation acc: 0.6930000185966492
Start of epoch 2
Training loss (for one batch) at step 0: 1.4787451028823853
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.3541656732559204
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.2644202709197998
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.1630260944366455
Seen so far: 38464 samples
Training acc over epoch: 0.7215399742126465
Validation acc: 0.7721999883651733

 

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=843639, shape=(), dtype=float32, numpy=7.3508925>]

追跡された損失はまず 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=843700, shape=(), dtype=float32, numpy=7.0926685>]

訓練の間にこれらの損失を考慮するには、貴方が行なわなければならない総てのことは貴方の訓練ループを貴方のトータル損失に 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_weights)
    optimizer.apply_gradients(zip(grads, model.trainable_weights))

    # 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.404867172241211
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.515528678894043
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.382564067840576
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.3394205570220947
Seen so far: 38464 samples
Start of epoch 1
Training loss (for one batch) at step 0: 2.344683885574341
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.3267478942871094
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.3213391304016113
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.316208600997925
Seen so far: 38464 samples
Start of epoch 2
Training loss (for one batch) at step 0: 2.325348138809204
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.3166375160217285
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.3118178844451904
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.3112611770629883
Seen so far: 38464 samples
 

以上



TensorFlow 2.0 Beta : ガイド : Keras :- TensorFlow の Keras Functional API

TensorFlow 2.0 Beta : ガイド : Keras :- TensorFlow の Keras Functional API (翻訳/解説)

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

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

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

 

ガイド : Keras :- TensorFlow の Keras Functional API

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-beta0
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])
WARNING: Logging before flag parsing goes to stderr.
W0611 18:47:13.877890 139669971859200 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 48000 samples, validate on 12000 samples
Epoch 1/5
48000/48000 [==============================] - 3s 61us/sample - loss: 0.3511 - accuracy: 0.9009 - val_loss: 0.2005 - val_accuracy: 0.9433
Epoch 2/5
48000/48000 [==============================] - 2s 49us/sample - loss: 0.1664 - accuracy: 0.9505 - val_loss: 0.1469 - val_accuracy: 0.9576
Epoch 3/5
48000/48000 [==============================] - 2s 49us/sample - loss: 0.1205 - accuracy: 0.9640 - val_loss: 0.1220 - val_accuracy: 0.9647
Epoch 4/5
48000/48000 [==============================] - 2s 49us/sample - loss: 0.0930 - accuracy: 0.9724 - val_loss: 0.1210 - val_accuracy: 0.9629
Epoch 5/5
48000/48000 [==============================] - 2s 50us/sample - loss: 0.0772 - accuracy: 0.9761 - val_loss: 0.1038 - val_accuracy: 0.9703
Test loss: 0.10615633417717181
Test accuracy: 0.9684

モデル訓練と評価についての完全なガイドは、訓練と評価へのガイド を見てください。

 

セービングとシリアライゼーション

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 です

任意のモデルを、それを Input 上あるいは他の層の出力上で呼び出すことによって、それが層であるかのように扱うことができます。モデルを呼び出すことによりモデルのアーキテクチャを単に再利用しているのではなく、その重みを再利用していることに注意してください。

これを実際に見てみましょう。ここに 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)
Train on 1280 samples
Epoch 1/2
1280/1280 [==============================] - 14s 11ms/sample - loss: 1.2579 - priority_loss: 0.6955 - department_loss: 2.8122
Epoch 2/2
1280/1280 [==============================] - 14s 11ms/sample - loss: 1.2002 - priority_loss: 0.6436 - department_loss: 2.7833

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

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 すべきです。

より詳細な説明については、完全なガイド 訓練と評価へのガイド を参照してください。

 

toy 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)
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 11s 0us/step
Train on 40000 samples, validate on 10000 samples
40000/40000 [==============================] - 7s 187us/sample - loss: 1.9213 - acc: 0.2678 - val_loss: 1.6917 - val_acc: 0.3936

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

 

層を共有する

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 [==============================] - 35s 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__ で重みを作成しても良いでしょう)。

スクラッチから層を作成することについて更に学習するためには、ガイド 層とモデルをスクラッチから書くためのガイド を調べてください。

ここに 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 RNN はこの仮定に従いませんそして 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 Beta : ガイド : Keras :- 簡単な概要

TensorFlow 2.0 Beta : ガイド : Keras :- 簡単な概要 (翻訳/解説)

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

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

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

 

ガイド : Keras :- 簡単な概要

tf.keras をインポートする

tf.kerasKeras 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, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-beta0
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 モデルをどのように使用するかの完全な、短いサンプルは ここ で見つけられます。

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 0x7fba63a4a4a8>

 

訓練と評価

訓練のセットアップ

モデルが構築された後、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)
Train on 1000 samples
Epoch 1/10
1000/1000 [==============================] - 1s 573us/sample - loss: 11.5185 - categorical_accuracy: 0.0920
Epoch 2/10
1000/1000 [==============================] - 0s 89us/sample - loss: 11.4708 - categorical_accuracy: 0.1110
Epoch 3/10
1000/1000 [==============================] - 0s 84us/sample - loss: 11.4722 - categorical_accuracy: 0.1090
Epoch 4/10
1000/1000 [==============================] - 0s 85us/sample - loss: 11.4707 - categorical_accuracy: 0.0990
Epoch 5/10
1000/1000 [==============================] - 0s 87us/sample - loss: 11.4699 - categorical_accuracy: 0.1170
Epoch 6/10
1000/1000 [==============================] - 0s 77us/sample - loss: 11.4652 - categorical_accuracy: 0.1130
Epoch 7/10
1000/1000 [==============================] - 0s 84us/sample - loss: 11.4659 - categorical_accuracy: 0.1160
Epoch 8/10
1000/1000 [==============================] - 0s 84us/sample - loss: 11.4599 - categorical_accuracy: 0.1100
Epoch 9/10
1000/1000 [==============================] - 0s 84us/sample - loss: 11.4550 - categorical_accuracy: 0.1250
Epoch 10/10
1000/1000 [==============================] - 0s 85us/sample - loss: 11.4543 - categorical_accuracy: 0.1240

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

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 183us/sample - loss: 11.6683 - categorical_accuracy: 0.1030 - val_loss: 11.6501 - val_categorical_accuracy: 0.0900
Epoch 2/10
1000/1000 [==============================] - 0s 181us/sample - loss: 11.6628 - categorical_accuracy: 0.1070 - val_loss: 11.6470 - val_categorical_accuracy: 0.0800
Epoch 3/10
1000/1000 [==============================] - 0s 91us/sample - loss: 11.6612 - categorical_accuracy: 0.1140 - val_loss: 11.6525 - val_categorical_accuracy: 0.0600
Epoch 4/10
1000/1000 [==============================] - 0s 84us/sample - loss: 11.6591 - categorical_accuracy: 0.1160 - val_loss: 11.6753 - val_categorical_accuracy: 0.0900
Epoch 5/10
1000/1000 [==============================] - 0s 85us/sample - loss: 11.6573 - categorical_accuracy: 0.1250 - val_loss: 11.6517 - val_categorical_accuracy: 0.0700
Epoch 6/10
1000/1000 [==============================] - 0s 89us/sample - loss: 11.6545 - categorical_accuracy: 0.1000 - val_loss: 11.6656 - val_categorical_accuracy: 0.1100
Epoch 7/10
1000/1000 [==============================] - 0s 92us/sample - loss: 11.6509 - categorical_accuracy: 0.1510 - val_loss: 11.6775 - val_categorical_accuracy: 0.0700
Epoch 8/10
1000/1000 [==============================] - 0s 95us/sample - loss: 11.6452 - categorical_accuracy: 0.1350 - val_loss: 11.6693 - val_categorical_accuracy: 0.0800
Epoch 9/10
1000/1000 [==============================] - 0s 95us/sample - loss: 11.6426 - categorical_accuracy: 0.1430 - val_loss: 11.6636 - val_categorical_accuracy: 0.0900
Epoch 10/10
1000/1000 [==============================] - 0s 93us/sample - loss: 11.6357 - categorical_accuracy: 0.1490 - val_loss: 11.7268 - val_categorical_accuracy: 0.1300

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

 

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)
W0608 04:30:06.144505 140440776103680 training_utils.py:1436] Expected a shuffled dataset but input dataset `x` is not shuffled. Please invoke `shuffle()` on input dataset.

Epoch 1/10
30/30 [==============================] - 0s 6ms/step - loss: 11.6491 - categorical_accuracy: 0.1552
Epoch 2/10
 2/30 [=>............................] - ETA: 1s - loss: 11.2221 - categorical_accuracy: 0.1250
W0608 04:30:06.415772 140440776103680 training_generator.py:235] 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 0x7fba500b7748>

ここで、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.evaluatetf.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 70us/sample - loss: 11.5954 - categorical_accuracy: 0.0850
30/30 [==============================] - 0s 2ms/step - loss: 11.6008 - categorical_accuracy: 0.1698

[11.600848929087322, 0.16979167]

そして提供されたデータのために推論で最後の層の出力を予測します、NumPy 配列として :

result = model.predict(data, batch_size=32)
print(result.shape)
(1000, 10)

カスタム訓練ループをスクラッチからどのように書くかを含めて、訓練と評価の完全なガイドについては、訓練 & 評価へのガイド を見てください。

 

上級モデルを構築する

Functional API

tf.keras.Sequential モデルは層の単純なスタックで任意のモデルを表わすことはできません。次のような複雑なモデル・トポロジーを構築するには Keras functional API を使用します :

  • マルチ入力モデル、
  • マルチ出力モデル、
  • 共有層を持つモデル (同じ層が幾度も呼び出されます)、
  • 非-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)

与えられた入力と出力でモデルをインスタンス化します。

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)
Train on 1000 samples
Epoch 1/5
1000/1000 [==============================] - 0s 245us/sample - loss: 11.5521 - accuracy: 0.1080
Epoch 2/5
1000/1000 [==============================] - 0s 85us/sample - loss: 11.5213 - accuracy: 0.1210
Epoch 3/5
1000/1000 [==============================] - 0s 85us/sample - loss: 11.5114 - accuracy: 0.1040
Epoch 4/5
1000/1000 [==============================] - 0s 78us/sample - loss: 11.5055 - accuracy: 0.1220
Epoch 5/5
1000/1000 [==============================] - 0s 81us/sample - loss: 11.4977 - accuracy: 0.1240

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

 

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)
Train on 1000 samples
Epoch 1/5
1000/1000 [==============================] - 0s 191us/sample - loss: 11.5330 - accuracy: 0.0810
Epoch 2/5
1000/1000 [==============================] - 0s 73us/sample - loss: 11.5169 - accuracy: 0.1100
Epoch 3/5
1000/1000 [==============================] - 0s 75us/sample - loss: 11.5133 - accuracy: 0.1080
Epoch 4/5
1000/1000 [==============================] - 0s 72us/sample - loss: 11.5107 - accuracy: 0.1160
Epoch 5/5
1000/1000 [==============================] - 0s 78us/sample - loss: 11.5087 - accuracy: 0.1120

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

 

カスタム層

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)
Train on 1000 samples
Epoch 1/5
1000/1000 [==============================] - 0s 143us/sample - loss: 11.5147 - accuracy: 0.0890
Epoch 2/5
1000/1000 [==============================] - 0s 61us/sample - loss: 11.5111 - accuracy: 0.1110
Epoch 3/5
1000/1000 [==============================] - 0s 59us/sample - loss: 11.5084 - accuracy: 0.1030
Epoch 4/5
1000/1000 [==============================] - 0s 62us/sample - loss: 11.5068 - accuracy: 0.1160
Epoch 5/5
1000/1000 [==============================] - 0s 61us/sample - loss: 11.5048 - accuracy: 0.1130

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

Guide to writing layers and models from scratch でサブクラス化による新しい層とモデルのスクラッチからの作成について更に学習してください。

 

コールバック

コールバックは訓練の間にその挙動をカスタマイズして拡張するためにモデルに渡されるオブジェクトです。貴方自身のカスタム・コールバックを書いたり、以下を含む組み込み tf.keras.callbacks を使用できます :

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))s))
Train on 1000 samples, validate on 100 samples
Epoch 1/5
1000/1000 [==============================] - 0s 147us/sample - loss: 11.5031 - accuracy: 0.1180 - val_loss: 11.6469 - val_accuracy: 0.1200
Epoch 2/5
1000/1000 [==============================] - 0s 72us/sample - loss: 11.5022 - accuracy: 0.1230 - val_loss: 11.6491 - val_accuracy: 0.0600
Epoch 3/5
1000/1000 [==============================] - 0s 70us/sample - loss: 11.5009 - accuracy: 0.1200 - val_loss: 11.6551 - val_accuracy: 0.0900

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

 

セーブとリストア

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 0x7fb9fc10b9b0>

デフォルトでは、これはモデルの重みを 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
'{"keras_version": "2.2.4-tf", "class_name": "Sequential", "config": {"name": "sequential_3", "layers": [{"class_name": "Dense", "config": {"activation": "relu", "use_bias": true, "units": 64, "kernel_regularizer": null, "trainable": true, "batch_input_shape": [null, 32], "name": "dense_17", "bias_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "dtype": "float32", "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_constraint": null, "kernel_constraint": null, "activity_regularizer": null}}, {"class_name": "Dense", "config": {"activation": "softmax", "use_bias": true, "name": "dense_18", "kernel_regularizer": null, "trainable": true, "bias_regularizer": null, "bias_initializer": {"class_name": "Zeros", "config": {}}, "dtype": "float32", "units": 10, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_constraint": null, "kernel_constraint": null, "activity_regularizer": null}}]}, "backend": "tensorflow"}'
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')
Train on 1000 samples
Epoch 1/5
1000/1000 [==============================] - 0s 195us/sample - loss: 11.5139 - accuracy: 0.0980
Epoch 2/5
1000/1000 [==============================] - 0s 77us/sample - loss: 11.5105 - accuracy: 0.1000
Epoch 3/5
1000/1000 [==============================] - 0s 76us/sample - loss: 11.5097 - accuracy: 0.1070
Epoch 4/5
1000/1000 [==============================] - 0s 78us/sample - loss: 11.5092 - accuracy: 0.0990
Epoch 5/5
1000/1000 [==============================] - 0s 77us/sample - loss: 11.5088 - accuracy: 0.0950

Keras モデルのためのセーブとシリアライゼーションについて save and serialize models へのガイドで更に学習してください。

 

Eager execution

Eager execution は演算を直ちに評価する命令型プログラミング環境です。これは Keras に必要ではありませんが、tf.keras によりサポートされます、そして貴方のプログラムを調べてデバッグするために有用です。

総ての tf.keras モデル構築 API は eager execution と互換です。そして Sequential と functional API が使用できる一方で、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()
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
_________________________________________________________________

次に、通常のようにモデルを訓練します :

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)
Train on 32 steps
32/32 [==============================] - 1s 21ms/step - loss: 0.7022

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

より多くの情報については、full guide on Distributed Training in TensorFlow を見てください。

 

以上



TensorFlow 2.0 Beta : ガイド : TensorFlow 2.0 の tf.function と AutoGraph

TensorFlow 2.0 Beta : ガイド : TensorFlow 2.0 の tf.function と AutoGraph (翻訳/解説)

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

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

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

 

ガイド : TensorFlow 2.0 の tf.function と AutoGraph

TF 2.0 は eager execution の容易さと TF 1.0 のパワーを一つにまとめています。この融合の中心には tf.function があります、これは Python シンタクスのサブセットを可搬な、高パフォーマンスの TensorFlow グラフに変換することを可能にします。

tf.function のクールな新しい特徴は AutoGraph です、これは貴方に自然な Python シンタクスを使用してグラフコードを書かせます。AutoGraph で使用できる Python 特徴のリストについては、AutoGraph Capabilities and Limitations を見てください。tf.function についてのより多くの詳細は、RFC TF 2.0: Functions, not Sessions を見てください。AutoGraph についてのより詳細は、tf.autograph を見てください。

このチュートリアルは tf.function と AutoGraph の基本的な特徴を貴方にウォークスルーしてもらいます。

 

セットアップ

TensorFlow 2.0 Preview Nightly をインポートして TF 2.0 モードを有効にします :

from __future__ import absolute_import, division, print_function, unicode_literals
import numpy as np
!pip install -q tensorflow==2.0.0-beta0
import tensorflow as tf

 

tf.function デコレータ

関数を tf.function でアノテートするとき、依然として任意の他の関数のようにそれを呼び出すことができます。しかしそれはグラフ内にコンパイルされ、これは高速な実行の恩恵を得ることを意味し、GPU か TPU で実行されるか、SavedModel にエクスポートされます。

@tf.function
def simple_nn_layer(x, y):
  return tf.nn.relu(tf.matmul(x, y))


x = tf.random.uniform((3, 3))
y = tf.random.uniform((3, 3))

simple_nn_layer(x, y)
<tf.Tensor: id=23, shape=(3, 3), dtype=float32, numpy=
array([[0.28093395, 0.38601664, 0.1492369 ],
       [0.18872553, 0.49841696, 0.21768065],
       [0.5309121 , 0.94758576, 0.38262835]], dtype=float32)>

アノテーションの結果を調べればそれが TensorFlow ランタイムとの総ての相互作用を扱う特別な callable であることが見て取れるでしょう。

simple_nn_layer
<tensorflow.python.eager.def_function.Function at 0x7efc950bfef0>

貴方のコードが複数の関数を使用する場合、それら総てをアノテートする必要はありません – アノテートされた関数から呼び出された任意の関数もまたグラフモードで動作します。

def linear_layer(x):
  return 2 * x + 1


@tf.function
def deep_net(x):
  return tf.nn.relu(linear_layer(x))


deep_net(tf.constant((1, 2, 3)))
<tf.Tensor: id=36, shape=(3,), dtype=int32, numpy=array([3, 5, 7], dtype=int32)>

多くの小さい ops を持つグラフに対しては、Functions は eager コードよりも高速であり得ます。しかし少数の高価な ops を持つグラフに対しては、多量のスピードアップを見ないかもしれません。

import timeit
conv_layer = tf.keras.layers.Conv2D(100, 3)

@tf.function
def conv_fn(image):
  return conv_layer(image)

image = tf.zeros([1, 200, 200, 100])
# warm up
conv_layer(image); conv_fn(image)
print("Eager conv:", timeit.timeit(lambda: conv_layer(image), number=10))
print("Function conv:", timeit.timeit(lambda: conv_fn(image), number=10))
print("Note how there's not much difference in performance for convolutions")
Eager conv: 0.2274835030000304
Function conv: 0.1842926219999299
Note how there's not much difference in performance for convolutions
lstm_cell = tf.keras.layers.LSTMCell(10)

@tf.function
def lstm_fn(input, state):
  return lstm_cell(input, state)

input = tf.zeros([10, 10])
state = [tf.zeros([10, 10])] * 2
# warm up
lstm_cell(input, state); lstm_fn(input, state)
print("eager lstm:", timeit.timeit(lambda: lstm_cell(input, state), number=10))
print("function lstm:", timeit.timeit(lambda: lstm_fn(input, state), number=10))
eager lstm: 0.005612198999983775
function lstm: 0.004945082999938677

 

Python 制御フローを使用する

tf.function 内でデータ依存制御フローを使用しているとき、Python 制御フロー・ステートメントを使用することができてそして AutoGraph はそれらを適切な TensorFlow ops に変換します。例えば、if ステートメントはそれらが Tensor に依存する場合 tf.cond() に変換されるでしょう。

下の例では、x は Tensor ですがステートメントは期待通りに動作します :

@tf.function
def square_if_positive(x):
  if x > 0:
    x = x * x
  else:
    x = 0
  return x


print('square_if_positive(2) = {}'.format(square_if_positive(tf.constant(2))))
print('square_if_positive(-2) = {}'.format(square_if_positive(tf.constant(-2))))
square_if_positive(2) = 4
square_if_positive(-2) = 0

 
Note: 前の例はスカラー値を伴う単純な条件節を使用しています。現実世界のコードでは典型的にはバッチ処理が使用されます。

AutoGraph は while, for, if, break, continue と return のような一般的な Python ステートメントをネスティングのためのサポートとともに、サポートします。それは while と if ステートメントの条件で Tensor 式を使用したり、for ループで Tensor に渡り iterate できることを意味します。

@tf.function
def sum_even(items):
  s = 0
  for c in items:
    if c % 2 > 0:
      continue
    s += c
  return s


sum_even(tf.constant([10, 12, 15, 20]))
<tf.Tensor: id=606, shape=(), dtype=int32, numpy=42>

AutoGraph はまた上級ユーザのために低位 API も提供します。例えば生成されたコードを見るためにそれを使用できます。

print(tf.autograph.to_code(sum_even.python_function))
def tf__sum_even(items):
  do_return = False
  retval_ = ag__.UndefinedReturnValue()
  s = 0

  def loop_body(loop_vars, s_2):
    c = loop_vars
    continue_ = False
    cond = c % 2 > 0

    def get_state():
      return ()

    def set_state(_):
      pass

    def if_true():
      continue_ = True
      return continue_

    def if_false():
      return continue_
    continue_ = ag__.if_stmt(cond, if_true, if_false, get_state, set_state)
    cond_1 = ag__.not_(continue_)

    def get_state_1():
      return ()

    def set_state_1(_):
      pass

    def if_true_1():
      s_1, = s_2,
      s_1 += c
      return s_1

    def if_false_1():
      return s_2
    s_2 = ag__.if_stmt(cond_1, if_true_1, if_false_1, get_state_1, set_state_1)
    return s_2,
  s, = ag__.for_stmt(items, None, loop_body, (s,))
  do_return = True
  retval_ = s
  cond_2 = ag__.is_undefined_return(retval_)

  def get_state_2():
    return ()

  def set_state_2(_):
    pass

  def if_true_2():
    retval_ = None
    return retval_

  def if_false_2():
    return retval_
  retval_ = ag__.if_stmt(cond_2, if_true_2, if_false_2, get_state_2, set_state_2)
  return retval_

ここにより複雑な制御フローのサンプルがあります :

@tf.function
def fizzbuzz(n):
  msg = tf.constant('')
  for i in tf.range(n):
    if tf.equal(i % 3, 0):
      tf.print('Fizz')
    elif tf.equal(i % 5, 0):
      tf.print('Buzz')
    else:
      tf.print(i)

fizzbuzz(tf.constant(15))
Fizz
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14

 

Keras と AutoGraph

tf.function はオブジェクトのメソッドでもまた使用できます。例えば、典型的にはモデルの call 関数をアノテートすることで貴方のカスタム Keras モデルをデコレートできます。より多くの情報については、tf.keras を見てください。

class CustomModel(tf.keras.models.Model):

  @tf.function
  def call(self, input_data):
    if tf.reduce_mean(input_data) > 0:
      return input_data
    else:
      return input_data // 2


model = CustomModel()

model(tf.constant([-2, -4]))
<tf.Tensor: id=723, shape=(2,), dtype=int32, numpy=array([-1, -2], dtype=int32)>

 

副作用

ちょうど eager モードでのように、通常は tf.function 内で tf.assign や tf.print のような、副作用を持つ演算を使用することができて、それはそれらが順番に実行されることを確実にするために必要な制御依存性を挿入します。

v = tf.Variable(5)

@tf.function
def find_next_odd():
  v.assign(v + 1)
  if tf.equal(v % 2, 0):
    v.assign(v + 1)


find_next_odd()
v
<tf.Variable 'Variable:0' shape=() dtype=int32, numpy=7>

 

例題: 単純なモデルを訓練する

AutoGraph はまたより多くの計算を TensorFlow 内に移すことを可能にします。例えば、訓練ループはちょうど制御フローですので、それは実際には TensorFlow に持ち込むことができます。

 

データをダウンロードする

def prepare_mnist_features_and_labels(x, y):
  x = tf.cast(x, tf.float32) / 255.0
  y = tf.cast(y, tf.int64)
  return x, y

def mnist_dataset():
  (x, y), _ = tf.keras.datasets.mnist.load_data()
  ds = tf.data.Dataset.from_tensor_slices((x, y))
  ds = ds.map(prepare_mnist_features_and_labels)
  ds = ds.take(20000).shuffle(20000).batch(100)
  return ds

train_dataset = mnist_dataset()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

 

モデルを定義する

model = tf.keras.Sequential((
    tf.keras.layers.Reshape(target_shape=(28 * 28,), input_shape=(28, 28)),
    tf.keras.layers.Dense(100, activation='relu'),
    tf.keras.layers.Dense(100, activation='relu'),
    tf.keras.layers.Dense(10)))
model.build()
optimizer = tf.keras.optimizers.Adam()

 

訓練ループを定義する

compute_loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

compute_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()


def train_one_step(model, optimizer, x, y):
  with tf.GradientTape() as tape:
    logits = model(x)
    loss = compute_loss(y, logits)

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

  compute_accuracy(y, logits)
  return loss


@tf.function
def train(model, optimizer):
  train_ds = mnist_dataset()
  step = 0
  loss = 0.0
  accuracy = 0.0
  for x, y in train_ds:
    step += 1
    loss = train_one_step(model, optimizer, x, y)
    if tf.equal(step % 10, 0):
      tf.print('Step', step, ': loss', loss, '; accuracy', compute_accuracy.result())
  return step, loss, accuracy

step, loss, accuracy = train(model, optimizer)
print('Final step', step, ': loss', loss, '; accuracy', compute_accuracy.result())
Step 10 : loss 1.83085191 ; accuracy 0.336
Step 20 : loss 1.03369474 ; accuracy 0.532
Step 30 : loss 0.636070907 ; accuracy 0.627
Step 40 : loss 0.49779284 ; accuracy 0.6745
Step 50 : loss 0.485161096 ; accuracy 0.7116
Step 60 : loss 0.574727654 ; accuracy 0.7365
Step 70 : loss 0.307435066 ; accuracy 0.756714284
Step 80 : loss 0.495543599 ; accuracy 0.7725
Step 90 : loss 0.34826988 ; accuracy 0.785
Step 100 : loss 0.334337503 ; accuracy 0.7955
Step 110 : loss 0.290210605 ; accuracy 0.805090904
Step 120 : loss 0.320541888 ; accuracy 0.813166678
Step 130 : loss 0.307672709 ; accuracy 0.821230769
Step 140 : loss 0.235306799 ; accuracy 0.826928556
Step 150 : loss 0.252060503 ; accuracy 0.833266675
Step 160 : loss 0.177218169 ; accuracy 0.8395
Step 170 : loss 0.292944938 ; accuracy 0.844470561
Step 180 : loss 0.249169499 ; accuracy 0.848555565
Step 190 : loss 0.43364051 ; accuracy 0.852210522
Step 200 : loss 0.25526756 ; accuracy 0.85565
Final step tf.Tensor(200, shape=(), dtype=int32) : loss tf.Tensor(0.25526756, shape=(), dtype=float32) ; accuracy tf.Tensor(0.85565, shape=(), dtype=float32)

 

バッチ処理

実際のアプリケーションではバッチ処理はパフォーマンスのために重要です。AutoGraph に変換する最善のコードはそこでは制御フローがバッチレベルで決定されるようなコードです。個々のサンプルレベルで決定を行なう場合には、パフォーマンスを維持するために batch API を利用してみてください。

例えば、Python で次のようなコードを持つ場合 :

def square_if_positive(x):
  return [i ** 2 if i > 0 else i for i in x]


square_if_positive(range(-5, 5))
[-5, -4, -3, -2, -1, 0, 1, 4, 9, 16]

TensorFlow で次のようにそれを書く誘惑にかられるかもしれません (そしてこれは動作します!) :

@tf.function
def square_if_positive_naive(x):
  result = tf.TensorArray(tf.int32, size=x.shape[0])
  for i in tf.range(x.shape[0]):
    if x[i] > 0:
      result = result.write(i, x[i] ** 2)
    else:
      result = result.write(i, x[i])
  return result.stack()


square_if_positive_naive(tf.range(-5, 5))
<tf.Tensor: id=1834, shape=(10,), dtype=int32, numpy=array([-5, -4, -3, -2, -1,  0,  1,  4,  9, 16], dtype=int32)>

しかしこの場合、次を書くことができることが判明します :

def square_if_positive_vectorized(x):
  return tf.where(x > 0, x ** 2, x)


square_if_positive_vectorized(tf.range(-5, 5))
<tf.Tensor: id=1844, shape=(10,), dtype=int32, numpy=array([-5, -4, -3, -2, -1,  0,  1,  4,  9, 16], dtype=int32)>
 

以上



TensorFlow 2.0 Beta : ガイド : Variables

TensorFlow 2.0 Beta : ガイド : Variables (翻訳/解説)

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

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

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

 

ガイド : Variables

TensorFlow 変数は貴方のプログラムで操作される共有される、持続性のある状態を表わすために最善の方法です。

変数は tf.Variable クラスを通して操作されます。tf.Variable は tensor を表し、その値はその上で ops を実行することにより変更できます。特定の ops はこの tensor の値を読みそして変更することを可能にします。tf.keras のような高位ライブラリはモデル・パラメータをストアするために tf.Variable を使用します。このガイドは TensorFlow で tf.Variables をどのように作成し、更新してそして管理するかをカバーしています。

 

変数を作成する

変数を作成するには、単純に初期値を提供します。

my_variable = tf.Variable(tf.zeros([1., 2., 3.]))

これはゼロで満たされた shape [1, 2, 3] を持つ 3 次元 tensor である変数を作成します。この変数はデフォルトでは dtype tf.float32 を持ちます。dtype は指定されなければ、初期値から推論されます。

アクティブな tf.device スコープがあれば、変数はそのデバイス上に置かれます ; そうでなければ変数はその dtype と互換な “最速の” デバイス上に置かれるでしょう (これは一つの GPU が利用可能であれば殆どの変数は自動的に GPU 上に置かれることを意味します)。例えば、次のスニペットは v という名前の変数を作成してそれを 2 番目の GPU デバイス上に置きます :

with tf.device("/device:GPU:1"):
  v = tf.Variable(tf.zeros([10, 10]))

理想的には tf.distribute API を使用するべきですが、何故ならばそれはコードを一度だけ書いてそれを多くの異なる分散セットアップ下で動作させることを可能にするからです。

 

変数を使用する

TensorFlow グラフで tf.Variable の値を使用するには、単純にそれを通常の tf.Tensor のように扱います :

v = tf.Variable(0.0)
w = v + 1  # w is a tf.Tensor which is computed based on the value of v.
           # Any time a variable is used in an expression it gets automatically
           # converted to a tf.Tensor representing its value.

値を変数に割り当てるには、tf.Variable クラスのメソッド assign, assign_add そして friends を使用します。例えば、ここにこれらのメソッドをどのように呼び出すかがあります :

v = tf.Variable(0.0)
v.assign_add(1)

殆どの TensorFlow optimizer はある勾配降下-like なアルゴリズムに従って変数の値を効率的に更新する特別な ops を持ちます。optimizer をどのように使用するかの説明については tf.keras.optimizers.Optimizer を見てください。

read_value を使用して、変数の現在値を明示的に読むこともまたできます :

v = tf.Variable(0.0)
v.assign_add(1)
v.read_value()  # 1.0

tf.Variable への最後の参照がスコープから出るときにそのメモリは解放されます。

 

変数を追跡し続ける

TensorFlow の変数は Python オブジェクトです。層、モデル、optimizer そして他の関連ツールを構築するとき、貴方は (例えば) モデルの総ての変数のリストを得ることを望みがちでしょう。

貴方自身の Python コードで変数をその場しのぎで追跡し続けられる一方で tf.Module を変数を所有する貴方のクラスの基底クラスとして使用することを推奨します。tf.Module のインスタンスは変数と (潜在的には他のモジュールを通り抜ける) そのモデルから到達可能な総ての (訓練可能な) 変数を返す trainable_variables メソッドを持ちます。

class MyModuleOne(tf.Module):
  def __init__(self):
    self.v0 = tf.Variable(1.0)
    self.vs = [tf.Variable(x) for x in range(10)]
    
class MyOtherModule(tf.Module):
  def __init__(self):
    self.m = MyModuleOne()
    self.v = tf.Variable(10.0)
    
m = MyOtherModule()
len(m.variables)  # 12; 11 from m.m and another from m.v

層を実装する場合、tf.keras.Layer がより良い基底クラスかもしれないことに注意してください、何故ならばそのインターフェイスを実装することは貴方の層を完全に Keras に統合させて、model.fit と他の上手く統合された API を使用することを可能にするからです。tf.keras.Layer の変数追跡は tf.Module のそれと同一です。

 

以上



TensorFlow 2.0 Beta : ガイド : Eager エッセンシャル

TensorFlow 2.0 Beta : ガイド : Eager エッセンシャル (翻訳/解説)

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

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

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

 

ガイド : Eager エッセンシャル

TensorFlow の eager execution は命令型プログラミング環境で、これはグラフを構築することなく演算を直ちに評価します : 演算は後で実行する計算グラフを構築する代わりに具体的な値を返します。これは TensorFlow を始めてモデルをデバッグすることを容易にして、そしてまたそれはボイラープレート (なコード) を削減します。このガイドに沿ってフォローするためには、対話的 python インタープリタで下のコード・サンプルを実行してください。

Eager execution は研究と実験のための柔軟な機械学習プラットフォームで、以下を提供します :

  • 直感的なインターフェイス — 貴方のコードを自然に構造化して Python データ構造を使用します。小さなモデルと小さなデータ上で迅速に iterate します。
  • より容易なデバッギング — 実行中のモデルを調査して変更をテストするために ops を直接的に呼び出します。即時のエラー報告のために標準的な Python デバッギング・ツールを使用します。
  • 自然な制御フロー — グラフ制御フローの代わりに Python 制御フローを使用し、動的モデルの仕様を単純化します。

Eager execution は殆どの TensorFlow 演算と GPU アクセラレーションをサポートします。

Note: 幾つかのモデルでは eager execution が有効であると増大したオーバーヘッドを経験するかもしれません。パフォーマンス改善は進行中ですが、問題が見つかる場合にはバグをファイルしてベンチマークを共有してください。

 

セットアップと基本的な使用方法

TensorFlow の最新版にアップグレードします :

from __future__ import absolute_import, division, print_function, unicode_literals

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

import cProfile

TensorFlow 2.0 では、eager execution はデフォルトで有効にされています。

tf.executing_eagerly()
True

今では貴方は TensorFlow 演算を実行できて結果は直ちに返ります :

x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m))
hello, [[4.]]

eager execution を有効にすると TensorFlow 演算がどのように挙動するかを変更します — 今ではそれらは即時に評価して値を Python に返します。tf.Tensor オブジェクトは計算グラフのノードへのシンボリックなハンドルの代わりに具体的な値を参照します。セッション内で構築して後で実行する計算グラフはありませんので、print() やデバッガーを使用して結果を調査することは容易です。tensor 値の評価、出力表示そしてチェックは勾配を計算するためのフローを壊しません。

Eager execution は NumPy と共に素晴らしく動作します。NumPy 演算は tf.Tensor 引数を受け取ります。TensorFlow math 演算 は Python オブジェクトと NumPy 配列を tf.Tensor オブジェクトに変換します。tf.Tensor.numpy メソッドはオブジェクトの値を NumPy ndarray として返します。

a = tf.constant([[1, 2],
                 [3, 4]])
print(a)
tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32)
# Broadcasting support
b = tf.add(a, 1)
print(b)
tf.Tensor(
[[2 3]
 [4 5]], shape=(2, 2), dtype=int32)
# Operator overloading is supported
print(a * b)
tf.Tensor(
[[ 2  6]
 [12 20]], shape=(2, 2), dtype=int32)
# Use NumPy values
import numpy as np

c = np.multiply(a, b)
print(c)
[[ 2  6]
 [12 20]]
# Obtain numpy value from a tensor:
print(a.numpy())
# => [[1 2]
#     [3 4]]
[[1 2]
 [3 4]]

 

動的制御フロー

eager execution の主なメリットはモデルが実行されている間にホスト言語の総ての機能が利用可能であることです。そのため例えば、fizzbuzz を書くことも容易です :

def fizzbuzz(max_num):
  counter = tf.constant(0)
  max_num = tf.convert_to_tensor(max_num)
  for num in range(1, max_num.numpy()+1):
    num = tf.constant(num)
    if int(num % 3) == 0 and int(num % 5) == 0:
      print('FizzBuzz')
    elif int(num % 3) == 0:
      print('Fizz')
    elif int(num % 5) == 0:
      print('Buzz')
    else:
      print(num.numpy())
    counter += 1
fizzbuzz(15)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

これは tensor 値に依拠する条件節を持ちこれらの値を実行時に出力表示します。

 

Eager 訓練

勾配を計算する

自動微分 はニューラルネットワークを訓練するための バックプロパゲーション のような機械学習アルゴリズムを実装するために有用です。eager execution の間は、後で勾配を計算するための演算を追跡するために tfe.GradientTape を利用します。

eager で訓練する and/or 勾配を計算するために tf.GradientTape を使用できます。それは複雑な訓練ループのために特に有用です。

異なる演算が各呼び出しの間に発生しますので、総ての forward パス演算は「テープ」に記録されます。勾配を計算するために、テープを反対に再生してから破棄します。特定の tf.GradientTape は 1 つの勾配を計算するだけです ; 続く呼び出しはランタイム・エラーを投げます。

w = tf.Variable([[1.0]])
with tf.GradientTape() as tape:
  loss = w * w

grad = tape.gradient(loss, w)
print(grad)  # => tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)
tf.Tensor([[2.]], shape=(1, 1), dtype=float32)

 

モデルを訓練する

次のサンプルは標準的な MNIST 手書き数字を分類する多層モデルを作成します。eager execution 環境で訓練可能なグラフを構築する optimizer と層 API を示します。

# Fetch and format the mnist data
(mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data()

dataset = tf.data.Dataset.from_tensor_slices(
  (tf.cast(mnist_images[...,tf.newaxis]/255, tf.float32),
   tf.cast(mnist_labels,tf.int64)))
dataset = dataset.shuffle(1000).batch(32)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
# Build the model
mnist_model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(16,[3,3], activation='relu',
                         input_shape=(None, None, 1)),
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(10)
])

訓練なしでさえも、eager execution ではモデルを呼び出して出力を調査します :

for images,labels in dataset.take(1):
  print("Logits: ", mnist_model(images[0:1]).numpy())
Logits:  [[-0.02539074 -0.01439482  0.00780122 -0.00887529 -0.01578783  0.02660074
   0.03275762 -0.01570328 -0.03225745  0.0271067 ]]

keras モデルが (fit メソッドを使用した) 組み込み訓練ループを持つ一方、時には更なるカスタマイズが必要でしょう。ここに、eager で実装された訓練ループのサンプルがあります :

optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

loss_history = []

Note: 条件が成り立つかを確認するために tf.debugging の assert 関数を使用してください。これは eager と graph execution で動作します。

def train_step(images, labels):
  with tf.GradientTape() as tape:
    logits = mnist_model(images, training=True)
    
    # Add asserts to check the shape of the output.
    tf.debugging.assert_equal(logits.shape, (32, 10))
    
    loss_value = loss_object(labels, logits)

  loss_history.append(loss_value.numpy().mean())
  grads = tape.gradient(loss_value, mnist_model.trainable_variables)
  optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables))
def train():
  for epoch in range(3):
    for (batch, (images, labels)) in enumerate(dataset):
      train_step(images, labels)
    print ('Epoch {} finished'.format(epoch))
train()
Epoch 0 finished
Epoch 1 finished
Epoch 2 finished
import matplotlib.pyplot as plt

plt.plot(loss_history)
plt.xlabel('Batch #')
plt.ylabel('Loss [entropy]')
Text(0, 0.5, 'Loss [entropy]')

 

Variable と optimizer

tf.Variable オブジェクトは自動微分をより容易にするために訓練の間にアクセスされるミュータブルな tf.Tensor 値をストアします。モデルのパラメータはクラス内に変数としてカプセル化できます。

モデル・パラメータは tf.Variable を tf.GradientTape と共に使用することでより良くカプセル化できます。例えば、上の自動微分サンプルは次のように書き換えることができます :

class Model(tf.keras.Model):
  def __init__(self):
    super(Model, self).__init__()
    self.W = tf.Variable(5., name='weight')
    self.B = tf.Variable(10., name='bias')
  def call(self, inputs):
    return inputs * self.W + self.B

# A toy dataset of points around 3 * x + 2
NUM_EXAMPLES = 2000
training_inputs = tf.random.normal([NUM_EXAMPLES])
noise = tf.random.normal([NUM_EXAMPLES])
training_outputs = training_inputs * 3 + 2 + noise

# The loss function to be optimized
def loss(model, inputs, targets):
  error = model(inputs) - targets
  return tf.reduce_mean(tf.square(error))

def grad(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss(model, inputs, targets)
  return tape.gradient(loss_value, [model.W, model.B])

# Define:
# 1. A model.
# 2. Derivatives of a loss function with respect to model parameters.
# 3. A strategy for updating the variables based on the derivatives.
model = Model()
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)

print("Initial loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))

# Training loop
for i in range(300):
  grads = grad(model, training_inputs, training_outputs)
  optimizer.apply_gradients(zip(grads, [model.W, model.B]))
  if i % 20 == 0:
    print("Loss at step {:03d}: {:.3f}".format(i, loss(model, training_inputs, training_outputs)))

print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
Initial loss: 68.494
Loss at step 000: 65.867
Loss at step 020: 30.344
Loss at step 040: 14.302
Loss at step 060: 7.052
Loss at step 080: 3.773
Loss at step 100: 2.289
Loss at step 120: 1.616
Loss at step 140: 1.311
Loss at step 160: 1.173
Loss at step 180: 1.110
Loss at step 200: 1.082
Loss at step 220: 1.069
Loss at step 240: 1.063
Loss at step 260: 1.060
Loss at step 280: 1.059
Final loss: 1.059
W = 2.968806028366089, B = 1.990594506263733

 

eager execution の間の状態のためにオブジェクトを使用する

TF 1.x グラフ実行では、(変数のような) プログラム状態はグローバル・コレクションにストアされてそれらのライフタイムは tf.Session オブジェクトで管理されます。対照的に、eager execuction の間は状態オブジェクトのライフタイムはそれらの対応する Python オブジェクトのライフタイムにより決定されます。

 

Variables はオブジェクト

eager execution の間、variable はオブジェクトへの最後の参照が除去されるまで存続し、それから削除されます。

if tf.test.is_gpu_available():
  with tf.device("gpu:0"):
    v = tf.Variable(tf.random.normal([1000, 1000]))
    v = None  # v no longer takes up GPU memory

 

オブジェクト・ベースのセービング

このセクションは 訓練チェックポイントへのガイド の短縮されたバージョンです。

tf.train.Checkpoint は tf.Variables をチェックポイントへ/からセーブしてリストアできます :

x = tf.Variable(10.)
checkpoint = tf.train.Checkpoint(x=x)
x.assign(2.)   # Assign a new value to the variables and save.
checkpoint_path = './ckpt/'
checkpoint.save('./ckpt/')
'./ckpt/-1'
x.assign(11.)  # Change the variable after saving.

# Restore values from the checkpoint
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))

print(x)  # => 2.0
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=2.0>

モデルをセーブしてロードするためには、隠れ変数を必要とすることなく、tf.train.Checkpoint はオブジェクトの内部状態をストアします。モデル、optimizer そしてグローバルステップの状態を記録するためには、それらを tf.train.Checkpoint に渡します :

import os

model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(10)
])
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
checkpoint_dir = 'path/to/model_dir'
if not os.path.exists(checkpoint_dir):
  os.makedirs(checkpoint_dir)
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
root = tf.train.Checkpoint(optimizer=optimizer,
                           model=model)

root.save(checkpoint_prefix)
root.restore(tf.train.latest_checkpoint(checkpoint_dir))
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f9976568828>

Note: 多くの訓練ループで、variables は tf.train.Checkpoint.restore が呼び出された後で作成されます。これらの variables はそれらが作成されるとすぐにリストアされて、チェックポイントが完全にロードされたかを確かなものにするために assertions が利用可能です。詳細は 訓練チェックポイントへのガイド を見てください。

 

オブジェクト指向メトリクス

tf.keras.metrics はオブジェクトとしてストアされます。新しいデータを callable に渡すことでメトリクスを更新して、そして tf.keras.metrics.result メソッドを使用して結果を取得します、例えば :

m = tf.keras.metrics.Mean("loss")
m(0)
m(5)
m.result()  # => 2.5
m([8, 9])
m.result()  # => 5.5
<tf.Tensor: id=1036628, shape=(), dtype=float32, numpy=5.5>

 

上級者のための自動微分トピック

動的モデル

tf.GradientTape はまた動的モデルでも利用できます。バックトラックする直線探索 (= line search) アルゴリズムのためのこのサンプルは、複雑な制御フローにもかかわらず、(勾配があり微分可能であることを除けば) 普通の NumPy コードのように見えます :

def line_search_step(fn, init_x, rate=1.0):
  with tf.GradientTape() as tape:
    # Variables are automatically recorded, but manually watch a tensor
    tape.watch(init_x)
    value = fn(init_x)
  grad = tape.gradient(value, init_x)
  grad_norm = tf.reduce_sum(grad * grad)
  init_value = value
  while value > init_value - rate * grad_norm:
    x = init_x - rate * grad
    value = fn(x)
    rate /= 2.0
  return x, value

 

カスタム勾配

カスタム勾配は勾配を override するための簡単な方法です。forward 関数内では、勾配は入力、出力、または中間結果に関する勾配を定義します。例えば、backward パスで勾配のノルムをクリップするための簡単な方法がここにあります :

@tf.custom_gradient
def clip_gradient_by_norm(x, norm):
  y = tf.identity(x)
  def grad_fn(dresult):
    return [tf.clip_by_norm(dresult, norm), None]
  return y, grad_fn

カスタム勾配は演算のシークエンスのための数値的安定な勾配を提供するために一般に使用されます :

def log1pexp(x):
  return tf.math.log(1 + tf.exp(x))

def grad_log1pexp(x):
  with tf.GradientTape() as tape:
    tape.watch(x)
    value = log1pexp(x)
  return tape.gradient(value, x)
# The gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# However, x = 100 fails because of numerical instability.
grad_log1pexp(tf.constant(100.)).numpy()
nan

ここで、log1pexp 関数はカスタム勾配で解析的に単純化できます。下の実装は forward パスの間に計算された tf.exp(x) のための値を再利用しています — 冗長な計算を除去することでそれをより効率的にしています :

@tf.custom_gradient
def log1pexp(x):
  e = tf.exp(x)
  def grad(dy):
    return dy * (1 - 1 / (1 + e))
  return tf.math.log(1 + e), grad

def grad_log1pexp(x):
  with tf.GradientTape() as tape:
    tape.watch(x)
    value = log1pexp(x)
  return tape.gradient(value, x)
# As before, the gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# And the gradient computation also works at x = 100.
grad_log1pexp(tf.constant(100.)).numpy()
1.0

 

パフォーマンス

eager execution の間は計算は GPU へと自動的にオフロードされます。計算が実行される場所について制御を望む場合にはそれを tf.device(‘/gpu:0’) ブロック (または CPU の同値のもの) で囲むことができます :

import time

def measure(x, steps):
  # TensorFlow initializes a GPU the first time it's used, exclude from timing.
  tf.matmul(x, x)
  start = time.time()
  for i in range(steps):
    x = tf.matmul(x, x)
  # tf.matmul can return before completing the matrix multiplication
  # (e.g., can return after enqueing the operation on a CUDA stream).
  # The x.numpy() call below will ensure that all enqueued operations
  # have completed (and will also copy the result to host memory,
  # so we're including a little more than just the matmul operation
  # time).
  _ = x.numpy()
  end = time.time()
  return end - start

shape = (1000, 1000)
steps = 200
print("Time to multiply a {} matrix by itself {} times:".format(shape, steps))

# Run on CPU:
with tf.device("/cpu:0"):
  print("CPU: {} secs".format(measure(tf.random.normal(shape), steps)))

# Run on GPU, if available:
if tf.test.is_gpu_available():
  with tf.device("/gpu:0"):
    print("GPU: {} secs".format(measure(tf.random.normal(shape), steps)))
else:
  print("GPU: not found")
Time to multiply a (1000, 1000) matrix by itself 200 times:
CPU: 0.963911771774292 secs
GPU: 0.03915715217590332 secs

tf.Tensor オブジェクトはその演算を実行するために異なるデバイスへとコピーできます :

if tf.test.is_gpu_available():
  x = tf.random.normal([10, 10])

  x_gpu0 = x.gpu()
  x_cpu = x.cpu()

  _ = tf.matmul(x_cpu, x_cpu)    # Runs on CPU
  _ = tf.matmul(x_gpu0, x_gpu0)  # Runs on GPU:0

 

ベンチマーク

GPU 上の ResNet50 訓練のような計算が重いモデルについては、eager execution パフォーマンスは tf.function 実行に匹敵します。しかしこの隔たりはより少ない計算を持つモデルのためにはより大きくなり、多くの小さい演算を持つモデルのためにホットコード・パスを最適化するために行われなければならない作業があります。

 

Work with functions

eager execution が開発とデバッグをより対話的にする一方で、TensorFlow 1.x スタイルのグラフ execution は分散訓練、パフォーマンス最適化、そしてプロダクション配備のために優位点を持ちます。この隔たりを埋めるために、TensorFlow 2.0 は tf.function API を通した関数を導入します。より多くの情報は、Autograph ガイドを見てください。

 

以上



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