Вариационный автоэнкодер с несколькими входами и выходами - PullRequest
2 голосов
/ 30 сентября 2019

Я построил автоматический кодировщик в Keras, который принимает несколько входов и один и тот же набор выходов, которые я хотел бы преобразовать в вариационный автоматический кодировщик. У меня возникли проблемы при объединении потери разницы между входом и выходом и потери вариационной части.

Чего я хочу достичь:

Для данных должен использоваться автоматический кодировщикнабор, содержащий как числовые, так и категориальные данные. Для этого я нормализую числовые столбцы и кодирую в горячем виде категориальные столбцы. Поскольку результирующие категориальные векторы и числовые векторы требуют различных функций потерь (среднеквадратичная ошибка для числовой и категориальной кросс-энтропии для категориальных столбцов) и очень большие векторы горячего кодирования 1-hot будут доминировать над потерями по сравнению смаленькие числовые столбцы, я решил поместить каждый столбец как свой собственный входной вектор. Таким образом, мой автоматический кодер принимает набор входных векторов, генерирует выходные векторы одинакового числа и форм.

Что я сделал до сих пор:

Это настройка для двух числовых входов и двух категориальныхс кодированием 20 и 30 в 1-горячем коде:

encWidth = 3
## Encoder
x = Concatenate(axis=1)([ Input(1,),Input(1,),Input(20,),Input(30,) ]) #<-configurable
x = Dense( 32, activation="relu")(x)
layEncOut = Dense( encWidth, activation="linear")(x)

layDecIn = Input( encWidth, name="In_Encoder" )
x = Dense( 32, activation="relu")(layDecIn)
layDecOut = [ outLayer(x) for outLayer in C.layOutputs ]

encoder = Model(C.layInputs, layEncOut, name="encoder")
decoder = Model( layDecIn, layDecOut, name="decoder" )

AE = Model(C.layInputs, decoder(encoder(C.layInputs)), name="autoencoder")
AE.compile(optimizer="adam", 
           loss=['mean_squared_error', 'mean_squared_error',
                 'categorical_crossentropy', 'categorical_crossentropy',], #<-configurable
                 loss_weights=[1.0, 1.0, 1.0, 1.0] #<-configurable
          )

Этот пример статичен, но в моей реализации числовые и категориальные поля являются настраиваемыми, поэтому входы, вид функций потерь и веса потерьдолжен конфигурироваться из объекта, в котором хранятся исходные столбцы из набора данных.

....
## Encoder
x = Concatenate(axis=1)( C.layInputs )
...
AE.compile(optimizer="adam", 
           loss=C.losses
           loss_weights=C.lossWeights
          )

Здесь C - это экземпляр класса, который имеет входной слой и функции / веса потерь в зависимости от того, какие столбцыЯ хочу иметь в авто кодировщике.

Моя проблема:

Теперь я расширил набор до вариационного авто кодировщика со скрытым слоем среднего и стандартного отклонения.

encWidth = 2

## Encoder
x = Concatenate(axis=1)(C.layInputs)
x = Dense( 32, activation="relu")(x)

### variational part
z_mean = Dense(encWidth, name='z_mean', activation=lrelu)(x)
z_log_var = Dense(encWidth, name='z_log_var', activation=lrelu)(x)
z = Lambda(sampling, name='z')([z_mean, z_log_var])

## Decoder
layDecodeInput = Input( encWidth, name="In_Encoder" )
x = Dense( 32, activation="relu")(layDecodeInput)
layOutDecoder = [ outLayer(x) for outLayer in C.layOutputs ]

### build the encoder model
vEncoder = Model(C.layInputs, [z_mean, z_log_var, z], name='v_encoder')

### build the decoder model
vDecoder = Model( layDecodeInput, layOutDecoder, name="v_decoder" )

## Autoencoder
vAE = Model(C.layInputs, vDecoder(vEncoder(C.layInputs)[2]))
vae_loss = variational_loss(z_mean, z_log_var)
vAE.compile(optimizer="adam",
            loss=vae_loss)

Теперь мне нужна пользовательская функция ошибок, которая сочетает в себе потерю разницы между входом и выходом (как в предыдущемпример) с потерей в вариационной части;это то, что я придумал до сих пор:

def variational_loss(z_mean, z_log_var, varLossWeight=1.):    
    def lossFct(yTrue, yPred):       

        var_loss = -0.5 * K.mean(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var))

        lossFunctions = [getattr(losses, "mean_squared_error") for losses in  C.losses]
        ac_loss = [
          lossFkt(yTrue, yPred) * lossWeigt for
          yt, yp, lossFkt, lossWeigt in zip(yTrue, yPred, lossFunctions, C.lossWeights) ]

        loss =  K.mean( ac_loss + [ kl_loss * varLossWeight ] )
        return loss
    return lossFct

Так что это функция генератора, которая возвращает функцию, принимающую yTrue и yPredicted, но работает в вариационной части. Цикл for должен зациклить все входы и соответствующие выходы и сравнить их, используя соответствующую функцию потерь (либо среднеквадратичная ошибка для числового значения, либо категориальная перекрестная энтропия для категориальных признаков)

Но, очевидно, цикл for длязацикливание набора входных векторов и сравнение их с набором выходных векторов не допускается;Я получаю сообщение об ошибке

Tensor objects are only iterable when eager execution is enabled. To iterate over this tensor use tf.map_fn.

Как мне получить удобное поведение функции Model.compile(), где я могу просто сказать использовать разные функции потерь на разных входах и выходах в сочетании с вариационными потерями?

1 Ответ

1 голос
/ 30 сентября 2019

Я думаю, что будет проще добавить слой дивергенции aKL в сеть, который заботится о потере VAE. Вы можете сделать это следующим образом (где бета - это вес потери):

import keras.backend as K
from keras.layers import Layer

class KLDivergenceLayer(Layer):

    """ Identity transform layer that adds KL divergence
    to the final model loss.
    """

    def __init__(self, beta=.5, *args, **kwargs):
        self.is_placeholder = True
        self.beta = beta
        super(KLDivergenceLayer, self).__init__(*args, **kwargs)

    def call(self, inputs):

        mu, log_var = inputs

        kl_batch = - self.beta * K.sum(1 + log_var -
                                K.square(mu) -
                                K.exp(log_var), axis=-1)

        self.add_loss(K.mean(kl_batch), inputs=inputs)

        return inputs

Затем вы можете добавить эту строку в свой код после вычисления среднего значения и логарифма var:

z_mean , z_log_var = KLDivergenceLayer()([z_mean , z_log_var])

Этот уровень является уровнем идентификации, который добавляет потери KL к окончательной потере. Тогда ваша последняя потеря может быть той, которую вы использовали выше.

Я нашел этот пост от Луиса С. Тяо: https://tiao.io/post/tutorial-on-variational-autoencoders-with-a-concise-keras-implementation/

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