# Building a Cat Detector using Convolutional Neural Networks — TensorFlow for Hackers (Part III)

## Is it a cat or a dog?

Have you ever stood still, contemplating about how cool would it be to build a model that can distinguish cats from dogs? Don’t be shy now! Of course you did! Let’s get going!

# Cat or a dog?

We have 25,000 labeled pictures of dogs and cats. The data comes from Kaggle’s Dogs vs Cats challenge. That’s how a bunch of them look like:

Let’s focus on a specific image. Each picture can be represented as a 3-dimensional array. We will resize all training image to 50 x 50 pixels. Here’s a crazy example:

Additionally, we will remove all color and turn them pictures into grayscale ones. First things first, let’s prepare our environment:

# Setting up

Download the train and test zip files from Kaggle and extract them into your current working directory.

`import cv2import numpy as npimport os         from random import shuffle from tqdm import tqdm      import tensorflow as tfimport matplotlib.pyplot as pltimport tflearnfrom tflearn.layers.conv import conv_2d, max_pool_2dfrom tflearn.layers.core import input_data, dropout, fully_connectedfrom tflearn.layers.estimator import regression%matplotlib inlineTRAIN_DIR = 'train'TEST_DIR = 'test'IMG_SIZE = 50LR = 1e-3MODEL_NAME = 'dogs-vs-cats-convnet'`

# Image preprocessing

We have 25,000 images for training and 12,500 for testing. Let’s create a function that encodes the labels of the training images:

`def create_label(image_name):    """ Create an one-hot encoded vector from image name """    word_label = image_name.split('.')[-3]    if word_label == 'cat':        return np.array([1,0])    elif word_label == 'dog':        return np.array([0,1])`

Now, for the actual reading of training and test data. Every image will be resized to 50 x 50 pixels and read as grayscale:

`def create_train_data():    training_data = []    for img in tqdm(os.listdir(TRAIN_DIR)):        path = os.path.join(TRAIN_DIR, img)        img_data = cv2.imread(path, cv2.IMREAD_GRAYSCALE)        img_data = cv2.resize(img_data, (IMG_SIZE, IMG_SIZE))        training_data.append([np.array(img_data), create_label(img)])    shuffle(training_data)    np.save('train_data.npy', training_data)    return training_datadef create_test_data():    testing_data = []    for img in tqdm(os.listdir(TEST_DIR)):        path = os.path.join(TEST_DIR,img)        img_num = img.split('.')        img_data = cv2.imread(path, cv2.IMREAD_GRAYSCALE)        img_data = cv2.resize(img_data, (IMG_SIZE, IMG_SIZE))        testing_data.append([np.array(img_data), img_num])            shuffle(testing_data)    np.save('test_data.npy', testing_data)    return testing_data`

Now, let’s split the data. 24,500 images for training and 500 for testing. We also need to reshape the data appropriately for TensorFlow:

`# If dataset is not created:train_data = create_train_data()test_data = create_test_data()# If you have already created the dataset:# train_data = np.load('train_data.npy')# test_data = np.load('test_data.npy')train = train_data[:-500]test = train_data[-500:]X_train = np.array([i for i in train]).reshape(-1, IMG_SIZE, IMG_SIZE, 1)y_train = [i for i in train]X_test = np.array([i for i in test]).reshape(-1, IMG_SIZE, IMG_SIZE, 1)y_test = [i for i in test]`

# Convolutional Neural Nets

How will we do it? Isn’t that just too hard of a task? Convolutional Neural Networks to the rescue!

In the past, people had to think of and code different kinds of features that might be relevant to the task at hand. Examples of that would be whiskers, ears, tails, legs, fur type detectors. These days, we can just use Convolutional NNs. They can learn features from raw data. How do they work?

Ok, got it? It was a great explanation. You can think of convolutions as small sliding lenses (let’s say a 5 x 5) that are “activated” when are placed above some feature that is familiar to them. That way, convolutions can make sense of larger portions of the image, not just single pixels.

# Building our model

Finally, the fun part begins! We will use tflearn to build our Convolutional Neural Network. One additional bonus will be the use of a Dropout layer. Here’s the model:

`tf.reset_default_graph()convnet = input_data(shape=[None, IMG_SIZE, IMG_SIZE, 1], name='input')convnet = conv_2d(convnet, 32, 5, activation='relu')convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 64, 5, activation='relu')convnet = max_pool_2d(convnet, 5)convnet = fully_connected(convnet, 1024, activation='relu')convnet = dropout(convnet, 0.8)convnet = fully_connected(convnet, 2, activation='softmax')convnet = regression(convnet, optimizer='adam', learning_rate=LR, loss='categorical_crossentropy', name='targets')model = tflearn.DNN(convnet, tensorboard_dir='log', tensorboard_verbose=0)model.fit({'input': X_train}, {'targets': y_train}, n_epoch=10,           validation_set=({'input': X_test}, {'targets': y_test}),           snapshot_step=500, show_metric=True, run_id=MODEL_NAME)`

The results:

`Training Step: 3829  | total loss: [1m[32m11.45499[0m[0m | time: 35.818s| Adam | epoch: 010 | loss: 11.45499 - acc: 0.5025 -- iter: 24448/24500Training Step: 3830  | total loss: [1m[32m11.49676[0m[0m | time: 36.938s| Adam | epoch: 010 | loss: 11.49676 - acc: 0.5007 | val_loss: 11.60503 - val_acc: 0.4960 -- iter: 24500/24500--`

We resized our images to 50 x 50 x 1 matrices and that is the size of the input we are using.

Next, a convolutional layer with 32 filters and stride = 5 is created. The activation function is ReLU. Right after that, a max pool layer is added. That same trickery is repeated again with 64 filters.

Next, a fully-connected layer with 1024 neurons is added. Finally, a dropout layer with keep probability of 0.8 is used to finish our model.

We use Adam as optimizer with learning rate set to 0.001. Our loss function is categorical cross entropy. Finally, we train our Deep Neural Net for 10 epochs.

All that is great, but our validation accuracy doesn’t seem that good. Flipping a coin might be a better model than the one we created. Let’s go bigger and better (hopefully):

# Building our (bigger) model

`tf.reset_default_graph()convnet = input_data(shape=[None, IMG_SIZE, IMG_SIZE, 1], name='input')convnet = conv_2d(convnet, 32, 5, activation='relu')convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 64, 5, activation='relu')convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 128, 5, activation='relu')convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 64, 5, activation='relu')convnet = max_pool_2d(convnet, 5)convnet = conv_2d(convnet, 32, 5, activation='relu')convnet = max_pool_2d(convnet, 5)convnet = fully_connected(convnet, 1024, activation='relu')convnet = dropout(convnet, 0.8)convnet = fully_connected(convnet, 2, activation='softmax')convnet = regression(convnet, optimizer='adam', learning_rate=LR, loss='categorical_crossentropy', name='targets')model = tflearn.DNN(convnet, tensorboard_dir='log', tensorboard_verbose=0)model.fit({'input': X_train}, {'targets': y_train}, n_epoch=10,           validation_set=({'input': X_test}, {'targets': y_test}),           snapshot_step=500, show_metric=True, run_id=MODEL_NAME)`

The results:

`Training Step: 3829  | total loss: [1m[32m0.34434[0m[0m | time: 44.501s| Adam | epoch: 010 | loss: 0.34434 - acc: 0.8466 -- iter: 24448/24500Training Step: 3830  | total loss: [1m[32m0.35046[0m[0m | time: 45.619s| Adam | epoch: 010 | loss: 0.35046 - acc: 0.8432 | val_loss: 0.50006 - val_acc: 0.7860 -- iter: 24500/24500--`

That is pretty much the same model. One difference is the number of convolutional and max pool layers we added. So, our model has much more parameters and can learn more complex functions. One proof of that is the validation accuracy that is around 0.8. Let’s take our model for a spin!

# Did we do well?

Let’s have a look at a single prediction:

`d = test_dataimg_data, img_num = ddata = img_data.reshape(IMG_SIZE, IMG_SIZE, 1)prediction = model.predict([data])fig = plt.figure(figsize=(6, 6))ax = fig.add_subplot(111)ax.imshow(img_data, cmap="gray")print(f"cat: {prediction}, dog: {prediction}")`
`cat: 0.8773844838142395, dog: 0.12261549383401871`

That doesn’t look right. How about some more predictions:

`fig=plt.figure(figsize=(16, 12))for num, data in enumerate(test_data[:16]):        img_num = data    img_data = data        y = fig.add_subplot(4, 4, num+1)    orig = img_data    data = img_data.reshape(IMG_SIZE, IMG_SIZE, 1)    model_out = model.predict([data])        if np.argmax(model_out) == 1:         str_label='Dog'    else:        str_label='Cat'            y.imshow(orig, cmap='gray')    plt.title(str_label)    y.axes.get_xaxis().set_visible(False)    y.axes.get_yaxis().set_visible(False)plt.show()`

# Conclusion

There you have it! Santa is a dog! More importantly, you built a model that can distinguish cats from dogs using only raw pixels (albeit, with a tiny bit of preprocessing). Additionally, it trains pretty fast on relatively old machines! Can you improve the model? Maybe change the architecture, keep probability parameter of the Dropout layer or the optimizer? What results did you get?

The only thing left for you to do is snap a photo of your cat or dog and run it through your model. Was the net correct?

# References

More from TensorFlow for Hackers series:

Originally published at curiousily.com on April 24, 2017.

Written by

Written by

## More From Medium

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just \$5/month. Upgrade
{"ft_lang_label":"__label__en","ft_lang_prob":0.656469,"math_prob":0.9617629,"size":9728,"snap":"2020-24-2020-29","text_gpt3_token_len":2671,"char_repetition_ratio":0.12433155,"word_repetition_ratio":0.15128593,"special_character_ratio":0.29574424,"punctuation_ratio":0.19583778,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9922651,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2020-07-02T20:11:29Z\",\"WARC-Record-ID\":\"<urn:uuid:f976972d-348a-46bf-9574-a5f94f8819a9>\",\"Content-Length\":\"254914\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:10ef12cc-70bd-44e7-8267-8d8608fa9c81>\",\"WARC-Concurrent-To\":\"<urn:uuid:7a5bddda-d0b1-4b63-9cf2-dcb9d384b3bb>\",\"WARC-IP-Address\":\"104.16.122.127\",\"WARC-Target-URI\":\"https://medium.com/@curiousily/tensorflow-for-hackers-part-iii-convolutional-neural-networks-c077618e590b\",\"WARC-Payload-Digest\":\"sha1:GQOS6WWF5Y32F7VGI44WPXEWS6647XQO\",\"WARC-Block-Digest\":\"sha1:LXAGOXXFVXS76SGN3M4BPEKJZ5MZVHK4\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2020/CC-MAIN-2020-29/CC-MAIN-2020-29_segments_1593655879738.16_warc_CC-MAIN-20200702174127-20200702204127-00543.warc.gz\"}"}