Coders Packet

Food Item Classifier in Python using Tensorflow

By Mirza Yusuf

Classification of 101 food items from the dataset food101 in Python using TensorFlow.

In this project, we will learn how to classify different objects using neural networks made from scratch. We will classify 100 food items.

Image Classification generally requires a big dataset, in this case, the dataset is relatively small when it comes to individual food items. Therefore we will be using a pre-trained model Resnet152V2. We will fine-tune the last 15 layers and add a few more layers.

DATASET

The food dataset can be found here food101.

DEPENDENCIES

First and most import is installing the dependencies and then importing libraries. Here we will be using quite a few for eg: TensorFlow, matplotlib etc.

pip install tensorflow
pip install matplotlib
pip install numpy

Numpy is optional.

Now that we done with the dependencies we can go forward with installing the libraries.

Installing Libraries

import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras import layers
from tensorflow.keras import Model  

With the libraries installed we can now move forward to preprocessing our data.

Data Preprocessing

In this code we will keep the image height and width relatively moderate. Batch size of 100 that means it will take 100 images at a time.

batch_size = 100
img_h = 128
img_w = 128
num_clas=100

Now to define the name of the classes.

clas = ['apple_pie', 'baby_back_ribs', 'baklava', 'beef_carpaccio', 'beef_tartare', 'beet_salad', 'beignets', 'bibimbap',
           'bread_pudding', 'breakfast_burrito', 'bruschetta', 'caesar_salad', 'cannoli', 'caprese_salad', 'carrot_cake',
           'ceviche', 'cheese_plate', 'cheesecake', 'chicken_curry', 'chicken_quesadilla','chicken_wings','chocolate_cake',
           'chocolate_mousse','churros', 'clam_chowder', 'club_sandwich', 'crab_cakes', 'creme_brulee', 'croque_madame',
           'cup_cakes', 'deviled_eggs', 'donuts', 'dumplings', 'edamame', 'eggs_benedict', 'escargots', 'falafel', 'filet_mignon'
           'fish_and_chips', 'foie_gras', 'french_fries', 'french_onion_soup', 'french_toast', 'fried_calamari', 'fried_rice',
           'frozen_yogurt', 'garlic_bread', 'gnocchi', 'greek_salad', 'grilled_cheese_sandwich', 'grilled_salmon', 'guacamole',
           'gyoza', 'hamburger', 'hot_and_sour_soup', 'hot_dog', 'huevos_rancheros', 'hummus', 'ice_cream', 'lasagna', 'lobster_bisque',
           'lobster_roll_sandwich', 'macaroni_and_cheese', 'macarons', 'miso_soup', 'mussels', 'nachos','omelette', 'onion_rings',
           'oysters', 'pad_thai', 'paella', 'pancakes', 'panna_cotta', 'peking_duck', 'pho', 'pizza', 'pork_chop', 'poutine',
           'prime_rib', 'pulled_pork_sandwich', 'ramen', 'ravioli', 'red_velvet_cake', 'risotto', 'samosa', 'sashimi', 'scallops',
           'seaweed_salad', 'shrimp_and_grits', 'spaghetti_bolognese', 'spaghetti_carbonara', 'spring_rolls', 'steak', 'strawberry_shortcake',
           'sushi', 'tacos', 'takoyaki', 'tiramisu', 'tuna_tartare', 'waffles' ]

These are all the names of the food items we are trying to classify.

Train and Validation Datagen

Even though our dataset is big, our images per food item is less. That's why we are going to use ImageGenerator which is used for data augmentation, to extend our dataset by randomly zooming or rotating our training images etc.

train_datagen = ImageDataGenerator(rotation_range=50,
                                    width_shift_range=0.2,
                                    height_shift_range=0.2,
                                    zoom_range=0.3,
                                    horizontal_flip=True,
                                    vertical_flip=True,
                                    fill_mode='constant',
                                    cval=0,
                                    rescale=1./255)
valid_datagen = ImageDataGenerator(rotation_range=45,
                                    width_shift_range=0.2,
                                    height_shift_range=0.2,
                                    zoom_range=0.3,
                                    horizontal_flip=True,
                                    vertical_flip=True,
                                    fill_mode='constant',
                                    cval=0,
                                    rescale=1./255)

test_datagen = ImageDataGenerator(rescale=1./255)

Loading the dataset

train_dir = '../input/food101/training'
val_dir = '../input/food101/validation'

Preprocessing and Rescaling

We selecting a random seed value so that our shuffle data remains consistent. Rescaling to 128x128 which is the size we want. Dividing it in batches so that training becomes easy.

seeed = 10
train_gen = train_datagen.flow_from_directory(train_dir,
                                              batch_size = batch_size,
                                              target_size = (128,128),
                                              classes = clas,
                                              class_mode = 'categorical',
                                              shuffle = True,
                                              seed = seeed)
valid_gen = valid_datagen.flow_from_directory(val_dir,
                                              batch_size = batch_size,
                                              target_size = (128,128),
                                              classes = clas,
                                              class_mode = 'categorical',
                                              shuffle = True,
                                              seed = seeed)

 

Loading the pretrained model

We are using Resnet152v2 which is a very deep model, pretraining it on the 'imagenet' dataset. Then fine tuning the last 15 layers for our dataset.

from tensorflow.keras import regularizers
from tensorflow.keras.layers import GlobalAveragePooling2D,Flatten, Dense, Dropout
Res_model = tf.keras.applications.Resnet152V2(weights='imagenet', include_top=False, input_shape=(128, 128, 3))

# The last 15 layers fine tune
for layer in Res_model.layers[:-15]:
    layer.trainable = False

x = Res_model.output
x = GlobalAveragePooling2D()(x)
x = Flatten()(x)
x = Dense(units=512, activation='swish')(x)
x = Dropout(0.3)(x)
x = Dense(units=512, activation='swish')(x)
x = Dropout(0.3)(x)
output  = Dense(units=100, activation='softmax')(x)
model = Model(Res_model.input, output)


model.summary()

After the pretraining model we are adding a few of our layers for better efficiency, here instead of using the generic 'relu' activation function for the Dense layer 'swish' has been used. Swish is a new activation layer by google which outperforms relu in deeper neural networks.

Swish Activation

from keras.backend import sigmoid
def swish(x, beta =1):
    return(x*sigmoid(beta*x))

Compiling the Model

Here we are using Adam optimizer and 'categorical_crossentropy' since we dividing them into categories.

model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

Fitting and optimizing Learning Rate

Now it's time to fit our model. Since this is a very huge model finding the optimum loss becomes a little difficult. That is why the learning rate needs to be changed as the model progresses. Using the ReduceLROnPlateau reduces our learning rate whenever our graph orientation changes by a certain extent. Which in turn helps us to find the optimal and minimum point in the graph for our loss.

from tensorflow.keras.callbacks import ReduceLROnPlateau

learn_rate = ReduceLROnPlateau(monitor='val_accuracy', 
                        patience=3, 
                        verbose=1, 
                        factor=0.4, 
                        min_lr=0.0001)


callbacks = [learn_rate]

STEP_SIZE_TRAIN=train_gen.n//train_gen.batch_size
STEP_SIZE_VALID=valid_gen.n//valid_gen.batch_size
history = model.fit_generator(generator=train_gen,
                   steps_per_epoch=STEP_SIZE_TRAIN,
                   validation_data=valid_gen,
                   validation_steps=STEP_SIZE_VALID,
                   epochs=50,
                   callbacks=callbacks)

Here we training it for 50 epochs. That means 50 times the entire dataset will pass through the network. The number of epochs is high because the dataset is very distinguished hence it takes time for the network to indentify the features.

Evaluating the Model

After 50 epochs our training accuracy reaches around 90% and validation around 85%. Now its time to Evaluate the model.

model.evaluate_generator(generator=valid_gen,steps=STEP_SIZE_VALID)

Plotting the loss and accuracy

After the training we can plot our losses for training and validation along with the accuracy. 

%matplotlib inline
import matplotlib.pyplot as plt
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'r', label='Training accuracy')
plt.plot(epochs, val_acc, 'b', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()

plt.plot(epochs, loss, 'r', label='Training Loss')
plt.plot(epochs, val_loss, 'b', label='Validation Loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

Test

Finally we can insert the images of our choice and test the model

def predict_food(imagepath):
    image = cv2.imread(imagepath)
    res = cv2.resize(image, dsize = (150, 150),interpolation=cv2.INTER_AREA)
    res = np.array(res)
    res  = np.expand_dims(res, axis = 0)
    predict = model.predict(res)
    return predict

print (predict_food('Your file directory'))

 

 

 

Download Complete Code

Comments

No comments yet

Download Packet

Reviews Report

Submitted by Mirza Yusuf (Yusuf)

Download packets of source code on Coders Packet