Search code examples
arrayskerasreshapeautoencoder

How to solve "total size of new array must be unchanged error" in Python?


I am implementing the following model:

def ConnectomeCNNAutoencoder(input_shape, keep_pr=0.65, n_filter=32, n_dense1=64, n_classes=2, 
                      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)
conv2 = Conv2D(filters=n_filter*2 , kernel_size=(input_shape[1],1), strides=(1, 1),
                                 padding= "valid", activation="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) 

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

# Decoder
dense2 = Dense(n_dense1, activation="selu", name="dense2")(output) 
dim_reconstruct = tuple(encoded.get_shape().as_list())
reshape2 = Reshape(dim_reconstruct[1:], name="reshape2")(dense2)

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)
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":
    model = keras.models.Model(inputs=input_1, outputs=encoded)
elif mode == "decoder":
    model = keras.models.Model(inputs=input_1, outputs=reconstructed_input)
return model

The model works fine when n_filter=32 and n_dense1=64, but when I change these variable for other values, this error pops up: "ValueError: total size of new array must be unchanged". I know that is related tothe use of Reshape in reshape2, but I don't know how to solve this.

How can I solve this?

Thanks!


Solution

  • The problem appears in this line:

    reshape2 = Reshape(dim_reconstruct[1:], name="reshape2")(dense2)
    

    Tensor dense2 should be of the shape that could be 'transformed' into shape of dim_reconstruct[1:]. It means that the product of values of dim_reconstruct[1:] should be equal to the shape of dense2 (excluding zeroth dimension - batch size, because keras doesn't count it when derives dimensionalities of tensors).

    If n_filters = 30, dim_reconstruct[1:] will be [1, 1, 60] - because you multiplied n_filters by 2. But number of dense filters has to be equal to the product of values from [1, 1, 60], i.e., 60.

    I couldn't find any image with transformation of 1d into 3d array. But there's and example with 2d arrays: one can't fit array [1,2,3,4,5] into 2x3 2d array, but can transform [1,2,3,4,5,6] into something like [[1, 2, 3], [4, 5, 6]]

    So, you could set n_units1 to 60 when call ConnectomeCNNAutoencoder, or you could derive it automatically instead:

        # 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)
    

    Complete example (I removed some arguments that were equal to default values):

    import numpy as np
    import tensorflow as tf
    from tensorflow import keras
    from tensorflow.keras import Input
    from tensorflow.keras.layers import Conv2D, Dropout, Reshape, Dense, Conv2DTranspose
    
    
    def ConnectomeCNNAutoencoder(input_shape,
                                 keep_pr=0.65,
                                 n_filter=32,
                                 n_dense1=64,
                                 n_classes=2,
                                 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),
                       activation="selu",  # "selu"
                       bias_initializer=bias_init,
                       name="conv1")(input_1)
        dropout1 = Dropout(keep_pr, name="dropout1")(conv1)
        conv2 = Conv2D(filters=n_filter * 2,
                       kernel_size=(input_shape[1], 1),
                       strides=(1, 1),
                       activation="selu",
                       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)
    
        if n_classes == 1:
            activation = "sigmoid"
        else:
            activation = "softmax"
    
        output = Dense(n_classes, activation=activation, name="output")(dense1)
    
        # Decoder - Changes here
        dim_reconstruct = tuple(encoded.get_shape().as_list())
        n_dense2 = np.product(dim_reconstruct[1:])
        dense2 = Dense(n_dense2, activation="selu", name="dense2")(output)
        reshape2 = Reshape(dim_reconstruct[1:], name="reshape2")(dense2)
    
        conv3 = Conv2DTranspose(filters=n_filter * 2,
                                kernel_size=(1, 1),
                                strides=(1, 1),
                                activation="selu",  # "selu"
                                bias_initializer=bias_init,
                                name="conv3")(reshape2)
        conv4 = Conv2DTranspose(filters=n_filter,
                                kernel_size=(input_shape[1], 1),
                                strides=(1, 1),
                                activation="selu",  # "selu"
                                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),
                                              activation=reconstructed_activation,
                                              bias_initializer=bias_init,
                                              name='autoencoder')(conv4)
    
        if mode == "autoencoder":
            model = keras.models.Model(inputs=input_1, outputs=[output, reconstructed_input])
        elif mode == "encoder":
            model = keras.models.Model(inputs=input_1, outputs=encoded)
        elif mode == "decoder":
            model = keras.models.Model(inputs=input_1, outputs=reconstructed_input)
        else:
            raise ValueError("Unexpected mode: %s" % mode)
        return model
    
    
    model = ConnectomeCNNAutoencoder((32, 32, 3), n_filter=30, n_dense1=65)