DEV Community

Cover image for Convolutional Neural Networks in TensorFlow
Aman Gupta
Aman Gupta

Posted on

1

Convolutional Neural Networks in TensorFlow

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
    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

    !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

    # 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

    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

    # 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

    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

    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

    history = model.fit(
                train_generator,
                epochs=15,
                validation_data=validation_generator,
                verbose=2
                )
    
  • Visualisations of convolutions

    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

  • Evaluating acc and loss

    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

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/
  • 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 -

    # 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 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

    # 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

    # 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

    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

    # 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

    # 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

    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

    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

    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)
    

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs