albermax / innvestigate

A toolbox to iNNvestigate neural networks' predictions!
Other
1.24k stars 235 forks source link

How does LRP works when used in Autoencoders? #272

Open HugoTex98 opened 2 years ago

HugoTex98 commented 2 years ago

Hello everyone!

I decided to use this amazing toolbox in my Autoencoder model but I'm having doubts about how it works in this type of models...

How will relevance scores be calculated in this case? Is it done in my encoder or when input is reconstructed?

Can anyone help me in this question?

My model is the following: `

def ConnectomeCNNAutoencoder(input_shape, keep_pr=0.65, n_filter=32, n_dense1=64, n_classes=2, # keep_pr=0.65, n_filter=32, n_dense1=64, mode="autoencoder", sign="neg"):
input_1 = Input(shape=input_shape)
# Convolutional Encoder 
bias_init = tf.constant_initializer(value=0.001)
conv1 = Conv2D(filters=n_filter , kernel_size=(1,input_shape[1]), strides=(1, 1),
                                 padding= "valid", activation="selu", # "selu"
                                 kernel_initializer="glorot_uniform",
                                 bias_initializer=bias_init, name="conv1")(input_1)

# dropout1 = Dropout(keep_pr, name="dropout1")(conv1)
dropout1 = SpatialDropout2D(keep_pr, name="dropout1")(conv1)
conv2 = Conv2D(filters=n_filter*2 , kernel_size=(input_shape[1],1), strides=(1, 1),
                                 padding= "valid", activation="selu", # "selu"
                                 kernel_initializer="glorot_uniform",
                                 bias_initializer=bias_init, name="conv2")(dropout1)
encoded = Dropout(keep_pr, name="dropout2")(conv2)

# Classification
reshape = Reshape((n_filter*2,), name="reshape1")(encoded)
dense1 = Dense(n_dense1, activation="selu", name="dense1", kernel_regularizer=keras.regularizers.l1_l2())(reshape) 
# dense1 = Dense(n_dense1, activation="selu", name="dense1", kernel_regularizer=keras.regularizers.l1_l2(l1=0.001,l2=0.001))(reshape) 
# dropout2 = Dropout(0.5, name="dropout2")

if n_classes == 1:
    activation = "sigmoid"
else:
    activation = "softmax"
output = Dense(n_classes, activation=activation, name="output")(dense1)

# Decoder
dim_reconstruct = tuple(encoded.get_shape().as_list())  # say, (1, 1, 60)
n_dense2 = np.product(dim_reconstruct[1:])  # will be 60
dense2 = Dense(n_dense2, activation="selu", name="dense2")(output)
reshape2 = Reshape(dim_reconstruct[1:], name="reshape2")(dense2)

# dropout3 = Dropout(keep_pr, name="dropout3")(reshape2)

conv3 = Conv2DTranspose(filters=n_filter*2 , kernel_size=(1,1), strides=(1, 1),
                                  padding= "valid", activation="selu", # "selu"
                                  kernel_initializer="glorot_uniform",
                                  bias_initializer=bias_init, name="conv3")(reshape2)
# dropout3 = Dropout(keep_pr, name="dropout3")(conv3)
conv4 = Conv2DTranspose(filters=n_filter , kernel_size=(input_shape[1],1), strides=(1, 1),
                                  padding= "valid", activation="selu", # "selu"
                                  kernel_initializer="glorot_uniform",
                                  bias_initializer=bias_init, name="conv4")(conv3)

if sign == "pos":
    reconstructed_activation = "sigmoid"
elif sign == "neg":
    reconstructed_activation = "tanh"

reconstructed_input = Conv2DTranspose(filters=input_shape[-1], kernel_size=(1,input_shape[1]), strides=(1, 1),
                                  padding= "valid", activation=reconstructed_activation, 
                                  kernel_initializer="glorot_uniform",
                                  bias_initializer=bias_init, name='autoencoder')(conv4)

if mode == "autoencoder":
    model = keras.models.Model(inputs=input_1, outputs=[output, reconstructed_input])
elif mode =="encoder":
    # Work as a feature extractor for others models
    model = keras.models.Model(inputs=input_1, outputs=encoded)
elif mode == "decoder":
    model = keras.models.Model(inputs=input_1, outputs=reconstructed_input)
return model

`

enryH commented 2 years ago

As you wrap a keras model into an explainer, it is up to you if you only want to analyse your latent space after your encoder or if you want to see what the reconstructions explained: Do you want to explain single dimensions in your latent space or do you want to explain single reconstructions in your reconstructions.

Most other applications have a classic supervised setup, where the predicted class (logits/score) is explained.

HugoTex98 commented 2 years ago

Thanks for your reply @enryH !

If I want to explain the reconstructions of my inputs, do I need to change anything in the normal configuration of my LRP analyzer?

enryH commented 2 years ago

Normally it is advised (in papers) to look at the logits, so you need to remove the activation. What loss are you using?

HugoTex98 commented 2 years ago

Yes, I already removed the activation function! For the loss I'm using the MSE @enryH

lenbrocki commented 2 years ago

@HugoTex98 If you like you can also check out our paper https://arxiv.org/abs/1910.13140 that goes beyond explaining single dimensions in the latent space.