Функциональный API против создания подклассов объектно-ориентированного вариационного автоэнкодера - PullRequest
0 голосов
/ 05 августа 2020

Во-первых, я написал вариационный автоэнкодер через Functional API, используя тензорный поток v2.3, следуя этому примеру tf :

input_dim = X_train.shape[1:]
layer_dim = 128

# Build encoder
inputs = Input(shape = input_dim)
z1 = Flatten()(inputs)
z = Dense(layer_dim, activation='elu', kernel_initializer=he_uniform())(z1)
z = Dense(layer_dim, activation='elu', kernel_initializer=he_uniform())(z)
c1 = Concatenate()([z,z1])
z = Dense(layer_dim, activation='elu', kernel_initializer=he_uniform())(c1)
z = Dense(layer_dim, activation='elu', kernel_initializer=he_uniform())(z)
c2 = Concatenate()([z,c1])

# Latent layers and sampling
z_mean = Dense(z_size)(c2)
z_log_var = Dense(z_size)(c2)
z = Sampling(beta)([z_mean, z_log_var])

variational_encoder = Model(inputs=[inputs], outputs=[z_mean, z_log_var, z], name='Encoder')

# Build decoder
decoder_inputs = Input(shape=[z_size])
x = Dense(layer_dim, activation='elu', kernel_initializer=he_uniform())(decoder_inputs)
x = Dense(layer_dim, activation='elu', kernel_initializer=he_uniform())(x)
c1 = Concatenate()([x,decoder_inputs])
x = Dense(layer_dim, activation='elu', kernel_initializer=he_uniform())(c1)
x = Dense(layer_dim, activation='elu', kernel_initializer=he_uniform())(x)
c2 = Concatenate()([x,c1])
x = Dense(np.multiply(*input_dim), activation='linear')(c2)
outputs = Reshape(input_dim)(x)

variational_decoder = Model(inputs=[decoder_inputs], outputs=[outputs], name='Decoder')

# Latent space and decoded latent space
_, _, codings = variational_encoder(inputs)
reconstructions = variational_decoder(codings)

# Build autoencoder
variational_ae = Model(inputs=[inputs], outputs=[reconstructions], name='VAE')

# Mean Squared Error loss
def loss(inputs, reconstructions):
    mse_loss = np.multiply(*input_dim) * mse(K.flatten(inputs), K.flatten(reconstructions))
    return (1.-beta) * K.mean(mse_loss)

def rounded_accuracy(y_true, y_pred):
    return binary_accuracy(tf.round(y_true), tf.round(y_pred))


variational_ae.compile(loss = loss,
                        optimizer = Adam())

history = variational_ae.fit(X_train, X_train, 
                            epochs = N_epochs,
                            batch_size = batch_size,
                            shuffle = True,
                            validation_split = 0.2,
                            callbacks=[checkpointer])

где выборка определяется как

class Sampling(Layer):
    """Uses (z_mean, z_log_var) to sample z, the vector encoding a digit."""
    def __init__(self, beta):
        super(Sampling, self).__init__()
        self.beta = beta

    def call(self, inputs):
        mean, log_var = inputs
        batch = tf.shape(mean)[0]
        dim = tf.shape(mean)[1]

        # Add Kullback-Leibler divergence
        kl_loss = - 0.5 * K.sum(1 + log_var - K.square(mean) - K.exp(log_var), axis=-1)
        self.add_loss(self.beta * K.mean(kl_loss))

        # Add sampling
        epsilon = K.random_normal(shape=(batch, dim))
        return epsilon * K.exp(0.5 * log_var) + mean

Он отлично работает и очень хорошо реконструирует ввод. Однако я попытался узнать что-то новое и немного очистить свой код и попытался использовать метод подкласса, снова на той же странице ref . Я попытался воспроизвести приведенный выше код, но теперь результаты намного хуже, и я не знаю, откуда берутся различия, единственное различие, которое я вижу, - это слой Input (я пытался добавить его, но результатов нет. изменение). Новый код:

class Encoder(Model):
    """Maps CaloLayerEnergyDistributions to a triplet (z_mean, z_log_var, z)."""

    def __init__(self, beta, latent_dim=128, intermediate_dim=128, name="Encoder", **kwargs):
        super(Encoder, self).__init__(name=name, **kwargs)
        self.intermediate = [Dense(intermediate_dim, activation='elu', kernel_initializer=he_uniform()) for layer in range(4)]
        self.flatten = Flatten()
        self.concatenate = Concatenate()
        self.mean = Dense(latent_dim)
        self.log_var = Dense(latent_dim)
        self.sampling = Sampling(beta)

    def call(self, inputs):
        # Build encoder
        z1 = self.flatten(inputs)
        z = self.intermediate[0](z1)
        z = self.intermediate[1](z)
        c1 = self.concatenate([z, z1])
        z = self.intermediate[2](c1)
        z = self.intermediate[3](z)
        c2 = self.concatenate([z, c1])

        # Latent layers and sampling
        z_mean = self.mean(c2)
        z_log_var = self.log_var(c2)
        z = self.sampling((z_mean, z_log_var))
        return z_mean, z_log_var, z


class Decoder(Model):
    """Converts z, the encoded digit vector, back into energy distributions."""

    def __init__(self, original_dim, intermediate_dim=128, name="Decoder", **kwargs):
        super(Decoder, self).__init__(name=name, **kwargs)
        self.intermediate = [Dense(intermediate_dim, activation='elu', kernel_initializer=he_uniform()) for layer in range(4)]
        self.concatenate = Concatenate()
        self.outputs = Dense(np.multiply(*original_dim), activation='linear')
        self.reshape = Reshape(original_dim)

    def call(self, inputs):
        # Build decoder
        x = self.intermediate[0](inputs)
        x = self.intermediate[1](x)
        c1 = self.concatenate([x, inputs])
        x = self.intermediate[2](c1)
        x = self.intermediate[3](x)
        c2 = self.concatenate([x, c1])
        x = self.outputs(c2)
        return self.reshape(x)


class VariationalAutoEncoder(Model):
    """Combines the encoder and decoder into an end-to-end model for training."""

    def __init__(
        self,
        beta,
        original_dim,
        intermediate_dim=128,
        latent_dim=128,
        name="AutoEncoder",
        **kwargs
    ):
        super(VariationalAutoEncoder, self).__init__(name=name, **kwargs)
        self.original_dim = original_dim
        self.encoder = Encoder(beta)
        self.decoder = Decoder(original_dim)

    def call(self, inputs):
        _, _, codings = self.encoder(inputs)
        reconstructed = self.decoder(codings)

        return reconstructed

encoder = Encoder(beta)
decoder = Decoder(original_dim=input_dim)
bVAE = VariationalAutoEncoder(beta, input_dim)
bVAE.compile(loss = mse_loss, optimizer=Adam())

history = bVAE.fit(X_train, X_train,
                   epochs=N_epochs,
                   batch_size=batch_size,
                   shuffle=True,
                   validation_split=0.2,
                   callbacks=[checkpointer])

(выборка и потери остались прежними). Вопрос немного длинный, но я надеюсь, что кто-нибудь сможет мне помочь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...