note.nkmk.me

TensorFlow, Kerasでレイヤーの名前からインデックスを取得

Posted: 2020-03-15 / Tags: Python, 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だとしても最も入力側のレイヤーであるとは限らない。要注意。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事