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

タグアーカイブ: TensorFlow 2.0 Guide

TensorFlow 2.0 : ガイド : Keras :- Keras でマスキングとパディング

TensorFlow 2.0 : ガイド : Keras :- Keras でマスキングとパディング (翻訳/解説)

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

* 本ページは、TensorFlow org サイトの Guide – Keras の以下のページを翻訳した上で
適宜、補足説明したものです:

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

ガイド : Keras :- Keras でマスキングとパディング

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

import numpy as np

import tensorflow as tf

from tensorflow.keras import layers

 

シークエンス・データをパディング

シークエンス・データを処理するとき、個々のサンプルが異なる長さを持つことは非常に一般的です。次のサンプルを考えます (単語としてトークン化されたテキスト) :

[
  ["The", "weather", "will", "be", "nice", "tomorrow"],
  ["How", "are", "you", "doing", "today"],
  ["Hello", "world", "!"]
]

語彙検索の後、データは整数としてベクトル化されるかもしれません、例えば :

[
  [83, 91, 1, 645, 1253, 927],
  [73, 8, 3215, 55, 927],
  [71, 1331, 4231]
]

データは 2D リストで、そこでは個々のサンプルはそれぞれ長さ 6, 5 と 3 を持ちます。深層学習モデルのための入力データは (この場合 e.g. (batch_size, 6, vocab_size) の shape の) 単一の tensorでなければならないので、最長の項目よりも短いサンプルはあるプレースホルダー値でパディングされる必要があります (代替的に、短いサンプルをパッドする前に長いサンプルを切る詰める (= truncate) こともするかもしれません)。

Keras は共通の長さにシークエンスを切り詰めてパディングする API を提供します : tf.keras.preprocessing.sequence.pad_sequences です。

raw_inputs = [
  [83, 91, 1, 645, 1253, 927],
  [73, 8, 3215, 55, 927],
  [711, 632, 71]
]

# By default, this will pad using 0s; it is configurable via the
# "value" parameter.
# Note that you could "pre" padding (at the beginning) or
# "post" padding (at the end).
# We recommend using "post" padding when working with RNN layers
# (in order to be able to use the 
# CuDNN implementation of the layers).
padded_inputs = tf.keras.preprocessing.sequence.pad_sequences(raw_inputs,
                                                              padding='post')

print(padded_inputs)
[[  83   91    1  645 1253  927]
 [  73    8 3215   55  927    0]
 [ 711  632   71    0    0    0]]

 

マスキング

総てのサンプルが統一された (= uniform) 長さを持つ今、モデルはデータのあるパートは実際にはパディングで無視されるべきであることを知らされなければなりません。そのメカニズムがマスキングです。

Keras モデルで入力マスクを導入するには 3 つの方法があります :

  • keras.layers.Masking 層を追加する。
  • keras.layers.Embedding 層を mask_zero=True で configure する。
  • mask 引数を手動で渡します、この引数をサポートする層を呼び出すとき (e.g. RNN 層)。

 

マスク生成層 : Embedding と Masking

内部的には、これらの層はマスク tensor (shape (batch, sequence_length) の 2D tensor) を作成し、それを Masking か Embedding 層により返される tensor 出力にそれを装着します。

embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
masked_output = embedding(padded_inputs)

print(masked_output._keras_mask)
tf.Tensor(
[[ True  True  True  True  True  True]
 [ True  True  True  True  True False]
 [ True  True  True False False False]], shape=(3, 6), dtype=bool)
masking_layer = layers.Masking()
# Simulate the embedding lookup by expanding the 2D input to 3D,
# with embedding dimension of 10.
unmasked_embedding = tf.cast(
    tf.tile(tf.expand_dims(padded_inputs, axis=-1), [1, 1, 10]),
    tf.float32)

masked_embedding = masking_layer(unmasked_embedding)
print(masked_embedding._keras_mask)
tf.Tensor(
[[ True  True  True  True  True  True]
 [ True  True  True  True  True False]
 [ True  True  True False False False]], shape=(3, 6), dtype=bool)

プリントされた結果から見れるように、マスクは shape (batch_size, sequence_length) を持つ 2D ブーリアン tensor で、そこでは各個々の False エントリは対応する時間ステップは処理の間無視されるべきであることを示します。

 

Functional API と Sequential API のマスク伝播

Functional API か Sequential API を使用するとき、Embedding か Masking 層により生成されたマスクはそれらを使用できる任意の層 (例えば、RNN 層) に対してネットワークを通して伝播されます。Keras は入力に対応するマスクを自動的に取得してそれをどのように使用するかを知る任意の層に渡します。

サブクラス化されたモデルや層の call メソッドでは、マスクは自動的には伝播されませんので、それを必要とする任意の層に mask 引数を手動で渡す必要があることに注意してください。詳細は下のセクションを見てください。

例えば、次の Sequential モデルでは、LSTM 層は自動的にマスクを受け取ります、それはそれがパッドされた値を無視することを意味します :

model = tf.keras.Sequential([
  layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True),
  layers.LSTM(32),
])

これはまた次の Functional API モデルにも当てはまります :

inputs = tf.keras.Input(shape=(None,), dtype='int32')
x = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)(inputs)
outputs = layers.LSTM(32)(x)

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

 

マスク tensor を直接的に層に渡す

(LSTM 層のような) マスクを処理できる層は __call__ メソッドで mask 引数を持ちます。

一方で、マスクを生成する層 (e.g. Embedding) は呼び出せる compute_mask(input, previous_mask) メソッドを公開します。

こうして、このようなことができます :

class MyLayer(layers.Layer):
  
  def __init__(self, **kwargs):
    super(MyLayer, self).__init__(**kwargs)
    self.embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
    self.lstm = layers.LSTM(32)
    
  def call(self, inputs):
    x = self.embedding(inputs)
    # Note that you could also prepare a `mask` tensor manually.
    # It only needs to be a boolean tensor
    # with the right shape, i.e. (batch_size, timesteps).
    mask = self.embedding.compute_mask(inputs)
    output = self.lstm(x, mask=mask)  # The layer will ignore the masked values
    return output

layer = MyLayer()
x = np.random.random((32, 10)) * 100
x = x.astype('int32')
layer(x)
<tf.Tensor: id=4730, shape=(32, 32), dtype=float32, numpy=
array([[-0.00149354, -0.00657718,  0.0043684 , ...,  0.01915387,
         0.00254279,  0.00201567],
       [-0.00874859,  0.00249364,  0.00269479, ..., -0.01414887,
         0.00511035, -0.00541363],
       [-0.00457095, -0.0097013 , -0.00557693, ...,  0.00384533,
         0.00664415,  0.00333986],
       ...,
       [-0.00762534, -0.00543655,  0.0005238 , ...,  0.01187737,
         0.00214507, -0.00063268],
       [ 0.00428915, -0.00258686,  0.00012214, ...,  0.0064177 ,
         0.00800534,  0.00203928],
       [-0.01474019, -0.00349469, -0.00311312, ..., -0.0064069 ,
         0.00472621,  0.005593  ]], dtype=float32)>

 

貴方のカスタム層でマスキングをサポートする

時に貴方は (Embedding のような) マスクを生成する層や、現在のマスクを変更する必要がある層を書く必要があるかもしれません。

例えば、時間次元上で連結する Concatenate 層のような、入力とは異なる時間次元を持つ tensor を生成する任意の層は現在のマスクを変更する必要があるでしょう、その結果下流の層はマスクされた時間ステップを正しく考慮に入れることができます。

これを行なうために、貴方の層は layer.compute_mask() メソッドを実装するべきです、これは入力と現在のマスクが与えられたとき新しいマスクを生成します。

殆どの層は時間次元を変更しませんので、マスキングについて心配する必要はありません。そのような場合 compute_mask() のデフォルトの動作は単に現在のマスクを通過させるだけです。

ここに現在のマスクを変更する必要がある TemporalSplit 層のサンプルがあります。

class TemporalSplit(tf.keras.layers.Layer):
  """Split the input tensor into 2 tensors along the time dimension."""

  def call(self, inputs):
    # Expect the input to be 3D and mask to be 2D, split the input tensor into 2
    # subtensors along the time axis (axis 1).
    return tf.split(inputs, 2, axis=1)
    
  def compute_mask(self, inputs, mask=None):
    # Also split the mask into 2 if it presents.
    if mask is None:
      return None
    return tf.split(mask, 2, axis=1)

first_half, second_half = TemporalSplit()(masked_embedding)
print(first_half._keras_mask)
print(second_half._keras_mask)
tf.Tensor(
[[ True  True  True]
 [ True  True  True]
 [ True  True  True]], shape=(3, 3), dtype=bool)
tf.Tensor(
[[ True  True  True]
 [ True  True False]
 [False False False]], shape=(3, 3), dtype=bool)

ここに入力値からマスクを生成できる CustomEmbedding 層のもう一つのサンプルがあります :

class CustomEmbedding(tf.keras.layers.Layer):
  
  def __init__(self, input_dim, output_dim, mask_zero=False, **kwargs):
    super(CustomEmbedding, self).__init__(**kwargs)
    self.input_dim = input_dim
    self.output_dim = output_dim
    self.mask_zero = mask_zero
    
  def build(self, input_shape):
    self.embeddings = self.add_weight(
      shape=(self.input_dim, self.output_dim),
      initializer='random_normal',
      dtype='float32')
    
  def call(self, inputs):
    return tf.nn.embedding_lookup(self.embeddings, inputs)
  
  def compute_mask(self, inputs, mask=None):
    if not self.mask_zero:
      return None
    return tf.not_equal(inputs, 0)
  
  
layer = CustomEmbedding(10, 32, mask_zero=True)
x = np.random.random((3, 10)) * 9
x = x.astype('int32')

y = layer(x)
mask = layer.compute_mask(x)

print(mask)
tf.Tensor(
[[ True  True  True  True  True  True  True False  True  True]
 [ True  True  True  True  True  True  True  True  True  True]
 [False  True False  True  True  True False  True False  True]], shape=(3, 10), dtype=bool)

 

マスク情報を必要とする層を書く

いくつかの層はマスク消費者 (= consumer) です : それらは call で mask 引数を受け取りそれを特定の時間ステップをスキップするかどうかを決定するために使用します。

そのような層を書くために、単純に call シグネチャで mask=None 引数を追加できます。入力に関連するマスクはそれが利用可能なときにいつでも層に渡されます。

class MaskConsumer(tf.keras.layers.Layer):
  
  def call(self, inputs, mask=None):
    ...

 

要点

それが Keras でマスキングについて知る必要があることの総てです。まとめると :

  • 「マスキング」は層がシークエンス入力である時間ステップをいつスキップ/無視するかをどのように知ることができるかです。
  • 幾つかの層はマスク-generator です : Embedding は (mask_zero=True の場合) 入力値からマスクを生成できて、Masking 層もそれができます。
  • 幾つかの層はマスク-consumer です : それらは __call__ メソッドで mask 引数を公開します。これは RNN 層が当てはまります。
  • Functional API と Sequential API では、マスク情報は自動的に伝播されます。
  • サブクラス化されたモデルを書くときやスタンドアロンな方法で層を使用するとき、層に mask 引数を手動で渡します。
  • 現在のマスクを変更する層を容易に書くことができます、それは新しいマスクを生成するか、入力に関連するマスクを消費します。
 

以上



TensorFlow 2.0 : ガイド : Keras :- Keras でリカレント・ニューラルネットワーク (RNN)

TensorFlow 2.0 : ガイド : Keras :- Keras でリカレント・ニューラルネットワーク (RNN) (翻訳/解説)

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

* 本ページは、TensorFlow org サイトの Guide – Keras の以下のページを翻訳した上で
適宜、補足説明したものです:

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

ガイド : Keras :- Keras でリカレント・ニューラルネットワーク (RNN)

リカレント・ニューラルネットワーク (RNN) は、時系列や自然言語のようなシークエンス・データをモデル化するためのパワフルなニューラルネットワークのクラスです。

模式的には (= schematically)、RNN 層はシークエンスの時間ステップに渡り反復するために for ループを使用し、一方でそれがそこまでに見た時間ステップについての情報をエンコードする内部状態を保持します。

Keras RNN API は以下に焦点を絞って設計されています :

  • 利用の容易さ: 組込み tf.keras.layers.RNN, tf.keras.layers.LSTM, tf.keras.layers.GRU 層は難しい configuration 選択を行わなければならないことなくリカレント・モデルを素早く構築することを可能にします。
  • カスタマイズの容易さ: カスタム動作を持つ貴方自身の RNN セル層 (for ループの内側部) を定義し、そしてそれを一般的な tf.keras.layers.RNN 層 (for ループ自身) で使用することもできます。これは異なる研究アイデアを最小限のコードで柔軟な方法で素早くプロトタイピングすることを可能にします。

 

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

import collections
import matplotlib.pyplot as plt
import numpy as np

import tensorflow as tf

from tensorflow.keras import layers

 

単純なモデルを構築する

Keras には 3 つの組込み RNN があります :

  1. tf.keras.layers.SimpleRNN, 完全結合 RNN そこでは前の時間ステップからの出力は次の時間ステップに供給されます。
  2. tf.keras.layers.GRU, 最初に Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation 内で提案されました。
  3. tf.keras.layers.LSTM, 最初に Long Short-Term Memory で提案されました。

2015 年初期に、Keras は LSTM と GRU の最初の再利用可能なオープンソース Python 実装を持ちました。

ここに Sequential モデルの単純なサンプルがあります、これは整数のシークエンスを処理し、各整数を 64-次元ベクトルに埋め込み、それから LSTM 層を使用してベクトルのシークエンスを処理します。

model = tf.keras.Sequential()
# Add an Embedding layer expecting input vocab of size 1000, and
# output embedding dimension of size 64.
model.add(layers.Embedding(input_dim=1000, output_dim=64))

# Add a LSTM layer with 128 internal units.
model.add(layers.LSTM(128))

# Add a Dense layer with 10 units and softmax activation.
model.add(layers.Dense(10, activation='softmax'))

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 64)          64000     
_________________________________________________________________
lstm (LSTM)                  (None, 128)               98816     
_________________________________________________________________
dense (Dense)                (None, 10)                1290      
=================================================================
Total params: 164,106
Trainable params: 164,106
Non-trainable params: 0

 

出力と状態

デフォルトでは、RNN 層の出力はサンプル毎に単一のベクトルを含みます。このベクトルは最後の時間ステップに対応する RNN セル出力で、入力シークエンス全体についての情報を含みます。この出力の shape は (batch_size, units) です、ここで units は層のコンストラクタに渡される units 引数に対応します。

RNN 層はまた各サンプルに対する出力の全体のシークエンス (1 ベクトル per 時間ステップ per サンプル) を返すこともできます、return_sequences=True を設定する場合。この出力の shape は (batch_size, timesteps, units) です。

model = tf.keras.Sequential()
model.add(layers.Embedding(input_dim=1000, output_dim=64))

# The output of GRU will be a 3D tensor of shape (batch_size, timesteps, 256)
model.add(layers.GRU(256, return_sequences=True))

# The output of SimpleRNN will be a 2D tensor of shape (batch_size, 128)
model.add(layers.SimpleRNN(128))

model.add(layers.Dense(10, activation='softmax'))

model.summary() 
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, None, 64)          64000     
_________________________________________________________________
gru (GRU)                    (None, None, 256)         247296    
_________________________________________________________________
simple_rnn (SimpleRNN)       (None, 128)               49280     
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 361,866
Trainable params: 361,866
Non-trainable params: 0
_________________________________________________________________

加えて、RNN 層はその最終内部状態を返すことができます。返された状態は後で RNN 実行を再開するためにか、もう一つの RNN を初期化する ために利用できます。この設定は一般にエンコーダ・デコーダ sequence-to-sequence モデルで使用され、そこではエンコーダ最終状態はデコーダの初期状態として使用されます。

RNN 層をその内部状態を返すように configure するには、層を作成するとき return_state パラメータを True に設定します。LSTM は 2 状態 tensor を持ちますが、GRU は一つだけを持つことに注意してください。

層の初期状態を configure するためには、単に層を追加のキーワード引数 initial_state で呼び出します。状態の shape は下のサンプルのように、層の unit サイズに適合する必要があります。

encoder_vocab = 1000
decoder_vocab = 2000

encoder_input = layers.Input(shape=(None, ))
encoder_embedded = layers.Embedding(input_dim=encoder_vocab, output_dim=64)(encoder_input)

# Return states in addition to output
output, state_h, state_c = layers.LSTM(
    64, return_state=True, name='encoder')(encoder_embedded)
encoder_state = [state_h, state_c]

decoder_input = layers.Input(shape=(None, ))
decoder_embedded = layers.Embedding(input_dim=decoder_vocab, output_dim=64)(decoder_input)

# Pass the 2 states to a new LSTM layer, as initial state
decoder_output = layers.LSTM(
    64, name='decoder')(decoder_embedded, initial_state=encoder_state)
output = layers.Dense(10, activation='softmax')(decoder_output)

model = tf.keras.Model([encoder_input, decoder_input], output)
model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, None)]       0                                            
__________________________________________________________________________________________________
input_2 (InputLayer)            [(None, None)]       0                                            
__________________________________________________________________________________________________
embedding_2 (Embedding)         (None, None, 64)     64000       input_1[0][0]                    
__________________________________________________________________________________________________
embedding_3 (Embedding)         (None, None, 64)     128000      input_2[0][0]                    
__________________________________________________________________________________________________
encoder (LSTM)                  [(None, 64), (None,  33024       embedding_2[0][0]                
__________________________________________________________________________________________________
decoder (LSTM)                  (None, 64)           33024       embedding_3[0][0]                
                                                                 encoder[0][1]                    
                                                                 encoder[0][2]                    
__________________________________________________________________________________________________
dense_2 (Dense)                 (None, 10)           650         decoder[0][0]                    
==================================================================================================
Total params: 258,698
Trainable params: 258,698
Non-trainable params: 0
__________________________________________________________________________________________________

 

RNN 層と RNN セル

組込み RNN 層に加えて、RNN API はまたセルレベル API も提供します。入力シークエンスのバッチ全体を処理する RNN 層と違い、RNN セルは単一の時間ステップだけを処理します。

セルは RNN 層の for ループの内側にあります。tf.keras.layers.RNN 層の内側でのセルのラッピングはシークエンスのバッチを処理できる層を与えます, e.g. RNN(LSTMCell(10))。

数学的には、RNN(LSTMCell(10)) は LSTM (10) と同じ結果を生成します。実際に、TF v1.x のこの層の実装は単に対応する RNN セルを作成してそれを RNN 層でラッピングしていました。けれども組込み GRU と LSTM 層の使用は CuDNN の使用を有効にしますのでより良いパフォーマンスを見るかもしれません。

3 つの組込み RNN セルがあり、それらの各々は適合する RNN 層に対応します。

セル抽象は、一般的な tf.keras.layers.RNN クラスと一緒に、貴方の研究のためにカスタム RNN アーキテクチャを実装することを非常に容易にします。

 

交差バッチ statefulness

非常に長いシークエンス (多分無限大) を処理するとき、交差バッチ statefulness のパターンを使用することを望むかもしれません。

通常は、RNN 層の内部状態はそれが新しいバッチを見るたびにリセットされます (i.e. 層により見られる総てのサンプルは過去 (のもの) から独立的であると仮定されています)。層は与えられたサンプルを処理する間、状態を維持するだけです。

けれども非常に長いシークエンスを持つ場合、それらをより短いシークエンスに分解して (層の状態をリセットすることなく) それらの短いシークエンスを RNN 層にシーケンシャルに供給することは有用です。このようにして、層は、それが一度に一つの部分シークエンスを見ているだけですが、シークエンス全体についての情報を保持できます。

コンストラクタで stateful=True を設定することによりこれを行なうことができます。

もしシークエンス s = [t0, t1, … t1546, t1547] を持つ場合、それを例えば次のように分割できます :

s1 = [t0, t1, ... t100]
s2 = [t101, ... t201]
...
s16 = [t1501, ... t1547]

それからそれを次を通して処理するでしょう :

lstm_layer = layers.LSTM(64, stateful=True)
for s in sub_sequences:
  output = lstm_layer(s)

状態をクリアすることを望むとき、layer.reset_states() を使用できます。

Note: このセットアップでは、与えられたバッチ内のサンプル i は前のバッチ内のサンプル i の続きであることが仮定されています。これは総てのバッチはサンプルの同じ数 (バッチサイズ) を含むべきであることを意味します。例えば、もしバッチが [sequence_A_from_t0_to_t100, sequence_B_from_t0_to_t100] を含む場合、次のバッチは [sequence_A_from_t101_to_t200, sequence_B_from_t101_to_t200] を含むべきです。

ここに完全なサンプルがあります :

paragraph1 = np.random.random((20, 10, 50)).astype(np.float32)
paragraph2 = np.random.random((20, 10, 50)).astype(np.float32)
paragraph3 = np.random.random((20, 10, 50)).astype(np.float32)

lstm_layer = layers.LSTM(64, stateful=True)
output = lstm_layer(paragraph1)
output = lstm_layer(paragraph2)
output = lstm_layer(paragraph3)

# reset_states() will reset the cached state to the original initial_state.
# If no initial_state was provided, zero-states will be used by default.
lstm_layer.reset_states()

 

双方向 RNN

時系列以外のシークエンス (e.g. テキスト) については、RNN モデルはそれが最初から最後までシークエンスを処理するだけでなく反対にも処理する場合により良く遂行できることが多いです。例えば、センテンスの次の単語を予測するためには、その前に来る単語だけでなく、単語回りのコンテキストを持つことがしばしば有用です。

Keras はそのような双方向 RNN を構築するための容易な API を提供します : tf.keras.layers.Bidirectional ラッパーです。

model = tf.keras.Sequential()

model.add(layers.Bidirectional(layers.LSTM(64, return_sequences=True), 
                               input_shape=(5, 10)))
model.add(layers.Bidirectional(layers.LSTM(32)))
model.add(layers.Dense(10, activation='softmax'))

model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
bidirectional (Bidirectional (None, 5, 128)            38400     
_________________________________________________________________
bidirectional_1 (Bidirection (None, 64)                41216     
_________________________________________________________________
dense_3 (Dense)              (None, 10)                650       
=================================================================
Total params: 80,266
Trainable params: 80,266
Non-trainable params: 0
_________________________________________________________________

内部的には、Bidirectional は渡された RNN 層をコピーして新たにコピーされた層の go_backwards フィールドを反転します、その結果それは入力を反対の順序で処理します。

Bidirectional RNN の出力はデフォルトでは forward 層出力と backward 層出力の合計です。異なるマージ動作, e.g. 連結を必要とする場合には、Bidirectional ラッパー・コンストラクタで merge_mode パラメータを変更します。Bidirectional についてのより詳細は、API doc を確認してください。

 

パフォーマンス最適化と TensorFlow 2.0 の CuDNN カーネル

TensorFlow 2.0 では、組込み LSTM と GRU 層は GPU が利用可能なときデフォルトで CuDNN カーネルを活用するために更新されました。この変更で、以前の keras.layers.CuDNNLSTM/CuDNNGRU 層は deprecated となり、そして貴方はモデルをそれが動作するハードウェアについて心配することなく構築できます。

CuDNN カーネルはある仮定で構築されますので、これは組込み LSTM や GRU 層のデフォルトを変更する場合、層は CuDNN カーネルを使用できないことを意味します。例えば :

  • 活性化関数を tanh から他のものに変更する。
  • recurrent_activation 関数を sigmoid から他のものに変更する。
  • recurrent_dropout > 0 を使用する。
  • unroll を True に設定する、これは LSTM/GRU に内側の tf.while_loop を unrolled for ループに分解します。
  • use_bias を False に設定する。
  • 入力データが厳密に右パディングされないときマスキングを使用する (マスクが厳密に右パディングされたデータに対応する場合、CuDNN は依然として使用できます。これは最も一般的なケースです)。

制約の詳細なリストについては、LSTMGRU 層のためのドキュメントを見てください。

 

利用可能なとき CuDNN カーネルを使用する

パフォーマンスの違いを実演するために単純な LSTM モデルを構築しましょう。

入力シークエンスとしてMNIST 数字の行のシークエンスを使用します (ピクセルの各行を時間ステップとして扱います)、そして数字のラベルを予測します。

batch_size = 64
# Each MNIST image batch is a tensor of shape (batch_size, 28, 28).
# Each input sequence will be of size (28, 28) (height is treated like time).
input_dim = 28

units = 64
output_size = 10  # labels are from 0 to 9

# Build the RNN model
def build_model(allow_cudnn_kernel=True):
  # CuDNN is only available at the layer level, and not at the cell level.
  # This means `LSTM(units)` will use the CuDNN kernel,
  # while RNN(LSTMCell(units)) will run on non-CuDNN kernel.
  if allow_cudnn_kernel:
    # The LSTM layer with default options uses CuDNN.
    lstm_layer = tf.keras.layers.LSTM(units, input_shape=(None, input_dim))
  else:
    # Wrapping a LSTMCell in a RNN layer will not use CuDNN.
    lstm_layer = tf.keras.layers.RNN(
        tf.keras.layers.LSTMCell(units),
        input_shape=(None, input_dim))
  model = tf.keras.models.Sequential([
      lstm_layer,
      tf.keras.layers.BatchNormalization(),
      tf.keras.layers.Dense(output_size, activation='softmax')]
  )
  return model

 

MNIST データセットをロードする

mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
sample, sample_label = x_train[0], y_train[0]

 

モデルインスタンスを作成してそれをコンパイルする

モデルのための損失関数として sparse_categorical_crossentropy を選択します。モデルの出力は [batch_size, 10] の shape を持ちます。モデルのためのターゲットは整数ベクトルで、整数の各々は 0 から 9 の範囲にあります。

model = build_model(allow_cudnn_kernel=True)

model.compile(loss='sparse_categorical_crossentropy', 
              optimizer='sgd',
              metrics=['accuracy'])
model.fit(x_train, y_train,
          validation_data=(x_test, y_test),
          batch_size=batch_size,
          epochs=5)
Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 8s 126us/sample - loss: 0.9247 - accuracy: 0.7083 - val_loss: 0.5664 - val_accuracy: 0.8170
Epoch 2/5
60000/60000 [==============================] - 5s 85us/sample - loss: 0.3930 - accuracy: 0.8821 - val_loss: 0.3145 - val_accuracy: 0.9002
Epoch 3/5
60000/60000 [==============================] - 5s 85us/sample - loss: 0.2515 - accuracy: 0.9241 - val_loss: 0.2888 - val_accuracy: 0.9070
Epoch 4/5
60000/60000 [==============================] - 5s 86us/sample - loss: 0.1961 - accuracy: 0.9407 - val_loss: 0.1784 - val_accuracy: 0.9428
Epoch 5/5
60000/60000 [==============================] - 6s 93us/sample - loss: 0.1649 - accuracy: 0.9510 - val_loss: 0.1326 - val_accuracy: 0.9573

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

 

新しいモデルを CuDNN カーネルなしで構築する

slow_model = build_model(allow_cudnn_kernel=False)
slow_model.set_weights(model.get_weights())
slow_model.compile(loss='sparse_categorical_crossentropy', 
                   optimizer='sgd', 
                   metrics=['accuracy'])
slow_model.fit(x_train, y_train, 
               validation_data=(x_test, y_test), 
               batch_size=batch_size,
               epochs=1)  # We only train for one epoch because it's slower.
Train on 60000 samples, validate on 10000 samples
60000/60000 [==============================] - 21s 349us/sample - loss: 0.1457 - accuracy: 0.9567 - val_loss: 0.1881 - val_accuracy: 0.9387

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

見て取れるように、CuDNN で構築されたモデルは通常の TensorFlow カーネルを使用するモデルに比較して訓練するために遥かに高速です。

同じ CuDNN-有効モデルはまた CPU-only 環境で推論を実行するためにも使用できます。下の tf.device アノテーションは単にデバイス配置を強制します。モデルは GPU が利用可能でない場合にデフォルトで CPU 上で動作します。

最早貴方がその上で実行しているハードウェアについて心配する必要は単純にありません。Isn’t that pretty cool?

with tf.device('CPU:0'):
  cpu_model = build_model(allow_cudnn_kernel=True)
  cpu_model.set_weights(model.get_weights())
  result = tf.argmax(cpu_model.predict_on_batch(tf.expand_dims(sample, 0)), axis=1)
  print('Predicted result is: %s, target result is: %s' % (result.numpy(), sample_label))
  plt.imshow(sample, cmap=plt.get_cmap('gray'))
Predicted result is: [5], target result is: 5

 

リスト/辞書入力、またはネストされた入力を持つ RNN

ネストされた構造は実装者に単一の時間ステップ内により多くの情報を含めることを可能にします。例えば、ビデオフレームは音声とビデオ入力を同時に持てるでしょう。この場合のデータ shape は次のようなものであり得ます :

[batch, timestep, {“video”: [height, width, channel], “audio”: [frequency]}]

もう一つの例では、手書きデータは (ペンの) 圧力情報に加えて、ペンの現在の位置のための座標 x と y の両者を持てるでしょうそこでデータ表現は次のようなものでしょう :

[batch, timestep, {“location”: [x, y], “pressure”: [force]}]

以下のコードはそのような構造化入力を受け取るカスタム RNN セルをどのように構築するかの例を提供します。

 

ネストされた入力/出力をサポートするカスタムセルを定義する

NestedInput = collections.namedtuple('NestedInput', ['feature1', 'feature2'])
NestedState = collections.namedtuple('NestedState', ['state1', 'state2'])

class NestedCell(tf.keras.layers.Layer):

  def __init__(self, unit_1, unit_2, unit_3, **kwargs):
    self.unit_1 = unit_1
    self.unit_2 = unit_2
    self.unit_3 = unit_3
    self.state_size = NestedState(state1=unit_1, 
                                  state2=tf.TensorShape([unit_2, unit_3]))
    self.output_size = (unit_1, tf.TensorShape([unit_2, unit_3]))
    super(NestedCell, self).__init__(**kwargs)

  def build(self, input_shapes):
    # expect input_shape to contain 2 items, [(batch, i1), (batch, i2, i3)]
    input_1 = input_shapes.feature1[1]
    input_2, input_3 = input_shapes.feature2[1:]

    self.kernel_1 = self.add_weight(
        shape=(input_1, self.unit_1), initializer='uniform', name='kernel_1')
    self.kernel_2_3 = self.add_weight(
        shape=(input_2, input_3, self.unit_2, self.unit_3),
        initializer='uniform',
        name='kernel_2_3')

  def call(self, inputs, states):
    # inputs should be in [(batch, input_1), (batch, input_2, input_3)]
    # state should be in shape [(batch, unit_1), (batch, unit_2, unit_3)]
    input_1, input_2 = tf.nest.flatten(inputs)
    s1, s2 = states

    output_1 = tf.matmul(input_1, self.kernel_1)
    output_2_3 = tf.einsum('bij,ijkl->bkl', input_2, self.kernel_2_3)
    state_1 = s1 + output_1
    state_2_3 = s2 + output_2_3

    output = [output_1, output_2_3]
    new_states = NestedState(state1=state_1, state2=state_2_3)

    return output, new_states

 

ネストされた入力/出力を持つ RNN モデルを構築する

tf.keras.layers.RNN 層と丁度定義したカスタムセルを使用する Keras モデルを構築しましょう。

unit_1 = 10
unit_2 = 20
unit_3 = 30

input_1 = 32
input_2 = 64
input_3 = 32
batch_size = 64
num_batch = 100
timestep = 50

cell = NestedCell(unit_1, unit_2, unit_3)
rnn = tf.keras.layers.RNN(cell)

inp_1 = tf.keras.Input((None, input_1))
inp_2 = tf.keras.Input((None, input_2, input_3))

outputs = rnn(NestedInput(feature1=inp_1, feature2=inp_2))

model = tf.keras.models.Model([inp_1, inp_2], outputs)

model.compile(optimizer='adam', loss='mse', metrics=['accuracy'])

 

ランダムに生成されたデータでモデルを訓練する

このモデルのための良い候補データセットがありませんので、実演のためにランダムな Numpy データを使用します。

input_1_data = np.random.random((batch_size * num_batch, timestep, input_1))
input_2_data = np.random.random((batch_size * num_batch, timestep, input_2, input_3))
target_1_data = np.random.random((batch_size * num_batch, unit_1))
target_2_data = np.random.random((batch_size * num_batch, unit_2, unit_3))
input_data = [input_1_data, input_2_data]
target_data = [target_1_data, target_2_data]

model.fit(input_data, target_data, batch_size=batch_size)
Train on 6400 samples
6400/6400 [==============================] - 5s 804us/sample - loss: 0.3786 - rnn_1_loss: 0.1166 - rnn_1_1_loss: 0.2620 - rnn_1_accuracy: 0.0997 - rnn_1_1_accuracy: 0.0333

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

Keras tf.keras.layers.RNN 層では、貴方はシークエンス内の個々のステップのための数学ロジックを定義することが想定されるだけです、そして tf.keras.layers.RNN 層は貴方のためにシークエンス反復を処理します。それは新しい種類の RNN (e.g. LSTM 変種) を素早くプロトタイピングする非常にパワフルな方法です。

より詳細については、API doc を訪ねてください。

 

以上



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

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

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

* 本ページは、TensorFlow org サイトの Guide – Keras の以下のページを翻訳した上で
適宜、補足説明したものです:

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

ガイド : Keras :- 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

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
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

今、データの総てのバッチの開始と終わりを追跡するために単純なカスタム 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 02:22:16.589465
Training: batch 0 ends at 02:22:17.305300
Training: batch 1 begins at 02:22:17.305645
Training: batch 1 ends at 02:22:17.308415
Training: batch 2 begins at 02:22:17.308644
Training: batch 2 ends at 02:22:17.310919
Training: batch 3 begins at 02:22:17.311119
Training: batch 3 ends at 02:22:17.313492
Training: batch 4 begins at 02:22:17.313708
Training: batch 4 ends at 02:22:17.316000

 

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 02:22:17.371868
Evaluating: batch 0 ends at 02:22:17.426746
Evaluating: batch 1 begins at 02:22:17.427181
Evaluating: batch 1 ends at 02:22:17.429044
Evaluating: batch 2 begins at 02:22:17.429259
Evaluating: batch 2 ends at 02:22:17.430987
Evaluating: batch 3 begins at 02:22:17.431363
Evaluating: batch 3 ends at 02:22:17.433296
Evaluating: batch 4 begins at 02:22:17.433541
Evaluating: batch 4 ends at 02:22:17.435420

 

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   28.35.
For batch 1, loss is 1127.19.
For batch 2, loss is   19.46.
For batch 3, loss is   11.65.
For batch 4, loss is    6.62.
The average loss for epoch 0 is  238.65 and mean absolute error is    8.81.
For batch 0, loss is    6.15.
For batch 1, loss is    5.54.
For batch 2, loss is    6.50.
For batch 3, loss is    5.95.
For batch 4, loss is    4.40.
The average loss for epoch 1 is    5.71 and mean absolute error is    1.99.
For batch 0, loss is    4.47.
For batch 1, loss is    3.14.
For batch 2, loss is    4.38.
For batch 3, loss is    5.48.
For batch 4, loss is    7.15.
The average loss for epoch 2 is    4.92 and mean absolute error is    1.81.

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

_ = model.evaluate(x_test, y_test, batch_size=128, verbose=0, steps=20,
          callbacks=[LossAndErrorPrintingCallback()])
For batch 0, loss is    7.47.
For batch 1, loss is    5.97.
For batch 2, loss is    7.07.
For batch 3, loss is    6.74.
For batch 4, loss is    7.96.
For batch 5, loss is    6.84.
For batch 6, loss is    7.16.
For batch 7, loss is    6.78.
For batch 8, loss is    6.86.
For batch 9, loss is    8.03.
For batch 10, loss is    7.86.
For batch 11, loss is    8.37.
For batch 12, loss is    8.22.
For batch 13, loss is    8.79.
For batch 14, loss is    8.06.
For batch 15, loss is    6.77.
For batch 16, loss is    8.84.
For batch 17, loss is    9.73.
For batch 18, loss is    8.76.
For batch 19, loss is    6.63.

 

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   26.52.
For batch 1, loss is  931.17.
For batch 2, loss is   27.66.
For batch 3, loss is   12.00.
For batch 4, loss is    8.24.
The average loss for epoch 0 is  201.12 and mean absolute error is    8.41.
For batch 0, loss is    4.61.
For batch 1, loss is    5.70.
For batch 2, loss is    5.74.
For batch 3, loss is    4.41.
For batch 4, loss is    4.12.
The average loss for epoch 1 is    4.92 and mean absolute error is    1.77.
For batch 0, loss is    6.30.
For batch 1, loss is    5.78.
For batch 2, loss is    4.86.
For batch 3, loss is    4.71.
For batch 4, loss is    6.14.
The average loss for epoch 2 is    5.56 and mean absolute error is    1.91.
Restoring model weights from the end of the best epoch.
Epoch 00003: early stopping

 

学習率スケジューリング

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

Note: これは単なるサンプル実装です、より一般的な実装については callbacks.LearningRateSchedulerkeras.optimizers.schedules を見てください。

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   25.77.
For batch 1, loss is 1110.67.
For batch 2, loss is   19.85.
For batch 3, loss is    6.15.
For batch 4, loss is   10.22.
The average loss for epoch 0 is  234.53 and mean absolute error is    8.76.

Epoch 00001: Learning rate is 0.1000.
For batch 0, loss is    5.19.
For batch 1, loss is    6.96.
For batch 2, loss is    5.57.
For batch 3, loss is    4.37.
For batch 4, loss is    4.64.
The average loss for epoch 1 is    5.35 and mean absolute error is    1.88.

Epoch 00002: Learning rate is 0.1000.
For batch 0, loss is    6.18.
For batch 1, loss is    4.76.
For batch 2, loss is    4.18.
For batch 3, loss is    6.45.
For batch 4, loss is    5.20.
The average loss for epoch 2 is    5.36 and mean absolute error is    1.85.

Epoch 00003: Learning rate is 0.0500.
For batch 0, loss is    4.51.
For batch 1, loss is    3.69.
For batch 2, loss is    5.51.
For batch 3, loss is    5.06.
For batch 4, loss is    4.62.
The average loss for epoch 3 is    4.68 and mean absolute error is    1.74.

Epoch 00004: Learning rate is 0.0500.
For batch 0, loss is    5.08.
For batch 1, loss is    3.74.
For batch 2, loss is    7.16.
For batch 3, loss is    3.96.
For batch 4, loss is    3.88.
The average loss for epoch 4 is    4.76 and mean absolute error is    1.77.

Epoch 00005: Learning rate is 0.0500.
For batch 0, loss is    4.24.
For batch 1, loss is    5.68.
For batch 2, loss is    5.60.
For batch 3, loss is    5.19.
For batch 4, loss is    5.92.
The average loss for epoch 5 is    5.33 and mean absolute error is    1.82.

Epoch 00006: Learning rate is 0.0100.
For batch 0, loss is    8.06.
For batch 1, loss is    6.06.
For batch 2, loss is    5.00.
For batch 3, loss is    4.51.
For batch 4, loss is    4.58.
The average loss for epoch 6 is    5.64 and mean absolute error is    1.87.

Epoch 00007: Learning rate is 0.0100.
For batch 0, loss is    2.86.
For batch 1, loss is    4.01.
For batch 2, loss is    3.29.
For batch 3, loss is    4.14.
For batch 4, loss is    2.62.
The average loss for epoch 7 is    3.38 and mean absolute error is    1.44.

Epoch 00008: Learning rate is 0.0100.
For batch 0, loss is    4.35.
For batch 1, loss is    3.27.
For batch 2, loss is    4.16.
For batch 3, loss is    3.73.
For batch 4, loss is    3.89.
The average loss for epoch 8 is    3.88 and mean absolute error is    1.53.

Epoch 00009: Learning rate is 0.0050.
For batch 0, loss is    3.33.
For batch 1, loss is    4.63.
For batch 2, loss is    4.94.
For batch 3, loss is    4.90.
For batch 4, loss is    3.78.
The average loss for epoch 9 is    4.32 and mean absolute error is    1.67.

Epoch 00010: Learning rate is 0.0050.
For batch 0, loss is    3.38.
For batch 1, loss is    3.47.
For batch 2, loss is    5.72.
For batch 3, loss is    5.28.
For batch 4, loss is    3.75.
The average loss for epoch 10 is    4.32 and mean absolute error is    1.65.

Epoch 00011: Learning rate is 0.0050.
For batch 0, loss is    3.89.
For batch 1, loss is    3.62.
For batch 2, loss is    5.97.
For batch 3, loss is    5.10.
For batch 4, loss is    3.76.
The average loss for epoch 11 is    4.47 and mean absolute error is    1.64.

Epoch 00012: Learning rate is 0.0010.
For batch 0, loss is    4.98.
For batch 1, loss is    3.61.
For batch 2, loss is    2.90.
For batch 3, loss is    3.83.
For batch 4, loss is    3.35.
The average loss for epoch 12 is    3.74 and mean absolute error is    1.56.

Epoch 00013: Learning rate is 0.0010.
For batch 0, loss is    4.66.
For batch 1, loss is    3.20.
For batch 2, loss is    3.53.
For batch 3, loss is    3.40.
For batch 4, loss is    3.66.
The average loss for epoch 13 is    3.69 and mean absolute error is    1.53.

Epoch 00014: Learning rate is 0.0010.
For batch 0, loss is    2.49.
For batch 1, loss is    3.71.
For batch 2, loss is    4.35.
For batch 3, loss is    3.80.
For batch 4, loss is    5.67.
The average loss for epoch 14 is    4.01 and mean absolute error is    1.55.

 

標準的な Keras callback

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

 

以上



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

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

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

* 本ページは、TensorFlow org サイトの Guide – Keras の以下のページを翻訳した上で
適宜、補足説明したものです:

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

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

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

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

 

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

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)
Train on 60000 samples
60000/60000 [==============================] - 3s 48us/sample - loss: 0.3080
# 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, rtol=1e-6, 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
model.save('path_to_saved_model', save_format='tf')

# Recreate the exact same model
new_model = keras.models.load_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, rtol=1e-6, atol=1e-6)

# Note that the optimizer state is preserved as well:
# you can resume training where you left off.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/ops/resource_variable_ops.py:1781: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
INFO:tensorflow:Assets written to: path_to_saved_model/assets

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

  • モデル重みを含む TensorFlow チェックポイント。
  • 基礎となる TensorFlow グラフを含む SavedModel proto。

 

アーキテクチャ-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, rtol=1e-6, 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, rtol=1e-6, atol=1e-6)

# Note that the optimizer was not preserved.

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

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

 

TensorFlow チェックポイントを使用して重み-only セーブ

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

model.save_weights('path_to_my_tf_checkpoint')

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

model.save_weights('path_to_my_tf_checkpoint', 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 42us/sample - loss: 0.3062

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

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

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

* 本ページは、TensorFlow org サイトの Guide – Keras の以下のページを翻訳した上で
適宜、補足説明したものです:

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

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

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

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.03533589 -0.02663077 -0.0507721  -0.02178559]
 [-0.03533589 -0.02663077 -0.0507721  -0.02178559]], 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.03276853 -0.02655794  0.10825785  0.00806852]
 [-0.03276853 -0.02655794  0.10825785  0.00806852]], 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 ** 2)`,
# created by the `kernel_regularizer` above.
print(layer.losses)
[<tf.Tensor: id=247, shape=(), dtype=float32, numpy=0.0015638918>]

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

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

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

 

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

貴方のカスタム層が 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)
{'name': 'linear_8', 'trainable': True, 'dtype': 'float32', 'units': 64}

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

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

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

 

call メソッドの特権 training 引数

幾つかの層、特に BatchNormalization 層と Dropout 層は訓練と推論の間で異なる挙動を持ちます。そのような層のために、call メソッドで 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 モデルでは、Layer をサブクラス化した幾つかの 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)

epochs = 3

# Iterate over epochs.
for epoch in range(epochs):
  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.36229628, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(0.12543045, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(0.09899337, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(0.08906902, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(0.084127985, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(0.080847934, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.07870408, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.0770879, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.07593905, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.07492779, shape=(), dtype=float32)
Start of epoch 1
step 0: mean loss = tf.Tensor(0.07462462, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(0.073986255, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(0.073490135, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(0.07300871, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(0.072677866, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(0.07227722, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.07198606, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.07169241, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.071460575, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.07119254, shape=(), dtype=float32)
Start of epoch 2
step 0: mean loss = tf.Tensor(0.07112255, shape=(), dtype=float32)
step 100: mean loss = tf.Tensor(0.07095015, shape=(), dtype=float32)
step 200: mean loss = tf.Tensor(0.07082167, shape=(), dtype=float32)
step 300: mean loss = tf.Tensor(0.070664145, shape=(), dtype=float32)
step 400: mean loss = tf.Tensor(0.0705633, shape=(), dtype=float32)
step 500: mean loss = tf.Tensor(0.070415, shape=(), dtype=float32)
step 600: mean loss = tf.Tensor(0.07030472, shape=(), dtype=float32)
step 700: mean loss = tf.Tensor(0.07018032, shape=(), dtype=float32)
step 800: mean loss = tf.Tensor(0.07008379, shape=(), dtype=float32)
step 900: mean loss = tf.Tensor(0.06995715, 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 56us/sample - loss: 0.0745
Epoch 2/3
60000/60000 [==============================] - 3s 44us/sample - loss: 0.0676
Epoch 3/3
60000/60000 [==============================] - 3s 44us/sample - loss: 0.0676

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

 

オブジェクト指向開発を越えて : 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 [==============================] - 3s 56us/sample - loss: 0.0745
Epoch 2/3
60000/60000 [==============================] - 3s 47us/sample - loss: 0.0676
Epoch 3/3
60000/60000 [==============================] - 3s 47us/sample - loss: 0.0675

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

以上



TensorFlow 2.0 : ガイド : Keras :- Keras で訓練と評価

TensorFlow 2.0 : ガイド : Keras :- Keras で訓練と評価 (翻訳/解説)

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

* 本ページは、TensorFlow org サイトの Guide – Keras の以下のページを翻訳した上で
適宜、補足説明したものです:

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

 

ガイド : Keras :- Keras で訓練と評価

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

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

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

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

 

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

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

y_train = y_train.astype('float32')
y_test = y_test.astype('float32')

# 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 モメンタム) – RMSprop() – Adam() – 等。

損失: – MeanSquaredError() – KLDivergence() – CosineSimilarity() – 等。

メトリクス: – AUC() – Precision() – Recall() – 等。

 
カスタム損失

Keras でカスタム損失を提供するためには 2 つの方法があります。最初のサンプルは入力 y_true と y_pred を受け取る関数を作成します。次のサンプルは実データと予測の間の平均距離を計算する損失関数を示します :

def basic_loss_function(y_true, y_pred):
    return tf.math.reduce_mean(y_true - y_pred)

model.compile(optimizer=keras.optimizers.Adam(),
              loss=basic_loss_function)

model.fit(x_train, y_train, batch_size=64, epochs=3)
Train on 50000 samples
Epoch 1/3
50000/50000 [==============================] - 2s 38us/sample - loss: 4.3488
Epoch 2/3
50000/50000 [==============================] - 2s 33us/sample - loss: 4.3488
Epoch 3/3
50000/50000 [==============================] - 2s 32us/sample - loss: 4.3488

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

y_true と y_pred の他のパラメータを取る損失関数を必要とする場合、tf.keras.losses.Loss クラスをサブクラス化して次の 2 つのメソッドを実装することができます :

  • __init__(self) — 損失関数の呼び出しの間に渡すためのパラメータを受け取ります。
  • call(self, y_true, y_pred) — モデルの損失を計算するためにターゲット (y_true) とモデル予測 (y_pred) を使用します。

__init__() に渡されるパラメータは損失を計算するとき call() の間に使用できます。

次のサンプルはBinaryCrossEntropy 損失を計算する WeightedCrossEntropy 損失関数をどのように実装するかを示します、そこではあるクラスの損失か総ての関数がスカラーにより変更できます。

class WeightedBinaryCrossEntropy(keras.losses.Loss):
    """
    Args:
      pos_weight: Scalar to affect the positive labels of the loss function.
      weight: Scalar to affect the entirety of the loss function.
      from_logits: Whether to compute loss form logits or the probability.
      reduction: Type of tf.keras.losses.Reduction to apply to loss.
      name: Name of the loss function.
    """
    def __init__(self, pos_weight, weight, from_logits=False,
                 reduction=keras.losses.Reduction.AUTO,
                 name='weighted_binary_crossentropy'):
        super(WeightedBinaryCrossEntropy, self).__init__(reduction=reduction,
                                                         name=name)
        self.pos_weight = pos_weight
        self.weight = weight
        self.from_logits = from_logits

    def call(self, y_true, y_pred):
        if not self.from_logits:
            # Manually calculate the weighted cross entropy.
            # Formula is qz * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))
            # where z are labels, x is logits, and q is the weight.
            # Since the values passed are from sigmoid (assuming in this case)
            # sigmoid(x) will be replaced by y_pred

            # qz * -log(sigmoid(x)) 1e-6 is added as an epsilon to stop passing a zero into the log
            x_1 = y_true * self.pos_weight * -tf.math.log(y_pred + 1e-6)

            # (1 - z) * -log(1 - sigmoid(x)). Epsilon is added to prevent passing a zero into the log
            x_2 = (1 - y_true) * -tf.math.log(1 - y_pred + 1e-6)

            return tf.add(x_1, x_2) * self.weight 

        # Use built in function
        return tf.nn.weighted_cross_entropy_with_logits(y_true, y_pred, self.pos_weight) * self.weight


model.compile(optimizer=keras.optimizers.Adam(),
              loss=WeightedBinaryCrossEntropy(0.5, 2))

model.fit(x_train, y_train, batch_size=64, epochs=3)
Train on 50000 samples
Epoch 1/3
50000/50000 [==============================] - 2s 42us/sample - loss: 10.0479
Epoch 2/3
50000/50000 [==============================] - 2s 36us/sample - loss: 9.5181
Epoch 3/3
50000/50000 [==============================] - 2s 36us/sample - loss: 9.5174

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

 
カスタム・メトリクス

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 CategoricalTruePositives(keras.metrics.Metric):

    def __init__(self, name='categorical_true_positives', **kwargs):
      super(CategoricalTruePositives, 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.reshape(tf.argmax(y_pred, axis=1), shape=(-1, 1))
      values = 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=[CategoricalTruePositives()])
model.fit(x_train, y_train,
          batch_size=64,
          epochs=3)
Train on 50000 samples
Epoch 1/3
50000/50000 [==============================] - 2s 49us/sample - loss: 0.2017 - categorical_true_positives: 46988.0000
Epoch 2/3
50000/50000 [==============================] - 2s 41us/sample - loss: 0.0853 - categorical_true_positives: 48723.0000
Epoch 3/3
50000/50000 [==============================] - 2s 41us/sample - loss: 0.0703 - categorical_true_positives: 48949.0000

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

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

損失とメトリクスの圧倒的な大部分は 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 49us/sample - loss: 2.4801

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

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 48us/sample - loss: 0.3369 - std_of_activation: 1.0045

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

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 58us/sample - loss: 2.5077 - std_of_activation: 0.0020

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

 
自動的に検証取り置き (= 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=1, steps_per_epoch=1)
Train on 40000 samples, validate on 10000 samples
   64/40000 [..............................] - ETA: 7:57 - loss: 2.3474 - sparse_categorical_accuracy: 0.1875 - val_loss: 0.0000e+00 - val_sparse_categorical_accuracy: 0.0000e+00
<tensorflow.python.keras.callbacks.History at 0x7f3b6c34f940>

 

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.3421 - sparse_categorical_accuracy: 0.9039
Epoch 2/3
782/782 [==============================] - 2s 3ms/step - loss: 0.1603 - sparse_categorical_accuracy: 0.9521
Epoch 3/3
782/782 [==============================] - 2s 3ms/step - loss: 0.1166 - sparse_categorical_accuracy: 0.9647

# Evaluate
157/157 [==============================] - 0s 2ms/step - loss: 0.1380 - sparse_categorical_accuracy: 0.9567

[0.1379790138586715, 0.9567]

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 10ms/step - loss: 0.8005 - sparse_categorical_accuracy: 0.7956
Epoch 2/3
100/100 [==============================] - 1s 5ms/step - loss: 0.3299 - sparse_categorical_accuracy: 0.9091
Epoch 3/3
100/100 [==============================] - 0s 5ms/step - loss: 0.2584 - sparse_categorical_accuracy: 0.9286

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

 
検証 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 5ms/step - loss: 0.3376 - sparse_categorical_accuracy: 0.9043 - val_loss: 0.0000e+00 - val_sparse_categorical_accuracy: 0.0000e+00
Epoch 2/3
782/782 [==============================] - 3s 4ms/step - loss: 0.1623 - sparse_categorical_accuracy: 0.9523 - val_loss: 0.1406 - val_sparse_categorical_accuracy: 0.9602
Epoch 3/3
782/782 [==============================] - 3s 4ms/step - loss: 0.1213 - sparse_categorical_accuracy: 0.9638 - val_loss: 0.1233 - val_sparse_categorical_accuracy: 0.9632

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

各エポックの最後に、モデルは検証 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.3322 - sparse_categorical_accuracy: 0.9068 - val_loss: 0.0000e+00 - val_sparse_categorical_accuracy: 0.0000e+00
Epoch 2/3
782/782 [==============================] - 2s 3ms/step - loss: 0.1554 - sparse_categorical_accuracy: 0.9540 - val_loss: 0.2302 - val_sparse_categorical_accuracy: 0.9375
Epoch 3/3
782/782 [==============================] - 2s 3ms/step - loss: 0.1146 - sparse_categorical_accuracy: 0.9665 - val_loss: 0.1813 - val_sparse_categorical_accuracy: 0.9484

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

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

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

 

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

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

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

 

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

入力データとターゲットデータの他にも、fit を使用するときモデルにサンプル重みやクラス重みを渡すことも可能です :

  • Numpy データから訓練するとき: sample_weight と class_weight 引数を通して。
  • 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.}
print('Fit with class weight')
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.
print('\nFit with sample weight')

model = get_compiled_model()
model.fit(x_train, y_train,
          sample_weight=sample_weight,
          batch_size=64,
          epochs=4)
Fit with class weight
Train on 50000 samples
Epoch 1/4
50000/50000 [==============================] - 3s 52us/sample - loss: 0.1035 - sparse_categorical_accuracy: 0.9710
Epoch 2/4
50000/50000 [==============================] - 2s 46us/sample - loss: 0.0858 - sparse_categorical_accuracy: 0.9763
Epoch 3/4
50000/50000 [==============================] - 2s 46us/sample - loss: 0.0744 - sparse_categorical_accuracy: 0.9788
Epoch 4/4
50000/50000 [==============================] - 2s 46us/sample - loss: 0.0645 - sparse_categorical_accuracy: 0.9821

Fit with sample weight
Train on 50000 samples
Epoch 1/4
50000/50000 [==============================] - 3s 56us/sample - loss: 0.3751 - sparse_categorical_accuracy: 0.9004
Epoch 2/4
50000/50000 [==============================] - 2s 46us/sample - loss: 0.1703 - sparse_categorical_accuracy: 0.9530
Epoch 3/4
50000/50000 [==============================] - 2s 46us/sample - loss: 0.1269 - sparse_categorical_accuracy: 0.9646
Epoch 4/4
50000/50000 [==============================] - 2s 45us/sample - loss: 0.1032 - sparse_categorical_accuracy: 0.9713

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

ここに適合している 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.3659 - sparse_categorical_accuracy: 0.9045
Epoch 2/3
782/782 [==============================] - 3s 3ms/step - loss: 0.1710 - sparse_categorical_accuracy: 0.9529
Epoch 3/3
782/782 [==============================] - 3s 3ms/step - loss: 0.1244 - sparse_categorical_accuracy: 0.9652

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

 

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

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

次のモデルを考えます、それは shape (32, 32, 3) (それは (高さ、幅、チャネル)) の画像入力と shape (None, 10) (それは (時間ステップ, 特徴)) の時系列入力を持ちます。私達のモデルはこれらの入力の組み合わせから計算された 2 つの出力を持ちます : (shape (1,) の) 「スコア」と (shape (5,) の) 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 引数を使用して、異なる出力固有の損失に異なる重みを与えることも可能です (例えば、私達の例の「スコア」損失にクラス損失の 2 倍の重要性を与えることにより、特権を与えることを望むかもしれません) :

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_weights={'score_output': 2., 'class_output': 1.})

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

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

# Or dict loss version
model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={'class_output': keras.losses.CategoricalCrossentropy()})
WARNING:tensorflow: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)

# Alternatively, 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 [==============================] - 3s 28ms/sample - loss: 7.5105 - score_output_loss: 2.7916 - class_output_loss: 4.5724
Epoch 2/3
100/100 [==============================] - 0s 210us/sample - loss: 6.5208 - score_output_loss: 1.7519 - class_output_loss: 4.8270
Epoch 3/3
100/100 [==============================] - 0s 202us/sample - loss: 6.0499 - score_output_loss: 1.0947 - class_output_loss: 5.2064
Train on 100 samples
Epoch 1/3
100/100 [==============================] - 0s 307us/sample - loss: 5.7626 - score_output_loss: 0.8008 - class_output_loss: 4.9479
Epoch 2/3
100/100 [==============================] - 0s 200us/sample - loss: 5.5649 - score_output_loss: 0.6005 - class_output_loss: 4.7794
Epoch 3/3
100/100 [==============================] - 0s 188us/sample - loss: 5.4176 - score_output_loss: 0.4619 - class_output_loss: 5.0290

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

ここに 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 238ms/step - loss: 5.4252 - score_output_loss: 0.3387 - class_output_loss: 5.0865
Epoch 2/3
2/2 [==============================] - 0s 12ms/step - loss: 5.2802 - score_output_loss: 0.2856 - class_output_loss: 5.0243
Epoch 3/3
2/2 [==============================] - 0s 10ms/step - loss: 5.2754 - score_output_loss: 0.2699 - class_output_loss: 4.9820

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

 

コールバックを使用する

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 [==============================] - 3s 64us/sample - loss: 0.3884 - sparse_categorical_accuracy: 0.8889 - val_loss: 0.2472 - val_sparse_categorical_accuracy: 0.9264
Epoch 2/20
40000/40000 [==============================] - 2s 43us/sample - loss: 0.1775 - sparse_categorical_accuracy: 0.9481 - val_loss: 0.1777 - val_sparse_categorical_accuracy: 0.9472
Epoch 3/20
40000/40000 [==============================] - 2s 43us/sample - loss: 0.1276 - sparse_categorical_accuracy: 0.9624 - val_loss: 0.1595 - val_sparse_categorical_accuracy: 0.9520
Epoch 4/20
40000/40000 [==============================] - 2s 44us/sample - loss: 0.1009 - sparse_categorical_accuracy: 0.9694 - val_loss: 0.1451 - val_sparse_categorical_accuracy: 0.9598
Epoch 5/20
40000/40000 [==============================] - 2s 44us/sample - loss: 0.0826 - sparse_categorical_accuracy: 0.9748 - val_loss: 0.1361 - val_sparse_categorical_accuracy: 0.9607
Epoch 6/20
40000/40000 [==============================] - 2s 44us/sample - loss: 0.0709 - sparse_categorical_accuracy: 0.9786 - val_loss: 0.1339 - val_sparse_categorical_accuracy: 0.9622
Epoch 7/20
40000/40000 [==============================] - 2s 43us/sample - loss: 0.0603 - sparse_categorical_accuracy: 0.9815 - val_loss: 0.1459 - val_sparse_categorical_accuracy: 0.9599
Epoch 8/20
40000/40000 [==============================] - 2s 44us/sample - loss: 0.0525 - sparse_categorical_accuracy: 0.9844 - val_loss: 0.1366 - val_sparse_categorical_accuracy: 0.9657
Epoch 00008: early stopping

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

 
多くの組み込み 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
39872/40000 [============================>.] - ETA: 0s - loss: 0.3715 - sparse_categorical_accuracy: 0.8971
Epoch 00001: val_loss improved from inf to 0.22463, saving model to mymodel_1.h5
40000/40000 [==============================] - 2s 55us/sample - loss: 0.3713 - sparse_categorical_accuracy: 0.8971 - val_loss: 0.2246 - val_sparse_categorical_accuracy: 0.9329
Epoch 2/3
38592/40000 [===========================>..] - ETA: 0s - loss: 0.1751 - sparse_categorical_accuracy: 0.9485
Epoch 00002: val_loss improved from 0.22463 to 0.19152, saving model to mymodel_2.h5
40000/40000 [==============================] - 2s 44us/sample - loss: 0.1743 - sparse_categorical_accuracy: 0.9490 - val_loss: 0.1915 - val_sparse_categorical_accuracy: 0.9436
Epoch 3/3
38592/40000 [===========================>..] - ETA: 0s - loss: 0.1279 - sparse_categorical_accuracy: 0.9621
Epoch 00003: val_loss improved from 0.19152 to 0.15396, saving model to mymodel_3.h5
40000/40000 [==============================] - 2s 44us/sample - loss: 0.1279 - sparse_categorical_accuracy: 0.9620 - val_loss: 0.1540 - val_sparse_categorical_accuracy: 0.9558

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

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

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

 

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

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

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

 
スケジュールを optimizer に渡す

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

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

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

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

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

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

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

 

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

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

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

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

tensorboard --logdir=/full_path_to_your_logs

 
TensorBoard callback を使用する

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

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

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

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

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

 

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

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

 

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

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

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

# 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.
epochs = 3
for epoch in range(epochs):
  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.3747615814208984
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.264816999435425
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.1977667808532715
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.085158586502075
Seen so far: 38464 samples
Start of epoch 1
Training loss (for one batch) at step 0: 2.0625901222229004
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.97736656665802
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.9285550117492676
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.7810806035995483
Seen so far: 38464 samples
Start of epoch 2
Training loss (for one batch) at step 0: 1.7103501558303833
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.4607808589935303
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.5318548679351807
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.3345484733581543
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.
epochs = 3
for epoch in range(epochs):
  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.32742977142334
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.27957820892334
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.223424196243286
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.1545217037200928
Seen so far: 38464 samples
Training acc over epoch: 0.22378000617027283
Validation acc: 0.3995000123977661
Start of epoch 1
Training loss (for one batch) at step 0: 2.0469183921813965
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.9954742193222046
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.8717031478881836
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.8559406995773315
Seen so far: 38464 samples
Training acc over epoch: 0.5212399959564209
Validation acc: 0.6148999929428101
Start of epoch 2
Training loss (for one batch) at step 0: 1.8021643161773682
Seen so far: 64 samples
Training loss (for one batch) at step 200: 1.7000391483306885
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 1.4835299253463745
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 1.3361830711364746
Seen so far: 38464 samples
Training acc over epoch: 0.6601999998092651
Validation acc: 0.7210999727249146

 

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

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

訓練の間にこれらの損失を考慮するには、貴方が行なわなければならない総てのことは貴方の訓練ループをトータル損失に sum(model.losses) を追加するように変更することです :

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

epochs = 3
for epoch in range(epochs):
  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: 10.064319610595703
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.5281362533569336
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.400449275970459
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.380073308944702
Seen so far: 38464 samples
Start of epoch 1
Training loss (for one batch) at step 0: 2.3233108520507812
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.325932025909424
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.326887845993042
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.3155736923217773
Seen so far: 38464 samples
Start of epoch 2
Training loss (for one batch) at step 0: 2.3195061683654785
Seen so far: 64 samples
Training loss (for one batch) at step 200: 2.316711187362671
Seen so far: 12864 samples
Training loss (for one batch) at step 400: 2.3118293285369873
Seen so far: 25664 samples
Training loss (for one batch) at step 600: 2.311530828475952
Seen so far: 38464 samples
 

以上



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

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

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

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

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

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

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf

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

 

イントロダクション

貴方はモデルを作成するために既に 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=2)
print('Test loss:', test_scores[0])
print('Test accuracy:', test_scores[1])
Train on 48000 samples, validate on 12000 samples
Epoch 1/5
48000/48000 [==============================] - 3s 62us/sample - loss: 0.3500 - accuracy: 0.9003 - val_loss: 0.1853 - val_accuracy: 0.9460
Epoch 2/5
48000/48000 [==============================] - 2s 42us/sample - loss: 0.1686 - accuracy: 0.9496 - val_loss: 0.1413 - val_accuracy: 0.9575
Epoch 3/5
48000/48000 [==============================] - 2s 41us/sample - loss: 0.1225 - accuracy: 0.9625 - val_loss: 0.1227 - val_accuracy: 0.9633
Epoch 4/5
48000/48000 [==============================] - 2s 41us/sample - loss: 0.0975 - accuracy: 0.9708 - val_loss: 0.1179 - val_accuracy: 0.9657
Epoch 5/5
48000/48000 [==============================] - 2s 41us/sample - loss: 0.0814 - accuracy: 0.9763 - val_loss: 0.1152 - val_accuracy: 0.9690
10000/1 - 0s - loss: 0.0532 - accuracy: 0.9684
Test loss: 0.10518244824614376
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 [==============================] - 5s 4ms/sample - loss: 1.3011 - priority_loss: 0.7052 - department_loss: 2.9798
Epoch 2/2
1280/1280 [==============================] - 1s 406us/sample - loss: 1.2821 - priority_loss: 0.7023 - department_loss: 2.8992

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

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 [==============================] - 5s 129us/sample - loss: 1.9248 - acc: 0.2659 - val_loss: 1.6335 - val_acc: 0.3826

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

 

層を共有する

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, …):”, 等々。

以下を :

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 : ガイド : Keras :- Keras 概要

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

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

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

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

Keras :- Keras 概要

tf.keras をインポートする

tf.kerasKeras API 仕様 の TensorFlow 実装です。これはモデルを構築して訓練するための高位 API で、eager execution, tf.data パイプラインと Estimator のような TensorFlow-固有の機能のための first-class サポートを含みます。tf.keras は柔軟性やパフォーマンスを犠牲にすることなく TensorFlow を容易に利用できるようにします。

始めるには、貴方の TensorFlow プログラム・セットアップの一部として tf.keras をインポートします :

from __future__ import absolute_import, division, print_function, unicode_literals

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′ を渡してください (あるいは .h5 で終わるファイル名を渡します)。

 

単純なモデルを構築する

Sequential モデル

Keras では、モデルを構築するために層を集めます。モデルは (通常は) 層のグラフです。モデルの最も一般的なタイプは層のスタック : tf.keras.Sequential モデルです。

単純な、完全結合ネットワーク (i.e. 多層パーセプトロン) を構築するには :

from tensorflow.keras import layers

model = tf.keras.Sequential()
# Adds a densely-connected layer with 64 units to the model:
model.add(layers.Dense(64, activation='relu'))
# Add another:
model.add(layers.Dense(64, activation='relu'))
# Add a softmax layer with 10 output units:
model.add(layers.Dense(10, activation='softmax'))

Sequential モデルをどのように使用するかの完全な、短いサンプルは ここ で見つけられます。

Sequential モデルよりも進んだモデルの構築について学習するためには、 Guide to the Keras Functional – Guide to writing layers and models from scratch with subclassing を見てください。

 

層を configure する

利用可能な多くの tf.keras.layers があります。それらの殆どは幾つかの一般的なコンストラクタ引数を共有します :

  • activation: 層のための活性化関数を設定します。このパラメータは組み込み関数の名前か callable オブジェクトとして指定されます。デフォルトでは、activation は適用されません。
  • kernel_initializerbias_initializer: 層の重み (カーネルとバイアス) を作成する初期化スキームです。このパラメータは名前か callable オブジェクトです。これは “Glorot uniform” initializer がデフォルトです。
  • kernel_regularizebias_regularizer: L1 や L2 正則化のような、層の重み (カーネルとバイアス) に適用される正則化スキームです。デフォルトでは、正則化は適用されません。

以下はコンストラクタ引数を使用して tf.keras.layers.Dense 層をインスタンス化します :

# Create a sigmoid layer:
layers.Dense(64, activation='sigmoid')
# Or:
layers.Dense(64, activation=tf.keras.activations.sigmoid)

# A linear layer with L1 regularization of factor 0.01 applied to the kernel matrix:
layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))

# A linear layer with L2 regularization of factor 0.01 applied to the bias vector:
layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))

# A linear layer with a kernel initialized to a random orthogonal matrix:
layers.Dense(64, kernel_initializer='orthogonal')

# A linear layer with a bias vector initialized to 2.0s:
layers.Dense(64, bias_initializer=tf.keras.initializers.Constant(2.0))
<tensorflow.python.keras.layers.core.Dense at 0x7fe74f13eac8>

 

訓練と評価

訓練のセットアップ

モデルが構築された後、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.01),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

tf.keras.Model.compile は 3 つの重要な引数を取ります :

  • optimizer: このオブジェクトは訓練手続きを指定します。tf.keras.optimizers.Adamtf.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 です。
  • 追加として、モデルが eager モードで訓練して評価されることを確かなものにするために、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 941us/sample - loss: 292.6379 - categorical_accuracy: 0.1150
Epoch 2/10
1000/1000 [==============================] - 0s 79us/sample - loss: 1195.7818 - categorical_accuracy: 0.0940
Epoch 3/10
1000/1000 [==============================] - 0s 79us/sample - loss: 2382.5031 - categorical_accuracy: 0.0850
Epoch 4/10
1000/1000 [==============================] - 0s 77us/sample - loss: 4133.6880 - categorical_accuracy: 0.0900
Epoch 5/10
1000/1000 [==============================] - 0s 78us/sample - loss: 6172.5911 - categorical_accuracy: 0.1010
Epoch 6/10
1000/1000 [==============================] - 0s 77us/sample - loss: 8432.6698 - categorical_accuracy: 0.1120
Epoch 7/10
1000/1000 [==============================] - 0s 79us/sample - loss: 11067.2603 - categorical_accuracy: 0.0920
Epoch 8/10
1000/1000 [==============================] - 0s 80us/sample - loss: 14060.8606 - categorical_accuracy: 0.0980
Epoch 9/10
1000/1000 [==============================] - 0s 76us/sample - loss: 17402.4838 - categorical_accuracy: 0.0930
Epoch 10/10
1000/1000 [==============================] - 0s 77us/sample - loss: 21134.0576 - categorical_accuracy: 0.0910

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

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 178us/sample - loss: 24941.5426 - categorical_accuracy: 0.1110 - val_loss: 22170.3123 - val_categorical_accuracy: 0.0800
Epoch 2/10
1000/1000 [==============================] - 0s 95us/sample - loss: 27946.4825 - categorical_accuracy: 0.0990 - val_loss: 22531.8664 - val_categorical_accuracy: 0.1000
Epoch 3/10
1000/1000 [==============================] - 0s 95us/sample - loss: 33313.0864 - categorical_accuracy: 0.0960 - val_loss: 40268.1456 - val_categorical_accuracy: 0.1300
Epoch 4/10
1000/1000 [==============================] - 0s 93us/sample - loss: 39161.3360 - categorical_accuracy: 0.1050 - val_loss: 33861.4163 - val_categorical_accuracy: 0.1000
Epoch 5/10
1000/1000 [==============================] - 0s 100us/sample - loss: 44375.6921 - categorical_accuracy: 0.1040 - val_loss: 46568.1248 - val_categorical_accuracy: 0.0700
Epoch 6/10
1000/1000 [==============================] - 0s 95us/sample - loss: 48923.2586 - categorical_accuracy: 0.0970 - val_loss: 81242.8403 - val_categorical_accuracy: 0.0700
Epoch 7/10
1000/1000 [==============================] - 0s 94us/sample - loss: 58926.1624 - categorical_accuracy: 0.0910 - val_loss: 67530.7906 - val_categorical_accuracy: 0.0900
Epoch 8/10
1000/1000 [==============================] - 0s 92us/sample - loss: 61365.5712 - categorical_accuracy: 0.1020 - val_loss: 73100.3025 - val_categorical_accuracy: 0.1400
Epoch 9/10
1000/1000 [==============================] - 0s 94us/sample - loss: 71333.1506 - categorical_accuracy: 0.0980 - val_loss: 71719.7863 - val_categorical_accuracy: 0.1400
Epoch 10/10
1000/1000 [==============================] - 0s 94us/sample - loss: 74634.9986 - categorical_accuracy: 0.1100 - val_loss: 67717.8081 - val_categorical_accuracy: 0.1100

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

 

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)

model.fit(dataset, epochs=10)
Epoch 1/10
32/32 [==============================] - 0s 4ms/step - loss: 81467.1696 - categorical_accuracy: 0.1040
Epoch 2/10
32/32 [==============================] - 0s 3ms/step - loss: 95568.6485 - categorical_accuracy: 0.0970
Epoch 3/10
32/32 [==============================] - 0s 3ms/step - loss: 97389.2383 - categorical_accuracy: 0.1060
Epoch 4/10
32/32 [==============================] - 0s 3ms/step - loss: 104265.7252 - categorical_accuracy: 0.0950
Epoch 5/10
32/32 [==============================] - 0s 3ms/step - loss: 116323.9572 - categorical_accuracy: 0.1010
Epoch 6/10
32/32 [==============================] - 0s 3ms/step - loss: 124712.1657 - categorical_accuracy: 0.1090
Epoch 7/10
32/32 [==============================] - 0s 3ms/step - loss: 132602.0694 - categorical_accuracy: 0.0930
Epoch 8/10
32/32 [==============================] - 0s 3ms/step - loss: 144176.1806 - categorical_accuracy: 0.0930
Epoch 9/10
32/32 [==============================] - 0s 3ms/step - loss: 151150.5936 - categorical_accuracy: 0.1160
Epoch 10/10
32/32 [==============================] - 0s 3ms/step - loss: 163552.8846 - categorical_accuracy: 0.1040

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

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)
Epoch 1/10
32/32 [==============================] - 0s 4ms/step - loss: 174823.2109 - categorical_accuracy: 0.1040 - val_loss: 0.0000e+00 - val_categorical_accuracy: 0.0000e+00
Epoch 2/10
32/32 [==============================] - 0s 3ms/step - loss: 182694.2235 - categorical_accuracy: 0.0920 - val_loss: 207405.1328 - val_categorical_accuracy: 0.1000
Epoch 3/10
32/32 [==============================] - 0s 3ms/step - loss: 198014.8274 - categorical_accuracy: 0.0910 - val_loss: 254707.6133 - val_categorical_accuracy: 0.0900
Epoch 4/10
32/32 [==============================] - 0s 3ms/step - loss: 210588.8274 - categorical_accuracy: 0.1020 - val_loss: 169632.5312 - val_categorical_accuracy: 0.1300
Epoch 5/10
32/32 [==============================] - 0s 3ms/step - loss: 219237.8966 - categorical_accuracy: 0.1020 - val_loss: 213207.9141 - val_categorical_accuracy: 0.1300
Epoch 6/10
32/32 [==============================] - 0s 3ms/step - loss: 234645.3691 - categorical_accuracy: 0.1040 - val_loss: 273672.8672 - val_categorical_accuracy: 0.0900
Epoch 7/10
32/32 [==============================] - 0s 3ms/step - loss: 246899.7756 - categorical_accuracy: 0.1050 - val_loss: 215929.5312 - val_categorical_accuracy: 0.1000
Epoch 8/10
32/32 [==============================] - 0s 3ms/step - loss: 257410.2769 - categorical_accuracy: 0.1090 - val_loss: 303968.7422 - val_categorical_accuracy: 0.1300
Epoch 9/10
32/32 [==============================] - 0s 3ms/step - loss: 273098.0448 - categorical_accuracy: 0.0910 - val_loss: 279622.2812 - val_categorical_accuracy: 0.0900
Epoch 10/10
32/32 [==============================] - 0s 3ms/step - loss: 291685.4157 - categorical_accuracy: 0.0890 - val_loss: 277359.3203 - val_categorical_accuracy: 0.1000

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

 

評価と予測

tf.keras.Model.evaluatetf.keras.Model.predict メソッドは NumPy データと tf.data.Dataset を使用できます。

提供されたデータのために推論モードの損失とメトリクスをどのように評価するがここにあります :

# With Numpy arrays
data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

model.evaluate(data, labels, batch_size=32)

# With a Dataset
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)

model.evaluate(dataset)
1000/1 [========= ... =========] - 0s 53us/sample - loss: 249148.8575 - categorical_accuracy: 0.0960
32/32 [==============================] - 0s 2ms/step - loss: 262983.5693 - categorical_accuracy: 0.0960

[262983.5693359375, 0.096]

そして提供されたデータのために推論で最後の層の出力をどのように予測するかがここにあります、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 472us/sample - loss: 14.3942 - accuracy: 0.0980
Epoch 2/5
1000/1000 [==============================] - 0s 83us/sample - loss: 29.4704 - accuracy: 0.1010
Epoch 3/5
1000/1000 [==============================] - 0s 80us/sample - loss: 54.4626 - accuracy: 0.1120
Epoch 4/5
1000/1000 [==============================] - 0s 78us/sample - loss: 87.0312 - accuracy: 0.1020
Epoch 5/5
1000/1000 [==============================] - 0s 80us/sample - loss: 125.3873 - accuracy: 0.1160

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

 

Model サブクラス化

tf.keras.Model のサブクラス化と貴方自身の forward パスを定義することにより完全にカスタマイズ可能なモデルを構築します。__init__ メソッドで層を作成してそれらをクラス・インスタンスの属性として設定します。call メソッドで forward パスを定義します。

Model サブクラス化は eager execution が有効なときに特に有用です、何故ならばそれは forward パスが命令的に書かれることを可能にするからです。

Note: 貴方のモデルが常に命令的に実行されることが必要な場合には、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 [==============================] - 1s 572us/sample - loss: 11.4854 - accuracy: 0.0910
Epoch 2/5
1000/1000 [==============================] - 0s 78us/sample - loss: 11.4548 - accuracy: 0.0910
Epoch 3/5
1000/1000 [==============================] - 0s 83us/sample - loss: 11.4474 - accuracy: 0.1060
Epoch 4/5
1000/1000 [==============================] - 0s 81us/sample - loss: 11.4434 - accuracy: 0.1070
Epoch 5/5
1000/1000 [==============================] - 0s 80us/sample - loss: 11.4400 - accuracy: 0.1070

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

 

カスタム層

tf.keras.layers.Layer をサブクラス化して次のメソッドを実装することでカスタム層を作成します :

  • __init__: オプションでこの層により使用される部分層 (= sublayers) を定義します。
  • 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 289us/sample - loss: 11.4484 - accuracy: 0.1000
Epoch 2/5
1000/1000 [==============================] - 0s 62us/sample - loss: 11.4486 - accuracy: 0.1030
Epoch 3/5
1000/1000 [==============================] - 0s 65us/sample - loss: 11.4487 - accuracy: 0.1010
Epoch 4/5
1000/1000 [==============================] - 0s 64us/sample - loss: 11.4484 - accuracy: 0.1030
Epoch 5/5
1000/1000 [==============================] - 0s 64us/sample - loss: 11.4483 - accuracy: 0.1000

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

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))
Train on 1000 samples, validate on 100 samples
Epoch 1/5
1000/1000 [==============================] - 0s 157us/sample - loss: 11.4479 - accuracy: 0.1000 - val_loss: 11.7400 - val_accuracy: 0.1400
Epoch 2/5
1000/1000 [==============================] - 0s 82us/sample - loss: 11.4479 - accuracy: 0.1010 - val_loss: 11.7400 - val_accuracy: 0.1400
Epoch 3/5
1000/1000 [==============================] - 0s 79us/sample - loss: 11.4478 - accuracy: 0.1040 - val_loss: 11.7404 - val_accuracy: 0.1400
Epoch 4/5
1000/1000 [==============================] - 0s 80us/sample - loss: 11.4478 - accuracy: 0.1030 - val_loss: 11.7407 - val_accuracy: 0.1400

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

 

セーブとリストア

単に重み値をセーブする

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

デフォルトでは、これはモデルの重みを 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 をセーブする

モデルの configuration はセーブできます — これはどのような重みもなしでモデル・アーキテクチャをシリアライズします。セーブされた configuration は、元のモデルを定義したコードなしでさえも同じモデルを再作成して初期化できます。Keras は JSON と YAML シリアライゼーション形式をサポートします :

# Serialize a model to JSON format
json_string = model.to_json()
json_string
'{"class_name": "Sequential", "config": {"name": "sequential_3", "layers": [{"class_name": "Dense", "config": {"name": "dense_17", "trainable": true, "batch_input_shape": [null, 32], "dtype": "float32", "units": 64, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Dense", "config": {"name": "dense_18", "trainable": true, "dtype": "float32", "units": 10, "activation": "softmax", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}]}, "keras_version": "2.2.4-tf", "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 404us/sample - loss: 11.4749 - accuracy: 0.1130
Epoch 2/5
1000/1000 [==============================] - 0s 73us/sample - loss: 11.4722 - accuracy: 0.1040
Epoch 3/5
1000/1000 [==============================] - 0s 75us/sample - loss: 11.4959 - accuracy: 0.1060
Epoch 4/5
1000/1000 [==============================] - 0s 73us/sample - loss: 11.5455 - accuracy: 0.0990
Epoch 5/5
1000/1000 [==============================] - 0s 74us/sample - loss: 11.5848 - accuracy: 0.0970

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 : ガイド : 効果的な TensorFlow 2.0

TensorFlow 2.0 : ガイド : 効果的な TensorFlow 2.0 (翻訳/解説)

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

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

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

効果的な TensorFlow 2.0

TensorFlow ユーザをより生産力を高くするために TensorFlow 2.0 で複数の変更があります。TensorFlow 2.0 は 冗長な API を除去し、API をより首尾一貫性のあるものとし (統一 (= unified) RNN, 統一 Optimizer)、そして Eager execution で Python ランタイムとより良く統合します。

多くの RFC は TensorFlow 2.0 の作成に注がれてきた変更を説明しています。このガイドは TensorFlow 2.0 の開発がどのようなものであるべきかのビジョンを提示します。貴方が TensorFlow 1.x への何某かの馴染みを持つことを仮定しています。

 

主要な変更の簡潔な要約

API クリーンアップ

多くの API が なくなるか TF 2.0 に移されました。主要な変更の幾つかは、新しいオープンソース absl-py を支持して tf.app, tf.flags と tf.logging の除去、tf.contrib に存在するプロジェクトの rehome (ホーム変更)、そしてあまり使用されない関数を tf.math のようなサブパッケージに移動することによる主要な tf.* 名前空間のクリーンアップを含みます。幾つかの API はそれらの 2.0 等値である – tf.summary, tf.keras.metricstf.keras.optimizers に置き換えられました。これらの名前変更を自動的に適用する最も容易な方法は v2 upgrade スクリプト を利用することです。

 

Eager execution

TensorFlow 1.X は tf.* API コールによりユーザに 抽象構文木 (グラフ) を手動で一緒に縫い合わせる (= stitch) ことを要求しました。そしてそれは出力 tensor と入力 tensor のセットを session.run() コールに渡すことにより抽象構文木を手動でコンパイルすることをユーザに要求します。TensorFlow 2.0 は (Python が通常行なうように) 逐次命令的に(= eagerly) 実行します、そして 2.0 ではグラフとセッションは実装詳細のような感じです。

eager execution の一つの注目すべき副産物は tf.control_dependencies() はもはや必要とされないことです、何故ならばコードの総ての行は順番に実行されるからです (tf.function 内で、副作用を持つコードは書かれた順番に実行されます)。

 

No more globals

TensorFlow 1.X は大域 (= global) 名前空間に暗黙的に大きく依拠していました。tf.Variable() を呼び出すとき、それはデフォルト・グラフに置かれ、それを指す Python 変数の追跡を貴方が失った場合でさえもそこに存続します。それからその tf.Variable をリカバーできる可能性がありますが、貴方がそれが作成されたときの名前を知る場合に限ります。貴方が変数の作成を管理していなかった場合にはこれは行なうことが困難でした。結果的に、ユーザがそれらの変数を再度見つけることを助けようとするために、そしてフレームワークがユーザが作成した変数を見つけるために総ての種類のメカニズムが急増しました : Variable スコープ、global コレクション、tf.get_global_step(), tf.global_variables_initializer() のようなヘルパーメソッド、総ての訓練可能な変数に渡り暗黙的に勾配を計算する optimizer、等々。TensorFlow 2.0 はこれらのメカニズムの総てをデフォルト・メカニズムのために除去しました (Variables 2.0 RFC) : 貴方の変数を追跡してください!tf.Variable の追跡を失う場合、それはガベージコレクションされます。

変数を追跡するための要件はユーザのための幾つかの追加の作業を作成しますが、Keras オブジェクト (下を参照) で、重荷は最小化されます。

 

Functions, not sessions

session.run() コールは殆ど関数呼び出しのようなものです : 貴方は入力と呼び出される関数を指定し、出力のセットを取り戻します。TensorFlow 2.0 では、Python 関数を JIT コンパイルのためにマークするために tf.function() を使用して修飾 (デコレート) できます、その結果 TensorFlow はそれを単一グラフとして実行します (Functions 2.0 RFC)。このメカニズムは TensorFlow 2.0 にグラフモードの総ての恩恵を得ることを可能にします。

  • パフォーマンス: その関数は最適化できます (ノード枝狩り (= pruning), カーネル融合 etc.)。
  • 可搬性 (= Portability): その関数はエクスポート/再インポート可能 (SavedModel 2.0 RFC)で、ユーザにモジュール化された TensorFlow 関数を再利用して共有することを可能にします。
# TensorFlow 1.X
outputs = session.run(f(placeholder), feed_dict={placeholder: input})
# TensorFlow 2.0
outputs = f(input)

Python と TensorFlow コードを自由に散在させるパワーにより、ユーザが Python の表現力を活用できます。しかし可搬な TensorFlow はモバイル, C++ そして JavaScript のような、Python インタープリタがないコンテキストで実行されます。@tf.function を追加するときユーザが彼らのコードを書き直さなければならないことを回避する手助けをするために、AutoGraph は Python 構成部品のサブセットをそれらの TensorFlow の等価物に変換します :

  • for/while -> tf.while_loop (break と continue がサポートされます)
  • if -> tf.cond
  • for _ in dataset -> dataset.reduce

AutoGraph は制御フローの任意のネスティングをサポートします、これはシークエンスモデル、 強化学習、カスタム訓練ループ等のような多くの複雑な ML プログラムを性能良くそして簡潔に実装することを可能にします。

 

慣用的な TensorFlow 2.0 のための推奨

貴方のコードをより小さい関数にリファクタリングする

TensorFlow 1.X の一般的な使用方法パターンは「キッチン・シンク」ストラテジーでした、そこでは総ての可能な計算の結合が先取権のある状態で (= preemptively) 並べ立てられて、そしてそれから選択された tensors が session.run() を通して評価されました。TensorFlow 2.0 では、ユーザはコードを (必要に応じて呼び出される) より小さい関数にリファクタリングするべきです。一般に、これらのより小さい関数の各々を tf.function で修飾する必要はありません ; 高位な計算を修飾するために tf.function を使用するだけです – 例えば、訓練の 1 ステップや、モデルの forward パスです。

 

変数を管理するために Keras 層とモデルを使用する

Keras モデルと層は便利な variables と trainable_variables プロパティを提供します、これは総ての依存する変数を再帰的に集めます。これは変数を (それらが使用されている) ローカルで管理することを非常に容易にします。

次を :

def dense(x, W, b):
  return tf.nn.sigmoid(tf.matmul(x, W) + b)

@tf.function
def multilayer_perceptron(x, w0, b0, w1, b1, w2, b2 ...):
  x = dense(x, w0, b0)
  x = dense(x, w1, b1)
  x = dense(x, w2, b2)
  ...

# You still have to manage w_i and b_i, and their shapes are defined far away from the code.

Keras バージョンと比較してください :

# Each layer can be called, with a signature equivalent to linear(x)
layers = [tf.keras.layers.Dense(hidden_size, activation=tf.nn.sigmoid) for _ in range(n)]
perceptron = tf.keras.Sequential(layers)

# layers[3].trainable_variables => returns [w3, b3]
# perceptron.trainable_variables => returns [w0, b0, ...]

Keras 層/モデルは tf.train.Checkpointable から継承されて @tf.function と統合されます、これは Keras オブジェクトから直接的にチェックポイントするか SavedModels をエクスポートすることを可能にします。これらの統合を活用するために貴方は必ずしも Keras の fit() API を使用する必要はありません。

ここに転移学習のサンプルがあります、これは関連する変数のサブセットを集めることを Keras がどのように容易にするかを実演します。共有されるトランクを持つマルチヘッド・モデルを訓練しているとしましょう :

trunk = tf.keras.Sequential([...])
head1 = tf.keras.Sequential([...])
head2 = tf.keras.Sequential([...])

path1 = tf.keras.Sequential([trunk, head1])
path2 = tf.keras.Sequential([trunk, head2])

# Train on primary dataset
for x, y in main_dataset:
  with tf.GradientTape() as tape:
    prediction = path1(x)
    loss = loss_fn_head1(prediction, y)
  # Simultaneously optimize trunk and head1 weights.
  gradients = tape.gradient(loss, path1.trainable_variables)
  optimizer.apply_gradients(zip(gradients, path1.trainable_variables))

# Fine-tune second head, reusing the trunk
for x, y in small_dataset:
  with tf.GradientTape() as tape:
    prediction = path2(x)
    loss = loss_fn_head2(prediction, y)
  # Only optimize head2 weights, not trunk weights
  gradients = tape.gradient(loss, head2.trainable_variables)
  optimizer.apply_gradients(zip(gradients, head2.trainable_variables))

# You can publish just the trunk computation for other people to reuse.
tf.saved_model.save(trunk, output_path)

 

tf.data.Datasets と @tf.function を結合する (= combine)

メモリに fit する訓練データに渡り iterate するとき、通常の Python iteration を自由に使用してください。そうでないならば、tf.data.Dataset はディスクから訓練データをストリームするために最善の方法です。Datasets は iterable で (iterator ではありません)、Eager モードではちょうど他の Python iterable のように動作します。貴方はコードを tf.function() でラップすることにより dataset 非同期 prefetching/streaming 特徴を完全に活用できます、それは AutoGraph を使用して Python iteration を同値のグラフ演算に置き換えます。

@tf.function
def train(model, dataset, optimizer):
  for x, y in dataset:
    with tf.GradientTape() as tape:
      prediction = model(x)
      loss = loss_fn(prediction, y)
    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

Keras .fit() API を使用する場合には、dataset iteration について心配する必要はないでしょう。

model.compile(optimizer=optimizer, loss=loss_fn)
model.fit(dataset)

 

Python 制御フローで AutoGraph を活用する

AutoGraph はデータ依存制御フローを tf.condtf.while_loop のようなグラフモードの等価物に変換する方法を提供します。

データ依存制御フローが出現する一つの一般的な場所はシークエンス・モデル内です。tf.keras.layers.RNN は RNN セルをラップして、リカレンスを静的にあるいは動的に展開することを可能にします。デモのためには、動的展開 (= unroll) を次のように再実装できるでしょう :

class DynamicRNN(tf.keras.Model):

  def __init__(self, rnn_cell):
    super(DynamicRNN, self).__init__(self)
    self.cell = rnn_cell

  def call(self, input_data):
    # [batch, time, features] -> [time, batch, features]
    input_data = tf.transpose(input_data, [1, 0, 2])
    outputs = tf.TensorArray(tf.float32, input_data.shape[0])
    state = self.cell.zero_state(input_data.shape[1], dtype=tf.float32)
    for i in tf.range(input_data.shape[0]):
      output, state = self.cell(input_data[i], state)
      outputs = outputs.write(i, output)
    return tf.transpose(outputs.stack(), [1, 0, 2]), state

AutoGraph の特徴のより詳細な概要については、ガイド を見てください。

 

データを累積する tf.metrics とそれらをログ記録する tf.summary

要約をログ記録するためには、tf.summary.(scalar|histogram|…) を使用してそれをコンテキスト・マネージャを使用して writer にリダイレクトします。(コンテキスト・マネージャを省けば、何も起きません。)TF 1.x とは違い、要約は writer に直接送られます ; 個別の “merge” op と個別の add_summary() 呼び出しはなく、これは step value は callsite で提供されなければならないことを意味します。

summary_writer = tf.summary.create_file_writer('/tmp/summaries')
with summary_writer.as_default():
  tf.summary.scalar('loss', 0.1, step=42)

データをそれらをロギングする前に要約として蓄積するためには、tf.metrics を使用します。Metrics はステートフルです : それらは値を累積して .result() を呼び出すときに累積結果を返します。累積値は .reset_states() でクリアします。

def train(model, optimizer, dataset, log_freq=10):
  avg_loss = tf.keras.metrics.Mean(name='loss', dtype=tf.float32)
  for images, labels in dataset:
    loss = train_step(model, optimizer, images, labels)
    avg_loss.update_state(loss)
    if tf.equal(optimizer.iterations % log_freq, 0):
      tf.summary.scalar('loss', avg_loss.result(), step=optimizer.iterations)
      avg_loss.reset_states()

def test(model, test_x, test_y, step_num):
  loss = loss_fn(model(test_x), test_y)
  tf.summary.scalar('loss', loss, step=step_num)

train_summary_writer = tf.summary.create_file_writer('/tmp/summaries/train')
test_summary_writer = tf.summary.create_file_writer('/tmp/summaries/test')

with train_summary_writer.as_default():
  train(model, optimizer, dataset)

with test_summary_writer.as_default():
  test(model, test_x, test_y, optimizer.iterations)

TensorBoard を要約ログディレクトリをポイントさせることにより生成された要約を可視化します :

tensorboard --logdir /tmp/summaries

 

デバッグするとき tf.config.experimental_run_functions_eagerly() を使用する

TensorFlow 2.0 では、Eager Execution は shape、データ型と値を調査するために貴方にコードをステップ毎に実行させます。

tf.function, tf.keras 等のような特定の API はパフォーマンスと可搬性のために Graph 実行を使用するために設計されています。デバッグするとき、このコード内で Eager execution を使用するために experimental_run_functions_eagerly(True) を使用します。

例えば :

@tf.function
def f(x):
  if x > 0:
    import pdb
    pdb.set_trace()
    x = x + 1
  return x

tf.config.experimental_run_functions_eagerly(True)
f(tf.constant(1))
>>>f()
-> x = x + 1
(Pdb) l
  6      @tf.function
  7      def f(x):
  8        if x > 0:
  9          import pdb
 10          pdb.set_trace()
 11  ->      x = x + 1
 12        return x
 13
 14      tf.config.experimental_run_functions_eagerly(True)
 15      f(tf.constant(1))
[EOF]

これはまた Keras モデルと Eager execution をサポートする他の API 内で動作します。

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

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


tf.config.experimental_run_functions_eagerly(True)
model = CustomModel()
model(tf.constant([-2, -4]))
>>> call()
-> return input_data // 2
(Pdb) l
 10         if tf.reduce_mean(input_data) > 0:
 11           return input_data
 12         else:
 13           import pdb
 14           pdb.set_trace()
 15  ->       return input_data // 2
 16
 17
 18     tf.config.experimental_run_functions_eagerly(True)
 19     model = CustomModel()
 20     model(tf.constant([-2, -4]))

 

以上



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