0

I want to normalize the training and validation images with one generator, and get new images from the training and validation views with the other generator. Then I want to combine and train them separately. How can I do this merge operation? I'm getting an error.

ValueError: Layer model expects 1 input(s), but it received 2 input tensors. Inputs received: [<tf.Tensor 'IteratorGetNext:0' shape=(None, None, None, None) dtype=float32>, <tf.Tensor 'IteratorGetNext:1' shape=(None, None, None, None) dtype=float32>]

# Images Paths
train_path = "train/"
valid_path = "valid/"

from tensorflow.keras.preprocessing.image import ImageDataGenerator
import numpy as np
from keras.utils.np_utils import to_categorical

# *********************TRAINING **************************
train_datagen1 = ImageDataGenerator(rescale=1./255)
train_generator1 = train_datagen1.flow_from_directory(
    train_path,
    save_to_dir="train_augm/",
    target_size=(224, 224),
    batch_size=6)

train_datagen2 = ImageDataGenerator(
    rescale=1./255, 
    rotation_range=40,
    width_shift_range=0.2, 
    ....)
train_generator2 = train_datagen2.flow_from_directory(
    train_path,
    target_size=(224, 224),
    batch_size=6)    


# ****************** VALIDATION *******************************
validation_datagen1 = ImageDataGenerator(rescale=1./255)
validation_generator1 = validation_datagen1.flow_from_directory(
    valid_path,
    save_to_dir="valid_augm/",
    target_size=(224, 224),
    batch_size=3)

validation_datagen2 = ImageDataGenerator(
    rescale=1./255, 
    rotation_range=40,
    width_shift_range=0.2,
    ....)
validation_generator2 = validation_datagen2.flow_from_directory(
    valid_path, 
    target_size=(224, 224), 
    batch_size=3) 

def combine_generator1(gen1, gen2):
    while True:
        X1i = gen1.next()
        X2i = gen2.next()
        yield [X1i[0], X2i[0]], X2i[1]  #Yield both images and their mutual label

def combine_generator2(gen_v1, gen_v2):
    while True:
        V1i = gen_v1.next()
        V2i = gen_v2.next()
        yield [V1i[0], V2i[0]], V2i[1]  #Yield both images and their mutual label
        
train_generator = combine_generator1(train_generator1, train_generator2)    
validation_generator = combine_generator2(validation_generator1, validation_generator2)

    
# *********************TRAINING THE MODEL ************************* 
history = new_model.fit(
    train_generator,
    epochs=5,
    validation_data = validation_generator,
    shuffle = True,
    verbose = 1)
  • I tried this too. Again I got the same error. def combine_generator(gen1, gen2): while True: yield(next(gen1), next(gen2)) – Ayhan Sarı Nov 30 '21 at 10:41

2 Answers2

2

I found similar question/answer on stackoverflow as below:
How to join two generators in Python?

This solution has worked for me:

# Images generator for training set (Base without Augmented Data)
train_data_generator_Base = ImageDataGenerator(preprocessing_function = preprocess_input)          # data preprocessing

# Images generator for training set (Augmented Data)
train_data_generator_AD = ImageDataGenerator(rotation_range = 4,        # Degree range for random rotations
                                             width_shift_range = 0.05,  # Fraction of total width, if < 1, or pixels if >= 1
                                             height_shift_range = 0.05, # Fraction of total height, if < 1, or pixels if >= 1
                                             horizontal_flip = True,    # Randomly flip inputs horizontally
                                             vertical_flip = False,     # Randomly flip inputs vertically
                                             zoom_range = 0.05,         # Range for random zoom. If a float,
                                                                                                 # [lower, upper] = [1-zoom_range, 1+zoom_range]                                          
                                             preprocessing_function = preprocess_input)          # data preprocessing


# Concatenate Base and Augmented Data
train_generator_Base = train_data_generator_Base.flow_from_dataframe(dataframe = df_train,          # Dataframe
                                                                     directory = IMAGES_DIRECTORY,  # Directory with pics
                                                                     class_mode = 'categorical',    # Multiple classes
                                                                     target_size = TARGET_SIZE,     # Resizing
                                                                     x_col = 'filename',            # Column with filenames
                                                                     y_col = 'type',                # Column with classes
                                                                     batch_size = BATCH_SIZE)       # Batch size

train_generator_AD = train_data_generator_AD.flow_from_dataframe(dataframe = df_train,          # Dataframe
                                                                 directory = IMAGES_DIRECTORY,  # Directory with pics
                                                                 class_mode = 'categorical',    # Multiple classes
                                                                 target_size = TARGET_SIZE,     # Resizing
                                                                 x_col = 'filename',            # Column with filenames
                                                                 y_col = 'type',                # Column with classes
                                                                 batch_size = BATCH_SIZE)       # Batch size

def concat_generators(*gens):
    for gen in gens:
        yield from gen
        
train_generator = concat_generators(train_generator_Base, train_generator_AD)
Eric
  • 21
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 23 '22 at 16:12
0

I have used Sequence method. But that didn't work either.

# Images Paths
train_path = "train/"
valid_path = "valid/"

from tensorflow.keras.preprocessing.image import ImageDataGenerator
import numpy as np
from keras.utils.np_utils import to_categorical
from tensorflow.keras.utils import Sequence

# *********************TRAIN **************************
train_datagen1 = ImageDataGenerator(rescale=1./255)
train_generator1 = train_datagen1.flow_from_directory(
    train_path,
    target_size=(224, 224),
    batch_size=6)

train_datagen2 = ImageDataGenerator(
    rescale=1./255, 
    rotation_range=40,
    width_shift_range=0.2, 
    height_shift_range=0.2,
    ...)
train_generator2 = train_datagen2.flow_from_directory(
    train_path,
    target_size=(224, 224),
    batch_size=6)    


# ****************** VALIDATION *******************************
validation_datagen1 = ImageDataGenerator(rescale=1./255)
validation_generator1 = validation_datagen1.flow_from_directory(
    valid_path,
    target_size=(224, 224),
    batch_size=3)

validation_datagen2 = ImageDataGenerator(
    rescale=1./255, 
    rotation_range=40,
    width_shift_range=0.2, 
    ...)
validation_generator2 = validation_datagen2.flow_from_directory(
    valid_path,
    target_size=(224, 224), 
    batch_size=3) 

class MySequence(Sequence):
    def __init__(self, seq1, seq2):
        self.seq1, self.seq2 = seq1, seq2
    def __len__(self):
        return len(self.seq1)
    def __getitem(self, idx):
        x1, y1 = self.seq1[idx]
        x2, y2 = self.seq2[idx]
        return [x1, x2], [y1, y2]

my_seq_t = MySequence(train_generator1, train_generator2)
my_seq_v = MySequence(validation_generator1, validation_generator2)
 
# *********************TRAINING THE MODEL *************************
history = new_model.fit(
    my_seq_t,
    epochs=5,
    validation_data = my_seq_v,
    shuffle = True,
    verbose = 1)
  • While this is useful information, it is best to reserve answers for actual solutions to the original problem. This should be edited back into the question, and the code reduced to the relevant lines around the specific usage. – ouflak Nov 30 '21 at 12:05