In [5]:
import os, random, glob
import numpy as np
import pandas as pd
from PIL import Image
from sklearn.model_selection import train_test_split
from sklearn.metrics import log_loss
from sklearn.preprocessing import LabelEncoder

import matplotlib.pyplot as plt
from matplotlib import ticker
import seaborn as sns
%matplotlib inline 

from keras.models import Sequential, Model,load_model
from keras.layers import Convolution2D, BatchNormalization, LeakyReLU, GlobalAveragePooling2D, Flatten, Dropout, Dense
from keras.optimizers import Adam
from keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, TensorBoard
from keras.preprocessing.image import ImageDataGenerator
from keras.utils import np_utils
from keras import backend as K

In [2]:
TRAIN_DIR = '../data/train/'
TEST_DIR = '../data/test_stg1/'
FISH_CLASSES = ['ALB', 'BET', 'DOL', 'LAG', 'NoF', 'OTHER', 'SHARK', 'YFT']
ROWS = 256
COLS = 256
BatchSize = 64
LearningRate = 1e-4

In [3]:
#Loading data

import pickle
if os.path.exists('../data/data_train_{}_{}.pickle'.format(ROWS, COLS)):
    print ('Exist data_train_{}_{}.pickle. Loading data from file.'.format(ROWS, COLS))
    with open('../data/data_train_{}_{}.pickle'.format(ROWS, COLS), 'rb') as f:
        data_train = pickle.load(f)
    X_train = data_train['X_train']
    y_train = data_train['y_train']
else:
    print ('Loading data from original images. Generating data_train_{}_{}.pickle.'.format(ROWS, COLS))

    def get_images(fish):
        """Load files from train folder"""
        fish_dir = TRAIN_DIR+'{}'.format(fish)
        images = [fish+'/'+im for im in os.listdir(fish_dir)]
        return images

    def read_image(src):
        """Read and resize individual images"""
        im = Image.open(src)
        im = im.resize((COLS, ROWS), Image.BILINEAR)
        im = np.asarray(im)
        return im

    files = []
    y_train = []

    for fish in FISH_CLASSES:
        fish_files = get_images(fish)
        files.extend(fish_files)

        y_fish = np.tile(fish, len(fish_files))
        y_train.extend(y_fish)
        #print("{0} photos of {1}".format(len(fish_files), fish))

    y_train = np.array(y_train)
    X_train = np.ndarray((len(files), ROWS, COLS, 3), dtype=np.uint8)

    for i, im in enumerate(files): 
        X_train[i] = read_image(TRAIN_DIR+im)
        if i%1000 == 0: print('Processed {} of {}'.format(i, len(files)))

    #X_train = X_train / 255.
    #print(X_train.shape)

    # One Hot Encoding Labels
    y_train = LabelEncoder().fit_transform(y_train)
    y_train = np_utils.to_categorical(y_train)

    #save data to file
    data_train = {'X_train': X_train,'y_train': y_train }

    with open('../data/data_train_{}_{}.pickle'.format(ROWS, COLS), 'wb') as f:
        pickle.dump(data_train, f)

X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=None, stratify=y_train)


Exist data_train_256_256.pickle. Loading data from file.

In [4]:
#create my VGG16-alike model

optimizer = Adam(lr=LearningRate)

def create_model():
    model = Sequential()

    model.add(Convolution2D(32, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf', input_shape=(ROWS, COLS, 3)))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    model.add(Convolution2D(32, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf', subsample=(2, 2)))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    
    model.add(Convolution2D(64, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf'))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    model.add(Convolution2D(64, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf', subsample=(2, 2)))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))

    model.add(Convolution2D(128, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf'))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    model.add(Convolution2D(128, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf'))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    model.add(Convolution2D(128, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf', subsample=(2, 2)))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    
    model.add(Convolution2D(256, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf'))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    model.add(Convolution2D(256, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf'))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    model.add(Convolution2D(256, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf', subsample=(2, 2)))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    
    model.add(Convolution2D(256, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf'))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    model.add(Convolution2D(256, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf'))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    model.add(Convolution2D(256, 3, 3, init='he_normal', border_mode='same', dim_ordering='tf', subsample=(2, 2)))
    model.add(BatchNormalization())
    model.add(LeakyReLU(alpha=0.33))
    
    #model.add(AveragePooling2D(pool_size=(7, 7), dim_ordering='tf'))
    #model.add(Flatten())
    model.add(GlobalAveragePooling2D(dim_ordering='tf'))
    model.add(Dense(256, init='glorot_normal', activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(256, init='glorot_normal', activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(len(FISH_CLASSES), init='glorot_normal', activation='softmax'))

    model.compile(loss='categorical_crossentropy', optimizer=optimizer)
    return model

In [6]:
#data preprocessing

train_datagen = ImageDataGenerator(
    #featurewise_center=True,
    #featurewise_std_normalization=True,
    rescale=1./255,
    rotation_range=180,
    shear_range=np.pi/6.,
    zoom_range=[1,1.1],
    width_shift_range=0.1,
    height_shift_range=0.1,
    horizontal_flip=True,
    vertical_flip=True)

#train_datagen.fit(X_train)
train_generator = train_datagen.flow(X_train, y_train, batch_size=BatchSize, shuffle=True, seed=None)

valid_datagen = ImageDataGenerator(rescale=1./255)

valid_generator = valid_datagen.flow(X_valid, y_valid, batch_size=BatchSize, shuffle=True, seed=None)

In [7]:
#callbacks

early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=15, verbose=1, mode='auto')        

model_checkpoint = ModelCheckpoint(filepath='./checkpoints/weights.{epoch:03d}-{val_loss:.4f}.hdf5', monitor='val_loss', verbose=1, save_best_only=True, save_weights_only=False, mode='auto')
        
learningrate_schedule = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=10, verbose=1, mode='auto', epsilon=0.001, cooldown=0, min_lr=0)

tensorboard = TensorBoard(log_dir='./logs', histogram_freq=10, write_graph=True, write_images=True)

In [ ]:
#training model

model = create_model()
model.fit_generator(train_generator, samples_per_epoch=len(X_train), nb_epoch=300, verbose=1, 
                    callbacks=[early_stopping, model_checkpoint, learningrate_schedule, tensorboard], 
                    validation_data=valid_generator, nb_val_samples=len(X_valid), nb_worker=3, pickle_safe=True)

In [8]:
#resume training

from keras.models import load_model

files = glob.glob('./checkpoints/*')
val_losses = [float(f.split('-')[-1][:-5]) for f in files]
index = val_losses.index(min(val_losses))

model = load_model(files[index])

model.fit_generator(train_generator, samples_per_epoch=len(X_train), nb_epoch=300, verbose=1, 
                    callbacks=[early_stopping, model_checkpoint, learningrate_schedule, tensorboard], 
                    validation_data=valid_generator, nb_val_samples=len(X_valid), nb_worker=3, pickle_safe=True)


Epoch 1/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9385
/opt/anaconda3/lib/python3.5/site-packages/keras/engine/training.py:1470: UserWarning: Epoch comprised more than `samples_per_epoch` samples, which might affect learning results. Set `samples_per_epoch` correctly to avoid this warning.
  warnings.warn('Epoch comprised more than '
Epoch 00000: val_loss improved from inf to 1.03515, saving model to ./checkpoints/weights.000-1.0352.hdf5
3072/3021 [==============================] - 126s - loss: 0.9329 - val_loss: 1.0352
Epoch 2/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9286Epoch 00001: val_loss improved from 1.03515 to 0.88148, saving model to ./checkpoints/weights.001-0.8815.hdf5
3072/3021 [==============================] - 116s - loss: 0.9304 - val_loss: 0.8815
Epoch 3/300
2983/3021 [============================>.] - ETA: 1s - loss: 0.9968Epoch 00002: val_loss did not improve
3047/3021 [==============================] - 116s - loss: 0.9989 - val_loss: 1.1200
Epoch 4/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9623Epoch 00003: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.9623 - val_loss: 0.9749
Epoch 5/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9116Epoch 00004: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.9117 - val_loss: 1.0650
Epoch 6/300
2983/3021 [============================>.] - ETA: 1s - loss: 0.9478Epoch 00005: val_loss did not improve
3047/3021 [==============================] - 115s - loss: 0.9476 - val_loss: 1.8679
Epoch 7/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9810Epoch 00006: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.9788 - val_loss: 1.1377
Epoch 8/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9064Epoch 00007: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.9080 - val_loss: 1.2632
Epoch 9/300
2983/3021 [============================>.] - ETA: 1s - loss: 0.9427Epoch 00008: val_loss did not improve
3047/3021 [==============================] - 115s - loss: 0.9434 - val_loss: 1.4286
Epoch 10/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9087Epoch 00009: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.9081 - val_loss: 1.9333
Epoch 11/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9066Epoch 00010: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.9066 - val_loss: 1.0620
Epoch 12/300
2983/3021 [============================>.] - ETA: 1s - loss: 0.8937Epoch 00011: val_loss did not improve
3047/3021 [==============================] - 115s - loss: 0.8959 - val_loss: 1.2713
Epoch 13/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.8837Epoch 00012: val_loss improved from 0.88148 to 0.83451, saving model to ./checkpoints/weights.012-0.8345.hdf5
3072/3021 [==============================] - 116s - loss: 0.8811 - val_loss: 0.8345
Epoch 14/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.9193Epoch 00013: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.9179 - val_loss: 0.9807
Epoch 15/300
2983/3021 [============================>.] - ETA: 1s - loss: 0.9259Epoch 00014: val_loss did not improve
3047/3021 [==============================] - 115s - loss: 0.9285 - val_loss: 1.8863
Epoch 16/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.8809Epoch 00015: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.8790 - val_loss: 1.1441
Epoch 17/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.8658Epoch 00016: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.8677 - val_loss: 1.0663
Epoch 18/300
2983/3021 [============================>.] - ETA: 1s - loss: 0.8842Epoch 00017: val_loss did not improve
3047/3021 [==============================] - 115s - loss: 0.8849 - val_loss: 1.0647
Epoch 19/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.8523Epoch 00018: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.8520 - val_loss: 0.9408
Epoch 20/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.8455Epoch 00019: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.8438 - val_loss: 0.8984
Epoch 21/300
2983/3021 [============================>.] - ETA: 1s - loss: 0.8638Epoch 00020: val_loss did not improve
3047/3021 [==============================] - 115s - loss: 0.8665 - val_loss: 1.1570
Epoch 22/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.8646Epoch 00021: val_loss did not improve
3072/3021 [==============================] - 116s - loss: 0.8675 - val_loss: 0.8666
Epoch 23/300
3008/3021 [============================>.] - ETA: 0s - loss: 0.7785Epoch 00022: val_loss did not improve
3072/3021 [==============================] - 115s - loss: 0.7768 - val_loss: 1.1397
Epoch 24/300
2855/3021 [===========================>..] - ETA: 5s - loss: 0.8553
-----------------------------------------------------------------------
KeyboardInterrupt                     Traceback (most recent call last)
<ipython-input-8-ccfd1fd507d6> in <module>()
      7 model.fit_generator(train_generator, samples_per_epoch=len(X_train), nb_epoch=300, verbose=1, 
      8                     callbacks=[early_stopping, model_checkpoint, learningrate_schedule, tensorboard],
----> 9                     validation_data=valid_generator, nb_val_samples=len(X_valid), nb_worker=3, pickle_safe=True)

/opt/anaconda3/lib/python3.5/site-packages/keras/models.py in fit_generator(self, generator, samples_per_epoch, nb_epoch, verbose, callbacks, validation_data, nb_val_samples, class_weight, max_q_size, nb_worker, pickle_safe, **kwargs)
    905                                         max_q_size=max_q_size,
    906                                         nb_worker=nb_worker,
--> 907                                         pickle_safe=pickle_safe)
    908 
    909     def evaluate_generator(self, generator, val_samples,

/opt/anaconda3/lib/python3.5/site-packages/keras/engine/training.py in fit_generator(self, generator, samples_per_epoch, nb_epoch, verbose, callbacks, validation_data, nb_val_samples, class_weight, max_q_size, nb_worker, pickle_safe, initial_epoch)
   1449                     outs = self.train_on_batch(x, y,
   1450                                                sample_weight=sample_weight,
-> 1451                                                class_weight=class_weight)
   1452                 except:
   1453                     _stop.set()

/opt/anaconda3/lib/python3.5/site-packages/keras/engine/training.py in train_on_batch(self, x, y, sample_weight, class_weight)
   1224             ins = x + y + sample_weights
   1225         self._make_train_function()
-> 1226         outputs = self.train_function(ins)
   1227         if len(outputs) == 1:
   1228             return outputs[0]

/opt/anaconda3/lib/python3.5/site-packages/keras/backend/tensorflow_backend.py in __call__(self, inputs)
   1094             feed_dict[tensor] = value
   1095         session = get_session()
-> 1096         updated = session.run(self.outputs + [self.updates_op], feed_dict=feed_dict)
   1097         return updated[:len(self.outputs)]
   1098 

/opt/anaconda3/lib/python3.5/site-packages/tensorflow/python/client/session.py in run(self, fetches, feed_dict, options, run_metadata)
    715     try:
    716       result = self._run(None, fetches, feed_dict, options_ptr,
--> 717                          run_metadata_ptr)
    718       if run_metadata:
    719         proto_data = tf_session.TF_GetBuffer(run_metadata_ptr)

/opt/anaconda3/lib/python3.5/site-packages/tensorflow/python/client/session.py in _run(self, handle, fetches, feed_dict, options, run_metadata)
    913     if final_fetches or final_targets:
    914       results = self._do_run(handle, final_targets, final_fetches,
--> 915                              feed_dict_string, options, run_metadata)
    916     else:
    917       results = []

/opt/anaconda3/lib/python3.5/site-packages/tensorflow/python/client/session.py in _do_run(self, handle, target_list, fetch_list, feed_dict, options, run_metadata)
    963     if handle is None:
    964       return self._do_call(_run_fn, self._session, feed_dict, fetch_list,
--> 965                            target_list, options, run_metadata)
    966     else:
    967       return self._do_call(_prun_fn, self._session, handle, feed_dict,

/opt/anaconda3/lib/python3.5/site-packages/tensorflow/python/client/session.py in _do_call(self, fn, *args)
    970   def _do_call(self, fn, *args):
    971     try:
--> 972       return fn(*args)
    973     except errors.OpError as e:
    974       message = compat.as_text(e.message)

/opt/anaconda3/lib/python3.5/site-packages/tensorflow/python/client/session.py in _run_fn(session, feed_dict, fetch_list, target_list, options, run_metadata)
    952         return tf_session.TF_Run(session, options,
    953                                  feed_dict, fetch_list, target_list,
--> 954                                  status, run_metadata)
    955 
    956     def _prun_fn(session, handle, feed_dict, fetch_list):

KeyboardInterrupt: 

In [8]:
#test submission

import datetime

if os.path.exists('../data/data_test_{}_{}.pickle'.format(ROWS, COLS)):
    print ('Exist data_test_{}_{}.pickle. Loading test data from file.'.format(ROWS, COLS))
    with open('../data/data_test_{}_{}.pickle'.format(ROWS, COLS), 'rb') as f:
        data_test = pickle.load(f)
    X_test = data_test['X_test']
    test_files = data_test['test_files']
else:
    print ('Loading test data from original images. Generating data_test_{}_{}.pickle.'.format(ROWS, COLS))

    test_files = [im for im in os.listdir(TEST_DIR)]
    X_test = np.ndarray((len(test_files), ROWS, COLS, 3), dtype=np.uint8)

    for i, im in enumerate(test_files): 
        X_test[i] = read_image(TEST_DIR+im)
        if i%300 == 0: print('Processed {} of {}'.format(i, len(test_files)))
            
    data_test = {'X_test': X_test,'test_files': test_files }
    
    with open('../data/data_test_{}_{}.pickle'.format(ROWS, COLS), 'wb') as f:
        pickle.dump(data_test, f)
            
X_test = X_test / 255.

files = glob.glob('./checkpoints/*')
val_losses = [float(f.split('-')[-1][:-5]) for f in files]
index = val_losses.index(min(val_losses))
model = load_model(files[index])

test_preds = model.predict(X_test, batch_size=BatchSize, verbose=1)
#test_preds= test_preds / np.sum(test_preds,axis=1,keepdims=True)

submission = pd.DataFrame(test_preds, columns=FISH_CLASSES)
#submission.loc[:, 'image'] = pd.Series(test_files, index=submission.index)
submission.insert(0, 'image', test_files)

now = datetime.datetime.now()
info = 'Baseline_' + '{:.4f}'.format(min(val_losses))
sub_file = 'submission_' + info + '_' + str(now.strftime("%Y-%m-%d-%H-%M")) + '.csv'
submission.to_csv(sub_file, index=False)


Exist data_test_256_256.pickle. Loading test data from file.
1000/1000 [==============================] - 14s    

In [7]:
submission.head()


Out[7]:
image ALB BET DOL LAG NoF OTHER SHARK YFT
0 img_01373.jpg 0.621550 0.055312 0.028962 0.115232 0.009943 0.155889 0.000903 0.012209
1 img_05426.jpg 0.176921 0.037405 0.008449 0.190479 0.012963 0.549269 0.001621 0.022893
2 img_03283.jpg 0.106833 0.045370 0.071960 0.149157 0.021374 0.060491 0.005579 0.539236
3 img_05976.jpg 0.184785 0.031446 0.000875 0.000701 0.005245 0.070570 0.320857 0.385522
4 img_04872.jpg 0.373507 0.018715 0.004252 0.015369 0.311923 0.059999 0.002993 0.213242
###clear log an checkpoints folder if not os.path.exists('./checkpoints'): os.mkdir('./checkpoints') files = glob.glob('./checkpoints/*') for f in files: os.remove(f) #if not os.path.exists('./logs'): # os.mkdir('./logs') files = glob.glob('./logs/*') for f in files: os.remove(f)