Detecting Chihuahuas vs Muffins

Based on this, I wonder how good is a raw pretrained imagenet network using the pretrained network resnet34, provided by pytorch, using the fastai library.
According to this, the model has an accuracy of 73.3%. I will only test on these edge cases studied by Mariya.

Also, I wanted to show how little code is needed to do this using the fastai library. It is extraordinary how fast it can be! I also used the library to further train a model with a small set of muffin and chihuahuas images using transfer learning.

This experiment was made using fast.ai 1.0.38 and Pytorch 1.0.

Setup

In [1]:

%matplotlib inline
%reload_ext autoreload
%autoreload 2

from fastai import *
from fastai.vision import *

In [2]:

# Use in case of retina display
from IPython.display import set_matplotlib_formats
set_matplotlib_formats('retina')

In [3]:

path = Path('/home/duguet/data/chihuahua-muffin')

Here, I set up my data folder. Inside this folder, the easiest way for me is to separate the classes in folders. This would be the structure:

In [4]:

path_string = str(path)
!tree -d $path_string
/home/duguet/data/chihuahua-muffin
├── models
├── other
├── test
│ ├── chihuahua
│ └── muffin
└── train
├── chihuahua
└── muffin

8 directories

I created a folder train in which we are not interested right now for predicting, and a folder test which will have the images Mariya used for testing these algorithms. We see that for both folders, I created the subfolders chihuahua and muffin, which correspond to their different classes.

In [5]:

testset = path/'test'; trainset=path/'train'

In short, I downloaded the 16 images used for testing and put them in the testset folder.

In [6]:

!tree $path_string/test/home/duguet/data/chihuahua-muffin/test
├── chihuahua
│ ├── test12.png
│ ├── test14.png
│ ├── test15.png
│ ├── test2.png
│ ├── test4.png
│ ├── test6.png
│ ├── test7.png
│ └── test9.png
└── muffin
├── test1.png
├── test10.png
├── test11.png
├── test13.png
├── test16.png
├── test3.png
├── test5.png
└── test8.png

2 directories, 16 files

Test ImageNet Pretrained Network

If you want to predict using the pretrained model, there are 2 ways to do that: the PyTorch way and the fastai way.

The PyTorch way

In [7]:

s = open('/home/duguet/data/imagenet/imagenet1000_clsid_to_human.txt', 'r')
imagenet_classes = eval(s.read())
#imagenet_classes = pickle.load(open('/home/duguet/data/imagenet/classes.pkl', 'rb'))

To use a model for prediction, it needs to be set into eval mode.

In [8]:

imagenet_model = models.resnet34(pretrained=True).eval()

We also need to find the class numbers corresponding to chihuahua and muffin.

In [9]:

print(list(imagenet_classes.values()).index('Chihuahua'))
print(list(imagenet_classes.values()).index('muffin'))
151---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-9-603ad247df74> in <module>
1 print(list(imagenet_classes.values()).index('Chihuahua'))
----> 2 print(list(imagenet_classes.values()).index('muffin'))

ValueError: 'muffin' is not in list

Unfortunately muffin is not in the list of the 1000 clases that the pretrained network provides. In the imagenet website, we see that muffin appears to be in the misc subset. So the results for muffin will try to show the closest thing to it in the model. I'm curious to see what it is.

We go over all the files in the test set (both subfolders) and print out their image and predicted class.

In [10]:

import glob

In [11]:

# Evaluate the old PyTorch way
testlist = glob.glob(str(testset/'*/*'))

fig,ax = plt.subplots(4,4,figsize=(15,10))

for i,ax_ in enumerate(ax.flatten()):
try:
img_file = testlist[i]
img = open_image(img_file).resize(224)
batch = torch.stack([img.data])
out = imagenet_model(batch)
solution = to_np(out.detach())
idx = solution.argmax()
img.show(ax=ax_, title=imagenet_classes[idx])
# print(f'file: {img_file.name}. Detected {imagenet_classes[idx]}, {solution.max()}')
except:
continue

We can see all chihuahas except one are identified correctly. One was identified as a Labrador retriever :D. The muffin though, is identified as different classes, mainly dough, whiskey jug, bagel,beigel, and bakery, bakeshop, bakehouse.

The most unrelated one is whiskey jug, which according to the imagenet dataset looks completely different. Also, we see that bagel is one of the 1000 classes even though it is also a part of the misc subset.

The fastai way

The only way to use the fast.ai approach is to train the model (for which we need training images) so we have non random weights in its head. We will do that in the next section.

If you’re still interested in seeing how it would work:

In [16]:

data = ImageDataBunch.single_from_classes(path, list(imagenet_classes.values()), tfms=get_transforms(), size=224).normalize(imagenet_stats)

In [17]:

learner = create_cnn(data, models.resnet34, pretrained=True)

In [15]:

# Evaluate the old PyTorch way
learner.model.eval()
for img_file in (testset/'muffin').ls():
try:
img = open_image(img_file).resize(224)
pred,_,_ = learner.predict(img)
print(f'file: {img_file.name}. Detected {pred}')
except:
continue
file: test16.png. Detected sweatshirt
file: test5.png. Detected wreck
file: test13.png. Detected scale, weighing machine
file: test10.png. Detected French horn, horn
file: test8.png. Detected hook, claw
file: test3.png. Detected sweatshirt
file: test11.png. Detected ruddy turnstone, Arenaria interpres
file: test1.png. Detected sweatshirt

As we discussed before, with random weights on the model head, we get random values as well.

Train Network to Chihuahas and Muffins

For that, we need to create our own dataset of muffins and chihuahuas, and include them in our train folder. The fast.ai library also include some very handy scripts to make your own dataset of images using Google Images, inspired by Adrian Rosebrock.

Basically you get two text files with a list of image adresses. We go over them and download all files.

In [11]:

classes = ['chihuahua', 'muffin']

In [6]:

#Download images
for c in classes:
download_images(path/f'{c}.txt', trainset/c, max_pics=700)

100.00% [700/700 01:39<00:00]

Error https://dechihuahua.org/wp-content/uploads/2018/10/chihuahua-en-el-rio.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://dechihuahua.org/wp-content/uploads/2018/11/bolsos-tienda.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://www.zooportraits.com/wp-content/uploads/2018/05/Chihuahua-Dog-Puppy-Canis-Lupus-Familiaris.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://dechihuahua.org/wp-content/uploads/2018/11/cuadro-pintura-rosa-color.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://dechihuahua.org/wp-content/uploads/2018/11/cuadros-tienda.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://dechihuahua.org/wp-content/uploads/2018/11/cuadro-chihuahua-7-300x300.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://dechihuahua.org/wp-content/uploads/2018/11/cuadro-chihuahua-playa-gafas.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://dechihuahua.org/wp-content/uploads/2018/11/chihuahua-playa-dividido.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://dechihuahua.org/wp-content/uploads/2018/11/cuadro-chihuahua-5-300x300.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Error https://dechihuahua.org/wp-content/uploads/2018/11/cuadro-chihuahua-12.jpg ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))

We also verify the validity of the files.

In [7]:

# Verify images 
for c in classes:
print(c)
verify_images(trainset/c, delete=True, max_size=500)
chihuahua

100.00% [629/629 00:00<00:00]

muffin

100.00% [641/641 00:00<00:00]

Once we have gathered the images for training, creating a DataBunch is fairly straightforward.

In [33]:

# Create DataBunch
np.random.seed(42)
data= ImageDataBunch.from_folder(path, valid='test', ds_tfms=get_transforms(), size=224, num_workers=4).normalize(imagenet_stats)

In [34]:

data.show_batch(rows=3, figsize=(7,8))

We see from the following, that we have 2 classes, 1270 images for training and 16 for validation (our images from the original exercise).

In [35]:

data.classes, data.c, len(data.train_ds), len(data.valid_ds)

Out[35]:

(['chihuahua', 'muffin'], 2, 1270, 16)

Train model

In [36]:

learn = create_cnn(data, models.resnet34, metrics=error_rate)

In [37]:

learn.fit_one_cycle(4)

Total time: 00:21 epoch train_loss valid_loss error_rate 1 0.335307 0.001013 0.000000 2 0.157379 0.001282 0.000000 3 0.091850 0.001000 0.000000 4 0.060172 0.000836 0.000000

We see that the error is 0!!! The validation set is way too easy!!

Results

In [38]:

acc = accuracy(*learn.get_preds()); acc.item()

Out[38]:

1.0

In [39]:

learn.show_results(rows=4)

Really, since we are only classifying between two objects, the problems becomes way too easy that all images are correctly predicted. As a disclaimer, I need to check if these iamges are not included in the automatically generated training set. Because, if they are there, of course that could be cheating.

In [40]:

interp = ClassificationInterpretation.from_learner(learn)

In [41]:

interp.plot_confusion_matrix()

One last image for testing

In [42]:

img = open_image(path/'other/get.jpeg').resize(224)

In [46]:

pred,_,_ = learn.predict(img)
img.show(title=pred)

Here, we see that the muffin is detected first. In order to detect both images here, we should implement a multi classification network.

Originally published at HI, I AM CRISTIAN DUGUET.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store