TensorFlow : Tutorials : Eager : カスタム層 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/18/2018
* TensorFlow 1.9 でドキュメント構成が変わり新規にチュートリアル・ページも追加されました。
* 本ページは、TensorFlow 本家サイトの Tutorials – Research and Experimentation – Custom layers を
翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
ニューラルネットワークを構築するための高位レベル API として tf.keras の使用を推奨します。とは言え、殆どの TensorFlow API は eager execution とともに利用可能です。
import tensorflow as tf tfe = tf.contrib.eager tf.enable_eager_execution()
層: 有用な演算の一般的なセット
大抵の場合、機械学習モデルのためのコードを書くとき個々の演算や個々の変数の操作よりも抽象化のより高いレベルで演算することを望むでしょう。多くの機械学習モデルは比較的単純な層の組み立てやスタックとして表現できます、そして TensorFlow は多くの一般的な層のセットとともに、スクラッチからあるいは既存の層の組み立てとして貴方自身のアプリケーション固有の層を書く簡単な方法の両者を提供します。
TensorFlow は tf.keras パッケージにフル Keras API を含み、そして Keras 層は貴方自身のモデルを構築するとき非常に有用です。
# In the tf.keras.layers package, layers are objects. To construct a layer,# In th # 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]))
Out:
<tf.Tensor: id=30, 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 by calling layer.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.21914625, 0.0990991 , 0.18261564, 0.40257913, 0.34072709, -0.0625487 , -0.24020803, -0.12010807, 0.40558523, -0.03303367], [ 0.08877724, -0.40127528, -0.57334077, 0.10226113, -0.27305049, -0.29159862, -0.58058709, -0.60042459, -0.36712468, -0.22455806], [-0.2126697 , -0.15910214, 0.12656337, 0.44269663, 0.57688957, 0.18262208, -0.41651565, -0.33097616, 0.51486522, 0.57214183], [ 0.21742707, 0.23269236, 0.56807464, -0.6096639 , 0.25128335, -0.12213349, 0.21207756, -0.05127466, 0.02161229, 0.42359179], [-0.53448963, 0.55796593, 0.20331413, -0.5741179 , -0.10637027, -0.05225825, -0.4186154 , -0.51762027, -0.24687892, -0.08826667]], 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.21914625, 0.0990991 , 0.18261564, 0.40257913, 0.34072709, -0.0625487 , -0.24020803, -0.12010807, 0.40558523, -0.03303367], [ 0.08877724, -0.40127528, -0.57334077, 0.10226113, -0.27305049, -0.29159862, -0.58058709, -0.60042459, -0.36712468, -0.22455806], [-0.2126697 , -0.15910214, 0.12656337, 0.44269663, 0.57688957, 0.18262208, -0.41651565, -0.33097616, 0.51486522, 0.57214183], [ 0.21742707, 0.23269236, 0.56807464, -0.6096639 , 0.25128335, -0.12213349, 0.21207756, -0.05127466, 0.02161229, 0.42359179], [-0.53448963, 0.55796593, 0.20331413, -0.5741179 , -0.10637027, -0.05225825, -0.4186154 , -0.51762027, -0.24687892, -0.08826667]], 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 に基づいて遅延 (= late) variable 作成を可能にすることです。その一方で、variable を __init__ で作成することは variable を作成するために必要な 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=[input_shape[-1].value, self.num_outputs]) def call(self, input): return tf.matmul(input, self.kernel) layer = MyDenseLayer(10) print(layer(tf.zeros([10, 5]))) print(layer.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.24975419, -0.33780435, 0.17131561, 0.28679961, -0.27856019, -0.38586116, -0.19720221, 0.25158852, -0.61064029, -0.35856512], [ 0.52740663, 0.26937109, -0.61597472, 0.53265864, -0.05881637, -0.02134347, 0.59274155, -0.11058015, -0.22670561, 0.34645844], [ 0.41161889, -0.40521431, -0.49620956, 0.62973171, -0.20672497, 0.55795223, -0.29845288, 0.30644548, 0.26540279, 0.27750438], [ 0.54720205, -0.14807495, -0.18610999, -0.00718719, 0.23697406, -0.30066797, 0.34169638, 0.43261009, -0.08473277, -0.30719322], [-0.45203799, -0.57771409, -0.06893957, 0.62330216, 0.43259674, -0.15227216, -0.15664744, -0.32023922, 0.32174361, 0.3802709 ]], dtype=float32)>]
コード全体はそれが (可能なときはいつでも) 標準的な層を使用すれば読みやすく維持しやすいです、何故ならば他の読者は標準的な層の挙動に精通しているからです。
モデル: 層を構成する
機械学習モデルの多くの興味深い層ライクなものは既存の層を組み立てることにより実装されます。例えば、resnet の各残差ブロックは畳み込み、バッチ正規化、そしてショートカットの合成です。
他の層を含む層ライクなものを作成するとき使用される主要なクラスは 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.variables])
Out:
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', 'resnet_identity_block/batch_normalization/moving_mean:0', 'resnet_identity_block/batch_normalization/moving_variance:0', 'resnet_identity_block/batch_normalization_1/moving_mean:0', 'resnet_identity_block/batch_normalization_1/moving_variance:0', 'resnet_identity_block/batch_normalization_2/moving_mean:0', 'resnet_identity_block/batch_normalization_2/moving_variance:0' ]
けれでも大抵の場合、多くの層を構成するモデルは単に一つの層を他の層に続いて呼び出すだけです。これは tf.keras.Sequential を使用して非常に小さいコードで成されます。
my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1)), 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=503, 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)>
以上