{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"import time\nt_start = time.time()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport pandas as pd \nimport numpy as np\nimport os, gc, sys\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport keras\nfrom keras import backend as K\nfrom keras import layers, models, optimizers, applications\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.preprocessing.image import img_to_array, load_img\n\nfrom keras.models import Model, load_model\nfrom keras.applications.resnet50 import preprocess_input\nfrom keras.layers import Dense, Dropout, GlobalAveragePooling2D, Input\nfrom keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\n\n\nsys.path.append(os.path.abspath('../input/efficientnet/efficientnet-master/efficientnet-master/'))\nfrom efficientnet import EfficientNetB5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Model parameters\n\nIMG_WIDTH       = 512\nIMG_HEIGHT      = 512\nCHANNEL         = 3\n\nBATCH_SIZE      = 4\n\nOLD_DATA_EPOCHS = 6\nNEW_DATA_EPOCHS = 6\nWARMUP_EPOCHS   = 3\n\nNUM_CLASSES     = 5\nSEED            = 2\nn_folds         = 1\n\nES_PATIENCE     = 5\nRLROP_PATIENCE  = 2\nDECAY_DROP      = 0.2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def append_file_ext_png(file_name):\n    return file_name + \".png\"\n\ndef append_file_ext_jpeg(file_name):\n    return file_name + \".jpeg\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Training with Old competition Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"BASE_DIR  = '/kaggle/input/aptos2019-blindness-detection/'\n\nTRAIN_DIR = '/kaggle/input/diabetic-retinopathy-resized/resized_train/resized_train'\nTEST_DIR  = '/kaggle/input/aptos2019-blindness-detection/test_images'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TEST_DF          = pd.read_csv(BASE_DIR + \"test.csv\",dtype='object')\nTRAIN_DF         = pd.read_csv(\"/kaggle/input/diabetic-retinopathy-resized/trainLabels.csv\",dtype='object')\nTRAIN_DF.columns = ['id_code', 'diagnosis'] \n\nX_COL='id_code'\nY_COL='diagnosis'\n\nTRAIN_DF[X_COL] = TRAIN_DF[X_COL].apply(append_file_ext_jpeg)\nTEST_DF[X_COL]  = TEST_DF[X_COL].apply(append_file_ext_png)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df0 = TRAIN_DF.loc[TRAIN_DF['diagnosis'] == '0']\ndf1 = TRAIN_DF.loc[TRAIN_DF['diagnosis'] == '1']\ndf2 = TRAIN_DF.loc[TRAIN_DF['diagnosis'] == '2']\ndf3 = TRAIN_DF.loc[TRAIN_DF['diagnosis'] == '3']\ndf4 = TRAIN_DF.loc[TRAIN_DF['diagnosis'] == '4']\n\ndf0 = df0.head(2000)\ndf1 = df1.head(2000)\ndf2 = df2.head(2000)\n\nTRAIN_DF = df0.append([df1, df2, df3, df4],ignore_index = True)\nfrom sklearn.utils import shuffle\nTRAIN_DF = shuffle(TRAIN_DF)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(TRAIN_DF.head())\nprint('************************')\nprint(TEST_DF.head())\nprint('************************')\nprint(len(TRAIN_DF))\nprint('************************')\nprint(len(TEST_DF))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# def crop_image_from_gray(img, tol=7):\n#     # If for some reason we only have two channels\n#     if img.ndim == 2:\n#         mask = img > tol\n#         return img[np.ix_(mask.any(1),mask.any(0))]\n#     # If we have a normal RGB images\n#     elif img.ndim == 3:\n#         gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n#         mask = gray_img > tol\n        \n#         check_shape = img[:,:,0][np.ix_(mask.any(1),mask.any(0))].shape[0]\n#         if (check_shape == 0): # image is too dark so that we crop out everything,\n#             return img # return original image\n#         else:\n#             img1=img[:,:,0][np.ix_(mask.any(1),mask.any(0))]\n#             img2=img[:,:,1][np.ix_(mask.any(1),mask.any(0))]\n#             img3=img[:,:,2][np.ix_(mask.any(1),mask.any(0))]\n#             img = np.stack([img1,img2,img3],axis=-1)\n#         return img\n    \n# def preprocess_image(path, sigmaX=10):\n#     image = cv2.imread(path)\n#     image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n#     image = crop_image_from_gray(image)\n#     image = cv2.resize(image, (IMG_WIDTH, IMG_WIDTH))\n#     image = cv2.addWeighted (image,4, cv2.GaussianBlur(image, (0,0) ,sigmaX), -4, 128)\n#     return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def crop_image_from_gray(image):\n    IMAGE_SIZE = (IMG_WIDTH, IMG_WIDTH)\n    image = cv2.addWeighted(image, 4, cv2.GaussianBlur(image, (0, 0), 30), -4, 128)\n    \n    height, width, _ = image.shape\n    center_x = int(width / 2)\n    center_y = int(height / 2)\n    radius = min(center_x, center_y)\n    \n    circle_mask = np.zeros((height, width), np.uint8)\n    cv2.circle(circle_mask, (center_x, center_y), radius, color=1, thickness=-1)\n    image = cv2.resize(cv2.bitwise_and(image, image, mask=circle_mask)[center_y - radius:center_y + radius, center_x - radius:center_x + radius], IMAGE_SIZE)\n    \n    return image\n\ndef preprocess_image(path, sigmaX=10):\n    image = cv2.imread(path)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image = crop_image_from_gray(image)\n    return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Code Source: https://github.com/CyberZHG/keras-radam/blob/master/keras_radam/optimizers.py\nclass RAdam(keras.optimizers.Optimizer):\n    \"\"\"RAdam optimizer.\n    # Arguments\n        lr: float >= 0. Learning rate.\n        beta_1: float, 0 < beta < 1. Generally close to 1.\n        beta_2: float, 0 < beta < 1. Generally close to 1.\n        epsilon: float >= 0. Fuzz factor. If `None`, defaults to `K.epsilon()`.\n        decay: float >= 0. Learning rate decay over each update.\n        weight_decay: float >= 0. Weight decay for each param.\n        amsgrad: boolean. Whether to apply the AMSGrad variant of this\n            algorithm from the paper \"On the Convergence of Adam and\n            Beyond\".\n        total_steps: int >= 0. Total number of training steps. Enable warmup by setting a positive value.\n        warmup_proportion: 0 < warmup_proportion < 1. The proportion of increasing steps.\n        min_lr: float >= 0. Minimum learning rate after warmup.\n    # References\n        - [Adam - A Method for Stochastic Optimization](https://arxiv.org/abs/1412.6980v8)\n        - [On the Convergence of Adam and Beyond](https://openreview.net/forum?id=ryQu7f-RZ)\n        - [On The Variance Of The Adaptive Learning Rate And Beyond](https://arxiv.org/pdf/1908.03265v1.pdf)\n    \"\"\"\n\n    def __init__(self, lr=0.001, beta_1=0.9, beta_2=0.999,\n                 epsilon=None, decay=0., weight_decay=0., amsgrad=False,\n                 total_steps=0, warmup_proportion=0.1, min_lr=0., **kwargs):\n        super(RAdam, self).__init__(**kwargs)\n        with K.name_scope(self.__class__.__name__):\n            self.iterations = K.variable(0, dtype='int64', name='iterations')\n            self.lr = K.variable(lr, name='lr')\n            self.beta_1 = K.variable(beta_1, name='beta_1')\n            self.beta_2 = K.variable(beta_2, name='beta_2')\n            self.decay = K.variable(decay, name='decay')\n            self.weight_decay = K.variable(weight_decay, name='weight_decay')\n            self.total_steps = K.variable(total_steps, name='total_steps')\n            self.warmup_proportion = K.variable(warmup_proportion, name='warmup_proportion')\n            self.min_lr = K.variable(lr, name='min_lr')\n        if epsilon is None:\n            epsilon = K.epsilon()\n        self.epsilon = epsilon\n        self.initial_decay = decay\n        self.initial_weight_decay = weight_decay\n        self.initial_total_steps = total_steps\n        self.amsgrad = amsgrad\n\n    def get_updates(self, loss, params):\n        grads = self.get_gradients(loss, params)\n        self.updates = [K.update_add(self.iterations, 1)]\n\n        lr = self.lr\n\n        if self.initial_decay > 0:\n            lr = lr * (1. / (1. + self.decay * K.cast(self.iterations, K.dtype(self.decay))))\n\n        t = K.cast(self.iterations, K.floatx()) + 1\n\n        if self.initial_total_steps > 0:\n            warmup_steps = self.total_steps * self.warmup_proportion\n            decay_steps = self.total_steps - warmup_steps\n            lr = K.switch(\n                t <= warmup_steps,\n                lr * (t / warmup_steps),\n                lr * (1.0 - K.minimum(t, decay_steps) / decay_steps),\n            )\n\n        ms = [K.zeros(K.int_shape(p), dtype=K.dtype(p), name='m_' + str(i)) for (i, p) in enumerate(params)]\n        vs = [K.zeros(K.int_shape(p), dtype=K.dtype(p), name='v_' + str(i)) for (i, p) in enumerate(params)]\n\n        if self.amsgrad:\n            vhats = [K.zeros(K.int_shape(p), dtype=K.dtype(p), name='vhat_' + str(i)) for (i, p) in enumerate(params)]\n        else:\n            vhats = [K.zeros(1, name='vhat_' + str(i)) for i in range(len(params))]\n\n        self.weights = [self.iterations] + ms + vs + vhats\n\n        beta_1_t = K.pow(self.beta_1, t)\n        beta_2_t = K.pow(self.beta_2, t)\n\n        sma_inf = 2.0 / (1.0 - self.beta_2) - 1.0\n        sma_t = sma_inf - 2.0 * t * beta_2_t / (1.0 - beta_2_t)\n\n        for p, g, m, v, vhat in zip(params, grads, ms, vs, vhats):\n            m_t = (self.beta_1 * m) + (1. - self.beta_1) * g\n            v_t = (self.beta_2 * v) + (1. - self.beta_2) * K.square(g)\n\n            m_corr_t = m_t / (1.0 - beta_1_t)\n            if self.amsgrad:\n                vhat_t = K.maximum(vhat, v_t)\n                v_corr_t = K.sqrt(vhat_t / (1.0 - beta_2_t) + self.epsilon)\n                self.updates.append(K.update(vhat, vhat_t))\n            else:\n                v_corr_t = K.sqrt(v_t / (1.0 - beta_2_t) + self.epsilon)\n\n            r_t = K.sqrt((sma_t - 4.0) / (sma_inf - 4.0) *\n                         (sma_t - 2.0) / (sma_inf - 2.0) *\n                         sma_inf / sma_t)\n\n            p_t = K.switch(sma_t > 5, r_t * m_corr_t / v_corr_t, m_corr_t)\n\n            if self.initial_weight_decay > 0:\n                p_t += self.weight_decay * p\n\n            p_t = p - lr * p_t\n\n            self.updates.append(K.update(m, m_t))\n            self.updates.append(K.update(v, v_t))\n            new_p = p_t\n\n            # Apply constraints.\n            if getattr(p, 'constraint', None) is not None:\n                new_p = p.constraint(new_p)\n\n            self.updates.append(K.update(p, new_p))\n        return self.updates\n\n    def get_config(self):\n        config = {\n            'lr': float(K.get_value(self.lr)),\n            'beta_1': float(K.get_value(self.beta_1)),\n            'beta_2': float(K.get_value(self.beta_2)),\n            'decay': float(K.get_value(self.decay)),\n            'weight_decay': float(K.get_value(self.weight_decay)),\n            'epsilon': self.epsilon,\n            'amsgrad': self.amsgrad,\n            'total_steps': float(K.get_value(self.total_steps)),\n            'warmup_proportion': float(K.get_value(self.warmup_proportion)),\n            'min_lr': float(K.get_value(self.min_lr)),\n        }\n        base_config = super(RAdam, self).get_config()\n        return dict(list(base_config.items()) + list(config.items()))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_model(input_shape, n_out):\n    input_tensor = Input(shape=input_shape)\n    \n    base_model = EfficientNetB5(weights=None, \n                                       include_top=False,\n                                       input_tensor=input_tensor)\n    base_model.load_weights('/kaggle/input/efficientnet-keras-weights-b0b5/efficientnet-b5_imagenet_1000_notop.h5')\n        \n\n    x = GlobalAveragePooling2D()(base_model.output)\n    x = Dropout(0.5)(x)\n    x = Dense(2048, activation='relu')(x)\n    x = Dropout(0.5)(x)\n    x = Dense(1024, activation='relu')(x)\n    \n    final_output = Dense(n_out, activation='softmax', name='final_output')(x)\n    model = Model(input_tensor, final_output)\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"es = EarlyStopping(monitor='val_loss',\n                   mode='min', \n                   patience=ES_PATIENCE, \n                   restore_best_weights=True, \n                   verbose=1)\n\nrlrop = ReduceLROnPlateau(monitor='val_loss', \n                          mode='min', \n                          patience=RLROP_PATIENCE, \n                          factor=DECAY_DROP, \n                          verbose=1)\n\nmodel_checkpoint = ModelCheckpoint('EfficientNetB5.h5',\n                                   verbose=1, \n                                   save_best_only=True)\n\ncallback_list = [es, rlrop, model_checkpoint]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen         = ImageDataGenerator(\n                    rescale=1./255.,\n                    validation_split=0.25)\n\ntrain_generator = datagen.flow_from_dataframe(\n                    dataframe=TRAIN_DF,\n                    directory=TRAIN_DIR,\n                    x_col=X_COL,\n                    y_col=Y_COL,\n                    subset=\"training\",\n                    batch_size=BATCH_SIZE,\n                    seed=SEED,\n                    zoom_range=0.2,\n                    horizontal_flip=True,\n                    vertical_flip=True,\n                    class_mode=\"categorical\",\n                    preprocessing_function=preprocess_image,\n                    target_size=(IMG_WIDTH,IMG_HEIGHT))\n\nvalid_generator=datagen.flow_from_dataframe(\n                    dataframe=TRAIN_DF,\n                    directory=TRAIN_DIR,\n                    x_col=X_COL,\n                    y_col=Y_COL,\n                    subset=\"validation\",\n                    batch_size=BATCH_SIZE,\n                    seed=SEED,\n                    zoom_range=0.2,\n                    horizontal_flip=True,\n                    vertical_flip=True,\n                    class_mode=\"categorical\",\n                    preprocessing_function=preprocess_image,    \n                    target_size=(IMG_WIDTH,IMG_HEIGHT))\n\nSTEP_SIZE_TRAIN=train_generator.n//train_generator.batch_size\nSTEP_SIZE_VALID=valid_generator.n//valid_generator.batch_size","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = create_model(input_shape=(IMG_WIDTH, IMG_HEIGHT, CHANNEL), n_out=NUM_CLASSES)\n        \nfor layer in model.layers:\n    layer.trainable = False\n\nfor i in range(-10, 0):\n    model.layers[i].trainable = True\n\nmetric_list = [\"accuracy\"]\noptimizer = RAdam(lr=0.00005)\nmodel.compile(optimizer=optimizer, loss=\"categorical_crossentropy\",  metrics=metric_list)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history_warmup = model.fit_generator(generator=train_generator,\n                          steps_per_epoch=STEP_SIZE_TRAIN,\n                          validation_data=valid_generator,\n                          validation_steps=STEP_SIZE_VALID,\n                          epochs=WARMUP_EPOCHS,\n                          verbose=1).history","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for layer in model.layers:\n    layer.trainable = True\n\noptimizer = RAdam(lr=0.00005)\nmodel.compile(optimizer=optimizer, loss=\"binary_crossentropy\",  metrics=metric_list)\n\ngc.collect()\n\nhistory_finetunning = model.fit_generator(generator=train_generator,\n                              steps_per_epoch=STEP_SIZE_TRAIN,\n                              validation_data=valid_generator,\n                              validation_steps=STEP_SIZE_VALID,\n                              epochs=OLD_DATA_EPOCHS,\n                              callbacks=callback_list,\n                              verbose=1).history","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Training with New competition Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"NEW_TRAIN_DIR       = '/kaggle/input/aptos2019-blindness-detection/train_images'\nNEW_TRAIN_DF        = pd.read_csv(BASE_DIR + \"train.csv\",dtype='object')\nNEW_TRAIN_DF[X_COL] = NEW_TRAIN_DF[X_COL].apply(append_file_ext_png)\nNEW_TRAIN_DF.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen         = ImageDataGenerator(\n                    rescale=1./255.,\n                    validation_split=0.25)\n\ntrain_generator = datagen.flow_from_dataframe(\n                    dataframe=NEW_TRAIN_DF,\n                    directory=NEW_TRAIN_DIR,\n                    x_col=X_COL,\n                    y_col=Y_COL,\n                    subset=\"training\",\n                    batch_size=BATCH_SIZE,\n                    seed=SEED,\n                    zoom_range=0.2,\n                    horizontal_flip=True,\n                    vertical_flip=True,\n                    class_mode=\"categorical\",\n                    preprocessing_function=preprocess_image,\n                    target_size=(IMG_WIDTH,IMG_HEIGHT))\n\nvalid_generator=datagen.flow_from_dataframe(\n                    dataframe=NEW_TRAIN_DF,\n                    directory=NEW_TRAIN_DIR,\n                    x_col=X_COL,\n                    y_col=Y_COL,\n                    subset=\"validation\",\n                    batch_size=BATCH_SIZE,\n                    seed=SEED,\n                    zoom_range=0.2,\n                    horizontal_flip=True,\n                    vertical_flip=True,\n                    class_mode=\"categorical\",\n                    preprocessing_function=preprocess_image,    \n                    target_size=(IMG_WIDTH,IMG_HEIGHT))\n\nSTEP_SIZE_TRAIN=train_generator.n//train_generator.batch_size\nSTEP_SIZE_VALID=valid_generator.n//valid_generator.batch_size","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# for layer in model.layers:\n#     layer.trainable = True\n\n# optimizer = RAdam(lr=0.00005)\n# model.compile(optimizer=optimizer, loss=\"binary_crossentropy\",  metrics=metric_list)\n\ngc.collect()\n\nhistory_finetunning = model.fit_generator(generator=train_generator,\n                              steps_per_epoch=STEP_SIZE_TRAIN,\n                              validation_data=valid_generator,\n                              validation_steps=STEP_SIZE_VALID,\n                              epochs=NEW_DATA_EPOCHS,\n                              callbacks=callback_list,\n                              verbose=1).history","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# history = {'loss': history_warmup['loss'] + history_finetunning['loss'], \n#            'val_loss': history_warmup['val_loss'] + history_finetunning['val_loss'], \n#            'acc': history_warmup['acc'] + history_finetunning['acc'], \n#            'val_acc': history_warmup['val_acc'] + history_finetunning['val_acc']}\n\n# sns.set_style(\"whitegrid\")\n# fig, (ax1, ax2) = plt.subplots(2, 1, sharex='col', figsize=(20, 14))\n\n# ax1.plot(history['loss'], label='Train loss')\n# ax1.plot(history['val_loss'], label='Validation loss')\n# ax1.legend(loc='best')\n# ax1.set_title('Loss')\n\n# ax2.plot(history['acc'], label='Train Accuracy')\n# ax2.plot(history['val_acc'], label='Validation accuracy')\n# ax2.legend(loc='best')\n# ax2.set_title('Accuracy')\n\n# plt.xlabel('Epochs')\n# sns.despine()\n# plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"optimizer = RAdam(lr=0.00005)\nmetric_list = [\"accuracy\"]\nmodel = load_model('EfficientNetB5.h5', compile=False)\nmodel.compile(optimizer=optimizer, loss=\"binary_crossentropy\",  metrics=metric_list)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_datagen=ImageDataGenerator(rescale=1./255.)\n\ntest_generator=test_datagen.flow_from_dataframe(\n                    dataframe=TEST_DF,\n                    directory=TEST_DIR,\n                    x_col=X_COL,\n                    y_col=None,\n                    batch_size=BATCH_SIZE,\n                    seed=SEED,\n                    shuffle=False,\n                    class_mode=None,\n                    preprocessing_function=preprocess_image,    \n                    target_size=(IMG_WIDTH,IMG_HEIGHT))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if test_generator.n%BATCH_SIZE > 0:\n    PREDICTION_STEPS = (test_generator.n//BATCH_SIZE) + 1\nelse:\n    PREDICTION_STEPS = (test_generator.n//BATCH_SIZE)\n\nprint(PREDICTION_STEPS)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_generator.reset()\npreds = model.predict_generator(test_generator,\n                                steps=PREDICTION_STEPS, \n                                verbose=1) \npredictions = [np.argmax(pred) for pred in preds]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"filenames = test_generator.filenames\nresults = pd.DataFrame({'id_code':filenames, 'diagnosis':predictions})\nresults['id_code'] = results['id_code'].map(lambda x: str(x)[:-4])\nresults.astype({'diagnosis': 'int64'})\nresults.to_csv('submission.csv',index=False)\nprint(results.head(10))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"t_finish = time.time()\ntotal_time = round((t_finish-t_start) / 3600, 4)\nprint('Kernel runtime = {} hours ({} minutes)'.format(total_time, int(total_time*60)))","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}