{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Inference Kernel Demo "},{"metadata":{},"cell_type":"markdown","source":"### Rank 96\n### Private: 0.50585\n### Public: 0.30284"},{"metadata":{},"cell_type":"markdown","source":"## Import Required Libraries"},{"metadata":{"trusted":true},"cell_type":"code","source":"import os, sys, time\nimport cv2\nimport numpy as np\nimport pandas as pd\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n%matplotlib inline\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Get the test videos"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"test_dir = \"/kaggle/input/deepfake-detection-challenge/test_videos/\"\n\ntest_videos = sorted([x for x in os.listdir(test_dir) if x[-4:] == \".mp4\"])\nlen(test_videos)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Create helpers"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"PyTorch version:\", torch.__version__)\nprint(\"CUDA version:\", torch.version.cuda)\nprint(\"cuDNN version:\", torch.backends.cudnn.version())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gpu = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\ngpu","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import sys\nsys.path.insert(0, \"/kaggle/input/blazeface-pytorch\")\nsys.path.insert(0, \"/kaggle/input/inference-helper\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from blazeface import BlazeFace\nfacedet = BlazeFace().to(gpu)\nfacedet.load_weights(\"/kaggle/input/blazeface-pytorch/blazeface.pth\")\nfacedet.load_anchors(\"/kaggle/input/blazeface-pytorch/anchors.npy\")\n_ = facedet.train(False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from read_video_1 import VideoReader\nfrom face_extract_1 import FaceExtractor\n\nframes_per_video = 64 #frame_h * frame_l\nvideo_reader = VideoReader()\nvideo_read_fn = lambda x: video_reader.read_frames(x, num_frames=frames_per_video)\nface_extractor = FaceExtractor(video_read_fn, facedet)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"input_size = 224","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torchvision.transforms import Normalize\n\nmean = [0.485, 0.456, 0.406]\nstd = [0.229, 0.224, 0.225]\nnormalize_transform = Normalize(mean, std)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def isotropically_resize_image(img, size, resample=cv2.INTER_AREA):\n    h, w = img.shape[:2]\n    if w > h:\n        h = h * size // w\n        w = size\n    else:\n        w = w * size // h\n        h = size\n\n    resized = cv2.resize(img, (w, h), interpolation=resample)\n    return resized\n\n\ndef make_square_image(img):\n    h, w = img.shape[:2]\n    size = max(h, w)\n    t = 0\n    b = size - h\n    l = 0\n    r = size - w\n    return cv2.copyMakeBorder(img, t, b, l, r, cv2.BORDER_CONSTANT, value=0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## ResneXt"},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch.nn as nn\nimport torchvision.models as models\n\nclass MyResNeXt(models.resnet.ResNet):\n    def __init__(self, training=True):\n        super(MyResNeXt, self).__init__(block=models.resnet.Bottleneck,\n                                        layers=[3, 4, 6, 3], \n                                        groups=32, \n                                        width_per_group=4)\n        \n        self.fc = nn.Sequential(\n            nn.Dropout(p=0.5),\n            nn.ReLU(),\n            nn.Linear(2048, 1),\n        )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Xception"},{"metadata":{"trusted":true},"cell_type":"code","source":"class Head(torch.nn.Module):\n    def __init__(self, in_f, out_f):\n        super(Head, self).__init__()\n\n        self.f = nn.Flatten()\n        self.l = nn.Linear(in_f, 512)\n        self.d = nn.Dropout(0.75)\n        self.o = nn.Linear(512, out_f)\n        self.b1 = nn.BatchNorm1d(in_f)\n        self.b2 = nn.BatchNorm1d(512)\n        self.r = nn.ReLU()\n\n    def forward(self, x):\n        x = self.f(x)\n        x = self.b1(x)\n        x = self.d(x)\n\n        x = self.l(x)\n        x = self.r(x)\n        x = self.b2(x)\n        x = self.d(x)\n\n        out = self.o(x)\n        return out\n    \nclass FCN(torch.nn.Module):\n    def __init__(self, base, in_f):\n        super(FCN, self).__init__()\n        self.base = base\n        self.h1 = Head(in_f, 1)\n\n    def forward(self, x):\n        x = self.base(x)\n        return self.h1(x)\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Model preparation"},{"metadata":{"trusted":true},"cell_type":"code","source":"model_name = \"ULTIMATE_ENSEMBLE\"\n\nif model_name==\"ULTIMATE_ENSEMBLE\":\n    !pip install /kaggle/input/pytorchcv/pytorchcv-0.0.55-py2.py3-none-any.whl --quiet\n    from pytorchcv.model_provider import get_model\n\n    # ======== EFFICIENTB3 MODELS ==========  \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema8/model_efficientnet_b3b_0.pth\", map_location=gpu)    \n    model1 = FCN(model, 1536).to(gpu)  \n    model1.load_state_dict(checkpoint)\n    _ = model1.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema8/model_efficientnet_b3b_1.pth\", map_location=gpu)    \n    model2 = FCN(model, 1536).to(gpu)  \n    model2.load_state_dict(checkpoint)\n    _ = model2.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema8/model_efficientnet_b3b_2.pth\", map_location=gpu)    \n    model3 = FCN(model, 1536).to(gpu)  \n    model3.load_state_dict(checkpoint)\n    _ = model3.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakemodelzm/model_efficientnet_b3b_0.28_1.pth\", map_location=gpu)    \n    model4 = FCN(model, 1536).to(gpu)  \n    model4.load_state_dict(checkpoint)\n    _ = model4.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakemodelzm/model_efficientnet_b3b_0.286_2.pth\", map_location=gpu)    \n    model5 = FCN(model, 1536).to(gpu)  \n    model5.load_state_dict(checkpoint)\n    _ = model5.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakemodelzm/model_efficientnet_b3b_0.30_0.pth\", map_location=gpu)    \n    model6 = FCN(model, 1536).to(gpu)  \n    model6.load_state_dict(checkpoint)\n    _ = model6.eval()\n    del checkpoint, model    \n    \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakemodelzm/model_efficientnet_b3b_0.pth\", map_location=gpu)    \n    modelA = FCN(model, 1536).to(gpu)  \n    modelA.load_state_dict(checkpoint)\n    _ = modelA.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakemodelzm/model_efficientnet_b3b_1.1.pth\", map_location=gpu)    \n    modelB = FCN(model, 1536).to(gpu)  \n    modelB.load_state_dict(checkpoint)\n    _ = modelB.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b3b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakemodelzm/model_efficientnet_b3b_2.1.pth\", map_location=gpu)    \n    modelC = FCN(model, 1536).to(gpu)  \n    modelC.load_state_dict(checkpoint)\n    _ = modelC.eval()\n    del checkpoint, model    \n    #============= EFFB3 MODELS END ===============\n    \n    \n    #============= XCEPTION MODELS ======================\n    !pip install /kaggle/input/pytorchcv/pytorchcv-0.0.55-py2.py3-none-any.whl --quiet\n    from pytorchcv.model_provider import get_model\n    model = get_model(\"xception\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    model[0].final_block.pool = nn.Sequential(nn.AdaptiveAvgPool2d((1,1)))\n    checkpoint = torch.load(\"/kaggle/input/deepfakema8/model_xception_0.pth\", map_location=gpu)    \n    model7 = FCN(model, 2048).to(gpu)  \n    model7.load_state_dict(checkpoint)\n    _ = model7.eval()\n    del checkpoint, model \n    \n    model = get_model(\"xception\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    model[0].final_block.pool = nn.Sequential(nn.AdaptiveAvgPool2d((1,1)))\n    checkpoint = torch.load(\"/kaggle/input/deepfakema8/model_xception_1.pth\", map_location=gpu)    \n    model8 = FCN(model, 2048).to(gpu)  \n    model8.load_state_dict(checkpoint)\n    _ = model8.eval()\n    del checkpoint, model \n    \n    model = get_model(\"xception\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    model[0].final_block.pool = nn.Sequential(nn.AdaptiveAvgPool2d((1,1)))\n    checkpoint = torch.load(\"/kaggle/input/deepfakema8/model_xception_2.pth\", map_location=gpu)    \n    model9 = FCN(model, 2048).to(gpu)  \n    model9.load_state_dict(checkpoint)\n    _ = model9.eval()\n    del checkpoint, model \n    #============= XCEPTION MODELS END ======================\n    \n    #============= RESNEXT50 MODELS ================================  \n    checkpoint = torch.load(\"/kaggle/input/deepfakemodels/checkpoint_LB038.pth\", map_location=gpu)    \n    model10 = MyResNeXt().to(gpu)\n    model10.load_state_dict(checkpoint)\n    _ = model10.eval()\n    #============= RESNEXT50 MODELS END ================================\n    \n    #============= EFF B1, B2 MODELS ================================  \n    model = get_model(\"efficientnet_b1b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema7/model_efficientnet_b1b_0.pth\", map_location=gpu)    \n    model11 = FCN(model, 1280).to(gpu)  \n    model11.load_state_dict(checkpoint)\n    _ = model11.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b1b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema7/model_efficientnet_b1b_1.pth\", map_location=gpu)    \n    model12 = FCN(model, 1280).to(gpu)  \n    model12.load_state_dict(checkpoint)\n    _ = model12.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b1b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema7/model_efficientnet_b1b_2.pth\", map_location=gpu)    \n    model13 = FCN(model, 1280).to(gpu)  \n    model13.load_state_dict(checkpoint)\n    _ = model13.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b2b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema7/model_efficientnet_b2b_0.pth\", map_location=gpu)    \n    model14 = FCN(model, 1408).to(gpu)  \n    model14.load_state_dict(checkpoint)\n    _ = model14.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b2b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema7/model_efficientnet_b2b_1.pth\", map_location=gpu)    \n    model15 = FCN(model, 1408).to(gpu)  \n    model15.load_state_dict(checkpoint)\n    _ = model15.eval()\n    del checkpoint, model\n    \n    model = get_model(\"efficientnet_b2b\", pretrained=False)\n    model = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\n    checkpoint = torch.load(\"/kaggle/input/deepfakema7/model_efficientnet_b2b_2.pth\", map_location=gpu)    \n    model16 = FCN(model, 1408).to(gpu)  \n    model16.load_state_dict(checkpoint)\n    _ = model16.eval()\n    del checkpoint, model\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Prediction loop"},{"metadata":{"trusted":true},"cell_type":"code","source":"def predict_on_video(video_path, batch_size):\n    try:\n        # Find the faces for N frames in the video.\n        faces = face_extractor.process_video(video_path)\n        #print(video_path)\n\n        # Only look at one face per frame.\n        face_extractor.keep_only_best_face(faces)\n        \n        if len(faces) > 0:\n            # NOTE: When running on the CPU, the batch size must be fixed\n            # or else memory usage will blow up. (Bug in PyTorch?)\n            x = np.zeros((batch_size*2, input_size, input_size, 3), dtype=np.uint8)\n\n            # If we found any faces, prepare them for the model.\n            n = 0\n            for frame_data in faces:\n                for face in frame_data[\"faces\"]:\n                    # Resize to the model's required input size.\n                    # We keep the aspect ratio intact and add zero\n                    # padding if necessary.                    \n                    resized_face = isotropically_resize_image(face, input_size)\n                    resized_face = make_square_image(resized_face)\n                    \n                    x[n] = resized_face\n                    n += 1\n                        \n                    # Test time augmentation: horizontal flips.\n                    # TODO: not sure yet if this helps or not\n                    x[n] = cv2.flip(resized_face, 1)\n                    n += 1\n\n            if n > 0:\n                x = torch.tensor(x, device=gpu).float()\n\n                # Preprocess the images.\n                x = x.permute((0, 3, 1, 2))\n\n                for i in range(len(x)):\n                    x[i] = normalize_transform(x[i] / 255.)\n\n                # Make a prediction, then take the average.\n                with torch.no_grad():\n                    if model_name == \"ULTIMATE_ENSEMBLE\":\n                        y_pred1 = model1(x)\n                        y_pred2 = model2(x)\n                        y_pred3 = model3(x)\n                        y_pred4 = model4(x)\n                        y_pred5 = model5(x)\n                        y_pred6 = model6(x)\n                        y_predA = modelA(x)\n                        y_predB = modelB(x)\n                        y_predC = modelC(x)\n                        y_pred7 = model7(x)\n                        y_pred8 = model8(x)\n                        y_pred9 = model9(x)\n                        y_pred10 = model10(x)\n                        y_pred11 = model11(x)\n                        y_pred12 = model12(x)\n                        y_pred13 = model13(x)\n                        y_pred14 = model14(x)\n                        y_pred15 = model15(x)\n                        y_pred16 = model16(x)\n                           \n                        y_pred1 = torch.sigmoid(y_pred1.squeeze()).cpu()\n                        y_pred2 = torch.sigmoid(y_pred2.squeeze()).cpu()\n                        y_pred3 = torch.sigmoid(y_pred3.squeeze()).cpu()\n                        y_pred4 = torch.sigmoid(y_pred4.squeeze()).cpu()\n                        y_pred5 = torch.sigmoid(y_pred5.squeeze()).cpu()\n                        y_pred6 = torch.sigmoid(y_pred6.squeeze()).cpu()\n                        y_predA = torch.sigmoid(y_predA.squeeze()).cpu()\n                        y_predB = torch.sigmoid(y_predB.squeeze()).cpu()\n                        y_predC = torch.sigmoid(y_predC.squeeze()).cpu()\n                        y_pred7 = torch.sigmoid(y_pred7.squeeze()).cpu()\n                        y_pred8 = torch.sigmoid(y_pred8.squeeze()).cpu()\n                        y_pred9 = torch.sigmoid(y_pred9.squeeze()).cpu()\n                        y_pred10 = torch.sigmoid(y_pred10.squeeze()).cpu()\n                        y_pred11 = torch.sigmoid(y_pred11.squeeze()).cpu()\n                        y_pred12 = torch.sigmoid(y_pred12.squeeze()).cpu()\n                        y_pred13 = torch.sigmoid(y_pred13.squeeze()).cpu()\n                        y_pred14 = torch.sigmoid(y_pred14.squeeze()).cpu()\n                        y_pred15 = torch.sigmoid(y_pred15.squeeze()).cpu()\n                        y_pred16 = torch.sigmoid(y_pred16.squeeze()).cpu()\n         \n                        # Metrics\n                        y_pred = np.stack((y_pred1[:n].numpy(), y_pred2[:n].numpy(), y_pred3[:n].numpy(), \n                                           y_pred4[:n].numpy(),y_pred5[:n].numpy(),y_pred6[:n].numpy(),\n                                           y_predA[:n].numpy(),y_predB[:n].numpy(),y_predC[:n].numpy()), axis=0)\n                        y_pred_frame = np.median(y_pred, axis=0)\n                        pred_effb3 = np.median(y_pred_frame)\n                        \n                        y_pred = np.stack((y_pred7[:n].numpy(), y_pred8[:n].numpy(), y_pred9[:n].numpy()), axis=0)\n                        y_pred_frame = np.median(y_pred, axis=0)\n                        pred_xception = np.median(y_pred_frame)\n                        \n                        pred_resnext = np.median(y_pred10)\n                        \n                        y_pred = np.stack((y_pred11[:n].numpy(), y_pred12[:n].numpy(), y_pred13[:n].numpy(), \n                                           y_pred14[:n].numpy(),y_pred15[:n].numpy(),y_pred16[:n].numpy()), axis=0)\n                        y_pred_frame = np.median(y_pred, axis=0)\n                        pred_effb1b2 = np.median(y_pred_frame)\n                        \n                        return 0.6*pred_effb3 + 0.2*pred_xception + 0.1*pred_resnext + 0.1*pred_effb1b2\n                        \n                                        \n                    else:\n                        y_pred = model(x)\n                        y_pred = torch.sigmoid(y_pred.squeeze())\n                        return 0.0#y_pred[:n].mean().item()\n\n    except Exception as e:\n        print(\"Prediction error on video %s: %s\" % (video_path, str(e)))\n\n    return 0.5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from concurrent.futures import ThreadPoolExecutor\n\ndef predict_on_video_set(videos, num_workers):\n    def process_file(i):\n        filename = videos[i]\n        y_pred = predict_on_video(os.path.join(test_dir, filename), batch_size=frames_per_video)\n        return y_pred\n\n    with ThreadPoolExecutor(max_workers=num_workers) as ex:\n        predictions = ex.map(process_file, range(len(videos)))\n\n    return list(predictions)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"speed_test = False  # you have to enable this manually","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if speed_test:\n    start_time = time.time()\n    speedtest_videos = test_videos[:10]\n    predictions = predict_on_video_set(speedtest_videos, num_workers=4)\n    elapsed = time.time() - start_time\n    print(\"Elapsed %f sec. Average per video: %f sec.\" % (elapsed, elapsed / len(speedtest_videos)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\"\"\"\n2\taassnaulhq.mp4\t0.9977885484695435\n3\taayfryxljh.mp4\t0.00035705912159755826\n4\tacazlolrpz.mp4\t0.47398507595062256\n5\tadohdulfwb.mp4\t2.5707169697852805e-05\n6\tahjnxtiamx.mp4\t0.9993642568588257\n7\tajiyrjfyzp.mp4\t0.2767198085784912\n8\taktnlyqpah.mp4\t0.999808669090271\n9\talrtntfxtd.mp4\t0.9940245151519775\n10\taomqqjipcp.mp4\t0.9988095760345459\n11\tapedduehoy.mp4\t0.0102305356413126\n12\tapvzjkvnwn.mp4\t0.0001305717887589708\n13\taqrsylrzgi.mp4\t0.38465169072151184\n14\taxfhbpkdlc.mp4\t0.9944237470626831\n15\tayipraspbn.mp4\t0.010544270277023315\n16\tbcbqxhziqz.mp4\t0.04167022556066513\n17\tbcvheslzrq.mp4\t0.9400777816772461\n18\tbdshuoldwx.mp4\t0.9833802580833435\n19\tbfdopzvxbi.mp4\t0.48946961760520935\n20\tbfjsthfhbd.mp4\t0.4342306852340698\n21\tbjyaxvggle.mp4\t0.9987448453903198\n\"\"\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Make the submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"predictions = predict_on_video_set(test_videos, num_workers=4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df = pd.DataFrame({\"filename\": test_videos, \"label\": predictions})\nsubmission_df.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#submission_df.head()","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.6"}},"nbformat":4,"nbformat_minor":4}