{"cells":[{"metadata":{},"cell_type":"markdown","source":"This is a starter attempt at basic data exploration and a basic convolutional neural network architecture for our problem."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport os\nfrom tqdm import tqdm\nimport glob\nimport cv2\nfrom numpy.random import seed\nseed(42)\nfrom tensorflow import set_random_seed\nset_random_seed(42)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv('../input/train.csv')\ntest = pd.read_csv('../input/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"train_images = os.listdir('../input/train_images/')\nprint (len(train_images),len(train))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_images = os.listdir('../input/test_images/')\nprint (len(test_images),len(test))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_path = '../input/train_images/'\ntest_path = '../input/test_images/'\n\ntrain_ids = train['id_code'].values\ntest_ids = test['id_code'].values\n\ntrain_paths = []\nfor train_id in train_ids:\n    image = train_id + '.png'\n    path = os.path.join(train_path,image)\n    train_paths.append(path)\n    \ntrain_paths = np.array(train_paths)\ntrain['path'] = train_paths","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_paths = []\nfor test_id in test_ids:\n    image = test_id + '.png'\n    path = os.path.join(test_path,image)\n    test_paths.append(path)\n    \ntest_paths = np.array(test_paths)\ntest['path'] = test_paths","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The preprocessing function below is used to crop the image according to approximate radius estimation such that surrounding black pixels along the width can be removed. "},{"metadata":{"trusted":true},"cell_type":"code","source":"def find_radius(mid_pixels,mid_y_pixels,threshold_x,threshold_y):\n    \n    start_x = 0\n    end_x = mid_pixels.shape[0] - 1\n    \n    start_y = 0\n    end_y = mid_y_pixels.shape[0] - 1\n    \n    while True:\n        if np.sum(mid_pixels[start_x,:])>threshold_x:\n            break\n        start_x +=1\n    while True:\n        if np.sum(mid_pixels[end_x,:])>threshold_x:\n            break\n        end_x -= 1\n        \n    while True:\n        if np.sum(mid_y_pixels[start_y,:])>threshold_y:\n            break\n        start_y +=1\n    while True:\n        if np.sum(mid_y_pixels[end_y,:])>threshold_y:\n            break\n        end_y -= 1\n        \n    return start_x,end_x,start_y,end_y\n    \n    \n    \ndef preprocess_image(img):\n    mid = img.shape[1]//2\n    mid_pixels = img[mid,:]\n    mid_y_pixels = img[:,mid]\n    threshold_x = np.mean(mid_pixels)\n    threshold_y = np.mean(mid_y_pixels)\n    startx,endx,starty,endy = find_radius(mid_pixels,mid_y_pixels,threshold_x,threshold_y)\n    return cv2.resize(img[starty:endy,startx:endx],(img.shape[0],img.shape[1]))\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig1,axs1 = plt.subplots(1,2)\nimg = cv2.resize(cv2.imread('../input/train_images/9e2ba2b979f1.png'),(150,150))\nimg = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\naxs1[0].imshow(img)\n\nimg = preprocess_image(img)\naxs1[1].imshow(img)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig,axs = plt.subplots(3,2,figsize=(8,12))\ntrain_paths = train['path'].values\nfor i,path in enumerate(train_paths[150:153]):\n    img = cv2.imread(path)\n    img = cv2.resize(img,(150,150))\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    axs[i,0].imshow(img)\n    axs[i,0].set_title('Original')\n    img = preprocess_image(img)\n    print (img.shape,np.amin(img),np.amax(img))\n    axs[i,1].imshow(img)\n    axs[i,1].set_title('After Preprocessing')\n    \nfig.suptitle('Some Train images',fontsize=15)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig,axs = plt.subplots(1,3,figsize=(10,4))\ntest_paths = test['path'].values\nfor i,path in enumerate(test_paths[:3]):\n    img = cv2.imread(path)\n    img = cv2.resize(img,(150,150))\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    print (img.shape,np.amin(img),np.amax(img))\n    axs[i].imshow(img)\n    \n            \nfig.suptitle('Some Test images',fontsize=15)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The image distributions of the train and test images are different.\nWe see that the test images seem to zoomed in images. The train images also has noise in the form of black background in the corners. "},{"metadata":{},"cell_type":"markdown","source":"Now we will visualize the class distributions in train set."},{"metadata":{"trusted":true},"cell_type":"code","source":"train['diagnosis'].value_counts().plot.bar()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train = train['diagnosis'].values","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Computer class weights to penalize misclassifications of lower frequent classes"},{"metadata":{"trusted":true},"cell_type":"code","source":"from collections import Counter\ndef get_class_weights(y):\n    counter = Counter(y)\n    majority = max(counter.values())\n    return  {cls: round(float(majority)/float(count), 2) for cls, count in counter.items()}\n\nclass_weights = get_class_weights(y_train)\nclass_weights","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Splitting the training set into training and validation sets"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\ntrain_df,validation_df = train_test_split(train,test_size = 0.2,stratify=y_train,random_state = 42)\nprint (len(train_df),len(validation_df))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Confirming the split is indeed a stratified one!"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df['diagnosis'].value_counts().plot.bar()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validation_df['diagnosis'].value_counts().plot.bar()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.models import Sequential\nfrom keras.layers import Conv2D,MaxPooling2D,Dropout,Activation,Dense,Flatten,BatchNormalization,GlobalAveragePooling2D\nfrom keras.preprocessing.image import ImageDataGenerator\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen = ImageDataGenerator(\n        zoom_range=0.4,\n        rescale = 1./255,\n        fill_mode = 'constant',\n        horizontal_flip = True,\n        vertical_flip = True,\n        preprocessing_function = preprocess_image\n)","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":"train_df['id'] = train_df['id_code'].apply(lambda x: str(x)+'.png')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df['diagnosis'] = train_df['diagnosis'].apply(lambda x:str(x))\n","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":"train_generator = datagen.flow_from_dataframe(\ndataframe=train_df,\ndirectory=\"../input/train_images/\",\nx_col=\"id\",\ny_col=\"diagnosis\",\nbatch_size=32,\nseed=42,\nshuffle=True,\nclass_mode=\"categorical\",\ncolor_mode = 'rgb',\ntarget_size=(150,150))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_generator.class_indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"disp_x,disp_y = next(train_generator)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(disp_x[3])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"disp_y[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validation_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validation_x = []\nfor path in tqdm(validation_df['path'].values):\n    img = cv2.resize(cv2.imread(path),(150,150))\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    validation_x.append(img)\n    \n   ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validation_x = np.array(validation_x)\nvalidation_x = validation_x.astype(np.float32)/255.0\nprint (validation_x.shape)\nprint (np.amin(validation_x),np.amax(validation_x))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.utils import to_categorical\nvalidation_y = to_categorical(validation_df['diagnosis'].values,5)\nprint (validation_y.shape)\nprint (validation_y[:5])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Sequential()\nmodel.add(Conv2D(32,(3,3),input_shape=(150,150,3)))\nmodel.add(Activation('relu'))\nmodel.add(MaxPooling2D((2,2)))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.2))\n\nmodel.add(Conv2D(64,(3,3)))\nmodel.add(Activation('relu'))\nmodel.add(MaxPooling2D((2,2)))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.2))\n\nmodel.add(Conv2D(128,(3,3)))\nmodel.add(Activation('relu'))\nmodel.add(MaxPooling2D((2,2)))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.2))\n\nmodel.add(Conv2D(256,(3,3)))\nmodel.add(Activation('relu'))\nmodel.add(MaxPooling2D((2,2)))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.2))\n\nmodel.add(GlobalAveragePooling2D())\n\nmodel.add(Dense(64,activation='relu'))\nmodel.add(Dropout(0.5))\nmodel.add(Dense(5,activation='softmax'))\nmodel.summary()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.metrics import cohen_kappa_score\nfrom keras.callbacks import Callback\nclass Metrics(Callback):\n    def on_train_begin(self, logs={}):\n        self.val_kappas = []\n\n    def on_epoch_end(self, epoch, logs={}):\n        X_val, y_val = self.validation_data[0],self.validation_data[1]\n                \n        y_val = np.argmax(y_val,axis=1)\n        \n        y_pred = self.model.predict(X_val)\n        y_pred = np.argmax(y_pred,axis=1)\n\n        _val_kappa = cohen_kappa_score(\n            y_val,\n            y_pred, \n            weights='quadratic'\n        )\n\n        self.val_kappas.append(_val_kappa)\n\n        print(f\"val_kappa: {_val_kappa:.4f}\")\n        \n        if _val_kappa == max(self.val_kappas):\n            print(\"Validation Kappa has improved. Saving model.\")\n            self.model.save('model.h5')\n\n        return","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(loss= 'categorical_crossentropy',metrics=['accuracy'],optimizer='Adam')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint\nmetric = Metrics()\ncallback = [metric]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit_generator(train_generator,validation_data = (validation_x,validation_y),\n                              epochs = 20,steps_per_epoch = len(train_df)/32,callbacks = callback,verbose=1,\n                              class_weight = class_weights)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axs = plt.subplots(1,3,figsize=(12,8))\naxs[0].plot(history.history['acc'])\naxs[0].plot(history.history['val_acc'])\naxs[0].set_xlabel('Epoch')\naxs[0].set_ylabel('Accuracy')\naxs[0].legend(['train','validation'],loc='upper left')\naxs[0].set_title('Train and validation accuracy')\n\naxs[1].plot(history.history['loss'])\naxs[1].plot(history.history['val_loss'])\naxs[1].set_xlabel('Epoch')\naxs[1].set_ylabel('Loss')\naxs[1].legend(['train','validation'],loc='upper left')\naxs[1].set_title('Train and validation loss')\n\naxs[2].plot(metric.val_kappas)\naxs[2].set_ylabel('Quadratic weighted kappa')\naxs[2].set_xlabel('Epochs')\naxs[2].set_title('Validation data weighted cohen kappa scores')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.models import load_model\nmodel = load_model('model.h5')\nmodel.evaluate(validation_x,validation_y)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_x = []\nfor path in tqdm(test['path'].values):\n    img = cv2.resize(cv2.imread(path),(150,150))\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    test_x.append(img)\n    \ntest_x = np.array(test_x)\ntest_x = test_x.astype(np.float32)/255.0\nprint (test_x.shape)\nprint (np.amin(test_x),np.amax(test_x))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_y = model.predict(test_x)\ntest_y = np.argmax(test_y,axis=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Counter(test_y)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"output = pd.read_csv('../input/sample_submission.csv')\noutput.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"output['diagnosis'] = test_y\noutput.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"output.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":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}