TensorFlowについて勉強していく
グラフ(graph execution)やeager execution、tf.function
などについて
TensorFlow2は、基本的にeager excutionとして動く。
ただ、Kerasモデルはmodel.compile(run_eagerly=True)
としない限り、graph executionになっている。
eager executionでは通常のPythonコードと同様、インタラクティブに使用できる。
もちろんprint
で出力することができ、Tensor.numpy
で実際の値にアクセスすることもできる。
graph excutionではインタラクティブに使用できない反面、高速に演算できる。また、モデルをグラフとして保存することができる。print
が使えないためtf.print
を使う必要がある。また、Tensor.numpy
で実際の値にアクセスすることはできない。Tensor
はshape
とdtype
だけを保持する。
tf.function
を使用することで、PythonコードをTensorFlowのグラフに変換することができる。
初回はPythonとして実行される、それをもとにグラフが生成される。
tf.function
は以下のようにデコレータか、関数をラップする形で使用する。
# tf.functionでデコレーションする場合
@tf.function
def my_func(x):
print('Tracing.\n')
return tf.reduce_sum(x)
# tf.functionでラップする場合
my_graph_func = tf.function(my_func)
eager excutionで正常に動くことが確認できたら、graph executionで実行するのが良いと思う。
ただし、便宜上、Python で機械学習モデル(またはその他の計算)を定義した後、必要となったときに自動的にグラフを作成することをお勧めします。
kerasモデルをeager executionで実行するためには、model.compile(run_eagerly=True)
を使用するか、tf.config.experimental_run_functions_eagerly(True)
でグローバル実行モードを設定する。
※ 通常のeager executionのPythonコードをTensorFlowのグラフに変換する場合は、前述のようにtf.function
でラップする。
graph executionの場合にのみ発生するエラーに関しては、ここを参考にすると良さそう。
高価な演算がいくつか含まれるグラフ(畳み込みなど)では、eager executionでもgraph executionでも速度の差があまり見られないらしい。
tf.data.Dataset
について
tf.data.Dataset
はイテラブルであり、eager modeで扱える。
tf.data
はtf.data.Dataset.map
内の関数がグラフか否かにかかわらず、グラフとして実行する。
Dataset.map
内でPythonコードを扱うには、以下のいずれかの方法を用いる。
-
AutoGraph
を用いてPythonコードをグラフに変換する。 -
tf.py_function
を使う(AutoGraph
と比較してパフォーマンスは低下する)。 -
tf.numpy_function
を使う。
AutoGraphはよく分からん
tf.py_function
は以下のように、Dataset.map
に渡したい関数をラップして使用する。
def func(x):
- # graph executionなのでprintは初回のみ実行され、numpytは使えない
- print(x)
+ # eager executionなのでprintが使え、numpyで値にアクセスできる
+ print(x.numpy())
return x, x + 5
def map_func(x):
""""
tf.py_functionでラップする。
funcはDataset.mapに渡す関数、inpはfuncの引数、Toutはfuncの戻り値の型。
"""
return tf.py_function(func=func, inp=[x], Tout=[tf.int64, tf.int64])
dataset = tf.data.Dataset.range(5)
- dataset = dataset.map(func)
+ dataset = dataset.map(map_func) # tf.py_functionでラップした関数を渡す
for data in dataset:
- pass # Tensor("args_0:0", shape=(), dtype=int64)
+ pass # 0, 1, 2, 3, 4
tf.numpy_function
は基本的にtf.py_function
と同様に使う。
ただ、tf.numpy_function
ではTensor.numpy()
のように値にアクセスする必要がなく、直接値にアクセスできる。
- tensor.numpy()
+ tensor
tf.keras.layer.Layer
(tf.keras.models.Model
)の__init__
・build
・call
について
メソッドはそれぞれ呼び出されるタイミングが異なる。
class SimpleDense(Layer):
def __init__(self, units=32):
"""インスタンス生成時に呼び出される。"""
super(SimpleDense, 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
note
文字列はTensor.numpy().decode()
で取得する。
これDatasetだけ?graph executionのTensor全部?
Saved Model
これを見る限り、Saved ModelではMode.get_config()
とModel.from_config()
は必要ない。
ModelCheckPoint()
でうまく行かないと思っていたら、デフォルトでsave_weights_only=True
となっていたため、これをFalse
とする。
しかしながら、カスタムモデルまたはレイヤークラスを作成する場合は、常にget_configおよびfrom_configメソッドを使用して定義することをお勧めします。これにより、必要に応じて後で計算を簡単に更新できます。
https://www.tensorflow.org/guide/keras/save_and_serialize?hl=ja#savedmodel_によるカスタムオブジェクトの処理
は???
サブクラス化されたモデルとレイヤーのアーキテクチャは、メソッド__init__およびcallで定義されています。それらは Python バイトコードと見なされ、JSON と互換性のある構成にシリアル化できません。pickleなどを使用してバイトコードのシリアル化を試すことができますが、これは安全ではなく、モデルを別のシステムに読み込むことはできません。
カスタム定義されたレイヤーのあるモデル、またはサブクラス化されたモデルを保存/読み込むには、get_configおよびfrom_config(オプション) メソッドを上書きする必要があります。さらに、Keras が認識できるように、カスタムオブジェクトの登録を使用する必要があります。
https://www.tensorflow.org/guide/keras/save_and_serialize?hl=ja#カスタムオブジェクト
BatchNormalization
では、どのタイミングでBatch Normalizationを入れれば良いでしょうか。X自体なのか、変換後の値 WX+bなのか。元論文では、後者の WX+b, 正確には、バイアスは正規化処理で消えるため、 WXにあてることが推奨されています。
https://qiita.com/cfiken/items/b477c7878828ebdb0387