Pasta

In [1]:
%reload_ext autoreload
%autoreload 2
%matplotlib inline
In [2]:
from fastai import *
from fastai.vision import *
In [3]:
from tqdm import tqdm_notebook
import PIL
import os
import shutil

Prepare data

In [5]:
img_path = '/home/jupyter/custom_datasets/pasta/'
prepared_path = '/home/jupyter/custom_datasets/pasta_prepared/'
os.makedirs(prepared_path, exist_ok=True)
In [ ]:
for pasta_folder in os.listdir(img_path):
    pasta_type = pasta_folder.split()[0]
    filenames = os.listdir(os.path.join(img_path, pasta_folder))
    for filename in tqdm_notebook(filenames):
        ext = os.path.splitext(filename)[1]
        if ext in ['.jpg', '.jpeg', '.png']:
            full_src_path = os.path.join(img_path, pasta_folder, filename)
            try:
                img = PIL.Image.open(full_src_path)
            except:
                continue
            shutil.copyfile(
                os.path.join(img_path, pasta_folder, filename), 
                os.path.join(prepared_path, f'{pasta_type}_{filename}')
            )

Load and save images to fix weird exif warnings

In [ ]:
path_img = Path(prepared_path)
fnames = get_image_files(path_img)
for fname in fnames:    
    with PIL.Image.open(fname) as img:
        img.save(fname)        
In [6]:
path_img = Path(prepared_path)
fnames = get_image_files(path_img)
fnames[:5], len(fnames)
Out[6]:
([PosixPath('/home/jupyter/custom_datasets/pasta_prepared/spaghetti_495.jpg'),
  PosixPath('/home/jupyter/custom_datasets/pasta_prepared/spaghetti_207.jpg'),
  PosixPath('/home/jupyter/custom_datasets/pasta_prepared/linguine_729.jpg'),
  PosixPath('/home/jupyter/custom_datasets/pasta_prepared/spaghetti_27.jpg'),
  PosixPath('/home/jupyter/custom_datasets/pasta_prepared/penne_435.png')],
 3682)

Resnet-34

In [7]:
pat = r'/([^/]+)_\d+.(?:jpg|png|jpeg)$'
In [18]:
np.random.seed(2)
data = ImageDataBunch.from_name_re(path_img, fnames, pat, ds_tfms=get_transforms(), size=224)
data.normalize(imagenet_stats)
In [19]:
data.show_batch(rows=5, figsize=(10,8))
In [20]:
print(data.classes)
len(data.classes),data.c
['spaghetti', 'linguine', 'penne', 'farfalle', 'fusilli']
Out[20]:
(5, 5)
In [21]:
learn = ConvLearner(data, models.resnet34, metrics=error_rate)
In [22]:
learn.fit_one_cycle(4)
Total time: 03:18
epoch  train loss  valid loss  error_rate
1      1.236885    0.717455    0.286649    (00:49)
2      0.896984    0.600923    0.240838    (00:48)
3      0.691726    0.550127    0.229058    (00:49)
4      0.591316    0.546946    0.222513    (00:50)

In [29]:
learn.recorder.plot_losses()
In [24]:
learn.save('stage-1')
In [25]:
learn.load('stage-1')

Results

In [30]:
interp = ClassificationInterpretation.from_learner(learn)
In [32]:
interp.plot_confusion_matrix(figsize=(12,12), dpi=60)
In [33]:
interp.most_confused(min_val=2)
Out[33]:
[('spaghetti', 'linguine', 36),
 ('linguine', 'spaghetti', 34),
 ('fusilli', 'farfalle', 20),
 ('farfalle', 'fusilli', 15),
 ('penne', 'fusilli', 10),
 ('fusilli', 'penne', 9),
 ('penne', 'farfalle', 8),
 ('linguine', 'farfalle', 6),
 ('linguine', 'penne', 5),
 ('farfalle', 'spaghetti', 5),
 ('penne', 'linguine', 4),
 ('spaghetti', 'penne', 3),
 ('farfalle', 'penne', 3),
 ('fusilli', 'spaghetti', 3)]
In [34]:
interp.plot_top_losses(9, figsize=(15,11))