ホーム » Eager execution » TensorFlow 2.0 Alpha : 上級 Tutorials : カスタマイズ :- カスタム層

TensorFlow 2.0 Alpha : 上級 Tutorials : カスタマイズ :- カスタム層

TensorFlow 2.0 Alpha : 上級 Tutorials : カスタマイズ :- カスタム層 (翻訳/解説)

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

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

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

 

カスタマイズ :- カスタム層

ニューラルネットワークを構築するために tf.keras を高位 API として使用することを推奨します。とは言うものの、殆どの TensorFlow API は eagar execution とともに利用可能です。

from __future__ import absolute_import, division, print_function
!pip install -q tensorflow==2.0.0-alpha0
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.1615879 ,  0.46249574, -0.3165233 , -0.57131535,  0.28231573,
          0.41940242,  0.54472786, -0.36617455,  0.10317039,  0.2870149 ],
        [-0.45558694,  0.47169524, -0.08489895, -0.10159177,  0.1472652 ,
         -0.34814078, -0.51777947, -0.52772474, -0.60088646, -0.15727717],
        [ 0.02545369, -0.16564536, -0.4673859 ,  0.16116858,  0.56057316,
         -0.26500726,  0.22149676, -0.44483763, -0.29092053, -0.31672582],
        [-0.13767487, -0.4095771 , -0.5532339 , -0.41653648,  0.07425189,
          0.27620834, -0.07661343,  0.31917644, -0.17139465,  0.19182056],
        [ 0.5932444 , -0.11990362,  0.60331434, -0.21757048,  0.34897828,
         -0.15943903, -0.04400104, -0.20088884,  0.10801703, -0.06577516]],
       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.1615879 ,  0.46249574, -0.3165233 , -0.57131535,  0.28231573,
          0.41940242,  0.54472786, -0.36617455,  0.10317039,  0.2870149 ],
        [-0.45558694,  0.47169524, -0.08489895, -0.10159177,  0.1472652 ,
         -0.34814078, -0.51777947, -0.52772474, -0.60088646, -0.15727717],
        [ 0.02545369, -0.16564536, -0.4673859 ,  0.16116858,  0.56057316,
         -0.26500726,  0.22149676, -0.44483763, -0.29092053, -0.31672582],
        [-0.13767487, -0.4095771 , -0.5532339 , -0.41653648,  0.07425189,
          0.27620834, -0.07661343,  0.31917644, -0.17139465,  0.19182056],
        [ 0.5932444 , -0.11990362,  0.60331434, -0.21757048,  0.34897828,
         -0.15943903, -0.04400104, -0.20088884,  0.10801703, -0.06577516]],
       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.29433665,  0.3032077 ,  0.17379493,  0.22563863,  0.11145729,
         0.4862786 ,  0.27326053,  0.32047486, -0.22835237, -0.12660092],
       [-0.02698117,  0.61029404, -0.4170686 , -0.2178267 , -0.3009189 ,
        -0.00762331,  0.5703806 ,  0.26016724, -0.3244212 ,  0.10364807],
       [ 0.4805221 , -0.4227686 , -0.52093095,  0.29172963, -0.20082054,
         0.0161925 , -0.51654464, -0.5200238 , -0.16889003, -0.24593168],
       [-0.18075305,  0.55908746, -0.17184687,  0.20220602, -0.43541452,
         0.60533124, -0.5995372 , -0.37583244, -0.3499168 , -0.35859576],
       [ 0.37054938,  0.00697386,  0.10999417, -0.45433554,  0.19696409,
         0.48864585,  0.19940716, -0.09753025,  0.08416837, -0.55388516]],
      dtype=float32)>]

変数を作成するために build が呼び出されるまで待たなくても構わないことに注意してください、それらを __init__ で作成することもできます。

全体のコードが可能なところでは標準的な層を使用すれば、他の読み手は標準的な層の挙動に慣れていますので、読みやすく維持しやすくなります、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_v2/gamma:0', 'resnet_identity_block/batch_normalization_v2/beta:0', 'resnet_identity_block/conv2d_1/kernel:0', 'resnet_identity_block/conv2d_1/bias:0', 'resnet_identity_block/batch_normalization_v2_1/gamma:0', 'resnet_identity_block/batch_normalization_v2_1/beta:0', 'resnet_identity_block/conv2d_2/kernel:0', 'resnet_identity_block/conv2d_2/bias:0', 'resnet_identity_block/batch_normalization_v2_2/gamma:0', 'resnet_identity_block/batch_normalization_v2_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]))

 

以上






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