Model predict giving same results after fitting - tensorflow

Hi I am a beginner in ML and Tensorflow so do pardon me for not understanding complex theories.
I was building an image classifier CNN as a form of practice. The model is trained using MobileNetv2 and its supposed to classify images of cats, dogs and pandas. After training my model (with decent accuracy of 92%), I tried using model.predict() to evaluate how it does with new images but I noticed that all my outputs were 1. This happens even if I used the same previous training data. By the way, I used 2700 (900 from each class) images for training and 300 for validation.
Here is my code
%tensorflow_version 2.x # this line is not required unless you are in a notebook
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
from keras.preprocessing.image import load_img
from keras.preprocessing.image import img_to_array
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
import PIL.Image
import tensorflow_datasets as tfds
import pathlib
from google.colab import drive
drive.mount('/content/gdrive')
IMAGE_SIZE=[150,150]
train_path = "/content/gdrive/MyDrive/Colab Notebooks/Cat_Dog_Panda_CNN/train"
test_path = "/content/gdrive/MyDrive/Colab Notebooks/Cat_Dog_Panda_CNN/test"
IMAGE_SHAPE=[150,150,3]
base_model = tf.keras.applications.MobileNetV2(input_shape=IMAGE_SHAPE,
include_top=False,
weights='imagenet')
base_model.trainable = False
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
prediction_layer = tf.keras.layers.Dense(3)
model = tf.keras.Sequential([
base_model,
global_average_layer,
prediction_layer
])
model.summary()
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate),
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
# creates a data generator object that transforms images
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
test_datagen = ImageDataGenerator (rescale=1./255)
training_set = train_datagen.flow_from_directory(train_path, target_size=IMAGE_SIZE, batch_size=32, class_mode='categorical')
testing_set = test_datagen.flow_from_directory(test_path, target_size=IMAGE_SIZE, batch_size=32, class_mode='categorical')
model.fit(
training_set,
epochs=3,
validation_data=testing_set)
img = Image.open("/content/gdrive/MyDrive/Colab Notebooks/Cat_Dog_Panda_CNN/test/panda/panda_00094.jpg").convert('RGB').resize((150, 150), Image.ANTIALIAS)
img = np.array(img)
predictions = model.predict(img[None,:,:])
np.argmax(predictions[0])

in model.compile you have loss=tf.keras.losses.BinaryCrossentropy(from_logits=True). Since you have 3 classes you should have
loss=tf.keras.losses.CategoricalCrossentropy()
The Mobilenet model was trained with pixel values scaled within the range -1 to +1. So you should set rescale to
rescale=1/127.5-1
When you want to feed an image into model.predict you must perform the same preprocessing
of the image as you did for the training images namely rescale the image and resize the image to be the same size you used in training, namely (150,150).

Related

Keras CNN predicts 2 classes out of 4

I have a problem about my CNN model made using tensorflow. The goal is to predict the classes of satellite images, corresponding to the type of clouds (data extracted from the kaggle competition "Planet: Understanding the Amazon from Space"). There are 4 classes : clear, cloudy, partly cloudy and haze.
Everything works fine until I try to test the model on individual images. Then, it always predicts 2 classes and nothing else. I noticed that if I run the model again, it may predict 2 other classes among the 4. The model was trained for 10 epochs, which gave an accuracy of 0.8717.
Here is my code :
import numpy as np
import pandas as pd
import cv2
from tqdm import tqdm
import h5py
import os
os.listdir("/kaggle/input/")
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense,MaxPooling2D,Conv2D,Flatten,Dropout,Activation
from tensorflow.keras.layers import BatchNormalization
from sklearn import svm
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
from tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img
from oauth2client.client import GoogleCredentials
import csv
#from keras.optimizers import RMSprop
from tensorflow.keras import Input, Model
batch_size = 128
img_width = 256
img_height = 256
train_data = ImageDataGenerator(
rescale = 1./255,
validation_split = 0.25)
train_generator = train_data.flow_from_directory(
'../input/clouds',
target_size=(img_height, img_width),
color_mode='rgb',
batch_size=batch_size,
shuffle = True,
class_mode="categorical",
subset = 'training'
)
valid_generator = train_data.flow_from_directory(
'../input/clouds',
target_size=(img_height, img_width),
batch_size=batch_size,
class_mode='categorical',
subset = 'validation'
)
num_classes = 4
model = Sequential([
Input(shape = [img_width, img_height, 3]),
Conv2D(128,4,activation = 'relu'),
MaxPooling2D(),
Conv2D(64,4,activation = 'relu'),
MaxPooling2D(),
Conv2D(32,4, activation = 'relu'),
MaxPooling2D(),
Conv2D(16,4,activation = 'relu'),
MaxPooling2D(),
Flatten(),
Dense(64, activation = 'relu'),
Dense(num_classes, activation = 'softmax')
])
model.compile(optimizer = "adam",
loss = 'categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_generator, validation_data = valid_generator, epochs = 10)
img_to_predict = cv2.imread('/kaggle/input/clouds-test/clouds_test/test_3877_6013089.jpg') #an augmented image from original dataset
img_to_predict = cv2.cvtColor(img_to_predict, cv2.COLOR_BGR2RGB)
img_to_predict = np.expand_dims(cv2.resize(img_to_predict, (256,256)), axis = 0)
res = model.predict(img_to_predict)
label_map = (train_generator.class_indices)
print(label_map)
print(list(label_map)[np.argmax(res, axis = -1)[0]])
Thank you for you help.

How to find class labels from a keras model

I am predicting classes, but there is something I don't get. In the simplified example below, I train a model to predict MNIST handwritten digits. My test set has an accuracy of 95%, when I use
model.evaluate(test_image, test_label)
However, when I use
model.predict(test_image)
and the extract the predicted labels using np.argmax(), this accuracy drops. When I run all the code again and again, this accuracy changes a lot.
I suspect now that the classes in the model are not ordered 0, 1 ... 9. Is there a way to see the class labels of a model? Or did I make another mistake?
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.datasets.mnist import load_data
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential
import numpy as np
# Load data
(train_image, train_label), (test_image, test_label) = load_data()
# Train
model = Sequential([
Flatten(input_shape=(28,28)),
Dense(100, activation="relu"),
Dense(100, activation="relu"),
Dense(10, activation="sigmoid")
])
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics='accuracy')
history = model.fit(train_image, train_label,
batch_size=32, epochs=50,
validation_data=(test_image, test_label),
verbose = 0)
eval = model.evaluate(test_image, test_label)
print('Accuracy (auto):', eval[1]) # This is always high
# Predict and evaluate manually
predictions = model.predict(test_image)
pred = np.array([np.argmax(pred) for pred in predictions])
true = test_label
print('Accuracy (manually):', np.mean(pred == true)) # This varies a lot

Webcam Frame to model.predict_generator - Jupyter Notebook & CV2

I have created this custom CNN, trained it, and now wish to try and pass frames from my webcam in real-time for testing the predictions.
The webcam video playback starts to capture the frame by frame, however, I am unsure what to do to the frame in order to get it working with the CNN model
Any advice would be appreciated
I have provided the full code of what I am trying to achieve
#imported necessities
import os
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import cv2
from matplotlib.image import imread
from IPython.display import clear_output
import time
import PIL.Image
from io import StringIO
import IPython.display
import numpy as np
from io import BytesIO
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Activation, Dense, Conv2D, MaxPool2D, Dropout, Flatten, MaxPooling2D
from tensorflow.keras.callbacks import EarlyStopping
#Data Paths
data_dir = 'C:\\Users\\User\\Desktop\\DATAWeather'
test_path = data_dir+'\\Test\\'
train_path = data_dir+'\\Train\\'
#Variable to resize all of the images
image_shape = (224,224,3) #224*224*3 = 150528 Data Points : thats why we need image batch
#Apply a generator so it does not always get the same format of picture (recognizes different things)
image_gen = ImageDataGenerator(rotation_range=20, width_shift_range=0.1, height_shift_range=0.1, rescale=1/255, shear_range=0.1, zoom_range=0.1,horizontal_flip=True,fill_mode='nearest')
#setting up a base convolutional layer
model = Sequential()
model.add(Conv2D(filters=32, kernel_size=(3,3),input_shape=image_shape, activation='relu',))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=64, kernel_size=(3,3),input_shape=image_shape, activation='relu',))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=64, kernel_size=(3,3),input_shape=image_shape, activation='relu',))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(4))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy',optimizer='adam', metrics=['accuracy']), #model.summary()
#Create an early EPOCH stoppage based on the validation loss based off TWO epochs
early_stop=EarlyStopping(monitor='val_loss', patience=2)
#TRAINING MODEL - use two to the power
batch_size=32
#TWO generators
train_image_gen = image_gen.flow_from_directory(train_path, target_size=image_shape[:2], color_mode='rgb', batch_size = batch_size, class_mode='categorical', shuffle=True)
test_image_gen = image_gen.flow_from_directory(test_path, target_size=image_shape[:2], color_mode='rgb', batch_size = batch_size, class_mode='categorical', shuffle=False)
results = model.fit_generator(train_image_gen, epochs=1, validation_data=test_image_gen, callbacks=[early_stop])
***
**def showarray(a, fmt='jpeg'):
f = BytesIO()
PIL.Image.fromarray(a).save(f, fmt)
IPython.display.display(IPython.display.Image(data=f.getvalue()))
def get_frame(cam):
# Capture frame-by-frame
ret, frame = cam.read()
#flip image for natural viewing
frame = cv2.flip(frame, 1)
return frame
cam = cv2.VideoCapture(0)
def make_1080p():
cam.set(3, 224)
cam.set(4, 224)
def change_res(width, height):
cam.set(3, width)
cam.set(4, height)
change_res(224, 224)
try:
while(True):
t1 = time.time()
frame = get_frame(cam)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
showarray(frame)
t2 = time.time()
print("%f FPS" % (1/(t2-t1)))
# Display the frame until new frame is available
clear_output(wait=True)
Weather_Prediction_Cell = (frame)
#Weather_Prediction_Cell /= 255
model.predict_generator(frame)
#print(Weather_Prediction_Cell)
print(pred)**
except KeyboardInterrupt:
cam.release()
print("Stream stopped")
***
Keras model has a method called "predict". It takes a single np.array or a list of np.arrays as input (which should have the exact same shape as your neural net. input, including the batch part: (batch_count, width, height, channels) for example). You input this to model.predict, then it returns you back the result as an np.array again, with the shape your neural network output layer has. I'm not used to webcam applications with opencv, but if you get the frame data in np.array somehow, you can feed it to your neural net. as well. Just be sure about its shape, and reshape it if needed.

Transfer learning InceptionV3 show poor validation accuracy with training

## MODEL IMPORTING ##
import tensorflow
import pandas as pd
import numpy as np
import os
import keras
import random
import cv2
import math
import seaborn as sns
from sklearn.metrics import confusion_matrix
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Dense,GlobalAveragePooling2D,Convolution2D,BatchNormalization
from tensorflow.keras.layers import Flatten,MaxPooling2D,Dropout
from tensorflow.keras.applications import InceptionV3
from tensorflow.keras.applications.densenet import preprocess_input
from tensorflow.keras.preprocessing import image
from tensorflow.keras.preprocessing.image import ImageDataGenerator,img_to_array
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau
import warnings
warnings.filterwarnings("ignore")
WIDTH = 299
HEIGHT = 299
CLASSES = 4
base_model = InceptionV3(weights='imagenet', include_top=False)
for layer in base_model.layers:
layer.trainable = False
x = base_model.output
x = GlobalAveragePooling2D(name='avg_pool')(x)
x = Dropout(0.4)(x)
predictions = Dense(CLASSES, activation='softmax')(x)
model = Model(inputs=base_model.input, outputs=predictions)
model.summary()
model.compile(optimizer='adam', ##also tried other optimiser --> similar poor accuracy found
loss='categorical_crossentropy',
metrics=['accuracy'])
## IMAGE DATA GENERATOR ##
from keras.applications.inception_v3 import preprocess_input
train_datagen = ImageDataGenerator(
preprocessing_function=preprocess_input,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest',
validation_split=0.2)
train_generator = train_datagen.flow_from_directory(
TRAIN_DIR,
target_size=(HEIGHT, WIDTH),
batch_size=BATCH_SIZE,
class_mode='categorical',
subset="training")
validation_generator = train_datagen.flow_from_directory(
TRAIN_DIR,
target_size=(HEIGHT, WIDTH),
batch_size=BATCH_SIZE,
class_mode='categorical',
subset="validation")
test_datagen = ImageDataGenerator(rescale=1./255)
generator_test = test_datagen.flow_from_directory(directory=TEST_DIR,
target_size=(HEIGHT, WIDTH),
batch_size=BATCH_SIZE,
class_mode='categorical',
shuffle=False)
## MODEL training ##
EPOCHS = 20
STEPS_PER_EPOCH = 320 #train_generator.n//train_generator.batch_size
VALIDATION_STEPS = 64 #validation_generator.n//validation_generator.batch_size
history = model.fit_generator(
train_generator,
epochs=EPOCHS,
steps_per_epoch=STEPS_PER_EPOCH,
validation_data=validation_generator,
validation_steps=VALIDATION_STEPS)
Result found:
VALIDATION ACCURACY around 0.55-0.67 fluctuating..
TRAINING ACCURACY 0.99
Questions:
What/Where is the problem in transfer learning process?
are train, validate and test data generator function parameter are chosen correctly?
Well I think you would be better off to train the entire model. So remove the code that makes the base model layers not trainable. If you look at the documentation for Inceptionv3 located here you can set pooling='max' which puts a GlobalMaxPooling2d layer as the output layer so if you do that you do not need to add your own layer as you did. Now I noticed you imported the callbacks ModelCheckpoint and ReduceLROnPlateau but you did not use them in model.fit. Using an adjustable learning rate will be beneficial to achieving a lower validation loss. ModelCheckpoint is useful to save the best model for use in predictions. See code below for implementations. save_loc is the directory where you want to store the results from ModelCheckpoint. NOTE in ModelCheckpoint I set save_weights_only=True. Reason is this is far faster than saving the entire model on each epoch for which the validation loss decreases.
checkpoint=tf.keras.callbacks.ModelCheckpoint(filepath=save_loc, monitor='val_loss', verbose=1, save_best_only=True,
save_weights_only=True, mode='auto', save_freq='epoch', options=None)
lr_adjust=tf.keras.callbacks.ReduceLROnPlateau( monitor="val_loss", factor=0.5, patience=1, verbose=1, mode="auto",
min_delta=0.00001, cooldown=0, min_lr=0)
callbacks=[checkpoint, lr_adjust]
history = model.fit_generator( train_generator, epochs=EPOCHS,
steps_per_epoch=STEPS_PER_EPOCH,validation_data=validation_generator,
validation_steps=VALIDATION_STEPS, callbacks=callbacks)
model.load_weights(save_loc) # load the saved weights
# after this use the model to evaluate or predict on the test set.
# if you are satisfied with the results you can then save the entire model with
model.save(save_loc)
Be careful on the test set generator. Ensure you do the same preprocessing on the test data as you did on the training data. I noticed you only rescaled the pixels. Not sure what the preprocess function does but I would use that.
I would also remove the dropout layer initially. Monitor the training loss and validation loss on each epoch and plot the results. If the training loss continues to decrease and the validation loss trends toward increasing then you are over fitting if so then restore the dropout layer if needed. If you do evaluation or prediction on the test set, you only want to go through the test set once. So select the test batch size to that no. of test samples/test batch size is an integer and use that integer as the number of test steps. Here is a
handy function that will determine that for you where length is the number of test samples and b_max is the maximum batch size you will allow based on your memory capacity.
def get_bs(length,b_max):
batch_size=sorted([int(length/n) for n in range(1,length+1) if length % n ==0 and length/n<=b_max],reverse=True)[0]
return batch_size,int(length/batch_size)
# example of use
batch_size, step=get_bs(10000,70)
#result is batch_size= 50 steps=200

How does Tensorflow's DirectoryIterator work?

I am used to using something like model.fix(train_data,train_labels, epochs=10) where i use glob to read a folder full of images into RAM. I wanted to read direct from the HDD as the training happens. I found:
https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/image/DirectoryIterator
Only I do not know how it works exactly. I have scoured the internet for more help then the documentation linked but I haven't found any. I have the labels and directories in the DirectoryIterator. I just don't know how to feed the DirectoryIterator into my model?
The code shows what I have done so far. I also tried to use a tensorflow sess and feed the DirectoryIterator as a feed_dict. Code is messy, just been trying this and that. In the code I try to use fit_generator to fit the DirectoryIterator.
import numpy as np
import pandas as pd
import tensorflow as tf
import tensorflow.keras as keras
import cv2 as ocv
import glob
import matplotlib.pyplot as plt
from tensorflow import image
import glob
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# Plot inline
%matplotlib inline
# Load an color image in 1-colour 0-grayscale -1-bw
img = ocv.imread('C:/Users/ew/Documents/Python Scripts/Noodles/my.png',1)
RGB_im = ocv.cvtColor(img, ocv.COLOR_BGR2RGB)
img.shape
plt.imshow(RGB_im)
cv_img = []
for img in glob.glob("C:\\Users\\EW\\pictures\\Noodles\\Banana\\*.jpg"):
cv_img.append(img)
#n= ocv.imread(img)
#cv_img.append(n)
print(cv_img[1])
image_data_generator = keras.preprocessing.image.ImageDataGenerator(featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False, zca_epsilon=1e-06,
rotation_range=0,
width_shift_range=0.0,
height_shift_range=0.0,
brightness_range=None,
shear_range=0.0,
zoom_range=0.0,
channel_shift_range=0.0,
fill_mode='nearest',
cval=0.0,
horizontal_flip=False,
vertical_flip=False,
rescale=None,
preprocessing_function=None,
data_format='channels_last',
validation_split=0.3,
# interpolation_order=1,
dtype='float32')
noodle_data = directory = "C:\\Users\\EW\\pictures\\Noodles\\"
image_set = keras.preprocessing.image.DirectoryIterator(directory,
image_data_generator,
target_size=(256, 256),
color_mode='rgb',
classes=None,
class_mode='categorical',
batch_size=32,
shuffle=True,
seed=None,
data_format=None,
save_to_dir=None,
save_prefix='',
save_format='png',
follow_links=False,
subset=None,
interpolation='nearest',
dtype=None)
model = Sequential()
#add model layers
model.add(Dense(10, activation='relu', input_shape=(256,256)))
model.add(Dense(10, activation='relu'))
model.add(Dense(1))
model.fit_generator(noodle_data , steps_per_epoch=16, validation_data=val_it, validation_steps=8)
---> 12 model.fit_generator(prawn_data , steps_per_epoch=16, validation_data=prawn_data, validation_steps=8)
AttributeError: 'str' object has no attribute 'shape'
I think you should write like this:
image_data_generator = keras.preprocessing.image.ImageDataGenerator(featurewise_center=False,
...,
dtype='float32')
directory = "C:\\Users\\EW\\pictures\\Noodles\\"
image_set = image_data_generator.flow_from_directory(directory,
target_size=(256, 256),
color_mode='rgb',
...,
dtype=None)
So, you call an intance of ImageDataGenerator(), which is named image_data_generator and use its method flow_from_directory() to read from directory. And you shouldn't pass image_data_generator as your parameter in flow_from_directory.