Using Deep Learning there is a vast amount of amazing things we can acheive. One of them being creating a model to classify images.

Using CNN (convolutional neural networks) we can analyze images and train models to understand and classify those images.

In this post I am going to go over the steps I took to create an Image classifying model which I did for a personal project.

(I will try to break down the steps and keep it as simple as possible so anyone following along can keep up.)

deeplearning.png

Step 1: Choosing your data source

There are many datasets that you can download off the internet to follow along with this tutorial. One of the most famous ones being the MNIST-Fashion

For my own personal project I decided to create a model that classifed sports images. I wanted to be able to classify images across "Baseball, Basketball, Football, Hockey, and Soccer". To do this I web-scraped images off of Google and created my own data set.

If you are interested in following this route theres a very handy Python Package that you can use that will allow you to download your images very easily called the Google Images Downloader

Step 2: Get the Data

Like I mentioned in the first step I chose to web-scrape my own images to use to train my model.

If you have gone ahead and installed the "Google Images Downloader" package then your code should look something like this:

(before going any further I would suggest creating a folder where you can store all the work we're going to be doing)

from google_images_download import google_images_download
response = google_images_download.googleimagesdownload()   

arguments = {"keywords":"Baseball","Basketball","Hockey","Football","Soccer" #creating list of arguments
             "limit":1500, #Specify # of images to download per argument
             "format":"png", #Specify file type (optional)
             "size": "medium" #size of file (optional)
             "print_urls":True, #print file url (optional)
             "chromedriver":"C:\\Users\\v_sha\\OneDrive\\Desktop\\chromedriver_win32 (1)\\chromedriver.exe"}   
paths = response.download(arguments)   #passing the arguments to the function
print(paths)   #printing absolute paths of the downloaded images

("chromedriver" is needed to scroll through Google for images because only a certain amount are shown at once. More information can be found on the Google Images Downloader documentation page)

Running this code allowed me to download approximately 1500 images for each of my arguments (Baseball, Basketball, Hockey, Football and Soccer). I say approximately because some images can be corrupted and wont be downloaded.

The images should all be stored in a folder called downloads and there should be sub-folders for each argument(sport).

Once again, I chose to classify sports images for my own personal project, but you can use what ever you like to follow along.

Step 3: Load in Data(images)

Import Libraries

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import glob

from PIL import Image

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelBinarizer
from sklearn.metrics import confusion_matrix

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten, Input
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.utils import to_categorical

import warnings
warnings.filterwarnings('error', 'WARNING')


%matplotlib inline

Retrieve and list out Image folders in downloads

folder_names = [name for name in os.listdir("./downloads")]

folder_names
['Baseball', 'Basketball', 'Football', 'Hockey', 'Soccer']

Create X and y variables for training and test images/Load images

X = []
y = []

for folder in folder_names:
    files = glob.glob("downloads/" + folder + "/*")
    for file in files:
        img = Image.open(file)
        img = img.resize((200,200))
        img = np.asarray(img)
        img = np.array(img, dtype = "float32")
        img /= 255
        if img.shape == (200,200,3):
            X.append(img)
            label = folder.split("_")[0]
            y.append(label)
C:\Users\v_sha\AppData\Local\Programs\Python\Python37\lib\site-packages\PIL\TiffImagePlugin.py:804: UserWarning: Corrupt EXIF data.  Expecting to read 4 bytes but only got 0. 
  warnings.warn(str(msg))

Convert Data into Array and Inspect Shape

X = np.array(X)
y = np.array(y)

X.shape
(3329, 200, 200, 3)

Inspect Image

plt.imshow(X[0])
<matplotlib.image.AxesImage at 0x1ab070d0a48>

output_39_1.png

Now that we have loaded in our images and created our Train and Test set we can start prepping the data for the model

Train-Test-Split:

X_train, X_test, y_train, y_test = train_test_split(X, y)

Create Label (classes) for Images:

labels = {
    0: "Baseball",
    1: "Basketball",
    2: "Football",
    3: "Hockey",
    4: "Soccer"
}

y_train[0:5]
array(['Basketball', 'Hockey', 'Football', 'Soccer', 'Football'],
      dtype='<U10')

I now have 5 classes representing the 5 different sports images I am aiming to classify.

LabelBinarize() and Transform Data:

lb = LabelBinarizer()

y_train = lb.fit_transform(y_train)
y_test = lb.transform(y_test)
lb.inverse_transform(y_train[[0]])
array(['Basketball'], dtype='<U10')
plt.imshow(X_train[0][:, :, 0])
<matplotlib.image.AxesImage at 0x1ab07150288>

output_53_1.png

As you can see, once we apply LabelBinarizer and Transform our data the images look different

Now for the good stuff! Time to start creating our Model:

Instantiate and Create Model

#Instantiate Model
cnn_model = Sequential()

# Add a convolutional layer
cnn_model.add(Conv2D(filters = 6,
                     kernel_size = 3,
                     activation = "relu",
                    input_shape=(200,200,3)))

# Add a pooling layer
cnn_model.add(MaxPooling2D(pool_size = (3,3)))

# Add a second convolutional layer
cnn_model.add(Conv2D(filters = 16,
                     kernel_size = 3,
                     activation = "relu"))

# Add a second pooling layer
cnn_model.add(MaxPooling2D(pool_size = (3,3)))

# Add a third convolutional layer
cnn_model.add(Conv2D(filters = 26,
                     kernel_size = 3,
                     activation = "relu"))

# Add a third pooling layer
cnn_model.add(MaxPooling2D(pool_size = (3,3)))

# Flatten the 3D array to 1D array
cnn_model.add(Flatten())

# Add in first perceptrons
cnn_model.add(Dense(128, activation = "relu"))

# Add in a Dropout
cnn_model.add(Dropout(0.5))

# Add in second perceptrons
cnn_model.add(Dense(64, activation = "relu"))

# Add in a second Dropout
cnn_model.add(Dropout(0.5))

# Output
cnn_model.add(Dense(len(lb.classes_), activation = "softmax"))

cnn_model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_6 (Conv2D)            (None, 198, 198, 6)       168       
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 66, 66, 6)         0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 64, 64, 16)        880       
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 21, 21, 16)        0         
_________________________________________________________________
conv2d_8 (Conv2D)            (None, 19, 19, 26)        3770      
_________________________________________________________________
max_pooling2d_8 (MaxPooling2 (None, 6, 6, 26)          0         
_________________________________________________________________
flatten_2 (Flatten)          (None, 936)               0         
_________________________________________________________________
dense_6 (Dense)              (None, 128)               119936    
_________________________________________________________________
dropout_4 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_7 (Dense)              (None, 64)                8256      
_________________________________________________________________
dropout_5 (Dropout)          (None, 64)                0         
_________________________________________________________________
dense_8 (Dense)              (None, 5)                 325       
=================================================================
Total params: 133,335
Trainable params: 133,335
Non-trainable params: 0
_________________________________________________________________
cnn_model.compile(loss='categorical_crossentropy',
                 optimizer = 'adam',
                 metrics=['accuracy'])
history = cnn_model.fit(X_train,
                       y_train,
                       batch_size=256,
                       validation_data = (X_test, y_test),
                       epochs = 10,
                       verbose =1)
Train on 2496 samples, validate on 833 samples
Epoch 1/10
2496/2496 [==============================] - 31s 12ms/sample - loss: 1.6120 - acc: 0.2115 - val_loss: 1.6009 - val_acc: 0.2605
Epoch 2/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.6022 - acc: 0.2268 - val_loss: 1.5963 - val_acc: 0.2521
Epoch 3/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.5875 - acc: 0.2588 - val_loss: 1.5820 - val_acc: 0.3073
Epoch 4/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.5807 - acc: 0.2628 - val_loss: 1.5615 - val_acc: 0.3205
Epoch 5/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.5417 - acc: 0.3037 - val_loss: 1.4808 - val_acc: 0.3950
Epoch 6/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.4813 - acc: 0.3409 - val_loss: 1.3949 - val_acc: 0.4550
Epoch 7/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.4035 - acc: 0.3870 - val_loss: 1.3110 - val_acc: 0.4754
Epoch 8/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.3337 - acc: 0.4403 - val_loss: 1.2511 - val_acc: 0.5414
Epoch 9/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.2757 - acc: 0.4692 - val_loss: 1.1841 - val_acc: 0.5582
Epoch 10/10
2496/2496 [==============================] - 29s 12ms/sample - loss: 1.2210 - acc: 0.4980 - val_loss: 1.1380 - val_acc: 0.6014

So we have just created a model that has an accuracy of roughly 60%.

Not the greatest model but since we are predicting over 5 classes (20% each) this actually isn't a horrible score.

(The purpose of this blog is to help you understand how to create models for image classification. You can go ahead and do further readings and research to better understand how to create better model's based on tuning certain parameters.)

Plot Results

#Plot Training and Testing Loss:

# Check out our train loss and test loss over epochs.
train_loss = history.history['loss']
test_loss = history.history['val_loss']

# Set figure size.
plt.figure(figsize=(12, 8))

# Generate line plot of training, testing loss over epochs.
plt.plot(train_loss, label='Training Loss', color='#185fad')
plt.plot(test_loss, label='Testing Loss', color='orange')

# Set title
plt.title('Training and Testing Loss by Epoch', fontsize = 25)
plt.xlabel('Epoch', fontsize = 18)
plt.ylabel('Categorical Crossentropy', fontsize = 18)
plt.xticks([0, 1, 2, 3, 4])

plt.legend(fontsize = 18);

output_65_0.png

Check Final Model Score

cnn_score = cnn_model.evaluate(X_test, y_test, verbose=1)

cnn_labels = cnn_model.metrics_names
833/833 [==============================] - 4s 5ms/sample - loss: 1.1380 - acc: 0.6014
print(f'CNN {cnn_labels[0]}  : {cnn_score[0]}')
print(f'CNN {cnn_labels[1]}   : {cnn_score[1]}')
print()
CNN loss  : 1.1379965717623644
CNN acc   : 0.6014405488967896

Test Model:

np.set_printoptions(suppress = True)

cnn_model.predict(np.array([X_test[0]]))
array([[0.14482361, 0.04204996, 0.27279547, 0.00418275, 0.5361482 ]],
      dtype=float32)

If you remember from before when we made our classes our set up was :

0 - Baseball

1 - Basketball

2 - Football

3 - Hockey

4 - Soccer

Based on the given array output up top

array([[0.14482361, 0.04204996, 0.27279547, 0.00418275, 0.5361482 ]]

Image in (X_test[0]) is predicted to be 53% soccer which is the highest score across all classes

y_pred_train = [np.argmax(i) for i in (cnn_model.predict(X_train))]
label = {i:name for i, name in enumerate(lb.classes_)}
y_pred_train = [label[i] for i in y_pred_train]
y
array(['Baseball', 'Baseball', 'Baseball', ..., 'Soccer', 'Soccer',
       'Soccer'], dtype='<U10')
lb.classes_
array(['Baseball', 'Basketball', 'Football', 'Hockey', 'Soccer'],
      dtype='<U10')

Inspect Predictions Using Confusion Matrix

con_matrix = confusion_matrix(lb.inverse_transform(y_train), y_pred_train)
pd.DataFrame(con_matrix, columns = lb.classes_, index = lb.classes_)
Baseball Basketball Football Hockey Soccer
Baseball 350 13 89 29 123
Basketball 52 136 144 58 64
Football 48 20 368 23 101
Hockey 12 17 60 335 0
Soccer 54 15 60 5 320
check = pd.DataFrame({"y_true": lb.inverse_transform(y_train), "y_pred" : y_pred_train})
check.head(10)
y_true y_pred
0 Basketball Baseball
1 Hockey Hockey
2 Football Soccer
3 Soccer Soccer
4 Football Football
5 Baseball Baseball
6 Basketball Football
7 Basketball Hockey
8 Basketball Basketball
9 Hockey Hockey

Here you can see the first 10 images I have. The very first one is suppose to be a "Basketball" image but the model has predicted it to be "Baseball" which is unfortunate but once again the purpose of this blog is to help you understand the steps in creating a Image Classification Model.

Well there you go folks. We have successfully created an Image Classification Model. Hopefully this was simple enough for you to follow along. Thanks for reading!