![]() |
![]() |
![]() |
![]() |
ニューラルネットワークの構築には、ハイレベルの API である tf.keras
を使うことを推奨します。しかしながら、TensorFlow API のほとんどは、eager execution でも使用可能です。
import tensorflow as tf
レイヤー:有用な演算の共通セット
機械学習モデルのコーディングでは、個々の演算やひとつひとつの変数のオペレーションよりは、より高度に抽象化されたオペレーションを行いたいのがほとんどだと思います。
多くの機械学習モデルは、比較的単純なレイヤーの組み合わせや積み重ねによって表現可能です。TensorFlow では、多くの一般的なレイヤーのセットに加えて、アプリケーションに特有なレイヤーを最初から記述したり、既存のレイヤーの組み合わせによって作るための、簡単な方法が提供されています。
TensorFlow には、tf.keras パッケージにKeras APIのすべてが含まれています。Keras のレイヤーは、独自のモデルを構築する際に大変便利です。
# tf.keras.layers パッケージの中では、レイヤーはオブジェクトです。
# レイヤーを構築するためにすることは、単にオブジェクトを作成するだけです。
# ほとんどのレイヤーでは、最初の引数が出力の次元あるいはチャネル数を表します。
layer = tf.keras.layers.Dense(100)
# 入力の次元数は多くの場合不要となっています。それは、レイヤーが最初に使われる際に
# 推定可能だからです。ただし、引数として渡すことで手動で指定することも可能です。
# これは複雑なモデルを構築する場合に役に立つでしょう。
layer = tf.keras.layers.Dense(10, input_shape=(None, 5))
既存のレイヤーのすべての一覧は、ドキュメントを参照してください。Dense(全結合レイヤー)、Conv2D、LSTM、BatchNormalization、Dropoutなどのたくさんのレイヤーが含まれています。
# レイヤーを使うには、単純にcallします。
layer(tf.zeros([10, 5]))
<tf.Tensor: 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)>
# レイヤーにはたくさんの便利なメソッドがあります。例えば、`layer.variables`を使って
# レイヤーのすべての変数を調べることができます。訓練可能な変数は、 `layer.trainable_variables`
# でわかります。この例では、全結合レイヤーには重みとバイアスの変数があります。
layer.variables
[<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy= array([[ 0.11475551, 0.06970757, 0.3077973 , -0.32507244, 0.24251252, -0.03514624, -0.19063246, -0.34674838, 0.02365488, -0.41029423], [ 0.4117133 , -0.26640266, -0.52261496, -0.18195471, 0.21620661, -0.6023835 , -0.37698945, -0.07256156, -0.3821837 , -0.41075024], [ 0.3776303 , 0.1881805 , -0.23990116, -0.07637244, 0.6027561 , 0.00191575, -0.28902376, -0.17382067, -0.08489913, 0.07040226], [-0.63206816, 0.23944938, -0.5087077 , -0.17376098, 0.14101517, -0.05821729, 0.57915026, 0.04935628, 0.5421639 , -0.5079661 ], [-0.3013111 , -0.19906491, 0.26680648, -0.2349402 , 0.41128987, 0.62836045, 0.10773581, -0.4004402 , 0.59058136, -0.3181666 ]], 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)>]
# これらの変数には便利なアクセサを使ってアクセス可能です。
layer.kernel, layer.bias
(<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy= array([[ 0.11475551, 0.06970757, 0.3077973 , -0.32507244, 0.24251252, -0.03514624, -0.19063246, -0.34674838, 0.02365488, -0.41029423], [ 0.4117133 , -0.26640266, -0.52261496, -0.18195471, 0.21620661, -0.6023835 , -0.37698945, -0.07256156, -0.3821837 , -0.41075024], [ 0.3776303 , 0.1881805 , -0.23990116, -0.07637244, 0.6027561 , 0.00191575, -0.28902376, -0.17382067, -0.08489913, 0.07040226], [-0.63206816, 0.23944938, -0.5087077 , -0.17376098, 0.14101517, -0.05821729, 0.57915026, 0.04935628, 0.5421639 , -0.5079661 ], [-0.3013111 , -0.19906491, 0.26680648, -0.2349402 , 0.41128987, 0.62836045, 0.10773581, -0.4004402 , 0.59058136, -0.3181666 ]], 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
, 入力のshape
を知った上で、残りの初期化を行うcall
, フォワード計算を行う
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.2653473 , -0.01079637, 0.3780468 , 0.37142426, 0.21437526, -0.30028182, 0.5738464 , -0.50313574, -0.42932943, 0.33047283], [-0.26841736, -0.42909962, -0.24391139, 0.2391457 , 0.10558498, -0.2766319 , 0.4379242 , 0.28657913, 0.53846794, 0.4771306 ], [-0.43419403, 0.0822345 , 0.28713316, -0.48987278, 0.5556113 , 0.32303274, -0.3706233 , 0.29805708, -0.46732226, 0.23999768], [-0.23843926, 0.29534268, -0.5573072 , 0.50020033, -0.28149998, -0.26134217, -0.17995027, 0.28124225, 0.34874243, -0.35252014], [-0.24468058, -0.1452389 , 0.52247363, -0.16353023, -0.21748093, -0.49478307, 0.60995466, -0.04198635, -0.16793266, 0.5774358 ]], dtype=float32)>] /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/engine/base_layer.py:2281: UserWarning: `layer.add_variable` is deprecated and will be removed in a future version. Please use `layer.add_weight` method instead. warnings.warn('`layer.add_variable` is deprecated and '
できるだけ標準のレイヤーを使ったほうが、概してコードは読みやすく保守しやすくなります。コードを読む人は標準的なレイヤーの振る舞いに慣れているからです。tf.keras.layers
にはないレイヤーを使いたい場合には、githubのイシューを登録するか、もっとよいのはプルリクエストを送ることです。
モデル:レイヤーの組み合わせ
機械学習では、多くのレイヤーに類するものが、既存のレイヤーを組み合わせることで実装されています。例えば、ResNetの残差ブロックは、畳込み、バッチ正規化とショートカットの組み合わせです。
他のレイヤーからなるレイヤーに類するものを定義する際の主役は、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: 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)>
次のステップ
それでは、前出のノートブックに戻り、線形回帰の例を、レイヤーとモデルを使って、より構造化された形で実装してみてください。