QML-RG@ICFO Homework 4: APS captcha cracker

Alejandro Pozas-Kerstjens


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))


Epoch 1/10
10/10 [==============================] - 19s - loss: 0.7928 - acc: 0.5927 - val_loss: 0.5994 - val_acc: 0.6829
Epoch 2/10
10/10 [==============================] - 18s - loss: 0.5664 - acc: 0.6927 - val_loss: 0.4642 - val_acc: 0.7317
Epoch 3/10
10/10 [==============================] - 18s - loss: 0.3954 - acc: 0.8293 - val_loss: 0.2944 - val_acc: 0.8780
Epoch 4/10
10/10 [==============================] - 18s - loss: 0.3143 - acc: 0.8634 - val_loss: 0.2411 - val_acc: 0.8780
Epoch 5/10
10/10 [==============================] - 18s - loss: 0.2979 - acc: 0.8659 - val_loss: 0.2415 - val_acc: 0.8780
Epoch 6/10
10/10 [==============================] - 18s - loss: 0.2097 - acc: 0.9024 - val_loss: 0.1935 - val_acc: 0.9024
Epoch 7/10
10/10 [==============================] - 18s - loss: 0.3542 - acc: 0.8805 - val_loss: 0.4002 - val_acc: 0.7805
Epoch 8/10
10/10 [==============================] - 18s - loss: 0.1779 - acc: 0.9317 - val_loss: 0.1206 - val_acc: 0.9268
Epoch 9/10
10/10 [==============================] - 18s - loss: 0.1101 - acc: 0.9512 - val_loss: 0.1534 - val_acc: 0.9268
Epoch 10/10
10/10 [==============================] - 18s - loss: 0.0751 - acc: 0.9707 - val_loss: 0.0410 - val_acc: 0.9756
41/41 [==============================] - 0s     
Baseline Error: 2.44%

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]))


einstein.png is either Einstein or Curie
helmet.png is NOT Einstein nor Curie
moon.png is NOT Einstein nor Curie
pumpkin.png is NOT Einstein nor Curie
shack.png is NOT Einstein nor Curie
truck.png is NOT Einstein nor Curie

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)))


Image 1.png is either Einstein or Curie
Image 10.png is either Einstein or Curie
Image 11.png is either Einstein or Curie
Image 12.png is either Einstein or Curie
Image 13.png is NOT Einstein nor Curie
Image 14.png is NOT Einstein nor Curie
Image 15.png is NOT Einstein nor Curie
Image 16.png is either Einstein or Curie
Image 17.png is NOT Einstein nor Curie
Image 18.png is NOT Einstein nor Curie
Image 19.png is NOT Einstein nor Curie
Image 2.png is NOT Einstein nor Curie
Image 20.png is NOT Einstein nor Curie
Image 21.png is NOT Einstein nor Curie
Image 22.png is NOT Einstein nor Curie
Image 23.png is NOT Einstein nor Curie
Image 24.png is NOT Einstein nor Curie
Image 25.png is NOT Einstein nor Curie
Image 26.png is NOT Einstein nor Curie
Image 27.png is NOT Einstein nor Curie
Image 28.png is NOT Einstein nor Curie
Image 29.png is NOT Einstein nor Curie
Image 3.png is NOT Einstein nor Curie
Image 30.png is NOT Einstein nor Curie
Image 31.png is NOT Einstein nor Curie
Image 32.png is either Einstein or Curie
Image 33.png is NOT Einstein nor Curie
Image 34.png is NOT Einstein nor Curie
Image 35.png is NOT Einstein nor Curie
Image 36.png is either Einstein or Curie
Image 37.png is NOT Einstein nor Curie
Image 38.png is NOT Einstein nor Curie
Image 39.png is either Einstein or Curie
Image 4.png is NOT Einstein nor Curie
Image 40.png is NOT Einstein nor Curie
Image 41.png is NOT Einstein nor Curie
Image 42.png is NOT Einstein nor Curie
Image 43.png is NOT Einstein nor Curie
Image 44.png is NOT Einstein nor Curie
Image 45.png is NOT Einstein nor Curie
Image 46.png is NOT Einstein nor Curie
Image 47.png is NOT Einstein nor Curie
Image 48.png is NOT Einstein nor Curie
Image 49.png is NOT Einstein nor Curie
Image 5.png is NOT Einstein nor Curie
Image 50.png is either Einstein or Curie
Image 51.png is NOT Einstein nor Curie
Image 52.png is NOT Einstein nor Curie
Image 53.png is NOT Einstein nor Curie
Image 54.png is NOT Einstein nor Curie
Image 55.png is NOT Einstein nor Curie
Image 56.png is NOT Einstein nor Curie
Image 57.png is NOT Einstein nor Curie
Image 58.png is NOT Einstein nor Curie
Image 59.png is NOT Einstein nor Curie
Image 6.png is NOT Einstein nor Curie
Image 60.png is NOT Einstein nor Curie
Image 61.png is NOT Einstein nor Curie
Image 62.png is NOT Einstein nor Curie
Image 63.png is NOT Einstein nor Curie
Image 64.png is NOT Einstein nor Curie
Image 65.png is NOT Einstein nor Curie
Image 66.png is NOT Einstein nor Curie
Image 67.png is NOT Einstein nor Curie
Image 68.png is NOT Einstein nor Curie
Image 69.png is NOT Einstein nor Curie
Image 7.png is NOT Einstein nor Curie
Image 70.png is NOT Einstein nor Curie
Image 71.png is NOT Einstein nor Curie
Image 72.png is NOT Einstein nor Curie
Image 8.png is NOT Einstein nor Curie
Image 9.png is NOT Einstein nor Curie
Successes in real_world set: 70 out of 72

In [ ]: