visit
Binary classification - the target class can be one out of two values.
Multiclass classification - the input value can be classified into one of the many classes, which is higher than two.
Multilabel classification - the input value can be classified into one or more than one class out of many classes.
The business problem is to classify images to their anomaly type. In the provided
As a prerequisite we have to load all the dependencies:
import os
import tensorflow as tf
import shutil
from sklearn.model_selection import train_test_splitfrom tensorflow.keras.models import Model, Sequential
from keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras.metrics import CategoricalAccuracy
from tqdm.notebook import tqdm
from sklearn.model_selection import KFold
import cv2
import matplotlib.pyplot as plt
import glob
import numpy as np
import pandas as pd
from hyperopt import tpe, hp, fmin, STATUS_OK, Trials
from hyperopt.pyll.base import scope
from hyperopt import space_eval
IMG_SIZE = (224, 224)
K_FOLDS = 5
PATH_TO_DATA = '/content/dataset/'
all_classes = os.listdir(PATH_TO_DATA)
dataset = []
for class_name in all_classes:
class_images = glob.glob(f"{PATH_TO_DATA}/{class_name}/*")
class_images = [[img_path, class_name] for img_path in class_images]
dataset.extend(class_images)
dataset_df = pd.DataFrame(dataset, index=range(len(dataset)), columns=['img_path', 'class'])
train_df, test_df = train_test_split(
dataset_df,
test_size=0.2,
random_state=42
)
Random search — randomly samples model hyperparameters and selects the best sample.
Grid search — samples all combinations of hyperparameters predefined in the table.
Bayesian optimization — optimizes sampling strategy by fitting the parameters of the distributions from the search space.
space = {
"batch_size": hp.choice('batch_size', list(range(8, 128))),
"act_fn": hp.choice('act_fn', [
'sigmoid',
'tanh',
'relu',
'relu6'
]),
"model": hp.choice('model', [
'efficientnet',
'inception_v3',
'resnet50'
]),
"opt": hp.choice('opt', [
'adadelta',
'adagrad',
'adam',
'ftlr',
'nadam',
'rmsprop',
'sgd'
]),
'num_epochs': hp.randint('num_epochs', 5),
'lr': hp.uniform('lr', 1e-5, 1e-2),
"latent_dim": hp.choice('latent_dim', list(range(20, 100)))
}
def build_model(config, backbone_model):
x = backbone_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(config['latent_dim'], activation=config['act_fn'])(x)
predictions = Dense(len(all_classes), activation='softmax')(x)
model = Model(inputs=backbone_model.input, outputs=predictions)
if config['opt'] == 'adadelta':
opt = tf.keras.optimizers.Adadelta(learning_rate=config['lr'])
if config['opt'] == 'adagrad':
opt = tf.keras.optimizers.Adagrad(learning_rate=config['lr'])
if config['opt'] == 'adam':
opt = tf.keras.optimizers.Adam(learning_rate=config['lr'])
if config['opt'] == 'ftlr':
opt = tf.keras.optimizers.Ftrl(learning_rate=config['lr'])
if config['opt'] == 'nadam':
opt = tf.keras.optimizers.Nadam(learning_rate=config['lr'])
if config['opt'] == 'rmsprop':
opt = tf.keras.optimizers.RMSprop(learning_rate=config['lr'])
if config['opt'] == 'sgd':
opt = tf.keras.optimizers.SGD(learning_rate=config['lr'])
model.compile(
optimizer=opt,
loss='categorical_crossentropy',
metrics=[
tf.keras.metrics.CategoricalAccuracy(),
tf.keras.metrics.Precision(),
tf.keras.metrics.Recall(),
tf.keras.metrics.AUC()
]
)
return model
def cross_validation(
model,
generator,
batch_size,
num_folds,
num_epochs
):
cv_scores = []
kf = KFold(n_splits=num_folds, random_state=None, shuffle=True)
X = np.array(train_df["img_path"])
i=0
for train_index, test_index in kf.split(X):
train_data = X[train_index]
test_data = X[test_index]
NUM_STEPS = int(len(train_data) / batch_size) + 1
train_data = train_df.loc[train_df["img_path"].isin(list(train_data))]
valid_data = train_df.loc[train_df["img_path"].isin(list(test_data))]
train_gen = generator.flow_from_dataframe(
dataframe=train_data,
directory=PATH_TO_DATA,
x_col = 'img_path',
y_col = 'class',
class_mode = 'categorical',
classes = all_classes,
target_size = IMG_SIZE,
color_mode = 'rgb',
batch_size = batch_size
)
valid_gen = generator.flow_from_dataframe(
dataframe=valid_data,
directory=PATH_TO_DATA,
x_col = 'img_path',
y_col = 'class',
class_mode = 'categorical',
classes = all_classes,
target_size = IMG_SIZE,
color_mode = 'rgb',
batch_size = batch_size
)
hist = model.fit(
train_gen,
steps_per_epoch= NUM_STEPS,
epochs = num_epochs,
validation_data=valid_gen
)
print(hist.history)
if i == 0:
score_name = 'val_categorical_accuracy'
else:
score_name = f'val_categorical_accuracy_{i}'
cv_scores.append(hist.history[score_name][-1])
i+=1
return cv_scores
trials = Trials()
best = fmin(
fn=objective,
space = space,
algo=tpe.suggest,
max_evals=100,
trials=trials
)
best_config = space_eval(space, best)
if best_config['model'] == 'resnet50':
from tensorflow.keras.applications.resnet50 import ResNet50 as BackboneModel
from tensorflow.keras.applications.resnet50 import preprocess_input as preprocessing_function
if best_config['model'] == 'efficientnet':
from tensorflow.keras.applications.efficientnet import EfficientNetB2 as BackboneModel
from tensorflow.keras.applications.efficientnet import preprocess_input as preprocessing_function
if best_config['model'] == 'inception_v3':
from tensorflow.keras.applications.inception_v3 import InceptionV3 as BackboneModel
from tensorflow.keras.applications.inception_v3 import preprocess_input as preprocessing_function
generator = ImageDataGenerator(
preprocessing_function=preprocessing_function
)
backbone = BackboneModel(weights='imagenet', include_top=False)
model = build_model(best_config, backbone)
train_gen = generator.flow_from_dataframe(
dataframe=train_df,
directory=PATH_TO_DATA,
x_col = 'img_path',
y_col = 'class',
class_mode = 'categorical',
classes = all_classes,
target_size = IMG_SIZE,
color_mode = 'rgb',
batch_size = best_config['batch_size']
)
test_gen = generator.flow_from_dataframe(
dataframe=test_df,
directory=PATH_TO_DATA,
x_col = 'img_path',
y_col = 'class',
class_mode = 'categorical',
classes = train_gen.class_indices.keys(),
target_size = IMG_SIZE,
color_mode = 'rgb',
batch_size = best_config['batch_size']
)
NUM_STEPS = int(len(train_df) / best_config['batch_size']) + 1
hist = model.fit(
train_gen,
steps_per_epoch= NUM_STEPS,
epochs = best_config['num_epochs'],
validation_data=test_gen
)
from sklearn.metrics import classification_report, confusion_matrix import seaborn as sns
from PIL import Image
from tqdm.notebook import tqdm tqdm.pandas()
def draw_confusion_matrix(true, preds):
conf_matx = confusion_matrix(true, preds)
sns.heatmap(
conf_matx,
annot=True,
annot_kws={"size": 12},
fmt='g',
cbar=False,
cmap="viridis"
)
plt.show()
def convert_to_labels(preds_array):
preds_df = pd.DataFrame(preds_array)
predicted_labels = preds_df.idxmax(axis=1)
return predicted_labels
def preprocess_image(img):
img = img.resize((224, 224))
img = np.array(img)
img = np.expand_dims(img, axis=0)
img = preprocessing_function(img)
return img
def make_prediction(model, img_path):
image = Image.open(img_path)
img_preprocessed = preprocess_image(image)
prediction = np.argmax(model.predict(img_preprocessed), axis=-1)[0]
return prediction
results = model.evaluate(test_gen, batch_size=best_config['batch_size'])
print('Test loss:', results[0])
print('Test categorical_accuracy:', results[1])
print('Test precision:', results[2])
print('Test recall:', results[3])
print('Test auc:', results[4])
test_df['class_id'] = test_df['class'].progress_apply(
lambda x: train_gen.class_indices[x]
)
test_df['prediction'] = test_df['img_path'].progress_apply(
lambda x: make_prediction(model, x)
)
test_preds_labels = test_df['prediction']
test_images_y = test_df['class_id']
print(classification_report(
test_images_y,
test_preds_labels,
target_names=train_gen.class_indices.keys())
)
draw_confusion_matrix(test_images_y, test_preds_labels)
A Classification report and confusion matrix will help us to analyze model weaknesses and understand where they can be improved.
mapping = dict(zip(
train_gen.class_indices.values(),
train_gen.class_indices.keys()
))
PATH_TO_PHOTO =
'/content/dataset/good_quality_photos/000000233771.jpg'
image = Image.open(PATH_TO_PHOTO)
img_preprocessed = preprocess_image(image)
prediction = np.argmax(model.predict(img_preprocessed), axis=-1)[0]