TensorFlow, Kerasで名前・インデックスからレイヤーを取得
TensorFlow, Kerasで構築したモデルにおいて、名前やインデックスを指定してレイヤーオブジェクトを取得する方法を説明する。
- 名前でレイヤーオブジェクトを取得:
get_layer()
- インデックスでレイヤーオブジェクトを取得:
get_layer()
,layers
- レイヤーオブジェクトの属性・メソッド
- 条件を満たすレイヤーオブジェクトを取得、設定変更
レイヤーのオブジェクト自体ではなく名前を取得したい場合は以下の記事を参照。
以下のサンプルコードのTensorFlowのバージョンは2.1.0
。TensorFlowに統合されたKerasを使う。
import tensorflow as tf
import pprint
print(tf.__version__)
# 2.1.0
Kerasに同梱されているVGG16のモデルを例とする。自分で構築したモデルでもやり方は同じ。
model = tf.keras.applications.VGG16(weights=None)
model.summary()
# Model: "vgg16"
# _________________________________________________________________
# Layer (type) Output Shape Param #
# =================================================================
# input_1 (InputLayer) [(None, 224, 224, 3)] 0
# _________________________________________________________________
# block1_conv1 (Conv2D) (None, 224, 224, 64) 1792
# _________________________________________________________________
# block1_conv2 (Conv2D) (None, 224, 224, 64) 36928
# _________________________________________________________________
# block1_pool (MaxPooling2D) (None, 112, 112, 64) 0
# _________________________________________________________________
# block2_conv1 (Conv2D) (None, 112, 112, 128) 73856
# _________________________________________________________________
# block2_conv2 (Conv2D) (None, 112, 112, 128) 147584
# _________________________________________________________________
# block2_pool (MaxPooling2D) (None, 56, 56, 128) 0
# _________________________________________________________________
# block3_conv1 (Conv2D) (None, 56, 56, 256) 295168
# _________________________________________________________________
# block3_conv2 (Conv2D) (None, 56, 56, 256) 590080
# _________________________________________________________________
# block3_conv3 (Conv2D) (None, 56, 56, 256) 590080
# _________________________________________________________________
# block3_pool (MaxPooling2D) (None, 28, 28, 256) 0
# _________________________________________________________________
# block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160
# _________________________________________________________________
# block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808
# _________________________________________________________________
# block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808
# _________________________________________________________________
# block4_pool (MaxPooling2D) (None, 14, 14, 512) 0
# _________________________________________________________________
# block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808
# _________________________________________________________________
# block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808
# _________________________________________________________________
# block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808
# _________________________________________________________________
# block5_pool (MaxPooling2D) (None, 7, 7, 512) 0
# _________________________________________________________________
# flatten (Flatten) (None, 25088) 0
# _________________________________________________________________
# fc1 (Dense) (None, 4096) 102764544
# _________________________________________________________________
# fc2 (Dense) (None, 4096) 16781312
# _________________________________________________________________
# predictions (Dense) (None, 1000) 4097000
# =================================================================
# Total params: 138,357,544
# Trainable params: 138,357,544
# Non-trainable params: 0
# _________________________________________________________________
名前でレイヤーオブジェクトを取得: get_layer()
モデルのget_layer()
メソッドの第一引数name
に名前を指定すると、対象のレイヤーのオブジェクトを取得できる。
l_block4_conv1 = model.get_layer('block4_conv1')
print(l_block4_conv1)
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13d288090>
print(type(l_block4_conv1))
# <class 'tensorflow.python.keras.layers.convolutional.Conv2D'>
レイヤーオブジェクトの属性やメソッドを用いて値を取得したりする方法については後述。
存在しない名前を指定するとエラーとなる。
# print(model.get_layer('xxx'))
# ValueError: No such layer: xxx
インデックスでレイヤーオブジェクトを取得: get_layer(), layers
get_layers()
メソッドでは、引数index
にインデックスを指定することもできる。名前で指定してもインデックスで指定しても、同じレイヤーであれば当然ながら同一オブジェクトが返される。
l_11 = model.get_layer(index=11)
print(l_11)
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13d288090>
print(type(l_11))
# <class 'tensorflow.python.keras.layers.convolutional.Conv2D'>
print(l_block4_conv1 is l_11)
# True
負の値で末尾からのインデックスも指定可能。-1
が最終レイヤー。ここでは分かりやすいように後述するname
属性を表示している。
print(model.get_layer(index=-1).name)
# predictions
print(model.get_layer(index=-3).name)
# fc1
存在しないインデックスを指定するとエラーとなる。
# print(model.get_layer(index=100))
# ValueError: Was asked to retrieve layer at index 100 but model only has 23 layers.
各レイヤーのオブジェクトが格納されたリストであるlayers
属性を使う方法もある。ここでは出力を見やすくするためpprintを使っている。
pprint.pprint(model.layers)
# [<tensorflow.python.keras.engine.input_layer.InputLayer object at 0x13d02a6d0>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x1108bc0d0>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13cff8c50>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x135eb4cd0>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x135ea4c50>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x135e99c10>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13d19ac90>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13d26c290>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13d270b90>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13d276fd0>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13d279e10>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13d288090>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13d28bd10>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13ddcfc90>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddd6fd0>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13dddbed0>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13dde7e10>,
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13ddecd90>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddf3e10>,
# <tensorflow.python.keras.layers.core.Flatten object at 0x13ddf5c90>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13ddf5d10>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13de03910>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13de09210>]
print(type(model.layers))
# <class 'list'>
print(model.layers[11])
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x13d288090>
print(model.layers[11] is l_11)
# True
layers
属性はただのリストなので、当然、負の値で末尾からのインデックスを指定することも可能。範囲外を指定するとエラーとなる。
print(model.layers[-1].name)
# predictions
print(model.layers[-3].name)
# fc1
# print(model.layers[100])
# IndexError: list index out of range
レイヤーオブジェクトの属性・メソッド
Dense
やConv2D
など、各レイヤーのクラスはtf.keras.layers.Layer
のサブクラス。
l_1 = model.get_layer(index=1)
print(l_1)
# <tensorflow.python.keras.layers.convolutional.Conv2D object at 0x1108bc0d0>
print(isinstance(l_1, tf.keras.layers.Layer))
# True
tf.keras.layers.Layer
の属性やメソッドについては公式ドキュメントの該当部分を参照。
- tf.keras.layers.Layer - Attributes | TensorFlow Core v2.1.0
- tf.keras.layers.Layer - Methods | TensorFlow Core v2.1.0
例えばname
属性でレイヤーの名前を取得したり、count_params()
メソッドでパラメータの総数を取得したりできる。
print(l_1.name)
# block1_conv1
print(l_1.count_params())
# 1792
そのほか、trainable
属性はそのレイヤーを学習するか(学習によって値を更新するか)しないかの設定。ミュータブルな属性なのでTrue
/ False
で切り替え可能。
print(l_1.trainable)
# True
l_1.trainable = False
print(l_1.trainable)
# False
学習済みモデルを使った転移学習において所望のレイヤーのみを学習させたい場合などに使う。
なお、name
はRead-onlyなので変更できない。
# l_1.name = 'new_name'
# AttributeError: Can't set the attribute "name", likely because it conflicts with an existing read-only @property of the object. Please choose a different name.
参考までに書いておくと、_name
は変更可能でname
にも反映される。
l_1._name = 'new_name'
print(l_1.name)
# new_name
ただし、_name
はドキュメントに記載されておらず、_
が先頭についていることからも外部から使うことは意図されていないと思われる。もし使用する場合は、想定外の挙動の原因となる可能性があることを十分理解しておく必要がある。
条件を満たすレイヤーオブジェクトを取得、設定変更
上述のようにlayers
属性はただのリストなので、リスト内包表記などを利用して条件を満たすレイヤーのみを抽出できる。
- 関連記事: Pythonリスト内包表記の使い方
名前に'pool'
が含まれるレイヤーを抽出する例。
l_pool = [l for l in model.layers if 'pool' in l.name]
pprint.pprint(l_pool)
# [<tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x135eb4cd0>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13d19ac90>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13d279e10>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddd6fd0>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddf3e10>]
レイヤーの種類(クラス)を条件とする例。
l_pool = [l for l in model.layers if isinstance(l, tf.keras.layers.MaxPooling2D)]
pprint.pprint(l_pool)
# [<tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x135eb4cd0>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13d19ac90>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13d279e10>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddd6fd0>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddf3e10>]
l_pool_dense = [l for l in model.layers
if isinstance(l, (tf.keras.layers.MaxPooling2D, tf.keras.layers.Dense))]
pprint.pprint(l_pool_dense)
# [<tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x135eb4cd0>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13d19ac90>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13d279e10>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddd6fd0>,
# <tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddf3e10>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13ddf5d10>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13de03910>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13de09210>]
範囲で指定したい場合はスライスを使う。
l_tail = model.layers[-5:]
pprint.pprint(l_tail)
# [<tensorflow.python.keras.layers.pooling.MaxPooling2D object at 0x13ddf3e10>,
# <tensorflow.python.keras.layers.core.Flatten object at 0x13ddf5c90>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13ddf5d10>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13de03910>,
# <tensorflow.python.keras.layers.core.Dense object at 0x13de09210>]
例えば、所望の複数のレイヤーのtrainable
属性を一括で変更したいといった場合は、リスト化する必要はなく、単純にfor
文を使えばいい。
Dense
レイヤーを変更。
for l in model.layers:
if isinstance(l, tf.keras.layers.Dense):
l.trainable = False
末尾の5つのレイヤーを変更。
for l in model.layers[-5:]:
l.trainable = False