{"cells":[{"metadata":{},"cell_type":"markdown","source":"\n# Source Kernels\n\n* [[No TTA] Cassava Resnext50_32x4d Inference lb0.903](https://www.kaggle.com/piantic/no-tta-cassava-resnext50-32x4d-inference-lb0-903/output)\n* [Clean_Inference_Kernel_8xTTA_LB902](https://www.kaggle.com/underwearfitting/clean-inference-kernel-8xtta-lb902/data)\n* [Cassava-ensemble-(efnetb3-resnet50)](https://www.kaggle.com/shubham108/cassava-ensemble-efnetb3-resnet50)"},{"metadata":{},"cell_type":"markdown","source":"# Some Modifications:\nbatch_size=128\n\nFinal prediction = 0.65*predictions + 0.35*np.mean\n\nResult: 101st place on Private (+332 from Public LB)\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"#for efficientnet\nBATCH_SIZE = 1\nimage_size = 512\nenet_type = ['tf_efficientnet_b4_ns'] * 5\nmodel_path = ['../input/cassavaroots/baseline_cld_fold0_epoch8_tf_efficientnet_b4_ns_512.pth', \n              '../input/cassavaroots/baseline_cld_fold1_epoch9_tf_efficientnet_b4_ns_512.pth', \n              '../input/cassavaroots/baseline_cld_fold2_epoch9_tf_efficientnet_b4_ns_512.pth',\n              '../input/cassavaroots/baseline_cld_fold3_epoch5_tf_efficientnet_b4_ns_512.pth',\n              '../input/cassavaroots/baseline_cld_fold4_epoch11_tf_efficientnet_b4_ns_512.pth']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# Library\n# ====================================================\nimport sys\nsys.path.append('../input/pytorch-image-models/pytorch-image-models-master')\n\nimport os\nimport math\nimport time\nimport random\nimport shutil\nimport albumentations\nfrom pathlib import Path\nfrom contextlib import contextmanager\nfrom collections import defaultdict, Counter\n\nimport scipy as sp\nfrom scipy.special import softmax\nimport numpy as np\nimport pandas as pd\n\nfrom sklearn import preprocessing\nfrom sklearn.metrics import accuracy_score\nfrom sklearn.model_selection import StratifiedKFold\n\nfrom tqdm.auto import tqdm\nfrom functools import partial\n\nimport cv2\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.optim import Adam, SGD\nimport torchvision.models as models\nfrom torch.nn.parameter import Parameter\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.optim.lr_scheduler import CosineAnnealingWarmRestarts, CosineAnnealingLR, ReduceLROnPlateau\n\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nimport timm\n\nimport warnings \nwarnings.filterwarnings('ignore')\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import sys\nsys.path.append('../input/pytorch-image-models/pytorch-image-models-master')\nimport timm\n\nimage_size = 512\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nimport os\n\nOUTPUT_DIR = './'\nMODEL_DIR = '../input/cassavaroots/'\nif not os.path.exists(OUTPUT_DIR):\n    os.makedirs(OUTPUT_DIR)\n    \nTRAIN_PATH = '../input/cassava-leaf-disease-classification/train_images'\nTEST_PATH = '../input/cassava-leaf-disease-classification/test_images'\n# ====================================================\n# CFG for Resnext\n# ====================================================\nclass CFG:\n    debug=False\n    num_workers=4\n    model_name='resnext50_32x4d'\n    size=512\n    batch_size=128\n    seed=1992\n    num_classes=5\n    target_col='label'\n    resnext = 'resnext50_32x4d'\n    n_fold=5\n    trn_fold=[0, 1, 2, 3, 4]\n    inference=True\ndef seed_all(seed: int):\n    if not seed:\n        seed = 10\n\n    print(\"[ Using Seed : \", seed, \" ]\")\n    \n    os.environ['PYTHONHASHSEED'] = str(seed)  \n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.cuda.manual_seed(seed) \n    np.random.seed(seed)\n    random.seed(seed) \n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n    torch.backends.cudnn.enabled = False\n    \ndef seed_worker(worker_id):\n    worker_seed = torch.initial_seed() % 2**32\n    np.random.seed(worker_seed)\n    random.seed(worker_seed)\nseed_all(seed=CFG.seed)\n\ntest = pd.read_csv('../input/cassava-leaf-disease-classification/sample_submission.csv')\ntest['filepath'] = test.image_id.apply(lambda x: os.path.join('../input/cassava-leaf-disease-classification/test_images', f'{x}'))\n#test.head()\n\n# ====================================================\n# Dataset for Resnext\n# ====================================================\nclass TestDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df\n        self.file_names = df['image_id'].values\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        file_name = self.file_names[idx]\n        file_path = f'{TEST_PATH}/{file_name}'\n        image = cv2.imread(file_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        if self.transform:\n            augmented = self.transform(image=image)\n            image = augmented['image']\n        return image\n    \n# ====================================================\n# Transforms for Resnext\n# ====================================================\nimport albumentations as A\ndef get_transforms(*, data):\n    if data == 'valid':\n        return A.Compose([\n            A.Resize(CFG.size, CFG.size),\n            A.Normalize(\n                mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225],\n            ),\n            ToTensorV2(),\n        ])\n    \n# ====================================================\n# ResNext Model\n# ====================================================\nclass CustomResNext(nn.Module):\n    def __init__(self, config, pretrained=False):\n        super().__init__()\n        self.model = timm.create_model(model_name=config.resnext, pretrained=pretrained)\n        n_features = self.model.fc.in_features\n        self.model.fc = nn.Linear(n_features, config.num_classes)\n        \n    def forward(self, input_neurons):\n        output_predictions = self.model(input_neurons)\n        return output_predictions\n# ====================================================\n# Helper functions for Resnext\n# ====================================================\ndef load_state(model_path):\n    model = CustomResNext(CFG, pretrained=False)\n    try:  # single GPU model_file\n        model.load_state_dict(torch.load(model_path)['model'], strict=True)\n        state_dict = torch.load(model_path)['model']\n    except:  # multi GPU model_file\n        state_dict = torch.load(model_path)['model']\n        state_dict = {k[7:] if k.startswith('module.') else k: state_dict[k] for k in state_dict.keys()}\n\n    return state_dict\n\n\ndef inference(model, states, test_loader, device):\n    model.to(device)\n    tk0 = tqdm(enumerate(test_loader), total=len(test_loader))\n    probs = []\n    for i, (images) in tk0:\n        images = images.to(device)\n        avg_preds = []\n        for state in states:\n            model.load_state_dict(state)\n            model.eval()\n            with torch.no_grad():\n                y_preds = model(images)\n                print(y_preds)\n            avg_preds.append(y_preds.softmax(1).to('cpu').numpy())\n        avg_preds = np.mean(avg_preds, axis=0)\n        probs.append(avg_preds)\n        \n    probs = np.concatenate(probs)\n    return probs\n\n#for Resnext\nmodel = CustomResNext(CFG, pretrained=False)\n#model = enet_v2(enet_type[i], out_dim=5)\nstates = [load_state(MODEL_DIR+f'{CFG.model_name}_fold{fold}.pth') for fold in CFG.trn_fold]\ntest_dataset = TestDataset(test, transform=get_transforms(data='valid'))\ntest_loader = DataLoader(test_dataset, batch_size=CFG.batch_size, shuffle=False, \n                         num_workers=CFG.num_workers, pin_memory=True)\n\n\npredictions = inference(model, states, test_loader, device)\npredictions","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Transform for efficientnet\ntransforms_valid = albumentations.Compose([\n    albumentations.CenterCrop(image_size, image_size, p=1),\n    albumentations.Resize(image_size, image_size),\n    albumentations.Normalize()\n])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Directory settings"},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# Directory settings for Resnext\n# ====================================================\nimport os\n\nOUTPUT_DIR = './'\nMODEL_DIR = '../input/cassavaroots/'\nif not os.path.exists(OUTPUT_DIR):\n    os.makedirs(OUTPUT_DIR)\n    \nTRAIN_PATH = '../input/cassava-leaf-disease-classification/train_images'\nTEST_PATH = '../input/cassava-leaf-disease-classification/test_images'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# CFG"},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# CFG for Resnext\n# ====================================================\nclass CFG:\n    debug=False\n    num_workers=4\n    model_name='resnext50_32x4d'\n    size=512\n    batch_size=128    #was 32\n    seed=1992\n    num_classes=5\n    target_col='label'\n    resnext = 'resnext50_32x4d'\n    n_fold=5\n    trn_fold=[0, 1, 2, 3, 4]\n    inference=True","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Utils"},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# Utils for Resnext\n# ====================================================\ndef get_score(y_true, y_pred):\n    return accuracy_score(y_true, y_pred)\n\n\n@contextmanager\ndef timer(name):\n    t0 = time.time()\n    LOGGER.info(f'[{name}] start')\n    yield\n    LOGGER.info(f'[{name}] done in {time.time() - t0:.0f} s.')\n\n\ndef init_logger(log_file=OUTPUT_DIR+'inference.log'):\n    from logging import getLogger, INFO, FileHandler,  Formatter,  StreamHandler\n    logger = getLogger(__name__)\n    logger.setLevel(INFO)\n    handler1 = StreamHandler()\n    handler1.setFormatter(Formatter(\"%(message)s\"))\n    handler2 = FileHandler(filename=log_file)\n    handler2.setFormatter(Formatter(\"%(message)s\"))\n    logger.addHandler(handler1)\n    logger.addHandler(handler2)\n    return logger\n\n#LOGGER = init_logger()\n\ndef seed_torch(seed=42):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n\nseed_torch(seed=CFG.seed)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def seed_all(seed: int):\n    if not seed:\n        seed = 10\n\n    print(\"[ Using Seed : \", seed, \" ]\")\n    \n    os.environ['PYTHONHASHSEED'] = str(seed) \n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.cuda.manual_seed(seed) \n    np.random.seed(seed) \n    random.seed(seed) \n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n    torch.backends.cudnn.enabled = False\n    \ndef seed_worker(worker_id):\n    worker_seed = torch.initial_seed() % 2**32\n    np.random.seed(worker_seed)\n    random.seed(worker_seed)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seed_all(seed=CFG.seed)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Loading"},{"metadata":{"trusted":true},"cell_type":"code","source":"test = pd.read_csv('../input/cassava-leaf-disease-classification/sample_submission.csv')\ntest['filepath'] = test.image_id.apply(lambda x: os.path.join('../input/cassava-leaf-disease-classification/test_images', f'{x}'))\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# Dataset for Resnext\n# ====================================================\nclass TestDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df\n        self.file_names = df['image_id'].values\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        file_name = self.file_names[idx]\n        file_path = f'{TEST_PATH}/{file_name}'\n        image = cv2.imread(file_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        if self.transform:\n            augmented = self.transform(image=image)\n            image = augmented['image']\n        return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# Dataset for efficientnet\n# ====================================================\nclass CLDDataset(Dataset):\n    def __init__(self, df, mode, transform=None):\n        self.df = df.reset_index(drop=True)\n        self.mode = mode\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, index):\n        row = self.df.loc[index]\n        image = cv2.imread(row.filepath)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        \n        if self.transform is not None:\n            res = self.transform(image=image)\n            image = res['image']\n        \n        image = image.astype(np.float32)\n        image = image.transpose(2,0,1)\n        if self.mode == 'test':\n            return torch.tensor(image).float()\n        else:\n            return torch.tensor(image).float(), torch.tensor(row.label).float()\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#for efficientnet\ntest_dataset_efficient = CLDDataset(test, 'test', transform=transforms_valid)\ntest_loader_efficient = torch.utils.data.DataLoader(test_dataset_efficient, batch_size=BATCH_SIZE, shuffle=False,  num_workers=4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# Transforms for Resnext\n# ====================================================\ndef get_transforms(*, data):\n    if data == 'valid':\n        return A.Compose([\n            A.Resize(CFG.size, CFG.size),\n            A.Normalize(\n                mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225],\n            ),\n            ToTensorV2(),\n        ])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# MODELS"},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# ResNext Model\n# ====================================================\nclass CustomResNext(nn.Module):\n    def __init__(self, config, pretrained=False):\n        super().__init__()\n        self.model = timm.create_model(model_name=config.resnext, pretrained=pretrained)\n        n_features = self.model.fc.in_features\n        self.model.fc = nn.Linear(n_features, config.num_classes)\n        \n    def forward(self, input_neurons):\n        output_predictions = self.model(input_neurons)\n        return output_predictions","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# EfficientNet Model\n# ====================================================\nclass enet_v2(nn.Module):\n\n    def __init__(self, backbone, out_dim, pretrained=False):\n        super(enet_v2, self).__init__()\n        self.enet = timm.create_model(backbone, pretrained=pretrained)\n        in_ch = self.enet.classifier.in_features\n        self.myfc = nn.Linear(in_ch, out_dim)\n        self.enet.classifier = nn.Identity()\n\n    def forward(self, x):\n        x = self.enet(x)\n        x = self.myfc(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import sys\nsys.path.append('../input/hongnangeffnet/gen-efficientnet-pytorch-master-hongnan')\nimport geffnet","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class configuration:\n    effnet = 'tf_efficientnet_b5_ns'\n    num_classes=5\nconfig = configuration","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class CustomEfficientNet(nn.Module):\n    def __init__(self, config: type, pretrained: bool=True):\n        super().__init__()\n        self.config = config\n        self.model = geffnet.create_model(\n        model_name=config.effnet,\n        pretrained=pretrained)\n        n_features = self.model.classifier.in_features\n        self.model.classifier = nn.Linear(n_features, config.num_classes)\n        \n\n    def forward(self, input_neurons):\n        output_predictions = self.model(input_neurons)\n        return output_predictions","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Helper functions"},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# Helper functions for Resnext\n# ====================================================\ndef load_state(model_path):\n    model = CustomResNext(CFG, pretrained=False)\n    try:  # single GPU model_file\n        model.load_state_dict(torch.load(model_path)['model'], strict=True)\n        state_dict = torch.load(model_path)['model']\n    except:  # multi GPU model_file\n        state_dict = torch.load(model_path)['model']\n        state_dict = {k[7:] if k.startswith('module.') else k: state_dict[k] for k in state_dict.keys()}\n\n    return state_dict\n\n\ndef inference(model, states, test_loader, device):\n    model.to(device)\n    tk0 = tqdm(enumerate(test_loader), total=len(test_loader))\n    probs = []\n    for i, (images) in tk0:\n        images = images.to(device)\n        avg_preds = []\n        for state in states:\n            model.load_state_dict(state)\n            model.eval()\n            with torch.no_grad():\n                y_preds = model(images)\n                print(y_preds)\n            avg_preds.append(y_preds.softmax(1).to('cpu').numpy())\n        avg_preds = np.mean(avg_preds, axis=0)\n        probs.append(avg_preds)\n        \n    probs = np.concatenate(probs)\n    return probs","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# Helper functions for efficientnet\n# ====================================================\ndef inference_func(test_loader):\n    model.eval()\n    bar = tqdm(test_loader)\n\n    LOGITS = []\n    PREDS = []\n    \n    with torch.no_grad():\n        for batch_idx, images in enumerate(bar):\n            x = images.to(device)\n            logits = model(x)\n            LOGITS.append(logits.cpu())\n            PREDS += [torch.softmax(logits, 1).detach().cpu()]\n        PREDS = torch.cat(PREDS).cpu().numpy()\n        LOGITS = torch.cat(LOGITS).cpu().numpy()\n    return PREDS\n\ndef tta_inference_func(test_loader):\n    model.eval()\n    bar = tqdm(test_loader)\n    PREDS = []\n    LOGITS = []\n\n    with torch.no_grad():\n        for batch_idx, images in enumerate(bar):\n            x = images.to(device)\n            x = torch.stack([x,x.flip(-1),x.flip(-2),x.flip(-1,-2),\n            x.transpose(-1,-2),x.transpose(-1,-2).flip(-1),\n            x.transpose(-1,-2).flip(-2),x.transpose(-1,-2).flip(-1,-2)],0)\n            x = x.view(-1, 3, image_size, image_size)\n            logits = model(x)\n            logits = logits.view(BATCH_SIZE, 8, -1).mean(1)\n            PREDS += [torch.softmax(logits, 1).detach().cpu()]\n            LOGITS.append(logits.cpu())\n\n        PREDS = torch.cat(PREDS).cpu().numpy()\n        \n    return PREDS","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# inference and Submit"},{"metadata":{},"cell_type":"markdown","source":"for resnext, the dataloader outputs\n\n    tensor([[[[ 0.1426,  0.3652,  0.7248,  ...,  0.5364,  0.4337, -0.7822],\n              [ 0.2796,  0.2282,  0.4508,  ...,  0.5193,  0.5707, -0.3541],\n              [ 0.2796,  0.1426,  0.2624,  ...,  0.5878,  0.8447,  0.6563],\n              ...,\n              [-1.5357, -1.5357, -1.5185,  ..., -1.6384, -1.5870, -1.5528],\n              [-1.7069, -1.6555, -1.6042,  ..., -1.8097, -1.7583, -1.7069],\n              [-1.8439, -1.7925, -1.7583,  ..., -1.7925, -1.7583, -1.7583]],\n\n             [[ 0.2052,  0.5553,  1.0805,  ...,  1.2731,  1.0630, -0.2325],\n              [ 0.3452,  0.4328,  0.8354,  ...,  1.2031,  1.1681,  0.1352],\n              [ 0.3978,  0.3627,  0.6254,  ...,  1.1856,  1.3782,  1.0980],\n              ...,\n              [-1.1253, -1.0903, -1.0903,  ..., -1.7031, -1.6506, -1.6155],\n              [-1.2654, -1.2129, -1.1429,  ..., -1.8782, -1.8256, -1.7731],\n              [-1.3880, -1.3354, -1.3004,  ..., -1.8606, -1.8256, -1.8256]],\n\n             [[ 0.0431,  0.3568,  0.8274,  ...,  0.2522,  0.3045, -0.7413],\n              [ 0.2348,  0.2871,  0.6182,  ...,  0.2348,  0.4265, -0.3404],\n              [ 0.3916,  0.3045,  0.5485,  ...,  0.3219,  0.7054,  0.6705],\n              ...,\n              [-0.9853, -0.9678, -0.9678,  ..., -1.4210, -1.3687, -1.3339],\n              [-1.1421, -1.0898, -1.0376,  ..., -1.5779, -1.5256, -1.4733],\n              [-1.2816, -1.2293, -1.1944,  ..., -1.5430, -1.5081, -1.5081]]]])"},{"metadata":{"trusted":true},"cell_type":"code","source":"# ====================================================\n# inference\n# ====================================================\n\n#for Resnext\nmodel = CustomResNext(CFG, pretrained=False)\nstates = [load_state(MODEL_DIR+f'{CFG.model_name}_fold{fold}.pth') for fold in CFG.trn_fold]\ntest_dataset = TestDataset(test, transform=get_transforms(data='valid'))\ntest_loader = DataLoader(test_dataset, batch_size=CFG.batch_size, shuffle=False, \n                         num_workers=CFG.num_workers, pin_memory=True)\nstates[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"array([[0.04188796, 0.04432843, 0.24820721, 0.03057373, 0.63500273]],\n      dtype=float32)"},{"metadata":{"trusted":true},"cell_type":"code","source":"#for Efficientnet\ntest_preds = []\nfor i in range(len(enet_type)):\n    model = enet_v2(enet_type[i], out_dim=5)\n    model = model.to(device)\n    model.load_state_dict(torch.load(model_path[i]))\n    test_preds += [tta_inference_func(test_loader_efficient)]\nnp.mean(test_preds, axis=0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"array([[0.03165458, 0.02644923, 0.15103994, 0.00727853, 0.7835777 ]],\n      dtype=float32)"},{"metadata":{"trusted":true},"cell_type":"code","source":"pred = 0.65*predictions + 0.35*np.mean(test_preds, axis=0)\ntest['label'] = softmax(pred).argmax(1)\ntest[['image_id', 'label']].to_csv(OUTPUT_DIR+'submission.csv', index=False)\ntest.head()","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":4}