How to train a multi-class image classifier with Keras - python

I was following the 2 class image classification tutorial here and wanted to convert it into a multi-class classifier.
I am trying to train a model to predict the brand of a watch from 17 classes. My accuracy after 50 epochs is only 21.88% so I'm not really sure where I am going wrong or if I am even doing this right.
Here is my code:
All the images are in their own separate folders under the /data or /valid folders.
Ex: ../watch finder/data/armani
Ex2: ../watch finder/data/gucci
import numpy as np
import matplotlib.pyplot as plt
import os
import cv2
from keras.utils import np_utils
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K
import keras.optimizers
img_width, img_height = 210, 210
train_data_dir = 'C:/Users/Adrian/Desktop/watch finder/data'
validation_data_dir = 'C:/Users/Adrian/Desktop/watch finder/valid'
nb_train_samples = 4761
nb_validation_samples = 612
epochs = 50
batch_size = 16
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=input_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(17))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
train_data_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='categorical')
validation_generator = test_datagen.flow_from_directory(
validation_data_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='categorical')
model.fit_generator(
train_generator,
samples_per_epoch=nb_train_samples // batch_size,
epochs=epochs,
validation_data=validation_generator,
validation_steps=nb_validation_samples // batch_size)
This is my first epoch:
Epoch 1/50
18/18 [==============================] - 8s 422ms/step - loss: 4.1104 - accuracy: 0.0833 - val_loss: 2.8369 - val_accuracy: 0.0592
And this is my 50th/last epoch:
Epoch 50/50
18/18 [==============================] - 7s 404ms/step - loss: 2.4840 - accuracy: 0.2188 - val_loss: 3.0823 - val_accuracy: 0.1795
I am fairly certain I am doing something wrong here but I am really new to deep learning so I'm not sure what that something is.
All the help is appreciated.
Also, each brand of watch has between 300-400 images and each image size is the same at 210x210.

There seems to be nothing wrong with your approach on a high level.
Has training stopped on the 50th epoch, or is it still learning? If so, then you might need to increase the learning rate so that it trains faster.
You need to also try different architectures and start tuning the hyperparameters.
Another point I'd like to make is that you have a really small number of images. Try using an established architecture for which you can find pretrained models for. These can help you significantly boost your performance.
One final note is that since you have 17 classes, if your model were predicting at random, you'd get an accuracy of just under 6%. This means that your model is at least learning something.

Related

Keras multiclass images classification and prediction

I am doing image classification with ImageDataGenerator. My data has this structure:
Train
101
102
103
104
Test
101
102
103
104
So, if I understood good, the ImageGenerator automatically does what is needed with labeling.
I train the model, and I get some kind of accuracy. Now I want to do the prediction.
- model.predict
- model.predict_proba
- model.predict_classes
All these give me the same value. Can you quickly explain or refer(I cannot find anything concerning my problem) how I should proceed, or maybe I did something terrible in the code. The biggest problem, I don't understand how the output will differ for 4 different classes. As predict_classes gives me an output [[1]], should not it give me the predicted class?
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, MaxPool2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.regularizers import l1, l2, l1_l2
model = Sequential()
model.add(Conv2D(60, (3, 3), input_shape=(480, 640,3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(60, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(100, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten()) # this converts our 3D feature maps to 1D feature vectors
model.add(Dense(100, activation='relu', activity_regularizer=l1(0.001)))
#model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('softmax'))
model.compile(loss='binary_crossentropy',
optimizer='Adam',
metrics=['accuracy'])
batch_size = 32
# augmentation configuration for train
train_datagen = ImageDataGenerator(
rotation_range=30,
width_shift_range=0.2,
height_shift_range=0.2,
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=False,
vertical_flip=True,
fill_mode = 'nearest')
# augmentation configuration for testing, only rescale
test_datagen = ImageDataGenerator(rescale=1./255)
# reading pictures and generating batches of augmented image data
train_generator = train_datagen.flow_from_directory(
'/media/data/working_dir/categories/readytotest/train',
target_size=(480, 640),
batch_size=batch_size,
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
'/media/data/working_dir/categories/readytotest/test',
target_size=(480, 640),
batch_size=batch_size,
class_mode='binary')
model.fit_generator(
train_generator,
steps_per_epoch=800 // batch_size,
epochs=15,
validation_data=validation_generator,
validation_steps=800 // batch_size)
Your model and the generators not for multi class but binary classification. First you need to fix your model last layer to get output with class size. Second you need to fix the generators to use in multi class.
(...)
model.add(Dense(CLS_SZ))
model.add(Activation('softmax'))
(...)
# I am not sure about this read some docs about generator you used.
train_generator = train_datagen.flow_from_directory(
'/media/data/working_dir/categories/readytotest/train',
target_size=(480, 640),
batch_size=batch_size,
class_mode=None)
validation_generator = test_datagen.flow_from_directory(
'/media/data/working_dir/categories/readytotest/test',
target_size=(480, 640),
batch_size=batch_size,
class_mode=None)

Keras stops after 1 completed epoch

Trying to run classification on the CIFAR-10 dataset with a simple CNN. However, the model stops after completing the first epoch and doesn't go on to complete all five. Please help.
INPUT:
cifar10 = tf.keras.datasets.cifar10
(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()
import os
import matplotlib.pyplot as plt
import numpy as np
import time
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers
from tensorflow.keras.applications import VGG16
from tensorflow.keras.preprocessing.image import ImageDataGenerator
model = models.Sequential()
# Convolutional base (feature extractor)
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
# Deep feed-forward classifier
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4), metrics=['acc'])
history = model.fit(
x=train_images,
y=train_labels,
steps_per_epoch=100,
epochs=5,
verbose=1,
validation_data=(test_images, test_labels),
validation_steps=50)
OUTPUT:
Train on 50000 samples, validate on 10000 samples
Epoch 1/5
50000/50000 [==============================] - 28s 564us/sample - loss: 2.1455 - acc: 0.2945 - val_loss: 2.0011 - val_acc: 0.3038
You should remove steps_per_epoh and validation_steps and use batch_size params.

Neural network isn't learning for a first few epochs on Keras

I'm testing simple networks on Keras with TensorFlow backend and I ran into an issue with using sigmoid activation function
The network isn't learning for first 5-10 epochs, and then everything is fine.
I tried using initializers and regularizers, but that only made it worse.
I use the network like this:
import numpy as np
import keras
from numpy import expand_dims
from keras.preprocessing.image import ImageDataGenerator
from matplotlib import pyplot
# load the image
(x_train, y_train), (x_val, y_val), (x_test, y_test) = netowork2_ker.load_data_shared()
# expand dimension to one sample
x_train = expand_dims(x_train, 2)
x_train = np.reshape(x_train, (50000, 28, 28))
x_train = expand_dims(x_train, 3)
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)
datagen = ImageDataGenerator(
rescale=1./255,
width_shift_range=[-1, 0, 1],
height_shift_range=[-1, 0, 1],
rotation_range=10)
epochs = 20
batch_size = 50
num_classes = 10
model = keras.Sequential()
model.add(keras.layers.Conv2D(64, (3, 3), padding='same',
input_shape=x_train.shape[1:],
activation='sigmoid'))
model.add(keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(keras.layers.Conv2D(100, (3, 3),
activation='sigmoid'))
model.add(keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(100,
activation='sigmoid'))
#model.add(keras.layers.Dropout(0.5))
model.add(keras.layers.Dense(num_classes,
activation='softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),
steps_per_epoch=len(x_train) / batch_size, epochs=epochs,
verbose=2, shuffle=True)
With the code above I get results like these:
Epoch 1/20
- 55s - loss: 2.3098 - accuracy: 0.1036
Epoch 2/20
- 56s - loss: 2.3064 - accuracy: 0.1038
Epoch 3/20
- 56s - loss: 2.3068 - accuracy: 0.1025
Epoch 4/20
- 56s - loss: 2.3060 - accuracy: 0.1079
...
For 7 epochs (different every time) and then the loss rapidly goes downward and i achieve 0.9623 accuracy in 20 epochs.
But if I change activation from sigmoid to relu it works great and gives me 0.5356 accuracy in the first epoch.
This issue makes sigmoid almost unusable for me and I'd like to know, I can do something about it. Is this a bug or am I doing something wrong?
Activation function suggestion:
In practice, the sigmoid non-linearity has recently fallen out of favor and it is rarely ever used. ReLU is the most common choice, if there are a large fraction of “dead” units in network, try Leaky ReLU and tanh. Never use sigmoid.
Reasons for not using the sigmoid:
A very undesirable property of the sigmoid neuron is that when the neuron’s activation saturates at either tail of 0 or 1, the gradient at these regions is almost zero. In addition, Sigmoid outputs are not zero-centered.

How can I increase the accuracy of my image classification keras model in Python?

I am trying to classify a group of bee images into two classes - bumble bee and honey bee with the resulting format being a CSV file like -
id,bumble_bee,honey_bee
20000,0.75, 0.25.
I have a running model, but the accuracy is very low and I've tried a bunch of different things such as adding a base_model like VGG16 or InceptionV3, adjusting the epochs, adjusting the optimizer type... and I just haven't noticed much difference. All of my changes still result in an accuracy of around 70-79%.
How can I increase the accuracy of my model?
Here is my code:
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K
# dimensions of our images.
img_width, img_height = 200, 200
train_data_dir = 'data/train/'
validation_data_dir = 'data/validation/'
nb_train_samples = 2978
nb_validation_samples = 991
epochs = 50
batch_size = 25
if K.image_data_format() == 'channels_first':
input_shape = (3, img_width, img_height)
else:
input_shape = (img_width, img_height, 3)
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=input_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(2))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy',
optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=['accuracy'])
# this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
# this is the augmentation configuration we will use for testing:
# only rescaling
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
train_data_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='categorical')
validation_generator = test_datagen.flow_from_directory(
validation_data_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='categorical')
model.fit_generator(
train_generator,
steps_per_epoch=nb_train_samples // batch_size,
epochs=epochs,
validation_data=validation_generator,
validation_steps=nb_validation_samples // batch_size)
model.save_weights('thirdtry.h5')
pred = model.predict(pred_images)
np.savetxt('resultsfrom3no.csv',pred)
Here is an example of its output:
Found 2978 images belonging to 2 classes.
Found 991 images belonging to 2 classes.
Epoch 1/50 119/119 [==============================] -
238s 2s/step - loss: 0.5570 - acc: 0.7697 - val_loss: 0.5275 -
val_acc: 0.7908
Epoch 2/50 119/119 [==============================] -
237s 2s/step - loss: 0.5337 - acc: 0.7894 - val_loss: 0.5270 -
val_acc: 0.7908
Epoch 3/50 119/119 [==============================] -
282s 2s/step - loss: 0.5299 - acc: 0.7939 - val_loss: 0.5215 -
val_acc: 0.7908
Use an image-net pre-trained VGGNet for feature extraction and build your classifier on top of it with Dense layers. You should set the image size (w x h) to original network input.
Since you have only two classes, it will be better to use binary class mode for data generators, binary cross entropy as loss function and sigmoid as final activation function.
First try without data augmentation for a quick test and then use augmentation to see if it helps.
While training your classifier network, start with a respectively high learning rate (such as 0.001) and experiment with lower rates (1e-4, 1e-5, etc...) if the acc value gets sticked.
Also in your code, decreasing the learning rate and playing with the momentum value will probably help.

Wrong prediction on images

In short
I'm going my first steps with Keras using my dataset.
When I try to load a model and test on test-images - I obtain only one class despite the previous output on training states the accuracy was 77%. Could you point out, what is misconfigured in the prediction code?
Detailed description:
The dataset are marked biological cells in gray-scale, size 64x64. There are 2 types of cells p and c.
I created the dataset directories as proposed in cat/dogs example.
train, test, val irectories with panca, canca in each. canca, canca directories contain images.
e.g.:
train-panca-<images>
train-canca-<images>
I've derived the code mainly also from cat/dog example and changed the image size.
After running the code, I obtain the output:
...
80/80 [==============================] - 8s 101ms/step - loss: 0.2995 - acc: 0.8910 - val_loss: 0.5150 - val_acc: 0.7560
Using TensorFlow backend.
Finished saving
Test loss: 0.46428223699331284
Test accuracy: 0.7759999990463257
This looks reasonable and promising - Accuracy of 77% with very litle data and short training time (ca. 3 minutes) is incredible.
I also tested replacing the test directory by train directory and obtain the accuracy of > 90%. So the model looks functional.
However, when I try to load it and test on test-images - I obtain only one class.
train.py
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K
# dimensions of the images.
img_width, img_height = 64, 64
train_dir = 'c:/tmp/anca_pc/train'
val_dir = 'c:/tmp/anca_pc/val'
test_dir = 'c:/tmp/anca_pc/test'
nb_train_samples = 2000
nb_validation_samples = 500
nb_test_samples = 500
epochs = 5
batch_size = 25
#Is right got gray-scale images? Shouldn't be 1 instead of 3
if K.image_data_format() == 'channels_first':
input_shape = (3, img_width, img_height)
else:
input_shape = (img_width, img_height, 3)
#Define model as proposed in keras tutorials
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=input_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
datagen = ImageDataGenerator( rescale=1. / 255 )
train_generator = datagen.flow_from_directory(
train_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='binary')
validation_generator = datagen.flow_from_directory(
val_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='binary')
test_generator = datagen.flow_from_directory(
test_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='binary')
model.fit_generator(
train_generator,
steps_per_epoch=nb_train_samples // batch_size,
epochs=epochs,
validation_data=validation_generator,
validation_steps=nb_validation_samples // batch_size)
#Save model architecture
model_json = model.to_json()
json_file = open("anca_pc.json", "w")
json_file.write(model_json)
json_file.close()
#Save model weights
model.save_weights("anca_pc.h5")
print("Finished saving")
score = model.evaluate_generator(test_generator, nb_test_samples // batch_size)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
print('Scode', score)
predict.py
import numpy as np
from keras.models import model_from_json
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input
import matplotlib.pyplot as plt
from scipy.misc import toimage
import os
classes = ['panca', 'canca']
directory = 'C:/tmp/anca_pc/test/'
json_file = open("anca_pc.json", "r")
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)
loaded_model.load_weights("anca_pc.h5")
loaded_model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
for c in classes:
aktdir=directory+c
print(aktdir)
for filename in os.listdir(aktdir):
fn=os.path.join(aktdir, filename)
#print(fn)
img = image.load_img(fn, target_size=(64, 64))
#plt.imshow(img)
#plt.show()
x = image.img_to_array(img)
x = x.astype('float32')
x /= 255
x = np.expand_dims(x, axis=0)
#x = preprocess_input(x)
prediction = loaded_model.predict(x)
#print(prediction)
print(c + " : " + classes[np.argmax(prediction)], " : ", prediction)
EDIT
Score output of model.predict_generator(test_generator, nb_test_samples // batch_size) run with 3 epochs
Test loss: 0.5996998563408852
Test accuracy: 0.7060000032186509
Score [[0.72468185]
[0.07171335]
[0.06702321]
[0.04176971]
[0.76247555]
[0.07743845]
[0.07435916]
[0.9960306 ]
[0.9270018 ]
[0.04746262]
[0.05305129]
[0.9993339 ]
[0.9986149 ]
[0.63527316]
[0.08033804]
[0.3816172 ]
[0.97601706]
[0.83666223]
[0.7226989 ]
[0.5351326 ]
[0.8407803 ]
[0.6953097 ]
[0.89651984]
[0.44985726]
[0.30889446]
[0.16406931]
[0.6346773 ]
[0.13678996]
[0.51343983]
[0.97438985]
[0.9981396 ]
[0.5485193 ]
[0.05270131]
[0.8029713 ]
[0.3295382 ]
[0.1865853 ]
[0.94497275]
[0.07609159]
[0.67434824]
[0.18562992]
[0.53442085]
[0.06662691]
[0.0388172 ]
[0.8763066 ]
[0.9875164 ]
...
np.argmax(prediction) will always return 0, because prediction contains only one value.
Since you have a binary output, you need to replace np.argmax with something like this:
def get_class(prediction):
return 1 if prediction > 0.5 else 0

Categories

Resources