{"cells":[{"metadata":{},"cell_type":"markdown","source":""},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nfrom IPython.display import SVG\nfrom keras.utils.vis_utils import model_to_dot","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport shutil\nprint(os.listdir(\"../input\"))\n\ntry:\n    os.makedirs('/tmp/.keras/datasets')\nexcept FileExistsError:\n    pass\n\ntry:\n    shutil.copytree(\"../input/keras-pretrained-models\", \"/tmp/.keras/models\")\nexcept FileExistsError:\n    pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os.path\nimport itertools\nfrom itertools import chain\n\nimport numpy as np\nimport pandas as pd\nfrom sklearn import datasets\nfrom sklearn import preprocessing\nfrom sklearn.decomposition import PCA\nfrom sklearn import cluster, datasets, mixture\nfrom sklearn.datasets import load_digits\nfrom sklearn.preprocessing import StandardScaler, OneHotEncoder\nfrom sklearn.metrics import f1_score, classification_report, confusion_matrix\nfrom sklearn.model_selection import train_test_split\n\nimport matplotlib.pyplot as plt\nfrom matplotlib.colors import ListedColormap\nimport seaborn as sns\n\nimport tensorflow as tf\n\nfrom keras.layers import Input, Embedding, LSTM, GRU, Dense, Dropout, Lambda, \\\n    Conv1D, Conv2D, Conv3D, \\\n    Conv2DTranspose, \\\n    AveragePooling1D, AveragePooling2D, \\\n    MaxPooling1D, MaxPooling2D, MaxPooling3D, \\\n    GlobalAveragePooling1D, GlobalAveragePooling2D, \\\n    GlobalMaxPooling1D, GlobalMaxPooling2D, GlobalMaxPooling3D, \\\n    LocallyConnected1D, LocallyConnected2D, \\\n    concatenate, Flatten, Average, Activation, \\\n    RepeatVector, Permute, Reshape, Dot, \\\n    multiply, dot, add, \\\n    PReLU, \\\n    Bidirectional, TimeDistributed, \\\n    SpatialDropout1D, \\\n    BatchNormalization\nfrom keras.models import Model, Sequential\nfrom keras import losses\nfrom keras.callbacks import BaseLogger, ProgbarLogger, Callback, History\nfrom keras.callbacks import ModelCheckpoint, LearningRateScheduler, ReduceLROnPlateau\nfrom keras.wrappers.scikit_learn import KerasClassifier\nfrom keras import regularizers\nfrom keras import initializers\nfrom keras.metrics import categorical_accuracy\nfrom keras.constraints import maxnorm, non_neg\nfrom keras.optimizers import RMSprop, Adam\nfrom keras.utils import to_categorical, plot_model, Sequence\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras import backend as K","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from PIL import Image\nfrom zipfile import ZipFile\nimport h5py\nimport cv2\nfrom tqdm import tqdm","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ls -la ../input","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"src_dir = '../input/aptos2019-blindness-detection'\nziped_src_dir_train = '../input/aptos2019-resize560x420-float16-train'\nziped_src_dir_test = '../input/aptos2019-resize560x420-float16-test'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_labels = pd.read_csv(os.path.join(src_dir, \"train.csv\"))\nprint(train_labels.shape)\ntrain_labels.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train = train_labels.diagnosis.values\ny_train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_cat_train = to_categorical(y_train)\ny_cat_train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_labels = pd.read_csv(os.path.join(src_dir, \"sample_submission.csv\"))\nprint(test_labels.shape)\ntest_labels.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### load float16 image data"},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train_img = np.load(os.path.join(ziped_src_dir_train, 'train_img_float16.npz'))['x']\nx_train_img.shape, x_train_img.min(), x_train_img.max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_test_img = np.load(os.path.join(ziped_src_dir_test, 'test_img_float16.npz'))['x']\nx_test_img.shape, x_test_img.min(), x_test_img.max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(x_train_img[0].astype('float32'))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"dic_train = dict(list(zip(*(train_labels['id_code'].tolist(), train_labels.index.tolist()))))\ndic_train","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"dic_test = dict(list(zip(*(test_labels['id_code'].tolist(), test_labels.index.tolist()))))\ndic_test","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_arr0(Id, test=False):\n    if test:\n        x_img = x_test_img\n        d = dic_test\n    else:\n        x_img = x_train_img\n        d = dic_train\n    #arr = x_img[d[Id]]\n    arr = x_img[d[Id]].astype('float32')\n    return arr\n\narr0 = get_arr0('0083ee8054ee')\nprint(arr0.shape)\nplt.imshow(arr0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"arr0 = get_arr0('006efc72b638', test=True)\nprint(arr0.shape)\nplt.imshow(arr0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen = ImageDataGenerator(\n    # set input mean to 0 over the dataset\n    featurewise_center=False,\n    # set each sample mean to 0\n    samplewise_center=False,\n    # divide inputs by std of dataset\n    featurewise_std_normalization=False,\n    # divide each input by its std\n    samplewise_std_normalization=False,\n    # apply ZCA whitening\n    zca_whitening=False,\n    # epsilon for ZCA whitening\n    zca_epsilon=1e-06,\n    # randomly rotate images in the range (deg 0 to 180)\n    rotation_range=10,\n    # randomly shift images horizontally\n    width_shift_range=0.0,\n    # randomly shift images vertically\n    height_shift_range=0.0,\n    # set range for random shear\n    shear_range=0.,\n    # set range for random zoom\n    zoom_range=0.,\n    # set range for random channel shifts\n    channel_shift_range=0.,\n    # set mode for filling points outside the input boundaries\n    fill_mode='nearest',\n    # value used for fill_mode = \"constant\"\n    cval=0.,\n    # randomly flip images\n    horizontal_flip=True,\n    # randomly flip images\n    vertical_flip=True,\n    # set rescaling factor (applied before any other transformation)\n    rescale=None,\n    # set function that will be applied on each input\n    preprocessing_function=None,\n    # image data format, either \"channels_first\" or \"channels_last\"\n    data_format=None,\n    # fraction of images reserved for validation (strictly between 0 and 1)\n    validation_split=0.0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"arr1 = datagen.random_transform(arr0)\nprint(arr1.shape)\nplt.imshow(arr1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import random\n\nclass Seq(object):\n    \n    def __init__(self, df, aug=False, test=False, batch_size=32):\n        self.shuffle = None\n        self.aug = aug\n        self.test = test\n        self.batch_size = batch_size\n        self.df = df\n        \n        # proccess\n        self.ids = self.df.id_code.tolist()\n        #self.reversed = sorted(range(SH_ALL[0]), reverse=True)\n        \n        # estimate self length\n        self.initialize_it()\n        self.len = 1\n        for _ in self.it:\n            self.len += 1\n        \n        self.initialize_it()\n    \n    def initialize_it(self):\n        if self.shuffle:\n            '''not implemented yet'''\n            raise NotImplementedError\n            #random.seed(self.state)\n            #random.shuffle(self.ids)\n        \n        self.it = iter(range(0, len(self.ids), self.batch_size))\n        self.idx_next = self.it.__next__()\n    \n    def __len__(self):\n        return self.len\n    \n    def __iter__(self):\n        return self\n    \n    def __next__(self):\n        idx = self.idx_next\n        self.ids_part = self.ids[idx:((idx+self.batch_size) if idx+self.batch_size<len(self.ids) else len(self.ids))]\n        res = self.getpart(self.ids_part)\n        try:\n            self.idx_next = self.it.__next__()\n        except StopIteration:\n            self.initialize_it()\n        return res\n    \n    def __getitem__(self, id0):\n        arr, tgts = self.get_data(id0)\n        cat = self.convert_tgts(tgts)\n        return arr, cat\n    \n    def random_transform(self, arr):\n        return datagen.random_transform(arr)\n    \n    def convert_tgts(self, tgts):\n        try:\n            cat = to_categorical(tgts, num_classes=5)\n        except TypeError:\n            cat = np.zeros((5,))\n        return cat\n    \n    def get_data(self, id0):\n        arr = get_arr0(id0, test=self.test)\n        \n        try:\n            y = (self.df.diagnosis[self.df.id_code == id0]).tolist()[0]\n        except AttributeError:\n            y = None\n        return arr, y\n    \n    def getpart(self, ids):\n        xs = []\n        ys = []\n        for id0 in ids:\n            self.extend_data(id0, xs, ys)\n        \n        x = np.stack(xs)\n        y = np.stack(ys)\n        return (x, y)\n    \n    def extend_data(self, id0, xs, ys):\n        arr0, cat = self[id0]\n        \n        if False:\n            mm = up_sample2[cat==1].max()\n            mm = int(mm)\n            #print(mm)\n            for ii in range(mm):\n                if self.aug:\n                    img = self.random_transform(arr0)\n                else:\n                    img = arr0\n                xs.append(img.flatten())\n                ys.append(cat)\n        else:\n            if self.aug:\n                img0 = self.random_transform(arr0)\n            else:\n                img0 = arr0\n            xs.append(img0)\n            ys.append(cat)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seq = Seq(train_labels, aug=True, batch_size=8)\nprint(len(seq))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seq['0083ee8054ee']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seq.get_data('0083ee8054ee')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x, y = next(seq)\nx.shape, y.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(x[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'test image'\nseq_test = Seq(test_labels, aug=False, batch_size=8, test=True)\nprint(len(seq_test))\nseq_test.get_data('006efc72b638')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ImageGenerator(Sequence):\n    \n    def __init__(self, seq):\n        self.seq = seq\n        \n    def __len__(self):\n        return len(self.seq)\n    \n    def __getitem__(self, idx):\n        ids = self.get_ids(idx)\n        x, y = self.seq.getpart(ids)\n        return x, y\n    \n    def get_ids(self, idx):\n        bs = self.seq.batch_size\n        ids = self.seq.ids[(idx*bs):(idx*bs+bs) if idx*bs+bs<len(self.seq.ids) else len(self.seq.ids)]\n        return ids","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img_gen = ImageGenerator(seq)\nimg_gen","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img_gen.get_ids(457)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x, y = img_gen[0]\nx.shape, y.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### make model"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras import applications\nIMG_DIM0 = (4, 210, 280, 3)\nIMG_DIM = (420, 560, 3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_trainable_false(model_resnet, trainable=False):\n    layers = model_resnet.layers\n    for ilayer in layers:\n        ilayer.trainable = trainable\n    return\n\nclass TrainableCtrl(object):\n    \n    def __init__(self, model_cnvt,\n                       model_resnet,\n                       model_classifier):\n        self.model_cnvt = model_cnvt\n        self.model_resnet = model_resnet\n        self.model_classifier = model_classifier\n        \n        self.model_dic = {\n            'cnvt': self.model_cnvt,\n            'resnet': self.model_resnet,\n            'classifier': self.model_classifier\n        }\n        self.trainable_dic = {}\n        \n        self.get_trainable()\n        \n    def get_trainable(self):\n        for k in self.model_dic:\n            model = self.model_dic[k]\n            res = []\n            for ilayer in model.layers:\n                res.append(ilayer.trainable)\n            self.trainable_dic[k] = res\n    \n    def set_trainable_false(self, model_key):\n        model = self.model_dic[model_key]\n        make_trainable_false(model)\n    \n    def set_trainable_true(self, model_key):\n        model = self.model_dic[model_key]\n        for ii, ilayer in enumerate(model.layers):\n            ilayer.trainable = self.trainable_dic[model_key][ii]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_model_cnvt(img_dim=IMG_DIM):\n    '''==============================\n    inputs\n    =============================='''\n    inp = Input(shape=img_dim)\n    oup = Conv2D(3,\n                 kernel_size=1,\n                 strides=1,\n                 padding='same',\n                 activation='tanh')(inp)\n    oup = Activation('linear')(oup)\n    model_cnvt = Model(inp, oup, name='model_cnvt')\n    return model_cnvt\n# def make_model_cnvt(img_dim=IMG_DIM0):\n#     '''==============================\n#     inputs\n#     =============================='''\n#     inp = Input(shape=img_dim)\n#     oup = Activation('linear')(inp)\n#     model_cnvt = Model(inp, oup, name='model_cnvt')\n#     return model_cnvt\n\nmodel_cnvt = make_model_cnvt()\nmodel_cnvt.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_resnet = applications.resnet50.ResNet50(\n    include_top=False,\n    weights='imagenet',\n    input_tensor=None,\n    input_shape=IMG_DIM,\n    pooling=None,\n    classes=None)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"model_resnet.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_model_classifier(input_dim=2048):\n    inp_cls = Input((input_dim,))\n    oup_cls = Dense(1024, activation='elu')(inp_cls)\n    oup_cls = Dense(5)(oup_cls)\n    oup_cls = Activation('softmax')(oup_cls)\n    model_classifier = Model(inp_cls, oup_cls, name='classifier')\n    return model_classifier\n\nmodel_classifier = make_model_classifier()\nmodel_classifier.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_classifier.predict(np.ones((2,2048)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"K.clear_session()\nmodel_cnvt = make_model_cnvt()\nmodel_classifier = make_model_classifier()\nmodel_resnet = applications.resnet50.ResNet50(\n    include_top=False,\n    weights='imagenet',\n    input_tensor=None,\n    input_shape=IMG_DIM,\n    pooling=None,\n    classes=None)\n\ndef make_model(model_cnvt, model_resnet, model_classifier):\n    inp0 = Input(shape=IMG_DIM, name='input0')\n    oup0 = model_cnvt(inp0)\n    oup0 = model_resnet(oup0)\n    oup2 = GlobalAveragePooling2D()(oup0)\n    oup0 = model_classifier(oup2)\n    oup0 = Activation('linear', name='path_cls_cls')(oup0)\n    model0 = Model(inp0, oup0, name='model0')\n    \n    '''==============================\n    inputs\n    =============================='''\n    model = model0\n    \n    return {\n        'model_classifier': model_classifier,\n        'model_resnet': model_resnet,\n        'model_cnvt': model_cnvt,\n        'model': model,\n        'model0': model0\n    }\n\nmodels = make_model(model_cnvt, model_resnet, model_classifier)\nmodels['model'].summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_ctrl = TrainableCtrl(model_cnvt, model_resnet, model_classifier)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''\nmodel_resnet : not train\n'''\ntrain_ctrl.set_trainable_false('resnet')\n\nmodels['model'].compile(loss='categorical_crossentropy',\n                        optimizer=Adam(0.0001),\n                        metrics=['categorical_accuracy'])\n\nmodels['model'].summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seq = Seq(train_labels, aug=True, batch_size=16)\nprint(len(seq))\nimg_gen = ImageGenerator(seq)\n\nhst = models['model'].fit_generator(img_gen, epochs=2,\n                                    steps_per_epoch=len(img_gen),\n                                    use_multiprocessing=False, workers=5)\n# hst = models['model'].fit_generator(img_gen, epochs=4,\n#                                     steps_per_epoch=len(img_gen),\n#                                     use_multiprocessing=False, workers=5,\n#                                     callbacks=callbacks)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seq_pred = Seq(train_labels, aug=False, batch_size=32)\nprint(len(seq_pred))\nimg_gen_pred = ImageGenerator(seq_pred)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred = models['model'].predict_generator(\n    img_gen_pred,\n    steps=len(img_gen_pred),\n    verbose=1, workers=5)\npred.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f1_score(y_train, np.argmax(pred, axis=1), average='macro'))\nprint(classification_report(y_train, np.argmax(pred, axis=1)))\nconfusion_matrix(y_train, np.argmax(pred, axis=1))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_ctrl.set_trainable_true('resnet')\n\nmodels['model'].compile(loss='categorical_crossentropy',\n                        optimizer='adam',\n                        metrics=['categorical_accuracy'])\n\nmodels['model'].summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def lr_schedule(epoch):\n    lr = 0.0005\n    if divmod(epoch,4)[1] == 3:\n        lr *= (1/8)\n    elif divmod(epoch,4)[1] == 2:\n        lr *= (1/4)\n    elif divmod(epoch,4)[1] == 1:\n        lr *= (1/2)\n    elif divmod(epoch,4)[1] == 0:\n        pass\n    print('Learning rate: ', lr)\n    return lr\n\nlr_scheduler = LearningRateScheduler(lr_schedule)\ncallbacks = [lr_scheduler]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seq = Seq(train_labels, aug=True, batch_size=16)\nprint(len(seq))\nimg_gen = ImageGenerator(seq)\n\n# hst = models['model'].fit_generator(img_gen, epochs=4,\n#                                     steps_per_epoch=len(img_gen),\n#                                     use_multiprocessing=False, workers=5)\nhst = models['model'].fit_generator(img_gen, epochs=4,\n                                    steps_per_epoch=len(img_gen),\n                                    use_multiprocessing=False, workers=5,\n                                    callbacks=callbacks)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seq_pred = Seq(train_labels, aug=False, batch_size=32)\nprint(len(seq_pred))\nimg_gen_pred = ImageGenerator(seq_pred)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred = models['model'].predict_generator(\n    img_gen_pred,\n    steps=len(img_gen_pred),\n    verbose=1, workers=5)\npred.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f1_score(y_train, np.argmax(pred, axis=1), average='macro'))\nprint(classification_report(y_train, np.argmax(pred, axis=1)))\nconfusion_matrix(y_train, np.argmax(pred, axis=1))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seq_test = Seq(test_labels, test=True, aug=False, batch_size=32)\nseq_test\nprint(len(seq_test))\nimg_gen_pred_test = ImageGenerator(seq_test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred_test = models['model'].predict_generator(\n    img_gen_pred_test,\n    steps=len(img_gen_pred_test),\n    verbose=1, workers=3)\npred_test.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"np.argmax(pred_test, axis=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = test_labels.copy()\nsubmission['diagnosis'] = np.argmax(pred_test, axis=1)\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.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}