{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":98076,"databundleVersionId":11782701},{"sourceType":"datasetVersion","sourceId":16138092,"datasetId":10348020,"databundleVersionId":17112063}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 🌊 Flood Image Segmentation — U-Net + VGG16\n**Competition:** Flood Image Segmentation | ML4IM 2024-2025  \n**Model:** U-Net with VGG16 Backbone (Pretrained on ImageNet)  \n**Framework:** PyTorch + segmentation_models_pytorch  \n**Metric:** DICE Score","metadata":{}},{"cell_type":"markdown","source":"## 1. Install & Import Libraries","metadata":{}},{"cell_type":"code","source":"!pip install segmentation-models-pytorch -q\n!pip install albumentations -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:06.879933Z","iopub.execute_input":"2026-05-07T09:52:06.880262Z","iopub.status.idle":"2026-05-07T09:52:13.912278Z","shell.execute_reply.started":"2026-05-07T09:52:06.880235Z","shell.execute_reply":"2026-05-07T09:52:13.911356Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom pathlib import Path\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\n\nimport segmentation_models_pytorch as smp\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm\n\n# ── Reproducibility ──\nSEED = 42\ntorch.manual_seed(SEED)\nnp.random.seed(SEED)\n\nDEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f'Using device: {DEVICE}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:13.914220Z","iopub.execute_input":"2026-05-07T09:52:13.914530Z","iopub.status.idle":"2026-05-07T09:52:13.923941Z","shell.execute_reply.started":"2026-05-07T09:52:13.914499Z","shell.execute_reply":"2026-05-07T09:52:13.922946Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Configuration","metadata":{}},{"cell_type":"code","source":"# ══════════════════════════════════════════════\n#  CONFIG — edit these paths to match your data\n# ══════════════════════════════════════════════\nTRAIN_IMAGES_DIR = '/kaggle/input/competitions/flood-image-segmentation-ml4im-2024-2025/Training/Images'\nTRAIN_MASKS_DIR  = '/kaggle/input/competitions/flood-image-segmentation-ml4im-2024-2025/Training/Masks'\nTEST_IMAGES_DIR  = '/kaggle/input/competitions/flood-image-segmentation-ml4im-2024-2025/Testing/Images'\n\nIMG_SIZE    = 256        # resize images to IMG_SIZE x IMG_SIZE\nBATCH_SIZE  = 8\nNUM_EPOCHS  = 30\nLR          = 1e-4\nVAL_SPLIT   = 0.15       # 15% of train used as validation\nTHRESHOLD   = 0.5        # binarization threshold for predictions\n\nENCODER       = 'vgg16'\nENCODER_WEIGHTS = 'imagenet'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:13.924994Z","iopub.execute_input":"2026-05-07T09:52:13.925328Z","iopub.status.idle":"2026-05-07T09:52:13.941413Z","shell.execute_reply.started":"2026-05-07T09:52:13.925296Z","shell.execute_reply":"2026-05-07T09:52:13.940720Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Explore the Data","metadata":{}},{"cell_type":"code","source":"train_image_paths = sorted(Path(TRAIN_IMAGES_DIR).glob('*'))\ntrain_mask_paths  = sorted(Path(TRAIN_MASKS_DIR).glob('*'))\ntest_image_paths  = sorted(Path(TEST_IMAGES_DIR).glob('*'))\n\nprint(f'Train images : {len(train_image_paths)}')\nprint(f'Train masks  : {len(train_mask_paths)}')\nprint(f'Test  images : {len(test_image_paths)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:13.943483Z","iopub.execute_input":"2026-05-07T09:52:13.943851Z","iopub.status.idle":"2026-05-07T09:52:14.429596Z","shell.execute_reply.started":"2026-05-07T09:52:13.943810Z","shell.execute_reply":"2026-05-07T09:52:14.428861Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ── Visualise a few training samples ──\nfig, axes = plt.subplots(3, 2, figsize=(10, 12))\nfor i in range(3):\n    img  = np.array(Image.open(train_image_paths[i]).convert('RGB'))\n    mask = np.array(Image.open(train_mask_paths[i]).convert('L'))\n    axes[i, 0].imshow(img)\n    axes[i, 0].set_title(f'Image {i+1}')\n    axes[i, 0].axis('off')\n    axes[i, 1].imshow(mask, cmap='gray')\n    axes[i, 1].set_title(f'Mask {i+1}')\n    axes[i, 1].axis('off')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:14.430555Z","iopub.execute_input":"2026-05-07T09:52:14.430877Z","iopub.status.idle":"2026-05-07T09:52:15.320637Z","shell.execute_reply.started":"2026-05-07T09:52:14.430842Z","shell.execute_reply":"2026-05-07T09:52:15.319741Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Train / Validation Split","metadata":{}},{"cell_type":"code","source":"# Since the test set has no labels we split train → train + val ourselves\ntrain_imgs, val_imgs, train_msks, val_msks = train_test_split(\n    train_image_paths,\n    train_mask_paths,\n    test_size=VAL_SPLIT,\n    random_state=SEED\n)\n\nprint(f'Train samples      : {len(train_imgs)}')\nprint(f'Validation samples : {len(val_imgs)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:15.321918Z","iopub.execute_input":"2026-05-07T09:52:15.322358Z","iopub.status.idle":"2026-05-07T09:52:15.331388Z","shell.execute_reply.started":"2026-05-07T09:52:15.322320Z","shell.execute_reply":"2026-05-07T09:52:15.330635Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Dataset & Augmentations","metadata":{}},{"cell_type":"code","source":"# ── Preprocessing stats expected by VGG16/ImageNet ──\nMEAN = [0.485, 0.456, 0.406]\nSTD  = [0.229, 0.224, 0.225]\n\ntrain_transform = A.Compose([\n    A.Resize(IMG_SIZE, IMG_SIZE),\n    A.HorizontalFlip(p=0.5),\n    A.VerticalFlip(p=0.3),\n    A.RandomRotate90(p=0.3),\n    A.ShiftScaleRotate(shift_limit=0.05, scale_limit=0.1, rotate_limit=15, p=0.4),\n    A.RandomBrightnessContrast(p=0.3),\n    A.GaussianBlur(blur_limit=3, p=0.2),\n    A.Normalize(mean=MEAN, std=STD),\n    ToTensorV2()\n])\n\nval_transform = A.Compose([\n    A.Resize(IMG_SIZE, IMG_SIZE),\n    A.Normalize(mean=MEAN, std=STD),\n    ToTensorV2()\n])\n\ntest_transform = val_transform  # same as val, no masks needed\n\n\nclass FloodDataset(Dataset):\n    \"\"\"Flood segmentation dataset — works for train/val (with masks) and test (without).\"\"\"\n\n    def __init__(self, image_paths, mask_paths=None, transform=None):\n        self.image_paths = image_paths\n        self.mask_paths  = mask_paths\n        self.transform   = transform\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        img  = np.array(Image.open(self.image_paths[idx]).convert('RGB'))\n\n        if self.mask_paths is not None:\n            mask = np.array(Image.open(self.mask_paths[idx]).convert('L'))\n            # Binarise: flood pixels → 1, background → 0\n            mask = (mask > 127).astype(np.float32)\n\n            if self.transform:\n                aug  = self.transform(image=img, mask=mask)\n                img  = aug['image']\n                mask = aug['mask'].unsqueeze(0)   # (1, H, W)\n            return img, mask\n        else:\n            fname = Path(self.image_paths[idx]).name\n            if self.transform:\n                aug = self.transform(image=img)\n                img = aug['image']\n            return img, fname","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:15.332529Z","iopub.execute_input":"2026-05-07T09:52:15.332849Z","iopub.status.idle":"2026-05-07T09:52:15.352443Z","shell.execute_reply.started":"2026-05-07T09:52:15.332814Z","shell.execute_reply":"2026-05-07T09:52:15.351591Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds = FloodDataset(train_imgs, train_msks, transform=train_transform)\nval_ds   = FloodDataset(val_imgs,   val_msks,   transform=val_transform)\ntest_ds  = FloodDataset(test_image_paths,        transform=test_transform)\n\ntrain_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True,  num_workers=2, pin_memory=True)\nval_loader   = DataLoader(val_ds,   batch_size=BATCH_SIZE, shuffle=False, num_workers=2, pin_memory=True)\ntest_loader  = DataLoader(test_ds,  batch_size=BATCH_SIZE, shuffle=False, num_workers=2, pin_memory=True)\n\nprint(f'Train batches : {len(train_loader)}')\nprint(f'Val   batches : {len(val_loader)}')\nprint(f'Test  batches : {len(test_loader)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:15.353536Z","iopub.execute_input":"2026-05-07T09:52:15.353975Z","iopub.status.idle":"2026-05-07T09:52:15.361359Z","shell.execute_reply.started":"2026-05-07T09:52:15.353950Z","shell.execute_reply":"2026-05-07T09:52:15.360601Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 6. Model — U-Net + VGG16","metadata":{}},{"cell_type":"code","source":"model = smp.Unet(\n    encoder_name=ENCODER,\n    encoder_weights=ENCODER_WEIGHTS,\n    in_channels=3,\n    classes=1,                  # binary segmentation\n    activation=None             # raw logits → we apply sigmoid in loss/metrics\n).to(DEVICE)\n\nprint(f'Model parameters: {sum(p.numel() for p in model.parameters()):,}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:52:15.362326Z","iopub.execute_input":"2026-05-07T09:52:15.362771Z","iopub.status.idle":"2026-05-07T09:52:23.015054Z","shell.execute_reply.started":"2026-05-07T09:52:15.362745Z","shell.execute_reply":"2026-05-07T09:52:23.014308Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 7. Loss Function & Metrics","metadata":{}},{"cell_type":"code","source":"# ── Combined BCE + Dice Loss for better convergence ──\nclass BCEDiceLoss(nn.Module):\n    def __init__(self, bce_weight=0.5):\n        super().__init__()\n        self.bce_weight = bce_weight\n        self.bce = nn.BCEWithLogitsLoss()\n\n    def dice_loss(self, logits, targets, smooth=1.0):\n        probs   = torch.sigmoid(logits)\n        num     = (probs * targets).sum(dim=(2, 3))\n        den     = probs.sum(dim=(2, 3)) + targets.sum(dim=(2, 3))\n        return 1 - (2 * num + smooth) / (den + smooth)\n\n    def forward(self, logits, targets):\n        bce  = self.bce(logits, targets)\n        dice = self.dice_loss(logits, targets).mean()\n        return self.bce_weight * bce + (1 - self.bce_weight) * dice\n\n\ndef dice_score(preds, targets, threshold=THRESHOLD, smooth=1.0):\n    \"\"\"Compute DICE score (matches competition metric).\"\"\"\n    preds   = (torch.sigmoid(preds) > threshold).float()\n    num     = (preds * targets).sum(dim=(2, 3))\n    den     = preds.sum(dim=(2, 3)) + targets.sum(dim=(2, 3))\n    return ((2 * num + smooth) / (den + smooth)).mean().item()\n\n\ncriterion = BCEDiceLoss(bce_weight=0.5)\noptimizer = optim.Adam(model.parameters(), lr=LR)\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer,\n    mode='max',\n    factor=0.5,\n    patience=5\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:53:38.000181Z","iopub.execute_input":"2026-05-07T09:53:38.000699Z","iopub.status.idle":"2026-05-07T09:53:38.009194Z","shell.execute_reply.started":"2026-05-07T09:53:38.000670Z","shell.execute_reply":"2026-05-07T09:53:38.008447Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 8. Training Loop","metadata":{}},{"cell_type":"code","source":"def train_one_epoch(model, loader, criterion, optimizer):\n    model.train()\n    total_loss, total_dice = 0, 0\n\n    for imgs, masks in tqdm(loader, desc='Train', leave=False):\n        imgs, masks = imgs.to(DEVICE), masks.to(DEVICE)\n\n        optimizer.zero_grad()\n        logits = model(imgs)\n        loss   = criterion(logits, masks)\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n        total_dice += dice_score(logits.detach(), masks)\n\n    n = len(loader)\n    return total_loss / n, total_dice / n\n\n\n@torch.no_grad()\ndef validate(model, loader, criterion):\n    model.eval()\n    total_loss, total_dice = 0, 0\n\n    for imgs, masks in tqdm(loader, desc='Val  ', leave=False):\n        imgs, masks = imgs.to(DEVICE), masks.to(DEVICE)\n        logits      = model(imgs)\n        loss        = criterion(logits, masks)\n\n        total_loss += loss.item()\n        total_dice += dice_score(logits, masks)\n\n    n = len(loader)\n    return total_loss / n, total_dice / n\n\n\n# ── Run training ──\nhistory = {'train_loss': [], 'train_dice': [], 'val_loss': [], 'val_dice': []}\nbest_val_dice = 0.0\nCHECKPOINT    = 'best_model.pth'\n\nfor epoch in range(1, NUM_EPOCHS + 1):\n    tr_loss, tr_dice = train_one_epoch(model, train_loader, criterion, optimizer)\n    vl_loss, vl_dice = validate(model, val_loader, criterion)\n    scheduler.step(vl_dice)\n\n    history['train_loss'].append(tr_loss)\n    history['train_dice'].append(tr_dice)\n    history['val_loss'].append(vl_loss)\n    history['val_dice'].append(vl_dice)\n\n    flag = ''\n    if vl_dice > best_val_dice:\n        best_val_dice = vl_dice\n        torch.save(model.state_dict(), CHECKPOINT)\n        flag = '  ✅ saved'\n\n    print(f'Epoch [{epoch:02d}/{NUM_EPOCHS}]  '\n          f'Train Loss: {tr_loss:.4f}  Train Dice: {tr_dice:.4f}  '\n          f'Val Loss: {vl_loss:.4f}  Val Dice: {vl_dice:.4f}{flag}')\n\nprint(f'\\n🏆 Best Val Dice: {best_val_dice:.4f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T09:53:39.903114Z","iopub.execute_input":"2026-05-07T09:53:39.903547Z","iopub.status.idle":"2026-05-07T10:41:21.128241Z","shell.execute_reply.started":"2026-05-07T09:53:39.903490Z","shell.execute_reply":"2026-05-07T10:41:21.127226Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 9. Training Curves","metadata":{}},{"cell_type":"code","source":"epochs = range(1, len(history['train_loss']) + 1)\n\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))\n\nax1.plot(epochs, history['train_loss'], label='Train Loss')\nax1.plot(epochs, history['val_loss'],   label='Val Loss')\nax1.set_title('Loss')\nax1.set_xlabel('Epoch')\nax1.legend()\n\nax2.plot(epochs, history['train_dice'], label='Train Dice')\nax2.plot(epochs, history['val_dice'],   label='Val Dice')\nax2.set_title('DICE Score')\nax2.set_xlabel('Epoch')\nax2.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T10:41:21.130283Z","iopub.execute_input":"2026-05-07T10:41:21.130552Z","iopub.status.idle":"2026-05-07T10:41:21.440085Z","shell.execute_reply.started":"2026-05-07T10:41:21.130504Z","shell.execute_reply":"2026-05-07T10:41:21.439350Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 10. Visualise Validation Predictions","metadata":{}},{"cell_type":"code","source":"# ── Load best weights ──\nmodel.load_state_dict(torch.load(CHECKPOINT, map_location=DEVICE))\nmodel.eval()\n\nimgs, masks = next(iter(val_loader))\nimgs, masks = imgs.to(DEVICE), masks.to(DEVICE)\n\nwith torch.no_grad():\n    logits = model(imgs)\n    preds  = (torch.sigmoid(logits) > THRESHOLD).float()\n\nimgs  = imgs.cpu().numpy()\nmasks = masks.cpu().numpy()\npreds = preds.cpu().numpy()\n\n# De-normalise images for display\nmean = np.array(MEAN)[:, None, None]\nstd  = np.array(STD)[:, None, None]\n\nn_show = min(4, len(imgs))\nfig, axes = plt.subplots(n_show, 3, figsize=(12, n_show * 4))\n\nfor i in range(n_show):\n    img_disp = np.clip((imgs[i] * std + mean).transpose(1, 2, 0), 0, 1)\n    axes[i, 0].imshow(img_disp)\n    axes[i, 0].set_title('Image')\n    axes[i, 0].axis('off')\n\n    axes[i, 1].imshow(masks[i, 0], cmap='gray')\n    axes[i, 1].set_title('Ground Truth')\n    axes[i, 1].axis('off')\n\n    axes[i, 2].imshow(preds[i, 0], cmap='gray')\n    axes[i, 2].set_title('Prediction')\n    axes[i, 2].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T10:41:21.441212Z","iopub.execute_input":"2026-05-07T10:41:21.441556Z","iopub.status.idle":"2026-05-07T10:41:23.153129Z","shell.execute_reply.started":"2026-05-07T10:41:21.441530Z","shell.execute_reply":"2026-05-07T10:41:23.151917Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 11. Generate Submission","metadata":{}},{"cell_type":"code","source":"def rle_encode(mask):\n    \"\"\"Run-Length Encoding — standard Kaggle format.\"\"\"\n    pixels = mask.flatten(order='F')\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs   = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\n\nmodel.load_state_dict(torch.load(CHECKPOINT, map_location=DEVICE))\nmodel.eval()\n\nsubmission = {'id': [], 'rle': []}\n\nwith torch.no_grad():\n    for imgs, fnames in tqdm(test_loader, desc='Inference'):\n        imgs   = imgs.to(DEVICE)\n        logits = model(imgs)\n        preds  = (torch.sigmoid(logits) > THRESHOLD).float().cpu().numpy()\n\n        for pred, fname in zip(preds, fnames):\n            mask_2d = pred[0].astype(np.uint8)\n\n            submission['id'].append(fname)\n            submission['rle'].append(rle_encode(mask_2d))\n\nsub_df = pd.DataFrame(submission)\nsub_df.to_csv('submission.csv', index=False)\n\nprint(sub_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-07T10:41:23.155569Z","iopub.execute_input":"2026-05-07T10:41:23.156111Z","iopub.status.idle":"2026-05-07T10:41:27.301983Z","shell.execute_reply.started":"2026-05-07T10:41:23.156055Z","shell.execute_reply":"2026-05-07T10:41:27.301241Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 12. Tips to Improve Your Score\n\n| Idea | Expected Gain |\n|------|---------------|\n| Increase `IMG_SIZE` to 384 or 512 | ++ |\n| Try `encoder_name='resnet34'` or `'efficientnet-b3'` | ++ |\n| Use `smp.UnetPlusPlus` instead of `Unet` | + |\n| Add Test-Time Augmentation (TTA: hflip + vflip) | + |\n| Try Focal Loss or Lovász Loss | + |\n| Fine-tune `THRESHOLD` on validation set | + |\n| Train longer with cosine annealing scheduler | + |\n","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}