具有多个输入和输出的变分自动编码器

Variational Autoencoder with multiple in and outputs

我在 Keras 中构建了一个自动编码器,它接受多个输入和相同数量的输出,我想将其转换为变分自动编码器。我无法将输入和输出之间差异的损失与变分部分的损失结合起来。

我想达到的目标:

自动编码器应该用于包含数字和分类数据的数据集。为此,我对数字列进行了标准化,并对分类列进行了 1-hot 编码。由于生成的分类向量和数值向量需要不同的损失函数(数值的均方误差和分类列的分类交叉熵),并且非常大的 1-hot 编码向量将主导损失小数字列,我决定将每一列作为其自己的输入向量。因此我的自动编码器接受一组输入向量生成相同数量和形状的输出向量。

到目前为止我做了什么:

这是两个数字输入和两个具有 20 和 30 宽 1-hot 编码的分类输入的设置:

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 是 class 的一个实例,它具有输入层和 loss-functions/weights,具体取决于我希望在自动编码器中包含哪些列。

我的问题:

我现在已经将设置扩展到变分自动编码器,具有均值和标准差的潜在层。

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

所以它是一个生成器函数,returns 一个接受 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() 函数的便捷行为,我可以告诉我在不同的输入和输出上使用不同的损失函数,并结合变分损失?

我认为在网络中添加一个 KL 发散层来处理 VAE 损失会更简单。你可以这样做,(其中 beta 是 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

然后你可以在你的代码中添加这一行,在你计算平均值和对数变量之后:

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

该层是将 KL 损失添加到最终损失的身份层。那么你最后的损失就可以是你上面使用的那个。

我从 Luis C. Tiao 那里找到了 post:https://tiao.io/post/tutorial-on-variational-autoencoders-with-a-concise-keras-implementation/