{"cells":[{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport albumentations as ab\nimport matplotlib.pyplot as plt\nimport cv2\nfrom sklearn.metrics import cohen_kappa_score\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm\nimport seaborn as sns\nfrom sklearn.utils import class_weight,shuffle\nimport keras as K\nfrom keras.utils import to_categorical\nfrom keras.models import Model\nfrom keras.regularizers import l1\nfrom keras.optimizers import Adam\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.callbacks import Callback,ModelCheckpoint,ReduceLROnPlateau\nimport imgaug.augmenters as iaa\nimport imgaug as ia","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"train_df_path='../input/aptos2019-blindness-detection/train.csv'\ntrain_img_path='../input/aptos2019-blindness-detection/train_images/'\ntest_df_path='../input/aptos2019-blindness-detection/test.csv'\ntest_images_path='../input/aptos2019-blindness-detection/test_images/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Hyperparameters\nSIZE=224\nbatch_size=35","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df=pd.read_csv(train_df_path)\ntest_df=pd.read_csv(test_df_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Train Data shape: {}'.format(train_df.shape))\nprint('Test Data shape: {}'.format(test_df.shape))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class_dict={0:'No DR',1:'Mild',2:'Moderate',3:'Severe',4:'Proliferative DR'}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"new_id_col=[i+'.png' for i in train_df['id_code']]\nnew_diagnosis=[class_dict[i] for i in train_df['diagnosis']]\ntrain_df['new_id_col']=new_id_col\ntrain_df['new_diagnosis']=new_diagnosis","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.distplot(train_df['diagnosis'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Print some samples\n#without Augmentation\nw=h=5\nfig=plt.figure(figsize=(10,10))\nrows,cols=3,3\nfor i in range(1,rows*cols+1):\n    img=cv2.imread(os.path.join(train_img_path,train_df.loc[i-1,'new_id_col']))\n    img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    fig.add_subplot(rows,cols,i)\n    plt.title('Diagnosis: {}'.format(train_df.loc[i-1,'new_diagnosis']))\n    plt.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class_weights = class_weight.compute_class_weight('balanced',\n                                                 np.unique(train_df['diagnosis']),\n                                                   train_df['diagnosis'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Split dataset\ntrain_data,val_data=train_test_split(train_df,test_size=0.15)\ntrain_data,val_data=train_data.reset_index(drop=True),val_data.reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Train Data Shape: {}'.format(train_data.shape))\nprint('Val Data Shape: {}'.format(val_data.shape))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Augmentation**"},{"metadata":{"trusted":true},"cell_type":"code","source":"sometimes = lambda aug: iaa.Sometimes(0.5, aug)\nseq = iaa.Sequential(\n    [# apply the following augmenters to most images\n        iaa.Fliplr(0.5), # horizontally flip 50% of all images\n        iaa.Flipud(0.4), # vertically flip 20% of all images\n        sometimes(iaa.Sharpen(alpha=(0, 1.0), lightness=(0.5, 1.5))),\n        iaa.Affine(\n            scale={\"x\": (0.8, 1.2), \"y\": (0.8, 1.2)}, # scale images to 80-120% of their size, individually per axis\n            translate_percent={\"x\": (-0.2, 0.2), \"y\": (-0.2, 0.2)}, # translate by -20 to +20 percent (per axis)\n            rotate=(-45, 45), # rotate by -45 to +45 degrees\n            shear=(-16, 16), # shear by -16 to +16 degrees\n           order=[0, 1], # use nearest neighbour or bilinear interpolation (fast)\n            cval=(0, 255), # if mode is constant, use a cval between 0 and 255\n            mode=ia.ALL # use any of scikit-image's warping modes (see 2nd image from the top for examples)\n        )\n    ])                  ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Custom datagenerator**"},{"metadata":{"trusted":true},"cell_type":"code","source":"class My_Generator(K.utils.Sequence):\n\n    def __init__(self, image_filenames, labels,\n                 batch_size,path, is_train=True,\n                 mix=False, augment=False):\n        self.image_filenames, self.labels = image_filenames, labels\n        self.batch_size = batch_size\n        self.is_train = is_train\n        self.is_augment = augment\n        self.path=path\n        if(self.is_train):\n            self.on_epoch_end()\n        self.is_mix = mix\n\n    def __len__(self):\n        return int(np.ceil(len(self.image_filenames) / float(self.batch_size)))\n\n    def __getitem__(self, idx):\n        batch_x = self.image_filenames[idx * self.batch_size:(idx + 1) * self.batch_size]\n        batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size]\n\n        if(self.is_train):\n            return self.train_generate(batch_x, batch_y)\n        return self.valid_generate(batch_x, batch_y)\n\n    def on_epoch_end(self):\n        if(self.is_train):\n            self.image_filenames, self.labels = shuffle(self.image_filenames, self.labels)\n        else:\n            pass\n    \n    def mix_up(self, x, y):\n        lam = np.random.beta(0.2, 0.4)\n        ori_index = np.arange(int(len(x)))\n        index_array = np.arange(int(len(x)))\n        np.random.shuffle(index_array)        \n        \n        mixed_x = lam * x[ori_index] + (1 - lam) * x[index_array]\n        mixed_y = lam * y[ori_index] + (1 - lam) * y[index_array]\n        \n        return mixed_x, mixed_y\n\n    def train_generate(self, batch_x, batch_y):\n        batch_images = []\n        for (sample, label) in zip(batch_x, batch_y):\n            img = cv2.imread(os.path.join(self.path,sample))\n            img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n            img = cv2.resize(img, (SIZE, SIZE))\n            if(self.is_augment):\n                img = seq.augment_image(img)\n            batch_images.append(img)\n        batch_images = np.array(batch_images, np.float32) / 255\n        batch_y = np.array(batch_y, np.float32)\n        batch_y=to_categorical(batch_y,num_classes=5)\n        if(self.is_mix):\n            batch_images, batch_y = self.mix_up(batch_images, batch_y)\n        return batch_images, batch_y\n\n    def valid_generate(self, batch_x, batch_y):\n        batch_images = []\n        for (sample, label) in zip(batch_x, batch_y):\n            img = cv2.imread(os.path.join(self.path,sample))\n            img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n            img = cv2.resize(img, (SIZE, SIZE))\n            batch_images.append(img)\n        batch_images = np.array(batch_images, np.float32) / 255\n        batch_y = np.array(batch_y, np.float32)\n        batch_y=to_categorical(batch_y,num_classes=5)\n        return batch_images, batch_y","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**QWK**"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.callbacks import Callback\nclass QWKEvaluation(Callback):\n    def __init__(self, validation_data=(), batch_size=batch_size, interval=1):\n        super(Callback, self).__init__()\n\n        self.interval = interval\n        self.batch_size = batch_size\n        self.valid_generator, self.y_val = validation_data\n        self.history = []\n\n    def on_epoch_end(self, epoch, logs={}):\n        if epoch % self.interval == 0:\n            y_pred = self.model.predict_generator(generator=self.valid_generator,\n                                                  steps=np.ceil(float(len(self.y_val)) / float(self.batch_size)),\n                                                  workers=1, use_multiprocessing=False,\n                                                  verbose=1)\n            def flatten(y):\n                return np.argmax(y, axis=1).reshape(-1)\n            \n            score = cohen_kappa_score(self.y_val,\n                                      flatten(y_pred),\n                                      labels=[0,1,2,3,4],\n                                      weights='quadratic')\n            print(\"\\n epoch: %d - QWK_score: %.6f \\n\" % (epoch+1, score))\n            self.history.append(score)\n            if score >= max(self.history):\n                print('saving checkpoint: ', score)\n                self.model.save('../working/classifier_6.h5')\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**GENERATORS**"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_generator=My_Generator(np.asarray(train_data['new_id_col']),np.asarray(train_data['diagnosis']),\n                            batch_size=batch_size,path=train_img_path,is_train=True,mix=False,augment=True)\n\nval_generator=My_Generator(np.asarray(val_data['new_id_col']),np.asarray(val_data['diagnosis']),\n                            batch_size=batch_size,path=train_img_path,is_train=False,mix=False,augment=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**QWK object**"},{"metadata":{"trusted":true},"cell_type":"code","source":"qwk = QWKEvaluation(validation_data=(val_generator, np.asarray(val_data['diagnosis'])),\n                    batch_size=batch_size, interval=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"reg = l1(0.001)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"base_layer=K.applications.densenet.DenseNet169(input_shape=(224,224,3),include_top=False,\n                                           weights='../input/densenet-keras/DenseNet-BC-169-32-no-top.h5')\nfor layer in base_layer.layers:\n    layer.trainable=True\n\nlstm_=K.layers.Reshape([7*7,1664])(base_layer.output)\nlstm_=K.layers.Bidirectional(K.layers.LSTM(832,recurrent_dropout=0.2))(lstm_)\nx=K.layers.Dense(1024,activation='relu',activity_regularizer=reg)(lstm_)\nx=K.layers.Dropout(0.3)(x)\nout=K.layers.Dense(5,activation='softmax')(x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model=Model(base_layer.input,out)\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(loss='categorical_crossentropy',optimizer=Adam(0.0001))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"rp=ReduceLROnPlateau(monitor='val_loss',factor=0.2,min_lr=0.00000001,patience=3,verbose=1)\nmc=ModelCheckpoint('../working/classifier_6.h5',monitor='val_loss',mode='min',period=1,save_best_only=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history=model.fit_generator(train_generator,epochs=30,steps_per_epoch=int(np.ceil(len(train_data)/batch_size)),\n                    validation_data=val_generator,\n                            validation_steps=int(np.ceil(len(val_data)/batch_size)),callbacks=[qwk,rp]\n                            ,class_weight=class_weights)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"loss = history.history['loss']\nval_loss = history.history['val_loss']\nscore=qwk.history\nepochs=range(1,len(loss)+1)\nplt.plot(epochs,loss,'b',color='red',label='Training Loss')\nplt.plot(epochs,val_loss,'b',color='blue',label='Validation Loss')\nplt.title('Training and Validation Loss')\nplt.legend()\nplt.figure()\nplt.plot(epochs,score,'b',color='red',label='Validation Kappa')\nplt.legend()\nplt.figure()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#delete unnecessary data\ndel train_generator,val_generator,train_data,val_data","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Load Model\nmodel.load_weights('../working/classifier_6.h5')\nprint('Weights Restored')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Test Predictions\ntest_df=pd.read_csv('../input/aptos2019-blindness-detection/sample_submission.csv')\npredictions=[]\nfor id_ in tqdm(test_df['id_code']):\n    img = cv2.imread(os.path.join(test_images_path,id_+'.png'))\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    img = cv2.resize(img, (SIZE, SIZE))\n    img=img/255.0\n    img=np.expand_dims(img,0)\n    predictions.extend(model.predict(img))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predictions=np.argmax(predictions,1)\nsub=pd.DataFrame({'id_code':test_df['id_code'],'diagnosis':predictions})\nsub.to_csv('submission.csv',index=False)","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.7"}},"nbformat":4,"nbformat_minor":1}