Convolutional Neural Networks in TensorFlow
Tags: python, deeplearning, tensorflow, learning
Published: 2023-12-26 13:07:01

### Week 1: Larger Datasets

- We will first review how to build CNNs, prepare your data with `ImageDataGenerator` and examine your results. You'll follow these steps:
    - Explore the example data of `Dogs vs. Cats`
    - Build and train a neural network to classify between the two pets
    - Evaluate the training and validation accuracy
    
    ```python
    import os
    import zipfile
    import random
    import shutil
    import tensorflow as tf
    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    from shutil import copyfile
    import matplotlib.pyplot as plt
    ```
    
- Download dataset and process it
    
    ```python
    !wget --no-check-certificate https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip
    
    import zipfile
    
    # Unzip the archive
    local_zip = './cats_and_dogs_filtered.zip'
    zip_ref = zipfile.ZipFile(local_zip, 'r')
    zip_ref.extractall()
    
    zip_ref.close()
    
    #printing the dataset
    import os
    
    base_dir = 'cats_and_dogs_filtered'
    
    print("Contents of base directory:")
    print(os.listdir(base_dir))
    
    print("\nContents of train directory:")
    print(os.listdir(f'{base_dir}/train'))
    
    print("\nContents of validation directory:")
    print(os.listdir(f'{base_dir}/validation'))
    
    #paths of the data
    train_dir = os.path.join(base_dir, 'train')
    validation_dir = os.path.join(base_dir, 'validation')
    
    # Directory with training cat/dog pictures
    train_cats_dir = os.path.join(train_dir, 'cats')
    train_dogs_dir = os.path.join(train_dir, 'dogs')
    
    # Directory with validation cat/dog pictures
    validation_cats_dir = os.path.join(validation_dir, 'cats')
    validation_dogs_dir = os.path.join(validation_dir, 'dogs')
    ```
    
- Make your own paths
    
    ```python
    # Define root directory
    root_dir = '/tmp/cats-v-dogs'
    
    # Empty directory to prevent FileExistsError is the function is run several times
    if os.path.exists(root_dir):
      shutil.rmtree(root_dir)
    
    def create_train_val_dirs(root_path):
     
      try:
        os.mkdir('/tmp/cats-v-dogs')
        os.mkdir('/tmp/cats-v-dogs/validation')
        os.mkdir('/tmp/cats-v-dogs/training')
        os.mkdir('/tmp/cats-v-dogs/training/dogs')
        os.mkdir('/tmp/cats-v-dogs/training/cats')
        os.mkdir('/tmp/cats-v-dogs/validation/cats')
        os.mkdir('/tmp/cats-v-dogs/validation/dogs')
      except OSError:
        pass
    
    try:
      create_train_val_dirs(root_path=root_dir)
    except FileExistsError:
      print("You should not be seeing this since the upper directory is removed beforehand")
    
    # Test your create_train_val_dirs function
    
    for rootdir, dirs, files in os.walk(root_dir):
        for subdir in dirs:
            print(os.path.join(rootdir, subdir))
    ```
    
- Splitting data
    
    ```python
    def split_data(SOURCE_DIR, TRAINING_DIR, VALIDATION_DIR, SPLIT_SIZE):
    
      files = []
      for filename in os.listdir(SOURCE_DIR):
        file = SOURCE_DIR + filename
        if os.path.getsize(file)>0:
          files.append(filename)
        else:
          print(filename +" is zero length, so ignoring.")
    
        training_length = int(len(files)*SPLIT_SIZE)
        testing_length = int(len(files)-training_length)
        random_set = random.sample(files,len(files))
        training_set = random_set[0:training_length]
        testing_set = random_set[training_length:]
    
      for filename in training_set:
        curr_file = SOURCE_DIR + filename
        target_dir = TRAINING_DIR + filename
        copyfile(curr_file,target_dir)
    
      for filename in testing_set:
        curr_file = SOURCE_DIR + filename
        target_dir = VALIDATION_DIR + filename
        copyfile(curr_file,target_dir)
      pass
    
     
    ```
    
- Testing the split
    
    ```python
    # Define paths
    CAT_SOURCE_DIR = "/tmp/PetImages/Cat/"
    DOG_SOURCE_DIR = "/tmp/PetImages/Dog/"
    
    TRAINING_DIR = "/tmp/cats-v-dogs/training/"
    VALIDATION_DIR = "/tmp/cats-v-dogs/validation/"
    
    TRAINING_CATS_DIR = os.path.join(TRAINING_DIR, "cats/")
    VALIDATION_CATS_DIR = os.path.join(VALIDATION_DIR, "cats/")
    
    TRAINING_DOGS_DIR = os.path.join(TRAINING_DIR, "dogs/")
    VALIDATION_DOGS_DIR = os.path.join(VALIDATION_DIR, "dogs/")
    
    # Empty directories in case you run this cell multiple times
    if len(os.listdir(TRAINING_CATS_DIR)) > 0:
      for file in os.scandir(TRAINING_CATS_DIR):
        os.remove(file.path)
    if len(os.listdir(TRAINING_DOGS_DIR)) > 0:
      for file in os.scandir(TRAINING_DOGS_DIR):
        os.remove(file.path)
    if len(os.listdir(VALIDATION_CATS_DIR)) > 0:
      for file in os.scandir(VALIDATION_CATS_DIR):
        os.remove(file.path)
    if len(os.listdir(VALIDATION_DOGS_DIR)) > 0:
      for file in os.scandir(VALIDATION_DOGS_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(CAT_SOURCE_DIR, TRAINING_CATS_DIR, VALIDATION_CATS_DIR, split_size)
    split_data(DOG_SOURCE_DIR, TRAINING_DOGS_DIR, VALIDATION_DOGS_DIR, split_size)
    
    # Check that the number of images matches the expected output
    
    # Your function should perform copies rather than moving images so original directories should contain unchanged images
    print(f"\n\nOriginal cat's directory has {len(os.listdir(CAT_SOURCE_DIR))} images")
    print(f"Original dog's directory has {len(os.listdir(DOG_SOURCE_DIR))} images\n")
    
    # Training and validation splits
    print(f"There are {len(os.listdir(TRAINING_CATS_DIR))} images of cats for training")
    print(f"There are {len(os.listdir(TRAINING_DOGS_DIR))} images of dogs for training")
    print(f"There are {len(os.listdir(VALIDATION_CATS_DIR))} images of cats for validation")
    print(f"There are {len(os.listdir(VALIDATION_DOGS_DIR))} images of dogs for validation")
    ```
    
- Model
    
    ```python
    import tensorflow as tf
    
    model = tf.keras.models.Sequential([
        # Note the input shape is the desired size of the image 150x150 with 3 bytes color
        tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150, 3)),
        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),
        # Flatten the results to feed into a DNN
        tf.keras.layers.Flatten(), 
        # 512 neuron hidden layer
        tf.keras.layers.Dense(512, activation='relu'), 
        # Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class ('cats') and 1 for the other ('dogs')
        tf.keras.layers.Dense(1, activation='sigmoid')  
    ])
    
    model.summary()
    
    from tensorflow.keras.optimizers import RMSprop
    
    model.compile(optimizer=RMSprop(learning_rate=0.001),
                  loss='binary_crossentropy',
                  metrics = ['accuracy'])
    
    ```
    
- Data pre processing using ImageDataGenerator
    
    ```python
    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    
    # All images will be rescaled by 1./255.
    train_datagen = ImageDataGenerator( rescale = 1.0/255. )
    test_datagen  = ImageDataGenerator( rescale = 1.0/255. )
    
    # --------------------
    # Flow training images in batches of 20 using train_datagen generator
    # --------------------
    train_generator = train_datagen.flow_from_directory(train_dir,
                                                        batch_size=20,
                                                        class_mode='binary',
                                                        target_size=(150, 150))     
    # --------------------
    # Flow validation images in batches of 20 using test_datagen generator
    # --------------------
    validation_generator =  test_datagen.flow_from_directory(validation_dir,
                                                             batch_size=20,
                                                             class_mode  = 'binary',
                                                             target_size = (150, 150))
    ```
    
- Training
    
    ```python
    history = model.fit(
                train_generator,
                epochs=15,
                validation_data=validation_generator,
                verbose=2
                )
    ```
    
- Visualisations of convolutions
    
    ```python
    import numpy as np
    import random
    from tensorflow.keras.utils import img_to_array, load_img
    
    # Define a new Model that will take an image as input, and will output
    # intermediate representations for all layers in the previous model
    successive_outputs = [layer.output for layer in model.layers]
    visualization_model = tf.keras.models.Model(inputs = model.input, outputs = successive_outputs)
    
    # Prepare a random input image from the training set.
    cat_img_files = [os.path.join(train_cats_dir, f) for f in train_cat_fnames]
    dog_img_files = [os.path.join(train_dogs_dir, f) for f in train_dog_fnames]
    img_path = random.choice(cat_img_files + dog_img_files)
    img = load_img(img_path, target_size=(150, 150))  # this is a PIL image
    x   = img_to_array(img)                           # Numpy array with shape (150, 150, 3)
    x   = x.reshape((1,) + x.shape)                   # Numpy array with shape (1, 150, 150, 3)
    
    # Scale by 1/255
    x /= 255.0
    
    # Run the image through the network, thus obtaining all
    # intermediate representations for this image.
    successive_feature_maps = visualization_model.predict(x)
    
    # These are the names of the layers, so you can have them as part of our plot
    layer_names = [layer.name for layer in model.layers]
    
    # Display the representations
    for layer_name, feature_map in zip(layer_names, successive_feature_maps):
      
      if len(feature_map.shape) == 4:
        
        #-------------------------------------------
        # Just do this for the conv / maxpool layers, not the fully-connected layers
        #-------------------------------------------
        n_features = feature_map.shape[-1]  # number of features in the feature map
        size       = feature_map.shape[ 1]  # feature map shape (1, size, size, n_features)
        
        # Tile the images in this matrix
        display_grid = np.zeros((size, size * n_features))
        
        #-------------------------------------------------
        # Postprocess the feature to be visually palatable
        #-------------------------------------------------
        for i in range(n_features):
          x  = feature_map[0, :, :, i]
          x -= x.mean()
          x /= x.std ()
          x *=  64
          x += 128
          x  = np.clip(x, 0, 255).astype('uint8')
          display_grid[:, i * size : (i + 1) * size] = x # Tile each filter into a horizontal grid
    
        #-----------------
        # Display the grid
        #-----------------
        scale = 20. / n_features
        plt.figure( figsize=(scale * n_features, scale) )
        plt.title ( layer_name )
        plt.grid  ( False )
        plt.imshow( display_grid, aspect='auto', cmap='viridis' )
    ```
    

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmt962718sbpujjuq643.png)


    
- Evaluating acc and loss
    
    ```python
    import matplotlib.pyplot as plt
    
    # Plot the model results
    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.figure()
    
    plt.plot(epochs, loss, 'r', label='Training Loss')
    plt.plot(epochs, val_loss, 'b', label='Validation Loss')
    plt.title('Training and validation loss')
    plt.legend()
    
    plt.show()
    ```
    

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8oosl00ye0hsj6uiw21h.png)


    

### Week 2: Augmentation

- Augmentation - the process of generating new transformed version of images from the given dataset to increase diversity, [https://keras.io/preprocessing/image/](https://keras.io/preprocessing/image/)
- It does bring randomness in the test data, but if test/validation data doesn’t have the same randomness it’ll give bad validation accuracy
- We implement this with the use of ImageDataGenerator with different arguments -
    
    ```python
    # Create new model
    model_for_aug = create_model()
    
    # This code has changed. Now instead of the ImageGenerator just rescaling
    # the image, we also rotate and do other operations
    train_datagen = ImageDataGenerator(
          rescale=1./255,
          rotation_range=40,
          width_shift_range=0.2,
          height_shift_range=0.2,
          shear_range=0.2,
          zoom_range=0.2,
          horizontal_flip=True,
          fill_mode='nearest')
    
    test_datagen = ImageDataGenerator(rescale=1./255)
    
    # Flow training images in batches of 20 using train_datagen generator
    train_generator = train_datagen.flow_from_directory(
            train_dir,  # This is the source directory for training images
            target_size=(150, 150),  # All images will be resized to 150x150
            batch_size=20,
            # Since we use binary_crossentropy loss, we need binary labels
            class_mode='binary')
    
    # Flow validation images in batches of 20 using test_datagen generator
    validation_generator = test_datagen.flow_from_directory(
            validation_dir,
            target_size=(150, 150),
            batch_size=20,
            class_mode='binary')
    
    # Train the new model
    history_with_aug = model_for_aug.fit(
          train_generator,
          steps_per_epoch=100,  # 2000 images = batch_size * steps
          epochs=EPOCHS,
          validation_data=validation_generator,
          validation_steps=50,  # 1000 images = batch_size * steps
          verbose=2)
    ```
    
- These are just a few of the options available:
    - `rotation_range` is a value in degrees (0–180) within which to randomly rotate pictures.
    - `width_shift` and `height_shift` are ranges (as a fraction of total width or height) within which to randomly translate pictures vertically or horizontally.
    - `shear_range` is for randomly applying shearing transformations.
    - `zoom_range` is for randomly zooming inside pictures.
    - `horizontal_flip` is for randomly flipping half of the images horizontally. This is relevant when there are no assumptions of horizontal assymmetry (e.g. real-world pictures).
    - `fill_mode` is the strategy used for filling in newly created pixels, which can appear after a rotation or a width/height shift.

### Week 3: Transfer Learning

- We will use the convolution layers of the [InceptionV3](https://colab.research.google.com/corgiredirector?site=https%3A%2F%2Farxiv.org%2Fabs%2F1512.00567) architecture as your base model. To do that, you need to:
    - Set the input shape to fit your application. In this case. set it to `150x150x3` as you've been doing in the last few labs.
    - Pick and freeze the convolution layers to take advantage of the features it has learned already.
    - Add dense layers which you will train
- Download pre trained weights and loading the model
    
    ```python
    # Download the pre-trained weights. No top means it excludes the fully connected layer it uses for classification.
    !wget --no-check-certificate \
        https://storage.googleapis.com/mledu-datasets/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5 \
        -O /tmp/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
    
    from tensorflow.keras.applications.inception_v3 import InceptionV3
    from tensorflow.keras import layers
    
    # Set the weights file you downloaded into a variable
    local_weights_file = '/tmp/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5'
    
    # Initialize the base model.
    # Set the input shape and remove the dense layers.
    pre_trained_model = InceptionV3(input_shape = (150, 150, 3), 
                                    include_top = False, 
                                    weights = None)
    
    # Load the pre-trained weights you downloaded.
    pre_trained_model.load_weights(local_weights_file)
    
    # Freeze the weights of the layers.
    for layer in pre_trained_model.layers:
      layer.trainable = False
    
    pre_trained_model.summary()
    ```
    
- Choosing the last layer
    
    ```python
    # Choose `mixed7` as the last layer of your base model
    last_layer = pre_trained_model.get_layer('mixed7')
    print('last layer output shape: ', last_layer.output_shape)
    last_output = last_layer.output
    ```
    
- Adding our own dense layer
    
    ```python
    from tensorflow.keras.optimizers import RMSprop
    from tensorflow.keras import Model
    
    # 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)           
    
    # Append the dense network to the base model
    model = Model(pre_trained_model.input, x) 
    
    # Print the model summary. See your dense network connected at the end.
    model.summary()
    
    # Set the training parameters
    model.compile(optimizer = RMSprop(learning_rate=0.0001), 
                  loss = 'binary_crossentropy', 
                  metrics = ['accuracy'])
    ```
    
- Preparing the dataset with augmentation
    
    ```python
    # Download the dataset
    !wget https://storage.googleapis.com/tensorflow-1-public/course2/cats_and_dogs_filtered.zip
    
    import os
    import zipfile
    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    
    # Extract the archive
    zip_ref = zipfile.ZipFile("./cats_and_dogs_filtered.zip", 'r')
    zip_ref.extractall("tmp/")
    zip_ref.close()
    
    # Define our example directories and files
    base_dir = 'tmp/cats_and_dogs_filtered'
    
    train_dir = os.path.join( base_dir, 'train')
    validation_dir = os.path.join( base_dir, 'validation')
    
    # Directory with training cat pictures
    train_cats_dir = os.path.join(train_dir, 'cats') 
    
    # Directory with training dog pictures
    train_dogs_dir = os.path.join(train_dir, 'dogs') 
    
    # Directory with validation cat pictures
    validation_cats_dir = os.path.join(validation_dir, 'cats') 
    
    # Directory with validation dog pictures
    validation_dogs_dir = os.path.join(validation_dir, 'dogs')
    
    # Add our data-augmentation parameters to ImageDataGenerator
    train_datagen = ImageDataGenerator(rescale = 1./255.,
                                       rotation_range = 40,
                                       width_shift_range = 0.2,
                                       height_shift_range = 0.2,
                                       shear_range = 0.2,
                                       zoom_range = 0.2,
                                       horizontal_flip = True)
    
    # Note that the validation data should not be augmented!
    test_datagen = ImageDataGenerator( rescale = 1.0/255. )
    
    # Flow training images in batches of 20 using train_datagen generator
    train_generator = train_datagen.flow_from_directory(train_dir,
                                                        batch_size = 20,
                                                        class_mode = 'binary', 
                                                        target_size = (150, 150))     
    
    # Flow validation images in batches of 20 using test_datagen generator
    validation_generator =  test_datagen.flow_from_directory( validation_dir,
                                                              batch_size  = 20,
                                                              class_mode  = 'binary', 
                                                              target_size = (150, 150))
    ```
    
- Training the model
    
    ```python
    # Train the model.
    history = model.fit(
                train_generator,
                validation_data = validation_generator,
                steps_per_epoch = 100,
                epochs = 20,
                validation_steps = 50,
                verbose = 2)
    ```
    
- Evaluating the model
    
    ```python
    import matplotlib.pyplot as plt
    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.figure()
    
    plt.show()
    ```
    

### Week 4: Classifications (multiple instead of binary)

- The only changes comes in the model definition
    
    ```python
    import tensorflow as tf
    
    model = tf.keras.models.Sequential([
        # Note the input shape is the desired size of the image 150x150 with 3 bytes color
        # This is the first convolution
        tf.keras.layers.Conv2D(64, (3,3), activation='relu', input_shape=(150, 150, 3)),
        tf.keras.layers.MaxPooling2D(2, 2),
        # The second convolution
        tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),
        # The third convolution
        tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),
        # The fourth convolution
        tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),
        # Flatten the results to feed into a DNN
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dropout(0.5),
        # 512 neuron hidden layer
        tf.keras.layers.Dense(512, activation='relu'),
        tf.keras.layers.Dense(3, activation='softmax')
    ])
    
    # Print the model summary
    model.summary()
    
    # Set the training parameters
    model.compile(loss = 'categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
    #if it doesn't work use loss='sparse_categorical_crossentropy'
    ```
    
- ImageDataGenerator
    
    ```python
    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    
    TRAINING_DIR = "tmp/rps-train/rps"
    training_datagen = ImageDataGenerator(
          rescale = 1./255,
    	    rotation_range=40,
          width_shift_range=0.2,
          height_shift_range=0.2,
          shear_range=0.2,
          zoom_range=0.2,
          horizontal_flip=True,
          fill_mode='nearest')
    
    VALIDATION_DIR = "tmp/rps-test/rps-test-set"
    validation_datagen = ImageDataGenerator(rescale = 1./255)
    
    train_generator = training_datagen.flow_from_directory(
    	TRAINING_DIR,
    	target_size=(150,150),
    	class_mode='categorical',
      batch_size=126
    )
    
    validation_generator = validation_datagen.flow_from_directory(
    	VALIDATION_DIR,
    	target_size=(150,150),
    	class_mode='categorical',
      batch_size=126
    )
    
    # Train the model
    history = model.fit(train_generator, epochs=25, steps_per_epoch=20, validation_data = validation_generator, verbose = 1, validation_steps=3)
    ```