'tuple' object has no attribute 'layer'

24,500

Solution 1

You have imported layers from tensorflow.keras while other functions you have imported from keras. You can either import your layers from keras or try importing other functions from tensorflow.keras which might work.

Solution 2

I am only new in Python and deep learning. But I wanted to share this, as it helped me with a similar error. Maybe, in the importing section, try:

import tensorflow.keras

For all the rest you have to import from keras, keep the tensorflow. in front. And type it as a whole when using anything from keras later. Eg:

model.add(tensorflow.keras.layers.Conv2D())

Then you will not need: import tensorflow as tf

The tuple probably refers to your input_shape. What I mention above solved my problem.

I cannot answer why exactly this happens, though.

Solution 3

Ran into this issue when using GPU/TPU on Colab and trying to use Tensorflow v1.14 by installing it using pip. The docs advise against installing tensorflow using pip on GPU/TPU, instead they recommend to use %tensorflow_version 1.x . Setting it as this, fixed my problem.

Docs: https://colab.research.google.com/notebooks/tensorflow_version.ipynb

Share:
24,500
Admin
Author by

Admin

Updated on February 11, 2022

Comments

  • Admin
    Admin about 2 years

    I am having many troubles trying to start training my model (a DCGAN). It is giving me the error:

    'tuple' object has no attribute 'layer'
    

    I read that this could be due to having both the TensorFlow version 1.14.0 and the Keras version 2.2 or higher. I tried to fix this by downgrading the Keras version to the 2.1.5, but I'm still having the same problem.

    from google.colab import drive 
    drive.mount('/mntDrive')
    
    import os,sys      #os es para gestionar directorios (trabajar con archivos); sys es para trabajar con variables del sistema
    
    # numpy
    import numpy as np
    
    # data processing, CSV file I/O (e.g. pd.read_csv)
    import pandas as pd
    from sklearn.model_selection import train_test_split
    
    # Charts
    import matplotlib.pyplot as plt
    from matplotlib.pyplot import imread
    #process
    from tqdm import tqdm
    
    # Image IO
    from PIL import Image
    import skimage.io
    import skimage.transform
    from skimage.transform import resize
    
    # Deep learning
    import tensorflow as tf
    
    from tensorflow.keras import layers
    from keras import optimizers
    from keras.models import Sequential, Model
    from keras.layers.advanced_activations import LeakyReLU
    from keras.layers import Dense, Conv2D, Flatten, MaxPool2D, Dropout, Input
    from keras.callbacks import ModelCheckpoint
    from keras.preprocessing.image import ImageDataGenerator
    from keras.optimizers import Adam
    from keras import initializers
    
    from __future__ import absolute_import, division, print_function, unicode_literals
    
    
    # To make sure that we can reproduce the experiment and get the same results
    np.random.seed(21)
    
    
    # The dimension of our random noise vector.
    random_dim = 100
    from skimage.color import rgb2gray
    import scipy.ndimage
    import scipy.misc
    import re
    images = []
    for root, dirnames, filenames in os.walk("/mntDrive/My Drive/Colab Notebooks/cubism"):
        for filename in filenames:
            if re.search("\.(jpg|jpeg|png)$", filename):
                filepath = os.path.join(root, filename)
                image = plt.imread(filepath, )
                image = (image.astype(np.float32) - 127.5)/127.5
                image_resized = resize(image, (112, 112))
                images.append(image_resized)
    images = np.array(images)
    
    
    print('Original image shape: {}'.format(images.shape))
    im_gray = rgb2gray(images)
    print('New image shape: {}'.format(im_gray.shape))
    images_resized = im_gray.reshape(320,12544)
    
    def get_optimizer():
      optimizer=tf.keras.optimizers.Adam(0.001)
      return optimizer
    
    
    def make_generator_model(optimizer):
        generator = tf.keras.Sequential()
        generator.add(layers.Dense(7*7*256, use_bias=False, input_shape=(random_dim,)))
        generator.add(layers.BatchNormalization())
        generator.add(layers.LeakyReLU())
    
        generator.add(layers.Reshape((7, 7, 256)))
        assert generator.output_shape == (None, 7, 7, 256) # Note: None is the batch size
    
        generator.add(layers.Conv2DTranspose(128, (5, 5), strides=(2, 2), padding='same', use_bias=False))
        assert generator.output_shape == (None, 14, 14, 128)
        generator.add(layers.BatchNormalization())
        generator.add(layers.LeakyReLU())
    
        generator.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
        assert generator.output_shape == (None, 28, 28, 64)
        generator.add(layers.BatchNormalization())
        generator.add(layers.LeakyReLU())
        
        generator.add(layers.Conv2DTranspose(32, (5, 5), strides=(2, 2), padding='same', use_bias=False))
        assert generator.output_shape == (None, 56, 56, 32)
        generator.add(layers.BatchNormalization())
        generator.add(layers.LeakyReLU())
    
        generator.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
        assert generator.output_shape == (None, 112, 112, 1)
        generator.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(0.001))
        
        return generator    
    
    def make_discriminator_model(optimizer):
        discriminator = tf.keras.Sequential()
        discriminator.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[112, 112, 1]))
        discriminator.add(layers.LeakyReLU())
        discriminator.add(layers.Dropout(0.3))
    
        discriminator.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
        discriminator.add(layers.LeakyReLU())
        discriminator.add(layers.Dropout(0.3))
        
        discriminator.add(layers.Conv2D(256, (5, 5), strides=(2, 2), padding='same'))   
        discriminator.add(layers.LeakyReLU())
        discriminator.add(layers.Dropout(0.3))
        
        discriminator.add(layers.Flatten())
        discriminator.add(layers.Dense(1, activation='sigmoid' ))
        discriminator.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(0.001))    
        
        return discriminator
        
    def get_gan_network(discriminator, random_dim, generator, optimizer):
        # We initially set trainable to False since we only want to train either the
        # generator or discriminator at a time
        discriminator.trainable = False
        # gan input (noise) will be 100-dimensional vectors
        gan_input = Input(shape=(random_dim,))
        # the output of the generator (an image)
        x = generator(gan_input)
        # get the output of the discriminator (probability if the image is real or not)
        gan_output = discriminator(x)
        gan = Model(inputs=gan_input, outputs=gan_output)
        gan.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(0.001))
        return gan
    
    def plot_generated_images(epoch, generator, examples=64, dim=(10, 10), figsize=(100, 100)):
        noise = np.random.normal(0, 1, size=[examples, random_dim]) #mean, std deviation, size
        generated_images = generator.predict(noise)
        generated_images = generated_images.reshape(examples, 112, 112)
    
        plt.figure(figsize=figsize)
        for i in range(generated_images.shape[0]):
            plt.subplot(dim[0], dim[1], i+1)
            plt.imshow(generated_images[i], interpolation='nearest', cmap='gray_r')
            plt.axis('off')
        plt.tight_layout()
        plt.savefig('dcgan_generated_Originals_2_epoch_%d.png' % epoch)
        
    
    #you can create a function which will save your generated images every 20 epochs
        # Create a wall of generated MNIST images
    def train(epochs=15000, batch_size=80):
        # Get the training and testing data
        images_resized
        # Split the training data into batches of size 80
        batch_count = images_resized.shape[0] // batch_size 
    
        # Build our GAN network
        optimizer=get_optimizer()
        generator = make_generator_model(optimizer)
        discriminator = make_discriminator_model(optimizer)
        gan = get_gan_network(discriminator, random_dim, generator, optimizer)
    
        for e in range(1, epochs+1):
            print ('-'*15, 'Epoch %d' % e, '-'*15)
            for _ in tqdm(range(batch_count)):
                # Get a random set of input noise and images
                noise = np.random.normal(0, 1, size=[batch_size, random_dim])
                image_batch = images_resized[np.random.randint(0, images_resized.shape[0], size=batch_size)]
    
                # Generate fake MNIST images
                generated_images = generator.predict(noise)
                X = np.concatenate([image_batch, generated_images])  #128x4096 --- 'a' x 4096
    
                # Labels for generated and real data
                y_dis = np.zeros(2*batch_size)
                y_dis[:batch_size] = 0.9    # One-sided label smoothing. Se refiere a que la mitad de y_dis seran 0.9 (reales) y la otra mitad seran 0 (falsos)
    
                
                # Train discriminator
                discriminator.trainable = True
                discriminator.train_on_batch(X, y_dis)      #Se entrena el Discriminador con dos vectores: X(tiene un batch(128) imagenes reales y un batch de imagenes generadas); y_dis(tiene un batch de 128 0.9(serian las reales) y otro batch de 128 ceros(serian las generadas). De esta manera, el algoritmo generador, aunque las genere bien, va a seguir optimizandose el numero de epochs que haga falta(seguimos en la fase de entrenamiento), ya que sus imagenes son continuamente rechazadas, comparandose con las reales y sacando continuamente diferencias. ) 
                                                            #Entra X(imagenes reales, imagenes generadas)--->y_dis(0.9 , 0)
                                                            #train_on_batch(x,y)---> realiza una actualizacion del gradiente en un batch----> x=array de training data (si el modelo tiene varias entradas pueden ser varios); y= array de target data (si el modelo tiene varias salidas pueden ser varios)
                
                # Train generator
                noise = np.random.normal(0, 1, size=[batch_size, random_dim])
                y_gen = np.ones(batch_size)
                discriminator.trainable = False
                gan.train_on_batch(noise, y_gen)            #Entra noise(pixeles desordenados)---->y_gen(todo son 1)
    
            if e == 1 or e % 50 == 0:
                plot_generated_images(e, generator)
                
                
    if __name__ == '__main__':
       train(30000, 80)
    

    This is the output:

    Drive already mounted at /mntDrive; to attempt to forcibly remount, call drive.mount("/mntDrive", force_remount=True).
    Original image shape: (320, 112, 112, 3)
    New image shape: (320, 112, 112)
    WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/init_ops.py:1251: calling VarianceScaling.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
    Instructions for updating:
    Call initializer instance with the dtype argument instead of passing it to the constructor
    WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/nn_impl.py:180: add_dispatch_support.<locals>.wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
    Instructions for updating:
    Use tf.where in 2.0, which has the same broadcast rule as np.where
    WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/keras/backend/tensorflow_backend.py:68: The name tf.get_default_graph is deprecated. Please use tf.compat.v1.get_default_graph instead.
    
    WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/keras/backend/tensorflow_backend.py:507: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.
    
        ---------------------------------------------------------------------------
        AttributeError                            Traceback (most recent call last)
        <ipython-input-18-6e3c9ece87ff> in <module>()
            196 
            197 if __name__ == '__main__':
        --> 198    train(30000, 80)
        
        11 frames
        <ipython-input-18-6e3c9ece87ff> in train(epochs, batch_size)
            161     generator = make_generator_model(optimizer)
            162     discriminator = make_discriminator_model(optimizer)
        --> 163     gan = get_gan_network(discriminator, random_dim, generator, optimizer)
            164 
            165     for e in range(1, epochs+1):
        
        <ipython-input-18-6e3c9ece87ff> in get_gan_network(discriminator, random_dim, generator, optimizer)
            128     gan_input = Input(shape=(random_dim,))
            129     # the output of the generator (an image)
        --> 130     x = generator(gan_input)
            131     # get the output of the discriminator (probability if the image is real or not)
            132     gan_output = discriminator(x)
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/base_layer.py in __call__(self, inputs, *args, **kwargs)
            632                     outputs = base_layer_utils.mark_as_return(outputs, acd)
            633                 else:
        --> 634                   outputs = call_fn(inputs, *args, **kwargs)
            635 
            636             except TypeError as e:
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/sequential.py in call(self, inputs, training, mask)
            245       if not self.built:
            246         self._init_graph_network(self.inputs, self.outputs, name=self.name)
        --> 247       return super(Sequential, self).call(inputs, training=training, mask=mask)
            248 
            249     outputs = inputs  # handle the corner case where self.layers is empty
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/network.py in call(self, inputs, training, mask)
            749                                 ' implement a `call` method.')
            750 
        --> 751     return self._run_internal_graph(inputs, training=training, mask=mask)
            752 
            753   def compute_output_shape(self, input_shape):
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/network.py in _run_internal_graph(self, inputs, training, mask)
            891 
            892           # Compute outputs.
        --> 893           output_tensors = layer(computed_tensors, **kwargs)
            894 
            895           # Update tensor_dict.
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/base_layer.py in __call__(self, inputs, *args, **kwargs)
            661               kwargs.pop('training')
            662             inputs, outputs = self._set_connectivity_metadata_(
        --> 663                 inputs, outputs, args, kwargs)
            664           self._handle_activity_regularization(inputs, outputs)
            665           self._set_mask_metadata(inputs, outputs, previous_mask)
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/base_layer.py in _set_connectivity_metadata_(self, inputs, outputs, args, kwargs)
           1706     kwargs.pop('mask', None)  # `mask` should not be serialized.
           1707     self._add_inbound_node(
        -> 1708         input_tensors=inputs, output_tensors=outputs, arguments=kwargs)
           1709     return inputs, outputs
           1710 
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/base_layer.py in _add_inbound_node(self, input_tensors, output_tensors, arguments)
           1793     """
           1794     inbound_layers = nest.map_structure(lambda t: t._keras_history.layer,
        -> 1795                                         input_tensors)
           1796     node_indices = nest.map_structure(lambda t: t._keras_history.node_index,
           1797                                       input_tensors)
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/util/nest.py in map_structure(func, *structure, **kwargs)
            513 
            514   return pack_sequence_as(
        --> 515       structure[0], [func(*x) for x in entries],
            516       expand_composites=expand_composites)
            517 
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/util/nest.py in <listcomp>(.0)
            513 
            514   return pack_sequence_as(
        --> 515       structure[0], [func(*x) for x in entries],
            516       expand_composites=expand_composites)
            517 
        
        /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/base_layer.py in <lambda>(t)
           1792             `call` method of the layer at the call that created the node.
           1793     """
        -> 1794     inbound_layers = nest.map_structure(lambda t: t._keras_history.layer,
           1795                                         input_tensors)
           1796     node_indices = nest.map_structure(lambda t: t._keras_history.node_index,
        AttributeError: 'tuple' object has no attribute 'layer'
        AttributeError: 'tuple' object has no attribute 'layer'
    
  • grofte
    grofte over 4 years
    This helped me a bunch! Thank you. And to cut down on typing either try import tensorflow as tf or from tensorflow.keras.layers import Dense, Conv2D, Flatten, MaxPool2D, Dropout, Input.
  • rikyeah
    rikyeah about 2 years
    can you provide the code on how to do it?