Very basic CNN for recognizing hand-drawn images of various objects, to which Gaussian noise was added.
One class of objects contain only noise.
The dataset is no longer available but the CNN results can still be seen below
tl;dr ~60% accuracy (disappointing). Noise reduction & feature extraction would probably help
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
import numpy as np
import pickle
import keras
from keras.preprocessing.image import ImageDataGenerator
from keras.utils.np_utils import to_categorical
from keras import models, layers
import os
import pandas as pd
SEED = 123
np.random.seed(SEED)
DIR = "input" # Location of input data
X_train = np.load(os.path.join(DIR, "train_images.npy"), encoding='latin1')
train_labels = np.genfromtxt(os.path.join(DIR, "train_labels.csv"), names=True, delimiter=',', dtype=[('Id', 'i8'), ('Category', 'S15')])
X_test = np.load(os.path.join(DIR, "test_images.npy"), encoding = 'latin1')
X_train = np.array(tuple(x[1] for x in X_train))
X_test = np.array(tuple(x[1] for x in X_test))
# Numerical encoding
y = train_labels[:]['Category']
le = preprocessing.LabelEncoder()
le.fit(y)
y = le.transform(y)
# converts images to greyscale
X_train /= X_train.max()
X_test /= X_test.max()
X_train, X_valid, y_train, y_valid = train_test_split(X_train,
y,
random_state=SEED,
stratify=y/len(y)
)
# Reshape to 4D tensor (last dimension is nb. of channels)
X_train = X_train.reshape(X_train.shape[0], 100, 100, 1)
X_valid = X_valid.reshape(X_valid.shape[0], 100, 100, 1)
X_test = X_test.reshape(X_test.shape[0], 100, 100, 1)
# One-hot encode labels
n_classes = len(np.unique(y))
y_train = to_categorical(y_train, n_classes)
y_valid = to_categorical(y_valid, n_classes)
# Training batch size
BATCH_SIZE = 32
# Load datagen if already saved
path_to_datagen = os.path.join(DIR, "datagen")
try:
with open(path_to_datagen, "rb") as f:
datagen = pickle.load(f)
except FileNotFoundError:
# Create datagen
datagen = ImageDataGenerator(rotation_range=30, # rotate images by [0,30] deg.
horizontal_flip=True,
)
datagen.fit(X_train,
seed=SEED
)
# Save datagen to file
path_to_datagen = os.path.join(DIR, "datagen")
with open(path_to_datagen, "wb") as f:
pickle.dump(datagen, f)
# Create a transformed generator of X_train
X_train_transformed = datagen.flow(X_train,
y_train,
batch_size=BATCH_SIZE,
seed=SEED
)
# Load CNN if already saved
path_to_cnn = os.path.join(DIR, "cnn.h5")
if os.path.isfile(path_to_cnn):
cnn = models.load_model(path_to_cnn)
else:
# Create CNN
# Architecture: ((2D convolution)*2->MaxPool->Dropout regularizer)*2->Flatten->Dropout->Probability vector
cnn = models.Sequential()
cnn.add(layers.Conv2D(filters=32,
kernel_size=(3, 3),
activation="relu",
input_shape=(100, 100, 1),
padding="same",
))
cnn.add(layers.Conv2D(filters=64,
kernel_size=(3, 3),
activation="relu",
padding="same",
))
cnn.add(layers.MaxPooling2D(pool_size=(2, 2)))
cnn.add(layers.Dropout(0.25))
cnn.add(layers.Conv2D(filters=64,
kernel_size=(3, 3),
activation="relu",
padding="same",
))
cnn.add(layers.Conv2D(filters=64,
kernel_size=(3, 3),
activation="relu",
padding="same",
))
cnn.add(layers.MaxPooling2D(pool_size=(2, 2)))
cnn.add(layers.Dropout(0.25))
cnn.add(layers.Flatten())
cnn.add(layers.Dense(256, activation="relu"))
cnn.add(layers.Dropout(0.5))
cnn.add(layers.Dense(n_classes, activation="softmax"))
cnn.compile(optimizer=keras.optimizers.Adadelta(),
loss="categorical_crossentropy",
metrics=["accuracy"])
# Fit CNN
losses = cnn.fit_generator(X_train_transformed,
epochs=75,
steps_per_epoch=X_train.shape[0] // BATCH_SIZE,
verbose=2,
validation_data=(X_valid, y_valid),
)
Epoch 1/75 - 26s - loss: 3.4000 - acc: 0.0466 - val_loss: 3.3884 - val_acc: 0.0572 Epoch 2/75 - 22s - loss: 3.3901 - acc: 0.0523 - val_loss: 3.3792 - val_acc: 0.0572 Epoch 3/75 - 22s - loss: 3.3823 - acc: 0.0559 - val_loss: 3.3660 - val_acc: 0.0564 Epoch 4/75 - 22s - loss: 3.3435 - acc: 0.0665 - val_loss: 3.2844 - val_acc: 0.0656 Epoch 5/75 - 22s - loss: 3.2781 - acc: 0.0815 - val_loss: 3.3462 - val_acc: 0.0668 Epoch 6/75 - 22s - loss: 3.2233 - acc: 0.0904 - val_loss: 3.1801 - val_acc: 0.1020 Epoch 7/75 - 22s - loss: 3.1760 - acc: 0.0967 - val_loss: 3.1188 - val_acc: 0.1208 Epoch 8/75 - 22s - loss: 3.1446 - acc: 0.1009 - val_loss: 3.0972 - val_acc: 0.1076 Epoch 9/75 - 22s - loss: 3.1117 - acc: 0.1110 - val_loss: 3.1106 - val_acc: 0.1064 Epoch 10/75 - 22s - loss: 3.0848 - acc: 0.1131 - val_loss: 3.0844 - val_acc: 0.1044 Epoch 11/75 - 22s - loss: 3.0702 - acc: 0.1222 - val_loss: 3.0531 - val_acc: 0.1324 Epoch 12/75 - 22s - loss: 3.0029 - acc: 0.1491 - val_loss: 2.8860 - val_acc: 0.1812 Epoch 13/75 - 22s - loss: 2.8905 - acc: 0.1706 - val_loss: 2.9766 - val_acc: 0.1352 Epoch 14/75 - 22s - loss: 2.8030 - acc: 0.1952 - val_loss: 2.7868 - val_acc: 0.1740 Epoch 15/75 - 22s - loss: 2.7497 - acc: 0.2042 - val_loss: 2.6058 - val_acc: 0.2284 Epoch 16/75 - 22s - loss: 2.6533 - acc: 0.2350 - val_loss: 2.5227 - val_acc: 0.2496 Epoch 17/75 - 22s - loss: 2.5674 - acc: 0.2492 - val_loss: 2.4525 - val_acc: 0.2632 Epoch 18/75 - 22s - loss: 2.5015 - acc: 0.2664 - val_loss: 2.3620 - val_acc: 0.2940 Epoch 19/75 - 22s - loss: 2.4456 - acc: 0.2780 - val_loss: 2.3450 - val_acc: 0.2964 Epoch 20/75 - 22s - loss: 2.3751 - acc: 0.3027 - val_loss: 2.3370 - val_acc: 0.3136 Epoch 21/75 - 22s - loss: 2.3572 - acc: 0.3093 - val_loss: 2.4342 - val_acc: 0.2752 Epoch 22/75 - 22s - loss: 2.2875 - acc: 0.3326 - val_loss: 2.2770 - val_acc: 0.3328 Epoch 23/75 - 22s - loss: 2.2602 - acc: 0.3372 - val_loss: 2.1578 - val_acc: 0.3600 Epoch 24/75 - 22s - loss: 2.2152 - acc: 0.3453 - val_loss: 2.4199 - val_acc: 0.2872 Epoch 25/75 - 22s - loss: 2.1619 - acc: 0.3664 - val_loss: 2.2176 - val_acc: 0.3432 Epoch 26/75 - 22s - loss: 2.1415 - acc: 0.3729 - val_loss: 2.1399 - val_acc: 0.3716 Epoch 27/75 - 22s - loss: 2.1027 - acc: 0.3872 - val_loss: 2.2814 - val_acc: 0.3300 Epoch 28/75 - 22s - loss: 2.0819 - acc: 0.3923 - val_loss: 1.9929 - val_acc: 0.4116 Epoch 29/75 - 22s - loss: 2.0310 - acc: 0.4072 - val_loss: 2.0946 - val_acc: 0.3884 Epoch 30/75 - 22s - loss: 2.0042 - acc: 0.4126 - val_loss: 2.0247 - val_acc: 0.4092 Epoch 31/75 - 22s - loss: 1.9849 - acc: 0.4216 - val_loss: 1.9591 - val_acc: 0.4432 Epoch 32/75 - 22s - loss: 1.9260 - acc: 0.4357 - val_loss: 1.9129 - val_acc: 0.4548 Epoch 33/75 - 22s - loss: 1.8921 - acc: 0.4393 - val_loss: 1.9010 - val_acc: 0.4500 Epoch 34/75 - 22s - loss: 1.8885 - acc: 0.4521 - val_loss: 1.8530 - val_acc: 0.4688 Epoch 35/75 - 22s - loss: 1.8620 - acc: 0.4527 - val_loss: 1.9407 - val_acc: 0.4548 Epoch 36/75 - 22s - loss: 1.8289 - acc: 0.4702 - val_loss: 2.0116 - val_acc: 0.4160 Epoch 37/75 - 22s - loss: 1.8066 - acc: 0.4765 - val_loss: 1.8501 - val_acc: 0.4856 Epoch 38/75 - 22s - loss: 1.7652 - acc: 0.4809 - val_loss: 1.9134 - val_acc: 0.4660 Epoch 39/75 - 22s - loss: 1.7429 - acc: 0.4897 - val_loss: 1.7785 - val_acc: 0.4920 Epoch 40/75 - 22s - loss: 1.7355 - acc: 0.5036 - val_loss: 1.7783 - val_acc: 0.4848 Epoch 41/75 - 22s - loss: 1.7108 - acc: 0.4957 - val_loss: 1.8340 - val_acc: 0.4696 Epoch 42/75 - 22s - loss: 1.7125 - acc: 0.5056 - val_loss: 1.7989 - val_acc: 0.4728 Epoch 43/75 - 22s - loss: 1.6727 - acc: 0.5118 - val_loss: 2.1937 - val_acc: 0.3636 Epoch 44/75 - 22s - loss: 1.6641 - acc: 0.5199 - val_loss: 1.7716 - val_acc: 0.4936 Epoch 45/75 - 22s - loss: 1.6244 - acc: 0.5279 - val_loss: 1.7167 - val_acc: 0.5192 Epoch 46/75 - 22s - loss: 1.6108 - acc: 0.5368 - val_loss: 1.7427 - val_acc: 0.5276 Epoch 47/75 - 22s - loss: 1.5840 - acc: 0.5369 - val_loss: 1.7339 - val_acc: 0.5172 Epoch 48/75 - 22s - loss: 1.5703 - acc: 0.5364 - val_loss: 1.8054 - val_acc: 0.4888 Epoch 49/75 - 22s - loss: 1.5661 - acc: 0.5410 - val_loss: 1.8507 - val_acc: 0.4984 Epoch 50/75 - 22s - loss: 1.5360 - acc: 0.5529 - val_loss: 1.6361 - val_acc: 0.5548 Epoch 51/75 - 22s - loss: 1.5206 - acc: 0.5584 - val_loss: 1.6115 - val_acc: 0.5492 Epoch 52/75 - 22s - loss: 1.5119 - acc: 0.5608 - val_loss: 1.6899 - val_acc: 0.5492 Epoch 53/75 - 22s - loss: 1.5150 - acc: 0.5635 - val_loss: 1.6747 - val_acc: 0.5476 Epoch 54/75 - 22s - loss: 1.4974 - acc: 0.5636 - val_loss: 1.5890 - val_acc: 0.5548 Epoch 55/75 - 22s - loss: 1.4784 - acc: 0.5718 - val_loss: 1.6427 - val_acc: 0.5472 Epoch 56/75 - 22s - loss: 1.4665 - acc: 0.5738 - val_loss: 1.6516 - val_acc: 0.5392 Epoch 57/75 - 22s - loss: 1.4500 - acc: 0.5793 - val_loss: 1.7005 - val_acc: 0.5352 Epoch 58/75 - 22s - loss: 1.4353 - acc: 0.5838 - val_loss: 1.6790 - val_acc: 0.5504 Epoch 59/75 - 22s - loss: 1.4281 - acc: 0.5857 - val_loss: 1.6089 - val_acc: 0.5472 Epoch 60/75 - 22s - loss: 1.4501 - acc: 0.5890 - val_loss: 1.6866 - val_acc: 0.5444 Epoch 61/75 - 22s - loss: 1.4025 - acc: 0.5885 - val_loss: 1.6510 - val_acc: 0.5336 Epoch 62/75 - 22s - loss: 1.3906 - acc: 0.6001 - val_loss: 1.6181 - val_acc: 0.5596 Epoch 63/75 - 22s - loss: 1.3800 - acc: 0.5954 - val_loss: 1.5918 - val_acc: 0.5600 Epoch 64/75 - 22s - loss: 1.3950 - acc: 0.6009 - val_loss: 1.6006 - val_acc: 0.5700 Epoch 65/75 - 22s - loss: 1.3817 - acc: 0.6016 - val_loss: 1.6468 - val_acc: 0.5716 Epoch 66/75 - 22s - loss: 1.3489 - acc: 0.6160 - val_loss: 1.7986 - val_acc: 0.5664 Epoch 67/75 - 22s - loss: 1.3506 - acc: 0.6128 - val_loss: 1.6805 - val_acc: 0.5588 Epoch 68/75 - 22s - loss: 1.3637 - acc: 0.6135 - val_loss: 1.6333 - val_acc: 0.5652 Epoch 69/75 - 22s - loss: 1.3152 - acc: 0.6181 - val_loss: 1.5735 - val_acc: 0.5900 Epoch 70/75 - 22s - loss: 1.3193 - acc: 0.6218 - val_loss: 1.5246 - val_acc: 0.5700 Epoch 71/75 - 22s - loss: 1.3229 - acc: 0.6225 - val_loss: 1.6264 - val_acc: 0.5720 Epoch 72/75 - 22s - loss: 1.2847 - acc: 0.6201 - val_loss: 1.5936 - val_acc: 0.5624 Epoch 73/75 - 22s - loss: 1.3131 - acc: 0.6262 - val_loss: 1.6841 - val_acc: 0.5512 Epoch 74/75 - 22s - loss: 1.3026 - acc: 0.6253 - val_loss: 1.5475 - val_acc: 0.5740 Epoch 75/75 - 22s - loss: 1.2590 - acc: 0.6329 - val_loss: 1.5790 - val_acc: 0.5704
cnn.save(os.path.join(DIR, "cnn.h5"))
import pandas as pd
predictions = cnn.predict(X_test).argmax(axis=1)
predictions_string = le.inverse_transform(predictions).astype('U15')
df = pd.DataFrame(predictions_string)
df.index.name = "Id"
df.to_csv(os.path.join(DIR, "submission.csv"), header = ["Category"])