import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns
from tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array
from tensorflow.keras import Sequential, applications
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.applications.vgg16 import decode_predictions, preprocess_input
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier
data_gen = ImageDataGenerator(rescale=1.0/255)
imgdir = 'a5_images'
img_size = 64
batch_size = 32
train_generator = data_gen.flow_from_directory(
imgdir + '/train',
target_size=(img_size, img_size),
batch_size=batch_size,
class_mode='binary',
classes=['other', 'car'],
seed=12345,
shuffle=True)
Xbatch, Ybatch = train_generator.next()
Xbatch.shape
Ybatch[0]
plt.imshow(Xbatch[0])
val_generator = data_gen.flow_from_directory(
imgdir + '/validation',
target_size=(img_size, img_size),
batch_size=batch_size,
class_mode='binary',
classes=['other', 'car'],
seed=12345,
shuffle=True)
def make_convnet():
input_shape = (img_size, img_size, 3)
model = Sequential([
Conv2D(32, (2, 2), (2, 2), activation='relu', input_shape=input_shape),# 2D general for images, 32 filters. relu is commonly used. Has no platteu on the positive side.
MaxPooling2D(pool_size=(2, 2), strides=(2, 2)), # Pooling also 2D for images and maxpooling the most commonly used pooling generally.
Conv2D(64, (2, 2), (2, 2), activation='relu'),
MaxPooling2D(pool_size=(2, 2), strides=(2, 2)),
Flatten(), # Keras technicality: from multidim to line up dimensions
Dense(100, activation='relu'), # Ordinary NN layer.
Dense(1, activation='sigmoid') # Ordinary NN layer, Output-layer. Since binary: Num classes = 1 and activation = sigmoid/logistic
])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics='acc') # Keep the Adam and choose LogLoss.
return model
n_epochs = 100
patience = 10
model = make_convnet()
callback = EarlyStopping(monitor='val_loss', patience=patience, restore_best_weights=True, verbose=1) # We specify that we look at the validation loss for the early stopping. We also choose to use the best weights, instead of the last weights as the predefined feature.
history = model.fit(train_generator, epochs=n_epochs, validation_data=val_generator, callbacks=[callback]) # Save the values from each run as "history"
train_loss = history.history['loss']
val_loss = history.history['val_loss']
train_acc = history.history['acc']
val_acc = history.history['val_acc']
sns.set_theme()
sns.lineplot(data=train_loss, color='b', label='train')
sns.lineplot(data=val_loss, color='r', label='val')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.show()
sns.lineplot(data=train_acc, color='b', label='train')
sns.lineplot(data=val_acc, color='r', label='val')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.show()
val_loss, val_acc = model.evaluate(val_generator)
print(f'Best validation accuracy: {val_acc * 100:.2f}%')
data_gen2 = ImageDataGenerator(rescale=1.0/255, rotation_range=15, shear_range=0.2, zoom_range=0.2, horizontal_flip=True, width_shift_range=0.2,
height_shift_range=0.2)
train_generator2 = data_gen2.flow_from_directory(
imgdir + '/train',
target_size=(img_size, img_size),
batch_size=batch_size,
class_mode='binary',
classes=['other', 'car'],
seed=12345,
shuffle=True)
history = model.fit(train_generator2, epochs=n_epochs, validation_data=val_generator, callbacks=[callback])
train_loss = history.history['loss']
val_loss = history.history['val_loss']
train_acc = history.history['acc']
val_acc = history.history['val_acc']
sns.set_theme()
sns.lineplot(data=train_loss, color='b', label='train')
sns.lineplot(data=val_loss, color='r', label='val')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.show()
sns.lineplot(data=train_acc, color='b', label='train')
sns.lineplot(data=val_acc, color='r', label='val')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.show()
val_loss, val_acc = model.evaluate(val_generator)
print(f'Best validation accuracy: {val_acc * 100:.2f}%')
vggmodel = applications.VGG16(weights='imagenet', include_top=True)
img = load_img('a5_images/train/other/0000.jpg', target_size=(224,224))
sns.reset_orig()
plt.imshow(img)
img = img_to_array(img)
img = preprocess_input(img)
img = img.reshape(1, 224, 224, 3)
preds = vggmodel.predict(img)
preds = decode_predictions(preds)
preds
feature_extractor = applications.VGG16(include_top=False, weights='imagenet', input_shape=(img_size, img_size, 3))
vgg_data_gen = ImageDataGenerator(preprocessing_function=preprocess_input)
def create_vgg16_features(load_path, save_path):
data_generator = vgg_data_gen.flow_from_directory(
load_path,
target_size=(img_size, img_size),
batch_size=batch_size,
class_mode='binary',
classes=['other', 'car'],
seed=12345,
shuffle=False)
cnn_features = feature_extractor.predict(data_generator)
with open(save_path, 'wb') as f:
np.save(f, cnn_features)
create_vgg16_features('a5_images/train', 'train_features')
create_vgg16_features('a5_images/validation', 'val_features')
def get_labels(n):
return np.array([0]*(n//2) + [1]*(n//2))
def train_on_cnnfeatures(train_dirr, val_dirr):
with open(train_dirr, 'rb') as f: #These two opens get the saved data from the file directories we inputed in the function.
train_data = np.load(f)
with open(val_dirr, 'rb') as f:
val_data = np.load(f)
x_train = np.reshape(train_data, (train_data.shape[0], -1)) #Reshape the data so that we can use it with our RFC
x_val = np.reshape(val_data, (val_data.shape[0], -1))
y_train, y_val = get_labels(1600), get_labels(576)
clf = RandomForestClassifier(n_estimators=200) #Create a random forest classifier that have 200 trees.
clf.fit(x_train, y_train) #Train the model on the data
y_pred = clf.predict(x_val) #Predict our validation data.
acc = accuracy_score(y_val, y_pred)
print(f'Validation accuracy: {acc * 100:.2f}%')
return clf #Returns the trained model.
train_directory = 'train_features'
val_directory = 'val_features'
trained_clf = train_on_cnnfeatures(train_directory, val_directory)
first_layer_weights = vggmodel.get_weights()[0]
first_layer_weights.shape
def kernel_image(weights, i, positive):
# extract the convolutional kernel at position i
k = weights[:,:,:,i].copy()
if not positive:
k = -k
# clip the values: if we're looking for positive
# values, just keep the positive part; vice versa
# for the negative values.
k *= k > 0
# rescale the colors, to make the images less dark
m = k.max()
if m > 1e-3:
k /= m
return k
n_filters = 10
print(f'Visualizing first {n_filters} filters - positive (left) and negative (right) patterns...')
for filter in range(n_filters):
print(f'Filter {filter+1}:')
plt.subplot(121)
k = kernel_image(first_layer_weights, filter, True)
plt.imshow(k)
plt.subplot(122)
k = kernel_image(first_layer_weights, filter, False)
plt.imshow(k)
plt.show()