Coursera

Week 4 Assignment: Create a VGG network

In this exercise, you will build a class that implements a VGG network that can be trained to classify images. The model will look something like this:

It is primarily made up of a series of Conv2D layers followed by a softmax activated layers to classify the image. As you can see, this will be a handful and the code will look huge if you specify each layer individually. As shown in the lectures, you can instead use model subclassing to build complex architectures. You can encapsulate repeating parts of a network then reuse that code when building the final model. You will get to practice that in this exercise. Let’s get started!

import tensorflow as tf
import tensorflow_datasets as tfds
import utils

Create named-variables dynamically

In this assignment, you will see the use of the Python function vars(). This will allow you to use a for loop to define and set multiple variables with a similar name, such as var1, var2, var3.

Please go through the following examples to get familiar with vars(), as you will use it when building the VGG model.

# Define a small class MyClass
class MyClass:
    def __init__(self):
        # One class variable 'a' is set to 1
        self.var1 = 1

# Create an object of type MyClass()
my_obj = MyClass()

Python classes have an attribute called __dict__.

my_obj.__dict__
{'var1': 1}

If you call vars() and pass in an object, it will call the object’s __dict__ attribute, which is a Python dictionary containing the object’s instance variables and their values as ke

vars(my_obj)
{'var1': 1}

You may be familiar with adding new variable like this:

# Add a new instance variable and give it a value
my_obj.var2 = 2

# Calls vars() again to see the object's instance variables
vars(my_obj)
{'var1': 1, 'var2': 2}

Here is another way that you can add an instance variable to an object, using vars().

# Call vars, passing in the object.  Then access the __dict__ dictionary using square brackets
vars(my_obj)['var3'] = 3

# Call vars() to see the object's instance variables
vars(my_obj)
{'var1': 1, 'var2': 2, 'var3': 3}

Why this is helpful!

You may be wondering why you would need another way to access an object’s instance variables.

Try this!

# Use a for loop to increment the index 'i'
for i in range(4,10):
    # Format a string that is var
    vars(my_obj)[f'var{i}'] = 0
    
# View the object's instance variables!
vars(my_obj)
{'var1': 1,
 'var2': 2,
 'var3': 3,
 'var4': 0,
 'var5': 0,
 'var6': 0,
 'var7': 0,
 'var8': 0,
 'var9': 0}

There are couple equivalent ways in Python to format a string. Here are two of those ways:

# Format a string using f-string notation
i=1
print(f"var{i}")

# Format a string using .format notation
i=2
print("var{}".format(i))
var1
var2

You can access the variables of a class inside the class definition using vars(self)

# Define a small class MyClass
class MyClass:
    def __init__(self):
        # Use vars(self) to access the class's dictionary of variables
        vars(self)['var1'] = 1

# Create an object of type MyClass()
my_obj = MyClass()
vars(my_obj)
{'var1': 1}

You’ll see this in the upcoming code. Now you’ll start building the VGG network!

Create a generic VGG block (TODO)

The VGG Network has blocks of layers, where each block has a varied number of layers.

__init__

In the constructor __init__, store the conv2D parameters and also define the number of conv2D layers using the parameters passed into __init__.

call

In call, you will connect the layers together.

# Please uncomment all lines in this cell and replace those marked with `# YOUR CODE HERE`.
# You can select all lines in this code cell with Ctrl+A (Windows/Linux) or Cmd+A (Mac), then press Ctrl+/ (Windows/Linux) or Cmd+/ (Mac) to uncomment.



class Block(tf.keras.Model):
    def __init__(self, filters, kernel_size, repetitions, pool_size=2, strides=2):
        super(Block, self).__init__()
        self.filters = filters
        self.kernel_size = kernel_size
        self.repetitions = repetitions
        self.pool_size = pool_size
        self.strides = strides
        
        # Define a conv2D_0, conv2D_1, etc based on the number of repetitions
        for i in range(repetitions):
            
            # Define a Conv2D layer, specifying filters, kernel_size, activation and padding.
            vars(self)[f'conv2D_{i}'] = tf.keras.layers.Conv2D(
                filters=self.filters,
                kernel_size=self.kernel_size,
                activation="relu",
                padding="same"
            )
        
        # Define the max pool layer that will be added after the Conv2D blocks
        self.max_pool = tf.keras.layers.MaxPool2D(
            pool_size=(self.pool_size, self.pool_size),
            strides=(self.strides, self.strides)
        )
  
    def call(self, inputs):
        # access the class's conv2D_0 layer
        conv2D_0 = vars(self)["conv2D_0"]
        
        # Connect the conv2D_0 layer to inputs
        x = conv2D_0(inputs)

        # for the remaining conv2D_i layers from 1 to `repetitions` they will be connected to the previous layer
        for i in range(1, self.repetitions):
            # access conv2D_i by formatting the integer `i`. (hint: check how these were saved using `vars()` earlier)
            conv2D_i = vars(self)[f"conv2D_{i}"]
            
            # Use the conv2D_i and connect it to the previous layer
            x = conv2D_i(x)

        # Finally, add the max_pool layer
        max_pool = self.max_pool(x)
        
        return max_pool
utils.test_block_class(Block)

 All public tests passed

Create the Custom VGG network (TODO)

This model stack has a series of VGG blocks, which can be created using the Block class that you defined earlier.

__init__

After block ‘e’, add the following layers:

call

Connect these layers together using the functional API syntax:

Return the classifier layer.

# Please uncomment all lines in this cell and replace those marked with `# YOUR CODE HERE`.
# You can select all lines in this code cell with Ctrl+A (Windows/Linux) or Cmd+A (Mac), then press Ctrl+/ (Windows/Linux) or Cmd+/ (Mac) to uncomment.



class MyVGG(tf.keras.Model):

    def __init__(self, num_classes):
        super(MyVGG, self).__init__()
        
        self.num_classes = num_classes

        # Creating blocks of VGG with the following 
        # (filters, kernel_size, repetitions) configurations
        self.block_a = Block(filters=64, kernel_size=3, repetitions=2)
        self.block_b = Block(filters=128, kernel_size=3, repetitions=2)
        self.block_c = Block(filters=256, kernel_size=3, repetitions=3)
        self.block_d = Block(filters=512, kernel_size=3, repetitions=3)
        self.block_e = Block(filters=512, kernel_size=3, repetitions=3)

        # Classification head
        # Define a Flatten layer
        self.flatten = tf.keras.layers.Flatten()
        # Create a Dense layer with 256 units and ReLU as the activation function
        self.fc = tf.keras.layers.Dense(256, activation="relu")
        # Finally add the softmax classifier using a Dense layer
        self.classifier = tf.keras.layers.Dense(self.num_classes, activation="softmax")

    def call(self, inputs):
        # Chain all the layers one after the other
        x = self.block_a(inputs)
        x = self.block_b(x)
        x = self.block_c(x)
        x = self.block_d(x)
        x = self.block_e(x)
        x = self.flatten(x)
        x = self.fc(x)
        x = self.classifier(x)
        return x
utils.test_myvgg_class(MyVGG, Block)


 All public tests passed

Load data and train the VGG network (Optional)

If you passed all tests above, then you’ve successfully built the model for your image classifier. Congratulations! You can submit your work now before proceeding.

The next steps in the pipeline will be loading the dataset and training your VGG network. The code is shown below but it is only for reference and is not required to complete the assignment. Please do not uncomment it because it will cause a grader timeout because of the slow training time. The grader environment does not have an accelerator enabled.

If you want to train with your VGG network, one way is to download your notebook (File -> Download As -> Notebook), then upload to Colab. From there, you can use a GPU runtime (Runtime -> Change Runtime type) prior to running the cells. Just make sure to comment out the imports and calls to utils.py so you don’t get File Not Found errors. Again, this part is only for reference and is not required for grading. For this lab, we will only grade how you built your model using subclassing. You will get to training and evaluating your models in the next courses of this Specialization.

# # For reference only. Please do not uncomment in Coursera Labs because it might cause the grader to time out.
# # You can upload your notebook to Colab instead if you want to try the code below.

# # Download the dataset
# dataset = tfds.load('cats_vs_dogs', split=tfds.Split.TRAIN, data_dir='data/')

# # Initialize VGG with the number of classes 
# vgg = MyVGG(num_classes=2)

# # Compile with losses and metrics
# vgg.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

# # Define preprocessing function
# def preprocess(features):
#     # Resize and normalize
#     image = tf.image.resize(features['image'], (224, 224))
#     return tf.cast(image, tf.float32) / 255., features['label']

# # Apply transformations to dataset
# dataset = dataset.map(preprocess).batch(32)

# # Train the custom VGG model
# vgg.fit(dataset, epochs=10)