Как структурировать график в tensorboard, созданный Keras code?

#python #tensorflow #keras #tensorboard

#python #tensorflow #keras #tensorboard

Вопрос:

Используя функциональный API, есть ли способ получить правильно структурированный график тензорной доски, созданный из кода keras?

Есть ли какой-нибудь способ, например, назвать блок инструкций в функциональном подходе как во время создания экземпляра, так и во время вызова?

Я пытаюсь получить график сети, хорошо структурированный в tensorboard.

По-видимому, при использовании функционального API tf.name_scope используется в графике при создании экземпляра модели / слоя, напротив, имя kwargs используется при вызове модели / слоя, который уже был создан. Более того, блоки tf.name_scope не отображаются в части графика, соответствующей вызову (они появляются в части создания экземпляра).

Единственный способ, которым я обнаружил, что одно и то же имя используется как при создании экземпляра, так и при вызове, а также при правильной структуре блоков слоев на графике, — это получение пользовательских слоев из слоя базового класса. Но то, что это действительно болезненно / излишне, делает код нечитаемым в больших моделях или может быть неосуществимым для некоторых моделей с несколькими входами / несколькими выходами.

Вот пример с кодом автокодирования.

Пример с функциональным API

 from keras import Model
from keras import backend as K
from keras.layers import Input, Dense, ReLU

layers=[1024,512,128,64]

graph = K.tf.Graph()
with graph.as_default():

    with K.name_scope('ns_encoder'):
        x = Input(shape=(layers[0],))
        z = x
        for hu in layers[1:]:
            with K.name_scope('ns_encblock'):
                z = Dense(hu)(z)
                z = ReLU()(z)
        encoder = Model(inputs=x,outputs=z, name='m_encoder')

    with K.name_scope('ns_decoder'):
        x = Input(shape=(layers[-1],))
        z = x
        for hu in layers[:-1][::-1]:
            with K.name_scope('ns_decblock'):
                z = Dense(hu)(z)
                z = ReLU()(z)
        decoder = Model(inputs=x,outputs=z, name='m_decoder')    

    with K.name_scope('ns_ae'):
        x = Input(shape=(layers[0],))
        z = encoder(x)
        xh = decoder(z)
        ae = Model(inputs=x,outputs=xh, name='m_ae')

writer = K.tf.summary.FileWriter(logdir='logdir', graph=graph)
writer.flush()
 

График, созданный функциональным API

Тот же слой re_lu_3 корректно отображается внутри блока в ns_encoder, но не в «m_encoder» из «ns_ae». Кроме того, name_scope ‘ns_encoder’ используется в определении модели кодера, в то время как name kwarg ‘m_encoder’ используется в функциональном использовании модели в «ns_ae»

Пример с производным классом

 from keras import Model
from keras import backend as K
from keras.layers import Input, Dense, ReLU, Layer

layers=[1024,512,128,64]

class Block(Layer):
    def __init__(self, hu, **kwargs):
        super().__init__(**kwargs)
        self.hu = hu
    def build(self,input_shape):
        self.dense =  Dense(self.hu)
        self.relu = ReLU()
        super().build(input_shape)
    def compute_output_shape(self, input_shape):
        return self.relu.compute_output_shape(
                self.dense.compute_output_shape(input_shape))
    def call(self,x):
        return self.relu(self.dense(x))   

graph = K.tf.Graph()
with graph.as_default():

    with K.name_scope('ns_encoder'):
        x = Input(shape=(layers[0],))
        z = x
        for hu in layers[1:]:
            with K.name_scope('ns_encblock'):
                z = Block(hu)(z)
        encoder = Model(inputs=x,outputs=z, name='m_encoder')

    with K.name_scope('ns_decoder'):
        x = Input(shape=(layers[-1],))
        z = x
        for hu in layers[:-1][::-1]:
            with K.name_scope('ns_decblock'):
                z = Block(hu)(z)
        decoder = Model(inputs=x,outputs=z, name='m_decoder')

    with K.name_scope('ns_ae'):
        x = Input(shape=(layers[0],))
        z = encoder(x)
        xh = decoder(z)
        ae = Model(inputs=x,outputs=xh, name='m_ae')

writer = K.tf.summary.FileWriter(logdir='logdir', graph=graph)
writer.flush()
 

График, созданный производным класса

На этот раз график правильно структурирован: block_3 отображается как в encoder, так и в full auto-encoder. Но код действительно перегружен, и эта парадигма не может применяться постоянно.