In [48]:
# Loading libraries, preprocessing images and definig parameters
import os
from keras.models import Sequential
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.layers.core import Flatten, Dense, Dropout
from keras.preprocessing.image import ImageDataGenerator
from keras.utils import np_utils
from image_loader import load_images
from numpy import loadtxt, floor, argmax, asarray
from keras import backend as K
trainedweightsPath = 'APS_Cracker_weights.h5' # Modify if we happen to have already trained the network
## Preprocess images: training set
train_images, train_labels = load_images('images/train/')
ninputs = len(train_images)
width = train_images[0].shape[0]
height = train_images[0].shape[1]
# Some images (baseball, boat, booth and curie) have different shapes than the rest. This is an attempt to fix this
for i in range(len(train_images)):
if train_images[i].shape != (width, height):
train_images[i] = train_images[i][:,:,0]
for i in range(len(train_images)): # Add a third dimension (number of channels) for datagen not to complain
train_images[i] = train_images[i].reshape((1,) + train_images[i].shape)
for _ in range(8):
train_images.append(train_images[6]) # Further post-processing: evening-out appearances
train_images.append(train_images[7]) # of both types of classes
train_labels.append(1)
train_labels.append(1)
train_images = asarray(train_images)
train_labels = asarray(train_labels)
train_images = train_images / 255
train_labels = np_utils.to_categorical(train_labels) # Each number to a vector. Useful when differentiating Einstein from Curie
## Preprocess images: test set
test_images, _ = load_images('images/test/')
for i in range(len(test_images)):
test_images[i] = test_images[i].reshape((1,) + test_images[i].shape)
test_images = asarray(test_images)
test_names = [] # Fancy stuff to feed the output
for file in os.listdir('images/test/'):
if file.endswith(".png"):
test_names.append(file)
## Preprocess images: real_world set
rw_images, _ = load_images('images/real_world/')
for i in range(len(rw_images)): # We just eliminate additional channels and additional rows symmetrically
if rw_images[i].shape != (rw_images[i].shape[0], rw_images[i].shape[1]):
rw_images[i] = rw_images[i][:,:,0]
if rw_images[i].shape != (100, 100):
offset = int(floor((rw_images[i].shape[1]-100-rw_images[i].shape[1] % 2) / 2))
rw_images[i] = rw_images[i][:,offset:rw_images[i].shape[1]-offset-(rw_images[i].shape[1] % 2)]
for i in range(len(rw_images)):
rw_images[i] = rw_images[i].reshape((1,) + rw_images[i].shape)
rw_images = asarray(rw_images)
rw_names = [] # Fancy stuff to feed the output
for file in os.listdir('images/real_world/'):
if file.endswith(".png"):
rw_names.append(file)
In [49]:
### Build the network
model = Sequential() # One layer after the other
# These first two layers will be used to process the images (shapes/edges detection...)
# The parameters of the processing filters are those to be learned
model.add(Conv2D(32, (3, 3), input_shape=(1, height, width), activation='relu',
padding='valid', kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(64, (2, 2), activation='relu', kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(128, (2, 2), activation='relu', kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
#model.add(Dropout(0.1))
# Now we flatten out the results and process linear stuff
model.add(Flatten())
# And do some more processing of the data
model.add(Dense(500, activation='relu', kernel_initializer='uniform'))
model.add(Dense(200, activation='relu', kernel_initializer='uniform'))
# Finally we define the last layer of the network
model.add(Dense(2, activation='softmax', kernel_initializer='uniform'))
# If we had already trained the network, then load the weights, otherwise train
if trainedweightsPath is not None:
model.load_weights(trainedweightsPath)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
In [42]:
### Data augmentation
# The training set is quite small. We now perform data augmentation to obtain new images from the original ones
datagen = ImageDataGenerator(rotation_range=10, shear_range=0.3, zoom_range=0.2,
width_shift_range=0.15, height_shift_range=0.15, fill_mode='constant', cval=1)
# Fit model with the augmented data, using as validation also augmented images
if trainedweightsPath is None:
model.fit_generator(datagen.flow(train_images, train_labels, batch_size=41), steps_per_epoch=10, epochs=10,
validation_data=datagen.flow(train_images, train_labels, batch_size=41), validation_steps=1)
model.save("APS_Cracker_weights.h5")
# Final evaluation of the model
scores = model.evaluate(train_images, train_labels, verbose=1)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))
In [50]:
### And make predictions!
# Test set
test_pred_labels = model.predict(test_images)
for i in range(len(test_images)):
if argmax(test_pred_labels[i]) == 0:
print('{} is NOT Einstein nor Curie'.format(test_names[i]))
elif argmax(test_pred_labels[i]) == 1:
print('{} is either Einstein or Curie'.format(test_names[i]))
In [51]:
# Real world set
rw_pred_labels = model.predict(rw_images)
for i in range(len(rw_images)):
if argmax(rw_pred_labels[i]) == 0:
print('Image {} is NOT Einstein nor Curie'.format(rw_names[i]))
elif argmax(rw_pred_labels[i]) == 1:
print('Image {} is either Einstein or Curie'.format(rw_names[i]))
# And as a final, see the accuracy in the real_world set
rw_real_labels = loadtxt('images/real_world/labels.txt')
count = 0
for i in range(len(rw_real_labels)):
ind = rw_names.index(str(int(rw_real_labels[i][0])) + '.png') # rw_real_labels and rw_pred_labels are ordered differently, so this is for comparing the same elements
if rw_real_labels[i][1] == argmax(rw_pred_labels[ind]) | (rw_real_labels[i][1] == 2 & argmax(rw_pred_labels[ind]) == 1):
count += 1
print('Successes in real_world set: {} out of {}'.format(count,len(rw_real_labels)))
In [ ]: