'Training a Machine Learning model on a custom dataset. How do I do it?

I followed the Machine Learning Crash Course from Google and saw they trained a model to predict cats and dogs, so i decided that would be a good starting point (https://colab.research.google.com/github/google/eng-edu/blob/main/ml/pc/exercises/image_classification_part3.ipynb?utm_source=practicum-IC&utm_campaign=colab-external&utm_medium=referral&hl=ro&utm_content=imageexercise3-colab#scrollTo=lRjyAkE62aOG). My dataset contains multiple pictures from inside a bar that frames 4 tables which I labelled as being busy or available. I would like to train a model to feed it a picture and tell me which of the 4 tables are busy or available.

import os
from tensorflow.keras import layers
from tensorflow.keras import Model
import tensorflow as tf
from tensorflow.keras.applications.inception_v3 import InceptionV3

local_weights_file = 'inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5'
pre_trained_model = InceptionV3(
    input_shape=(150, 150, 3), include_top=False, weights=None)
pre_trained_model.load_weights(local_weights_file)

for layer in pre_trained_model.layers:
  layer.trainable = False

last_layer = pre_trained_model.get_layer('mixed7')
    print('last layer output shape:', last_layer.output_shape)
    last_output = last_layer.output

from tensorflow.keras.optimizers import RMSprop
import keras

# Flatten the output layer to 1 dimension
x = layers.Flatten()(last_output)
# Add a fully connected layer with 1,024 hidden units and ReLU activation
x = layers.Dense(1024, activation='relu')(x)
# Add a dropout rate of 0.2
x = layers.Dropout(0.2)(x)
# Add a final sigmoid layer for classification
x = layers.Dense(1, activation='sigmoid')(x)

# Configure and compile the model
model = Model(pre_trained_model.input, x)

model.compile(loss='binary_crossentropy',
              optimizer=RMSprop(lr=0.0001),
              metrics=['acc'])

from tensorflow.keras.preprocessing.image import ImageDataGenerator
from custom_generator import *

# Define our example directories and files
base_dir = 'imagini'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')

train_df = xml_to_csv(train_dir)
val_df = xml_to_csv(validation_dir)

col=[]
for index, row in train_df.iterrows():
    val = {'xmin': train_df.at[index,'xmin'],
            'ymin': train_df.at[index,'ymin'],
            'xmax': train_df.at[index,'xmax'],
            'ymax': train_df.at[index,'ymax']}

    col.append(val)

train_df["coordinates"] = col
train_df['class'] = pd.factorize(train_df['class'])[0]

col_1 = []
for index, row in val_df.iterrows():
    val = {'xmin':val_df.at[index,'xmin'],
            'ymin':val_df.at[index,'ymin'],
            'xmax':val_df.at[index,'xmax'],
            'ymax':val_df.at[index,'ymax']}
    col_1.append(val)

val_df["coordinates"] = col_1
val_df['class'] = pd.factorize(val_df['class'])[0]
#val_df['class'] = np.asarray(val_df['class']).astype('float32').reshape((-1,1))

os.chdir(train_dir)
train_generator = CustomDataGen(train_df,
                                X_col = {'path': 'filename', 'bbox' : 'coordinates'},
                                y_col = {'name': 'class'},
                                batch_size = 20)

os.chdir('C:\\Users\\Alex\\Desktop\\LICENTa\\ML MODEL\\imagini\\validation\\')
validation_generator = CustomDataGen(val_df,
                                X_col = {'path': 'filename', 'bbox' : 'coordinates'},
                                y_col = {'name': 'class'},
                                batch_size = 20)

os.chdir("C:\\Users\\Alex\\Desktop\\LICENTa\\ML MODEL")

history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=2,
      validation_data=validation_generator,
      validation_steps=50,
      verbose=2)

I also made the function 'xml_to_csv' to put my data in DataFrame and a custom generator that looks like this:

def xml_to_csv(path):

    xml_list = []
    for xml_file in glob.glob(path + '/*.xml'):
        tree = ET.parse(xml_file)
        root = tree.getroot()
        filename = root.find('filename').text
        width = int(root.find('size').find('width').text)
        height = int(root.find('size').find('height').text)
        for member in root.findall('object'):
            bndbox = member.find('bndbox')
            value = (os.path.join(path, filename),
                     width,
                     height,
                     member.find('name').text,
                     int(bndbox.find('xmin').text),
                     int(bndbox.find('ymin').text),
                     int(bndbox.find('xmax').text),
                     int(bndbox.find('ymax').text),
                    )
            xml_list.append(value)
    column_name = ['filename', 'width', 'height',
                   'class', 'xmin', 'ymin', 'xmax', 'ymax']
    xml_df = pd.DataFrame(xml_list, columns=column_name)

    return xml_df

#train_df = xml_to_csv("imagini\\train")

#print(train_df)

class CustomDataGen(tf.keras.utils.Sequence):
    
    def __init__(self, df, X_col, y_col, batch_size, input_size=(150, 150, 3), shuffle=True):
        
        self.df = df.copy()
        self.X_col = X_col
        self.y_col = y_col
        self.batch_size = batch_size
        self.input_size = input_size
        self.shuffle = shuffle
        
        self.n = len(self.df)
        self.n_class = df[y_col['name']].nunique()
    
    def on_epoch_end(self):
        if self.shuffle:
            self.df = self.df.sample(frac=1).reset_index(drop=True)
    
    def __get_input(self, path, bbox, target_size):
    
        xmin, ymin, xmax, ymax = bbox['xmin'], bbox['ymin'], bbox['xmax'], bbox['ymax']

        image = tf.keras.preprocessing.image.load_img(path)
        image_arr = tf.keras.preprocessing.image.img_to_array(image)

        image_arr = image_arr[ymin:ymax, xmin:xmax]
        image_arr = tf.image.resize(image_arr,(target_size[0], target_size[1])).numpy()
        #image_arr = np.array(image_arr)

        return image_arr/255.
    
    def __get_output(self, label, num_classes):
        return tf.keras.utils.to_categorical(label, num_classes=num_classes)
    
    def __get_data(self, batches):
        # Generates data containing batch_size samples

        path_batch = batches[self.X_col['path']]
        bbox_batch = batches[self.X_col['bbox']]
        
        name_batch = batches[self.y_col['name']]

        X_batch = np.asarray([self.__get_input(x, y, self.input_size) for x, y in zip(path_batch, bbox_batch)])

        y0_batch = np.asarray([self.__get_output(y, self.n_class) for y in name_batch])

        return X_batch, [y0_batch]
    
    def __getitem__(self, index):
        
        batches = self.df[index * self.batch_size:(index + 1) * self.batch_size]
        X, y = self.__get_data(batches)        
        return X, y
    
    def __len__(self):
        return self.n // self.batch_size

When training I am geting an error:

AttributeError: 'tuple' object has no attribute 'shape'


Sources

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

Source: Stack Overflow

Solution Source