10 Minutes to Building a Fully-Connected Binary Image Classifier in TensorFlow | by Binh Phan | Towards Data Science

This is a short introduction to computer vision — namely, how to build a binary image classifier using only fully-connected layers in TensorFlow/Keras, geared mainly towards new users. This easy-to-follow tutorial is broken down into 3 sections:
The dataThe model architectureThe accuracy, ROC curve, and AUC
The data
The model architecture
The accuracy, ROC curve, and AUC
Requirements: Nothing! All you need to follow this tutorial is this Google Colab notebook containing the data and code. Google Colab allows you to write and run Python code in-browser without any setup, and includes free GPU access! Just make sure to make your own copy by clicking File -> Save a copy in Drive.
We’re going to build a dandelion and grass image classifier. I’ve created a small image dataset using images from Google Images, which you can download and parse in the first 8 cells of the tutorial.
By the end of those 8 lines, visualizing a sample of your image dataset will look something like this:
Note how some of the images in the dataset aren’t perfect representations of grass or dandelions. For simplicity’s sake, let’s make this okay and move on to how to easily create our training and validation dataset.
The data that we fetched earlier is divided into two folders, train and valid. In those folders, the foldersdandelion and grass contain the images of each class. To create a dataset, let’s use the keras.preprocessing.image.ImageDataGenerator class to create our training and validation dataset and normalize our data. What this class does is create a dataset and automatically does the labeling for us, allowing us to create a dataset in just one line!
In the beginning of this section, we first import TensorFlow.
Let’s then add a Flatten layer that flattens the input image, which then feeds into the next layer, a Dense layer, or fully-connected layer, with 128 hidden units. Finally, because our goal is to perform binary classification, our final layer will be a sigmoid, so that the output of our network will be a single scalar between 0 and 1, encoding the probability that the current image is of class 1 (class 1 being grass and class 0 being dandelion).
model =tf.keras.models.Sequential([tf.keras.layers.Flatten(input_shape = (200,200,3)),tf.keras.layers.Dense(128, activation=tf.nn.relu),tf.keras.layers.Dense(1, activation=tf.nn.sigmoid)])
Let’s see a summary of the model we have built:
Model: "sequential" _________________________________________________________________ Layer (type)                 Output Shape              Param #    ================================================================= flatten (Flatten)            (None, 120000)            0          _________________________________________________________________ dense (Dense)                (None, 128)               15360128   _________________________________________________________________ dense_1 (Dense)              (None, 1)                 129        ================================================================= Total params: 15,360,257 Trainable params: 15,360,257 Non-trainable params: 0
Note that since we’re using a fully-connected layer, every single unit of one layer is connected to the every single units in the layers next to it. That’s a lot of parameters!
Next, we’ll configure the specifications for model training. We will train our model with the binary_crossentropy loss. We will use the Adam optimizer. Adam is a sensible optimization algorithm because it automates learning-rate tuning for us (alternatively, we could also use RMSProp or Adagrad for similar results). We will add accuracy to metrics so that the model will monitor accuracy during training.
model.compile(optimizer = tf.optimizers.Adam(),loss = 'binary_crossentropy',metrics=['accuracy'])
Let’s train for 15 epochs:
history = model.fit(train_generator,steps_per_epoch=8,epochs=15,verbose=1,validation_data = validation_generator,validation_steps=8)
Let’s evaluate the accuracy of our model:
model.evaluate(validation_generator)
Now, let’s calculate our ROC curve and plot it.
First, let’s make predictions on our validation set. When using generators to make predictions, we must first turn off shuffle (as we did when we created validation_generator) and reset the generator:
STEP_SIZE_TEST=validation_generator.n//validation_generator.batch_sizevalidation_generator.reset()preds = model.predict(validation_generator,verbose=1)
To create the ROC curve and AUC, we’ll need to compute the false-positive rate and the true-positive rate:
fpr, tpr, _ = roc_curve(validation_generator.classes, preds)roc_auc = auc(fpr, tpr)plt.figure()lw = 2plt.plot(fpr, tpr, color='darkorange',lw=lw, label='ROC curve (area = %0.2f)' % roc_auc)plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')plt.xlim([0.0, 1.0])plt.ylim([0.0, 1.05])plt.xlabel('False Positive Rate')plt.ylabel('True Positive Rate')plt.title('Receiver operating characteristic example')plt.legend(loc="lower right")plt.show()
The ROC curve is a probability curve plotting the true-positive rate (TPR) against the false-positive rate (FPR).
Similarly, the AUC (area under curve), as shown in the legend above, measures how much our model is capable of distinguishing between our two classes, dandelions and grass. It is also used to compare different models, which I will do in future tutorials when I present how to build an image classifier using Convolutional Neural Networks and transfer learning with ResNet!
Finally, at the end of the notebook, you’ll have a chance to make predictions on your own images!
I hope this gives you a gentle introduction to building a simple binary image classifier using just fully-connected layers. If you are interested in similar easy-to-follow, no-nonsense tutorials like this, please check out my other stories!