TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- カスタム層 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/01/2019
* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Advanced Tutorials – Customization の以下のページを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
カスタマイズ :- カスタム層
ニューラルネットワークを構築するために tf.keras を高位 API として使用することを推奨します。とは言うものの、殆どの TensorFlow API は eagar execution とともに利用可能です。
from __future__ import absolute_import, division, print_function, unicode_literals
!pip install -q tensorflow==2.0.0-beta1 import tensorflow as tf
層: 有用な演算の一般的なセット
機械学習モデルのためのコードを書く時の殆どで個々の演算と個々の変数の操作よりも抽象度の高いレベルで操作することを望むでしょう。
多くの機械学習モデルは比較的単純な層の組み合わせとスタックとして表現可能で、そして TensorFlow は多くの一般的な層のセットに加えて貴方自身のアプリケーション固有の層をスクラッチからあるいは既存の層の組み合わせとして書くための容易な方法の両者を提供します。
TensorFlow は tf.keras パッケージで完全な Keras API を含み、そして Keras 層は貴方自身のモデルを構築するとき非常に有用です。
# In the tf.keras.layers package, layers are objects. To construct a layer, # simply construct the object. Most layers take as a first argument the number # of output dimensions / channels. layer = tf.keras.layers.Dense(100) # The number of input dimensions is often unnecessary, as it can be inferred # the first time the layer is used, but it can be provided if you want to # specify it manually, which is useful in some complex models. layer = tf.keras.layers.Dense(10, input_shape=(None, 5))
事前の既存の層の完全なリストは ドキュメント で見ることができます。それは Dense (完全結合層), Conv2D, LSTM, BatchNormalization, Dropout そしてその他多くを含みます。
# To use a layer, simply call it. layer(tf.zeros([10, 5]))
<tf.Tensor: id=29, shape=(10, 10), dtype=float32, numpy= array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)>
# Layers have many useful methods. For example, you can inspect all variables # in a layer using `layer.variables` and trainable variables using # `layer.trainable_variables`. In this case a fully-connected layer # will have variables for weights and biases. layer.variables
[<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy= array([[-0.45203224, -0.49100596, -0.49586484, -0.03597105, -0.02791589, 0.59075147, -0.10046268, -0.3485075 , 0.52843136, 0.37997633], [ 0.24188435, -0.11822426, 0.5947488 , 0.28323162, 0.6153639 , -0.54930526, 0.13033777, -0.04597318, 0.09440243, -0.08491254], [ 0.07092369, -0.4460328 , 0.43974787, -0.12983793, -0.5321488 , -0.53699183, 0.21715474, 0.49850827, -0.06530118, -0.45353276], [-0.36072668, 0.31528533, 0.3017155 , -0.5406943 , -0.4158225 , -0.09183133, -0.0244289 , 0.04464054, -0.5192156 , 0.20999378], [ 0.36308336, -0.5447986 , 0.32299954, -0.33683974, 0.07472193, -0.57537097, -0.066365 , -0.19609043, 0.36212164, 0.42469734]], dtype=float32)>, <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>]
# The variables are also accessible through nice accessors layer.kernel, layer.bias
(<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy= array([[-0.45203224, -0.49100596, -0.49586484, -0.03597105, -0.02791589, 0.59075147, -0.10046268, -0.3485075 , 0.52843136, 0.37997633], [ 0.24188435, -0.11822426, 0.5947488 , 0.28323162, 0.6153639 , -0.54930526, 0.13033777, -0.04597318, 0.09440243, -0.08491254], [ 0.07092369, -0.4460328 , 0.43974787, -0.12983793, -0.5321488 , -0.53699183, 0.21715474, 0.49850827, -0.06530118, -0.45353276], [-0.36072668, 0.31528533, 0.3017155 , -0.5406943 , -0.4158225 , -0.09183133, -0.0244289 , 0.04464054, -0.5192156 , 0.20999378], [ 0.36308336, -0.5447986 , 0.32299954, -0.33683974, 0.07472193, -0.57537097, -0.066365 , -0.19609043, 0.36212164, 0.42469734]], dtype=float32)>, <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>)
カスタム層を実装する
貴方自身の層を実装する最善の方法は tf.keras.Layer クラスを拡張して以下を実装することです : * __init__ , そこでは総ての入力独立な初期化を行なうことができます * build, そこでは入力 tensor の shape を知って初期化の残りを行なうことができます * call, そこでは forward 計算を行ないます。
貴方の変数を作成するために build が呼び出されるまで待たなくても構わないことに注意してください、それらを __init__ で作成することもできます。けれども、それらを build で作成する優位点は (層がその上で操作する) 入力の shape を基にそれが遅延変数作成を可能にすることです。他方、__init__ で変数を作成することは変数を作成するために必要な shape が明示的に指定される必要があることを意味します。
class MyDenseLayer(tf.keras.layers.Layer): def __init__(self, num_outputs): super(MyDenseLayer, self).__init__() self.num_outputs = num_outputs def build(self, input_shape): self.kernel = self.add_variable("kernel", shape=[int(input_shape[-1]), self.num_outputs]) def call(self, input): return tf.matmul(input, self.kernel) layer = MyDenseLayer(10) print(layer(tf.zeros([10, 5]))) print(layer.trainable_variables)
tf.Tensor( [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]], shape=(10, 10), dtype=float32) [<tf.Variable 'my_dense_layer/kernel:0' shape=(5, 10) dtype=float32, numpy= array([[-0.13745713, 0.20385987, 0.3308882 , -0.55763197, -0.2896679 , 0.5872863 , -0.22654337, 0.43126732, -0.09286654, 0.59604806], [ 0.53581923, -0.56217843, -0.14932257, 0.00366616, -0.0120728 , -0.30010343, 0.4884537 , 0.12585086, -0.14010605, 0.25820726], [ 0.4151489 , 0.19842285, -0.09367836, -0.28914934, -0.3939644 , 0.33803612, -0.57224464, -0.37771535, -0.21020746, -0.01422739], [ 0.00673819, 0.5517891 , 0.25531226, 0.17221254, 0.60322064, -0.3257994 , 0.04672939, -0.3532266 , 0.30166608, -0.06154853], [-0.29644936, -0.5298993 , 0.36587477, 0.03407788, 0.32965267, -0.5066039 , 0.3986141 , -0.544722 , 0.13702917, 0.57767636]], dtype=float32)>]
全体的なコードが可能なときには標準的な層を使用すれば、他の読み手は標準的な層の挙動に馴染みがありますので、読みやすく維持しやすくなります、tf.keras.layers にない層を使用することを望む場合、github issue を提出するか、更に良いことは pull リクエストを送ることです!
モデル: 層を組み合わせる
機械学習モデルの多くの興味深い層-like なものは既存の層を組み合わせることにより実装されます。例えば、resnet の各残差ブロックは畳み込み、バッチ正規化とショートカットの組み合わせです。
他の層を含む層-like なものを作成するときに使用される主なクラスは tf.keras.Model です。一つを実装することは tf.keras.Model から継承することにより成されます。
class ResnetIdentityBlock(tf.keras.Model): def __init__(self, kernel_size, filters): super(ResnetIdentityBlock, self).__init__(name='') filters1, filters2, filters3 = filters self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1)) self.bn2a = tf.keras.layers.BatchNormalization() self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same') self.bn2b = tf.keras.layers.BatchNormalization() self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1)) self.bn2c = tf.keras.layers.BatchNormalization() def call(self, input_tensor, training=False): x = self.conv2a(input_tensor) x = self.bn2a(x, training=training) x = tf.nn.relu(x) x = self.conv2b(x) x = self.bn2b(x, training=training) x = tf.nn.relu(x) x = self.conv2c(x) x = self.bn2c(x, training=training) x += input_tensor return tf.nn.relu(x) block = ResnetIdentityBlock(1, [1, 2, 3]) print(block(tf.zeros([1, 2, 3, 3]))) print([x.name for x in block.trainable_variables])
tf.Tensor( [[[[0. 0. 0.] [0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.] [0. 0. 0.]]]], shape=(1, 2, 3, 3), dtype=float32) ['resnet_identity_block/conv2d/kernel:0', 'resnet_identity_block/conv2d/bias:0', 'resnet_identity_block/batch_normalization/gamma:0', 'resnet_identity_block/batch_normalization/beta:0', 'resnet_identity_block/conv2d_1/kernel:0', 'resnet_identity_block/conv2d_1/bias:0', 'resnet_identity_block/batch_normalization_1/gamma:0', 'resnet_identity_block/batch_normalization_1/beta:0', 'resnet_identity_block/conv2d_2/kernel:0', 'resnet_identity_block/conv2d_2/bias:0', 'resnet_identity_block/batch_normalization_2/gamma:0', 'resnet_identity_block/batch_normalization_2/beta:0']
けれども大抵の時は、多くの層を構成するモデルは単純に他の (層の) 後に一つの層を呼び出します。これは tf.keras.Sequential を使用して非常に小さいなコードから成されます。
my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1), input_shape=( None, None, 3)), tf.keras.layers.BatchNormalization(), tf.keras.layers.Conv2D(2, 1, padding='same'), tf.keras.layers.BatchNormalization(), tf.keras.layers.Conv2D(3, (1, 1)), tf.keras.layers.BatchNormalization()]) my_seq(tf.zeros([1, 2, 3, 3]))
<tf.Tensor: id=749, shape=(1, 2, 3, 3), dtype=float32, numpy= array([[[[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]]], dtype=float32)>
以上