{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Preprocess Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport cv2\nimport pandas as pd\nimport math\nimport gc","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.layers import Dense, GlobalAveragePooling2D, Dropout\nfrom keras.optimizers import Adam\nfrom keras.applications import MobileNetV2\nfrom keras.models import Sequential\nfrom keras.callbacks import Callback,ModelCheckpoint, ReduceLROnPlateau, EarlyStopping","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_SIZE = 224","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def resize(image_path, img_size = IMG_SIZE):\n    img = cv2.imread(image_path)\n    pad_diff = max(img.shape) - img.shape[0], max(img.shape) - img.shape[1]\n    t, b = math.floor(pad_diff[0]/2), math.ceil(pad_diff[0]/2)\n    l, r = math.floor(pad_diff[1]/2), math.ceil(pad_diff[1]/2)\n    pad_width = ((t,b), (l,r), (0, 0))\n    padded = np.pad(img, pad_width=pad_width, mode='constant')\n    resized = cv2.resize(padded, (img_size,)*2).astype('uint8')\n    return resized","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Train Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_diag = pd.read_csv('../input/aptos2019-blindness-detection/train.csv')\ntrain_diag = train_diag.drop(train_diag[train_diag[\"diagnosis\"]==0].sample(600).index)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nx_train = []\nfor img_id in train_diag['id_code']:\n    x_train.append(resize(f'../input/aptos2019-blindness-detection/train_images/{img_id}.png'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train = pd.get_dummies(train_diag['diagnosis']).values\ndel train_diag\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Ref: https://www.kaggle.com/danmoller/make-best-use-of-a-kernel-s-limited-uptime-keras\nimport time \n\n#let's also import the abstract base class for our callback\nfrom keras.callbacks import Callback\n\n#defining the callback\nclass TimerCallback(Callback):\n    \n    def __init__(self, maxExecutionTime, byBatch = False, on_interrupt=None):\n        \n# Arguments:\n#     maxExecutionTime (number): Time in minutes. The model will keep training \n#                                until shortly before this limit\n#                                (If you need safety, provide a time with a certain tolerance)\n\n#     byBatch (boolean)     : If True, will try to interrupt training at the end of each batch\n#                             If False, will try to interrupt the model at the end of each epoch    \n#                            (use `byBatch = True` only if each epoch is going to take hours)          \n\n#     on_interrupt (method)          : called when training is interrupted\n#         signature: func(model,elapsedTime), where...\n#               model: the model being trained\n#               elapsedTime: the time passed since the beginning until interruption   \n\n        \n        self.maxExecutionTime = maxExecutionTime * 60\n        self.on_interrupt = on_interrupt\n        \n        #the same handler is used for checking each batch or each epoch\n        if byBatch == True:\n            #on_batch_end is called by keras every time a batch finishes\n            self.on_batch_end = self.on_end_handler\n        else:\n            #on_epoch_end is called by keras every time an epoch finishes\n            self.on_epoch_end = self.on_end_handler\n    \n    \n    #Keras will call this when training begins\n    def on_train_begin(self, logs):\n        self.startTime = time.time()\n        self.longestTime = 0            #time taken by the longest epoch or batch\n        self.lastTime = self.startTime  #time when the last trained epoch or batch was finished\n    \n    \n    #this is our custom handler that will be used in place of the keras methods:\n        #`on_batch_end(batch,logs)` or `on_epoch_end(epoch,logs)`\n    def on_end_handler(self, index, logs):\n        \n        currentTime      = time.time()                           \n        self.elapsedTime = currentTime - self.startTime    #total time taken until now\n        thisTime         = currentTime - self.lastTime     #time taken for the current epoch\n                                                               #or batch to finish\n        \n        self.lastTime = currentTime\n        \n        #verifications will be made based on the longest epoch or batch\n        if thisTime > self.longestTime:\n            self.longestTime = thisTime\n        \n        \n        #if the (assumed) time taken by the next epoch or batch is greater than the\n            #remaining time, stop training\n        remainingTime = self.maxExecutionTime - self.elapsedTime\n        if remainingTime < self.longestTime:\n            \n            self.model.stop_training = True  #this tells Keras to not continue training\n            print(\"\\n\\nTimerCallback: Finishing model training before it takes too much time. (Elapsed time: \" + str(self.elapsedTime/60.) + \" minutes )\\n\\n\")\n            \n            #if we have passed the `on_interrupt` callback, call it here\n            if self.on_interrupt is not None:\n                self.on_interrupt(self.model, self.elapsedTime)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# MobileNetV2"},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Sequential()\nmodel.add(MobileNetV2(weights=\"../input/pretrained-models/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224_no_top.h5\",\n                        include_top=False,\n                        input_shape=(IMG_SIZE, IMG_SIZE, 3)))\nmodel.add(GlobalAveragePooling2D())\nmodel.add(Dropout(0.2))\nmodel.add(Dense(5, activation='softmax'))\n\nmodel.compile(loss='categorical_crossentropy',\n              optimizer=Adam(lr=2e-5,amsgrad=True),\n              metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.fit(np.stack(x_train), \n          y_train,\n          epochs=10000000,\n          batch_size=32,\n          verbose=2,\n          shuffle=True,\n          callbacks=[TimerCallback(500)],\n         validation_split=0.25)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del x_train,y_train\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Test Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"test = pd.read_csv('../input/aptos2019-blindness-detection/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\ntest_resized = []\nfor img_id in test['id_code']:\n    test_resized.append(resize(f'../input/aptos2019-blindness-detection/test_images/{img_id}.png'))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prediction"},{"metadata":{"trusted":true},"cell_type":"code","source":"preds = model.predict(np.stack(test_resized))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test['diagnosis'] = preds.argmax(axis=1)\ntest.to_csv('submission.csv',index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.3"}},"nbformat":4,"nbformat_minor":1}