'TensorFlow: TypeError when itterating through model architectures

I have a script which goes through a simple 2d CNN and I'm trying to run through a range of different values for the number of layers and neurons per layer in my final dense layers after the convolution.

I have the models being produced correctly (which I can see through model.summary()), but when it comes to training I'm getting a type error after the first model is trained, just before it starts on a second model (shown below).

  File "/media/physsh/Working SSD/Python_scripts/Machine_Learning/First_attempt/2dcnn-SiB.py", line 222, in <module>
    validation_data=validation_generator)
  File "/home/physsh/Softwares/miniconda3/envs/Dylan/lib/python3.7/site-packages/tensorflow/python/keras/engine/training.py", line 1064, in fit
    steps_per_execution=self._steps_per_execution)
  File "/home/physsh/Softwares/miniconda3/envs/Dylan/lib/python3.7/site-packages/tensorflow/python/keras/engine/data_adapter.py", line 1105, in __init__
    epochs=epochs - initial_epoch,
TypeError: unsupported operand type(s) for -: 'range' and 'int'

I'm not changing anything between runs, so I don't know if it's some variable being left over between them? Either way I'm not sure how to proceed from here other than running each manually which is obviously not optimal.

Below is my whole script if it helps.

from keras.preprocessing.image import ImageDataGenerator
import tensorflow as tf

import matplotlib.pyplot as plt
import pandas as pd
from pathlib import Path
import os
import random
from shutil import copyfile

def create_train_val_dirs(root_path):
    """
    Creates directories for the train and test sets
    
    Args:
        root_path (string) - the base directory path to create subdirectories from
    
    Returns:
        None
    """  

    os.makedirs(root_path)

    val_path = os.path.join(root_path, 'validation')
    train_path = os.path.join(root_path, 'training')
    os.makedirs(train_path)
    os.makedirs(val_path)

    os.makedirs(os.path.join(val_path, 'Good'))
    os.makedirs(os.path.join(val_path, 'Bad'))
    os.makedirs(os.path.join(train_path, 'Good'))
    os.makedirs(os.path.join(train_path, 'Bad'))

def split_data(SOURCE_DIR, TRAINING_DIR, VALIDATION_DIR, SPLIT_SIZE):

    """
    Splits the data into train and test sets
    
    Args:
        SOURCE_DIR (string): directory path containing the images
        TRAINING_DIR (string): directory path to be used for training
        VALIDATION_DIR (string): directory path to be used for validation
        SPLIT_SIZE (float): proportion of the dataset to be used for training
        
    Returns:
        None
    """

    im_list = random.sample(os.listdir(SOURCE_DIR), len(os.listdir(SOURCE_DIR)))

    for index, im in enumerate(im_list):
        if os.path.getsize(os.path.join(SOURCE_DIR, im)) == 0:
            print(f"{im} is zero length, so ignoring.")
            continue
        if index <= SPLIT_SIZE*len(im_list):
            copyfile(os.path.join(SOURCE_DIR, im), os.path.join(TRAINING_DIR, im))
        else:
            copyfile(os.path.join(SOURCE_DIR, im), os.path.join(VALIDATION_DIR, im))

def train_val_generators(TRAINING_DIR, VALIDATION_DIR, batch_size, input_size):
    """
    Creates the training and validation data generators
  
    Args:
        TRAINING_DIR (string): directory path containing the training images
        VALIDATION_DIR (string): directory path containing the testing/validation images
        batch_size (integer): Integer number of batches (amount of images trained per loop).
        input_size (tensor: (int, int)): Tensor containing the size of the input image, or 
        preferred input size (if different to the actual size it will be scaled). 
    
    Returns:
        train_generator, validation_generator - tuple containing the generators
    """


    # Instantiate the ImageDataGenerator class (don't forget to set the arguments to augment the images)
    train_datagen = ImageDataGenerator(rescale=1./255.0,
                                    #  rotation_range=40,
                                    #  width_shift_range=0.2,
                                    #  height_shift_range=0.2,
                                    #  shear_range=0.2,
                                    #  zoom_range=0.2,
                                    horizontal_flip=True,
                                    vertical_flip=True,
                                    #  fill_mode='nearest'
                                    )

    # Pass in the appropriate arguments to the flow_from_directory method
    train_generator = train_datagen.flow_from_directory(directory=TRAINING_DIR,
                                                        batch_size=batch_size,
                                                        class_mode='binary',
                                                        target_size=target_size,
                                                        color_mode= 'grayscale')

    # Instantiate the ImageDataGenerator class (don't forget to set the rescale argument)
    validation_datagen = ImageDataGenerator(rescale=1./255.0)

    # Pass in the appropriate arguments to the flow_from_directory method
    validation_generator = validation_datagen.flow_from_directory(directory=VALIDATION_DIR,
                                                                batch_size=batch_size,
                                                                class_mode='binary',
                                                                target_size=target_size,
                                                                color_mode= 'grayscale')
    ### END CODE HERE
    return train_generator, validation_generator

# set tf_xla_enable_xla_devices flags
os.environ['TF_XLA_FLAGS'] = '--tf_xla_enable_xla_devices'

# Define paths
SOURCE_DIR = '/media/physsh/Working SSD/Extracted_images/B-Si/300_sample/Splits/Binary_1/Input_data'
ROOT_DIR = '/media/physsh/Working SSD/Extracted_images/B-Si/300_sample/Splits/Binary_1/ForRuns'

GOOD_SOURCE_DIR = os.path.join(SOURCE_DIR, "Good")
BAD_SOURCE_DIR = os.path.join(SOURCE_DIR,"Bad")

TRAINING_DIR = os.path.join(ROOT_DIR, 'training')
VALIDATION_DIR = os.path.join(ROOT_DIR, 'validation')

TRAINING_GOOD_DIR = os.path.join(TRAINING_DIR, "Good/")
VALIDATION_GOOD_DIR = os.path.join(VALIDATION_DIR, "Good/")

TRAINING_BAD_DIR = os.path.join(TRAINING_DIR, "Bad/")
VALIDATION_BAD_DIR = os.path.join(VALIDATION_DIR, "Bad/")

try:
  create_train_val_dirs(root_path=ROOT_DIR)
  print(f'Creating directory at {ROOT_DIR}...')
except FileExistsError:
  print(f"{ROOT_DIR} already exists.")

# Empty directories in case you run this cell multiple times
if len(os.listdir(TRAINING_BAD_DIR)) > 0:
    for file in os.scandir(TRAINING_BAD_DIR):
        os.remove(file.path)
if len(os.listdir(TRAINING_GOOD_DIR)) > 0:
    for file in os.scandir(TRAINING_GOOD_DIR):
        os.remove(file.path)
if len(os.listdir(VALIDATION_BAD_DIR)) > 0:
    for file in os.scandir(VALIDATION_BAD_DIR):
        os.remove(file.path)
if len(os.listdir(VALIDATION_GOOD_DIR)) > 0:
    for file in os.scandir(VALIDATION_GOOD_DIR):
        os.remove(file.path)

# Define proportion of images used for training
split_size = .9

# Run the function
# NOTE: Messages about zero length images should be printed out
split_data(GOOD_SOURCE_DIR, TRAINING_GOOD_DIR, VALIDATION_GOOD_DIR, split_size)
split_data(BAD_SOURCE_DIR, TRAINING_BAD_DIR, VALIDATION_BAD_DIR, split_size)

batch_size = 20
target_size = (140, 140)
epochs = 1

train_generator, validation_generator = train_val_generators(TRAINING_DIR, VALIDATION_DIR, batch_size=batch_size, input_size=target_size)

neurons = [32,64,128,256,512,1024,2048] 
# Apparently a maximum of two layers is all that we would ever need - I'll look into the theory later. 
num_layers = [1,2]

for i in neurons:
  for j in num_layers:
    tmp_i = i
    # Set initial layers - Usually input, conv and flatten before dense layers
    layers = [
        tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape= (140,140,1)),
        tf.keras.layers.MaxPooling2D((2,2)),
        tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D((2,2)),
        tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D((2,2)),
        tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D((2,2)),
        tf.keras.layers.Flatten()]

    for k in range(1, j+1):
      layers.append(tf.keras.layers.Dense(tmp_i, activation='relu'))
      layers.append(tf.keras.layers.Dropout(0.5))
      tmp_i *= 2

    # Add in the output layer
    layers.append(tf.keras.layers.Dense(1, activation='sigmoid'))

    model = tf.keras.models.Sequential(layers)
    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

    model.summary()

    # Bigin training!
    history = model.fit(train_generator,
                        epochs=epochs,
                        verbose=1,
                        validation_data=validation_generator)


    # Save the model, history and accuracy plot
    save_name = f'{epochs}epochs_{j}layers_{i}Starting'
    Models_dir = '/media/physsh/Working SSD/Extracted_images/B-Si/300_sample/Splits/Binary_1/Trained_models'

    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    loss = history.history['loss']
    val_loss = history.history['val_loss']

    epochs = range(len(acc))

    plt.plot(epochs, acc, 'r', label='Training accuracy')
    plt.plot(epochs, val_acc, 'b', label='Validation accuracy')
    plt.title('Training and validation accuracy')
    plt.legend(loc=0)

    plt.savefig(os.path.join(Models_dir, f'{save_name}.png'))
    model.save(os.path.join(Models_dir, f'{save_name}.h5'))


    # convert the history.history dict to a pandas DataFrame:     
    hist_df = pd.DataFrame(history.history) 

    # or save to csv: 
    hist_csv_file = os.path.join(Models_dir, f'{save_name}.csv')
    with open(hist_csv_file, mode='w') as f:
        hist_df.to_csv(f)

The task is a simple binary classification task and with the few tests I've done I'm already getting decent results, but I'd like to see something on how changing the number of neurons and number of layers changes things in terms of accuracy.

Thanks in advance for any help.



Solution 1:[1]

You use the same variable name epochs in the fit method and also in the plots after : epochs = range(len(acc)), that is why you get the error for the second model fitting.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 elbe