TensorFlow, Kerasでレイヤーの名前からインデックスを取得
TensorFlow, Kerasで構築したモデルにおいて、レイヤーの名前からインデックス(何層目か)を取得する方法を説明する。
- 関数を定義
- レイヤー名をキー、インデックスを値とする辞書を生成
- 全てのレイヤー名のリストを生成
- Subclassing APIの場合の注意点
以下のサンプルコードのTensorFlowのバージョンは2.1.0
。TensorFlowに統合されたKerasを使う。
import tensorflow as tf
import pprint
print(tf.__version__)
# 2.1.0
以下のモデルを例とする。ただの例なので、なにか意味のあるモデルではない。
model = tf.keras.Sequential([
tf.keras.layers.Dense(100, name='Layer_0', input_shape=(1000,)),
tf.keras.layers.Dense(10, name='Layer_1'),
tf.keras.layers.Dense(1, name='Layer_2')
])
model.summary()
# Model: "sequential"
# _________________________________________________________________
# Layer (type) Output Shape Param #
# =================================================================
# Layer_0 (Dense) (None, 100) 100100
# _________________________________________________________________
# Layer_1 (Dense) (None, 10) 1010
# _________________________________________________________________
# Layer_2 (Dense) (None, 1) 11
# =================================================================
# Total params: 101,121
# Trainable params: 101,121
# Non-trainable params: 0
# _________________________________________________________________
TensorFlow, Kerasについての基礎は以下の記事を参照。
関数を定義
以下のような関数でモデルのレイヤー名からインデックスを取得できる。
def get_layer_index(model, layer_name, not_found=None):
for i, l in enumerate(model.layers):
if l.name == layer_name:
return i
return not_found
第一引数にモデル、第二引数にインデックスを取得したいレイヤーの名前を指定する。
print(get_layer_index(model, 'Layer_1'))
# 1
存在しない名前の場合、デフォルトではNone
を返す。第三引数に存在しない場合の返り値を指定することも可能。
print(get_layer_index(model, 'xxxxx'))
# None
print(get_layer_index(model, 'xxxxx', -1))
# -1
ちなみに、関数の中で使っているモデルのlayers
属性は各レイヤーのオブジェクトが格納されたリスト。ここでは出力を見やすくするためにpprintを使っている。
print(type(model.layers))
# <class 'list'>
pprint.pprint(model.layers)
# [<tensorflow.python.keras.layers.core.Dense object at 0x12fa1a6d0>,
# <tensorflow.python.keras.layers.core.Dense object at 0x12fa19a90>,
# <tensorflow.python.keras.layers.core.Dense object at 0x12fa19690>]
レイヤーのname
属性で名前を取得できる。
print(type(model.layers[0]))
# <class 'tensorflow.python.keras.layers.core.Dense'>
print(model.layers[0].name)
# Layer_0
レイヤー名をキー、インデックスを値とする辞書を生成
レイヤー名をキー、インデックスを値とする辞書を生成して利用する方法もある。レイヤー名からインデックスを取得する処理を繰り返し行う場合はこちらのほうが効率的。
辞書内包表記で所望の辞書を生成できる。
d = {l.name: i for i, l in enumerate(model.layers)}
print(d)
# {'Layer_0': 0, 'Layer_1': 1, 'Layer_2': 2}
存在しない名前の場合、[]
でキー指定するとエラーとなるが、get()
メソッドを使うと所定の値が返される。
print(d['Layer_1'])
# 1
# print(d['xxxxx'])
# KeyError: 'xxxxx'
print(d.get('Layer_1'))
# 1
print(d.get('xxxxx'))
# None
print(d.get('xxxxx', -1))
# -1
全てのレイヤー名のリストを生成
全てのレイヤー名のリストを生成してindex()
メソッドを使う方法もある。
layer_names = [l.name for l in model.layers]
print(layer_names)
# ['Layer_0', 'Layer_1', 'Layer_2']
print(layer_names.index('Layer_1'))
# 1
続けて書いてもいい。
print([l.name for l in model.layers].index('Layer_1'))
# 1
存在しない名前の場合はエラーとなる。
# print([l.name for l in model.layers].index('xxxxx'))
# ValueError: 'xxxxx' is not in list
上の2つの方法に比べてメリットがあるわけではないが、Jupyter Notebookなどの対話的な環境であればこれでも十分。お好みで。
Subclassing APIの場合の注意点
Subclassing APIでモデルを生成した場合、レイヤーのインデックスはそのレイヤーがモデルの何層目にあるかを表しているとは限らないので注意。
以下のモデルを例とする。
class MyModel(tf.keras.Model):
def __init__(self):
super(MyModel, self).__init__()
self.l2 = tf.keras.layers.Dense(1, name='Layer_2')
self.l0 = tf.keras.layers.Dense(100, name='Layer_0')
self.l1 = tf.keras.layers.Dense(10, name='Layer_1')
def call(self, x):
x = self.l0(x)
x = self.l1(x)
x = self.l2(x)
return x
my_model = MyModel()
my_model.build((None, 1000))
my_model.summary()
# Model: "my_model"
# _________________________________________________________________
# Layer (type) Output Shape Param #
# =================================================================
# Layer_2 (Dense) multiple 11
# _________________________________________________________________
# Layer_0 (Dense) multiple 100100
# _________________________________________________________________
# Layer_1 (Dense) multiple 1010
# =================================================================
# Total params: 101,121
# Trainable params: 101,121
# Non-trainable params: 0
# _________________________________________________________________
summary()
の出力が、call()
で定義されたモデルにおけるレイヤーの順番ではなく、__init__()
で定義されたレイヤーの順番に表示されていることが分かる。ここでは、説明のため、あえてcall()
と__init__()
でレイヤーの順番を変えている。
インデックスもsummary()
の出力のように、__init__()
での定義の順番で割り振られている。
my_d = {l.name: i for i, l in enumerate(my_model.layers)}
print(my_d)
# {'Layer_2': 0, 'Layer_0': 1, 'Layer_1': 2}
print(my_d['Layer_0'])
# 1
print(my_d['Layer_1'])
# 2
print(my_d['Layer_2'])
# 0
Subclassing APIにおけるインデックスは各レイヤーに与えられた一意の値ではあるが、モデルにおけるレイヤーの位置を表すとは限らない。例えばインデックスが0
だとしても最も入力側のレイヤーであるとは限らない。要注意。