{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### **Setup**","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":0.007932,"end_time":"2022-10-13T13:21:46.286482","exception":false,"start_time":"2022-10-13T13:21:46.278550","status":"completed"},"tags":[]}},{"cell_type":"code","source":"%%bash\npip install timm -q","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:21:46.305509Z","iopub.status.busy":"2022-10-13T13:21:46.304912Z","iopub.status.idle":"2022-10-13T13:21:56.823437Z","shell.execute_reply":"2022-10-13T13:21:56.822248Z"},"papermill":{"duration":10.530464,"end_time":"2022-10-13T13:21:56.825926","exception":false,"start_time":"2022-10-13T13:21:46.295462","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Library Imports**","metadata":{"papermill":{"duration":0.004551,"end_time":"2022-10-13T13:21:56.835805","exception":false,"start_time":"2022-10-13T13:21:56.831254","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import os\nimport re\nimport cv2\nimport timm\nimport torch\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom time import time\nfrom PIL import Image\nfrom scipy import stats\nfrom typing import Union\nfrom torch import nn, optim\nfrom torch.utils.data import Dataset\nfrom torch.utils.data import DataLoader as DL\nfrom torchvision import models, transforms\n\nfrom sklearn.metrics import accuracy_score\nfrom sklearn.model_selection import StratifiedKFold","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:21:56.846802Z","iopub.status.busy":"2022-10-13T13:21:56.846484Z","iopub.status.idle":"2022-10-13T13:22:00.721782Z","shell.execute_reply":"2022-10-13T13:22:00.720645Z"},"papermill":{"duration":3.883867,"end_time":"2022-10-13T13:22:00.724314","exception":false,"start_time":"2022-10-13T13:21:56.840447","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Utilities and Helpers**","metadata":{"papermill":{"duration":0.004113,"end_time":"2022-10-13T13:22:00.733168","exception":false,"start_time":"2022-10-13T13:22:00.729055","status":"completed"},"tags":[]}},{"cell_type":"code","source":"SEED: int = 42\nSIZE: int = 384\n\n\ndef breaker(num: int=50, char: str=\"*\") -> None:\n    print(\"\\n\" + num*char + \"\\n\")\n\n    \ndef get_image(path: str, size: int=224) -> np.ndarray:\n    image = cv2.imread(path, cv2.IMREAD_COLOR)\n    if image is None: \n        image = np.asarray(Image.open(path))\n        if len(image.shape) == 2:\n            image = np.concatenate((\n                np.expand_dims(image, axis=2),\n                np.expand_dims(image, axis=2),\n                np.expand_dims(image, axis=2),\n            ), axis=2)\n    image = cv2.cvtColor(src=image, code=cv2.COLOR_BGR2RGB)\n    return cv2.resize(src=image, dsize=(size, size), interpolation=cv2.INTER_AREA)\n\n\ndef show_loss_graphs(L: list) -> None:\n    TL, VL = [], []\n    for i in range(len(L)):\n        TL.append(L[i][\"train\"])\n        VL.append(L[i][\"valid\"])\n    x_Axis = np.arange(1, len(TL) + 1)\n    plt.figure()\n    plt.plot(x_Axis, TL, \"r\", label=\"Train\")\n    plt.plot(x_Axis, VL, \"b\", label=\"Valid\")\n    plt.legend()\n    plt.grid()\n    plt.title(\"Loss Graph\")\n    plt.show()\n\n    \ndef show_accuracy_graphs(A: list) -> None:\n    TA, VA = [], []\n    for i in range(len(A)):\n        TA.append(A[i][\"train\"])\n        VA.append(A[i][\"valid\"])\n    x_Axis = np.arange(1, len(TA) + 1)\n    plt.figure()\n    plt.plot(x_Axis, TA, \"r\", label=\"Train\")\n    plt.plot(x_Axis, VA, \"b\", label=\"Valid\")\n    plt.legend()\n    plt.grid()\n    plt.title(\"Accuracy Graph\")\n    plt.show()\n    \n\ndef show_lr_graph(LR: list) -> None:\n    x_Axis = [i+1 for i in range(len(LR))]\n    plt.figure(figsize=(8, 6))\n    plt.plot(x_Axis, LR, \"rx\")\n    plt.grid()\n    plt.show()","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:22:00.744433Z","iopub.status.busy":"2022-10-13T13:22:00.743895Z","iopub.status.idle":"2022-10-13T13:22:00.757528Z","shell.execute_reply":"2022-10-13T13:22:00.756691Z"},"papermill":{"duration":0.022117,"end_time":"2022-10-13T13:22:00.759524","exception":false,"start_time":"2022-10-13T13:22:00.737407","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Configuration**","metadata":{"papermill":{"duration":0.004024,"end_time":"2022-10-13T13:22:00.767813","exception":false,"start_time":"2022-10-13T13:22:00.763789","status":"completed"},"tags":[]}},{"cell_type":"code","source":"class CFG(object):\n    def __init__(self, \n                 seed: int = 42,\n                 size: int = 224,\n                 n_splits: int = 5,\n                 batch_size: int = 16,\n                 epochs: int = 25,\n                 early_stopping: int = 5,\n                 lr: float = 1e-4,\n                 wd: float = 0.0,\n                 max_lr: float = 1e-3,\n                 pct_start: float = 0.2,\n                 steps_per_epoch: int = 100,\n                 div_factor: int = 1e3, \n                 final_div_factor: float = 1e3,\n                 ):\n        self.seed = seed\n        self.size = size\n        self.n_splits = n_splits\n        self.batch_size = batch_size\n        self.epochs = epochs\n        self.early_stopping = early_stopping\n        self.lr = lr\n        self.wd = wd\n        self.max_lr = max_lr\n        self.pct_start = pct_start\n        self.steps_per_epoch = steps_per_epoch\n        self.div_factor = div_factor\n        self.final_div_factor = final_div_factor\n        self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n        \n        if self.size == 224:\n            self.train_transform = transforms.Compose([\n                transforms.ToTensor(),\n                transforms.Normalize([0.65967, 0.62907, 0.59212], [0.23083, 0.23947, 0.26350]),\n                transforms.RandomAffine(degrees=(-45, 45), translate=(0.15, 0.15), scale=(0.5, 1.5)),\n                transforms.RandomHorizontalFlip(p=0.25),\n                transforms.RandomVerticalFlip(p=0.25),\n            ])\n            self.valid_transform = transforms.Compose([\n                transforms.ToTensor(),\n                transforms.Normalize([0.65967, 0.62907, 0.59212], [0.23083, 0.23947, 0.26350]),\n            ])\n        \n        if self.size == 384:\n            self.train_transform = transforms.Compose([\n                transforms.ToTensor(),\n                transforms.Normalize([0.65966, 0.62906, 0.59210], [0.23390, 0.24220, 0.26589]),\n                transforms.RandomAffine(degrees=(-45, 45), translate=(0.15, 0.15), scale=(0.5, 1.5)),\n                transforms.RandomHorizontalFlip(p=0.25),\n                transforms.RandomVerticalFlip(p=0.25),\n            ])\n            self.valid_transform = transforms.Compose([\n                transforms.ToTensor(),\n                transforms.Normalize([0.65966, 0.62906, 0.59210], [0.23390, 0.24220, 0.26589]),\n            ])\n        \n        if self.size == 512:\n            self.train_transform = transforms.Compose([\n                transforms.ToTensor(),\n                transforms.Normalize([0.65965, 0.62905, 0.59209], [0.23497, 0.24317, 0.26674]),\n                transforms.RandomAffine(degrees=(-45, 45), translate=(0.15, 0.15), scale=(0.5, 1.5)),\n                transforms.RandomHorizontalFlip(p=0.25),\n                transforms.RandomVerticalFlip(p=0.25),\n            ])\n            self.valid_transform = transforms.Compose([\n                transforms.ToTensor(),\n                transforms.Normalize([0.65965, 0.62905, 0.59209], [0.23497, 0.24317, 0.26674]),\n            ])\n                                \n        self.save_path = \"saves\"\n        if not os.path.exists(self.save_path): os.makedirs(self.save_path)\n    \ncfg = CFG(\n    seed=SEED, \n    size=SIZE\n)","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:22:00.777651Z","iopub.status.busy":"2022-10-13T13:22:00.777380Z","iopub.status.idle":"2022-10-13T13:22:00.860819Z","shell.execute_reply":"2022-10-13T13:22:00.859889Z"},"papermill":{"duration":0.090909,"end_time":"2022-10-13T13:22:00.862926","exception":false,"start_time":"2022-10-13T13:22:00.772017","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Dataset Template**","metadata":{"papermill":{"duration":0.004055,"end_time":"2022-10-13T13:22:00.871346","exception":false,"start_time":"2022-10-13T13:22:00.867291","status":"completed"},"tags":[]}},{"cell_type":"code","source":"class DS(Dataset):\n    def __init__(\n        self, \n        filepaths: np.ndarray, \n        size: int,\n        labels: Union[np.ndarray, None]=None, \n        transform=None\n    ):\n        \n        self.filepaths = filepaths\n        self.labels = labels\n        self.size = size\n        self.transform = transform\n    \n    def __len__(self):\n        return self.filepaths.shape[0]\n    \n    def __getitem__(self, idx):\n        image = get_image(self.filepaths[idx], self.size)\n        if self.labels is None:\n            return self.transform(image)\n        return self.transform(image), self.labels[idx]","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:22:00.880913Z","iopub.status.busy":"2022-10-13T13:22:00.880639Z","iopub.status.idle":"2022-10-13T13:22:00.887250Z","shell.execute_reply":"2022-10-13T13:22:00.886269Z"},"papermill":{"duration":0.013591,"end_time":"2022-10-13T13:22:00.889128","exception":false,"start_time":"2022-10-13T13:22:00.875537","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Model**","metadata":{"papermill":{"duration":0.003966,"end_time":"2022-10-13T13:22:00.897238","exception":false,"start_time":"2022-10-13T13:22:00.893272","status":"completed"},"tags":[]}},{"cell_type":"code","source":"class Model(nn.Module):\n    def __init__(self):\n        super(Model, self).__init__()\n            \n        self.model = timm.create_model(model_name=\"efficientnet_b4\", pretrained=True)\n        self.model.classifier = nn.Linear(in_features=self.model.classifier.in_features, out_features=6)\n            \n    def freeze(self):\n        for params in self.parameters(): params.requires_grad = False\n\n    def forward(self, x):\n        return nn.LogSoftmax(dim=1)(self.model(x))","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:22:00.907993Z","iopub.status.busy":"2022-10-13T13:22:00.906476Z","iopub.status.idle":"2022-10-13T13:22:00.913014Z","shell.execute_reply":"2022-10-13T13:22:00.912174Z"},"papermill":{"duration":0.013619,"end_time":"2022-10-13T13:22:00.915030","exception":false,"start_time":"2022-10-13T13:22:00.901411","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Fit and Predict**","metadata":{"papermill":{"duration":0.004033,"end_time":"2022-10-13T13:22:00.923255","exception":false,"start_time":"2022-10-13T13:22:00.919222","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def fit(\n    model=None,\n    optimizer=None, \n    scheduler_rlrop=None,\n    scheduler_oclr=None,\n    epochs=None, \n    early_stopping_patience=None, \n    dataloaders=None, \n    fold=None, \n    save_path=None,\n    device=None,\n    verbose=False\n) -> tuple:\n    \n    def get_accuracy(y_pred, y_true):\n        y_pred = torch.argmax(y_pred, dim=1)\n        return torch.count_nonzero(y_pred == y_true).item() / len(y_pred)\n    \n    if verbose:\n        breaker()\n        if fold: print(f\"Training Fold {fold}...\")\n        else: print(\"Training ...\")\n        breaker()\n        \n    bestLoss: dict = {\"train\" : np.inf, \"valid\" : np.inf} \n    bestAccs: dict = {\"train\" : 0.0, \"valid\" : 0.0}\n        \n    Losses: list = []\n    Accuracies: list = [] \n    LRs: list = []\n        \n    if fold: \n        ble_name = f\"ble_state_fold_{fold}.pt\"\n        bae_name = f\"bae_state_fold_{fold}.pt\"\n    else: \n        ble_name = f\"ble_state.pt\"\n        bae_name = f\"bae_state.pt\"\n    \n    start_time = time()\n    for e in range(epochs):\n        e_st = time()\n        epochLoss: dict = {\"train\" : 0.0, \"valid\" : 0.0} \n        epochAccs: dict = {\"train\" : 0.0, \"valid\" : 0.0}\n\n        for phase in [\"train\", \"valid\"]:\n            if phase == \"train\":\n                model.train()\n            else:\n                model.eval()\n            \n            lossPerPass: list = []\n            accsPerPass: list = []\n                \n            for X, y in dataloaders[phase]:\n                X, y = X.to(device), y.to(device).view(-1)\n\n                optimizer.zero_grad()\n                with torch.set_grad_enabled(phase == \"train\"):\n                    output = model(X)\n                    loss = torch.nn.NLLLoss()(output, y)\n                    if phase == \"train\":\n                        loss.backward()\n                        optimizer.step()\n                        if scheduler_oclr: scheduler_oclr.step()\n                lossPerPass.append(loss.item())\n                accsPerPass.append(get_accuracy(output, y))\n            epochLoss[phase] = np.mean(np.array(lossPerPass))\n            epochAccs[phase] = np.mean(np.array(accsPerPass))\n        if scheduler_oclr: LRs.append(scheduler_oclr.get_last_lr())\n        Losses.append(epochLoss)\n        Accuracies.append(epochAccs)\n    \n        if scheduler_oclr:\n            save_dict = {\"model_state_dict\"     : model.state_dict(),\n                         \"optim_state_dict\"     : optimizer.state_dict(),\n                         \"scheduler_state_dict\" : scheduler_oclr.state_dict()}\n        \n        elif scheduler_rlrop:\n            save_dict = {\"model_state_dict\"     : model.state_dict(),\n                         \"optim_state_dict\"     : optimizer.state_dict(),\n                         \"scheduler_state_dict\" : scheduler_rlrop.state_dict()}\n        \n        else:\n            save_dict = {\"model_state_dict\"     : model.state_dict(),\n                         \"optim_state_dict\"     : optimizer.state_dict()}\n        \n        \n        if early_stopping_patience:\n            if epochLoss[\"valid\"] < bestLoss[\"valid\"]:\n                bestLoss = epochLoss\n                BLE = e + 1\n                torch.save(save_dict, os.path.join(save_path, ble_name))\n                torch.save(save_dict, os.path.join(save_path, bae_name))\n                early_stopping_step = 0\n            else:\n                early_stopping_step += 1\n                if early_stopping_step > early_stopping_patience:\n                    print(\"\\nEarly Stopping at Epoch {}\".format(e + 1))\n                    break\n        \n        if epochLoss[\"valid\"] < bestLoss[\"valid\"]:\n            bestLoss = epochLoss\n            BLE = e + 1\n            torch.save(save_dict,os.path.join(save_path, ble_name))\n        \n        if epochAccs[\"valid\"] > bestAccs[\"valid\"]:\n            bestAccs = epochAccs\n            BAE = e + 1\n            torch.save(save_dict,os.path.join(save_path, bae_name))\n        \n        if scheduler_rlrop: scheduler_rlrop.step(epochLoss[\"valid\"])\n        \n        if verbose:\n            print(\"Epoch: {} | Train Loss: {:.5f} | Valid Loss: {:.5f} |\\\n Train Accs: {:.5f} | Valid Accs: {:.5f} | Time: {:.2f} seconds\".format(e+1, \n                                                                        epochLoss[\"train\"], epochLoss[\"valid\"], \n                                                                        epochAccs[\"train\"], epochAccs[\"valid\"], \n                                                                        time()-e_st))\n\n    if verbose:                                           \n        breaker()\n        print(f\"Best Validation Loss at Epoch {BLE}\")\n        breaker()\n        print(f\"Best Validation Accs at Epoch {BAE}\")\n        breaker()\n        print(\"Time Taken [{} Epochs] : {:.2f} minutes\".format(len(Losses), (time()-start_time)/60))\n    \n    return Losses, Accuracies, LRs, bestLoss, bestAccs, BLE, BAE, ble_name, bae_name\n\n\ndef predict_batch(model=None, dataloader=None, path=None, device=None) -> np.ndarray:\n    model.load_state_dict(torch.load(path, map_location=device)[\"model_state_dict\"])\n    model.to(device)    \n    model.eval()\n    \n    y_pred = torch.zeros(1, 1).to(device)\n    \n    for X in dataloader:\n        X = X.to(device)\n        with torch.no_grad():\n            output = torch.argmax(torch.exp(model(X)), dim=1)\n        y_pred = torch.cat((y_pred, output.view(-1, 1)), dim=0)\n    \n    return y_pred[1:].detach().cpu().numpy()","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:22:00.933061Z","iopub.status.busy":"2022-10-13T13:22:00.932789Z","iopub.status.idle":"2022-10-13T13:22:00.955721Z","shell.execute_reply":"2022-10-13T13:22:00.954913Z"},"papermill":{"duration":0.030244,"end_time":"2022-10-13T13:22:00.957661","exception":false,"start_time":"2022-10-13T13:22:00.927417","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Train**","metadata":{"papermill":{"duration":0.003988,"end_time":"2022-10-13T13:22:00.966715","exception":false,"start_time":"2022-10-13T13:22:00.962727","status":"completed"},"tags":[]}},{"cell_type":"code","source":"tr_df = pd.read_csv(\"../input/fdis-dataframe/dataframe.csv\")\n\nfilepaths = tr_df.filepaths.copy().values\nlabels = tr_df.labels.copy().values","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:22:00.977109Z","iopub.status.busy":"2022-10-13T13:22:00.976293Z","iopub.status.idle":"2022-10-13T13:22:01.030723Z","shell.execute_reply":"2022-10-13T13:22:01.029904Z"},"papermill":{"duration":0.061777,"end_time":"2022-10-13T13:22:01.032706","exception":false,"start_time":"2022-10-13T13:22:00.970929","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fold: int = 1\nBLs: list = []\nBAs: list = []\n\n# Test \ncfg.epochs = 2\ncfg.n_splits = 3\n    \nfor tr_idx, va_idx in StratifiedKFold(n_splits=cfg.n_splits, random_state=cfg.seed, shuffle=True).split(filepaths, labels):\n\n    tr_filepaths, va_filepaths = filepaths[tr_idx], filepaths[va_idx] \n    tr_labels, va_labels       = labels[tr_idx], labels[va_idx]\n    \n    tr_data_setup = DS(\n        filepaths=tr_filepaths, \n        labels=tr_labels.reshape(-1, 1),\n        size=cfg.size,\n        transform=cfg.train_transform\n    )\n    \n    va_data_setup = DS(\n        filepaths=va_filepaths, \n        labels=va_labels.reshape(-1, 1),\n        size=cfg.size,\n        transform=cfg.valid_transform\n    )\n\n    dataloaders = {\n        \"train\" : DL(tr_data_setup, batch_size=cfg.batch_size, shuffle=True, generator=torch.manual_seed(cfg.seed)),\n        \"valid\" : DL(va_data_setup, batch_size=cfg.batch_size, shuffle=False),\n    }\n\n    cfg.steps_per_epoch = len(dataloaders[\"train\"])\n    \n    torch.manual_seed(cfg.seed)\n    model = Model().to(cfg.device)\n\n    optimizer = optim.Adam([p for p in model.parameters() if p.requires_grad], lr=cfg.lr, weight_decay=cfg.wd)\n    # optimizer = optim.AdamW([p for p in model.parameters() if p.requires_grad], lr=cfg.lr, weight_decay=cfg.wd)\n    # optimizer = optim.SGD([p for p in model.parameters() if p.requires_grad], lr=cfg.lr, weight_decay=cfg.wd, momentum=0.9)\n    \n    scheduler_oclr = optim.lr_scheduler.OneCycleLR(\n        optimizer=optimizer, \n        max_lr=cfg.max_lr, \n        epochs=cfg.epochs, \n        steps_per_epoch=cfg.steps_per_epoch,\n        pct_start=cfg.pct_start, \n        div_factor=cfg.div_factor, \n        final_div_factor=cfg.final_div_factor\n    )\n\n    # scheduler_rlrop = optim.lr_scheduler.ReduceLROnPlateau(\n    #     optimizer=optimizer,\n    #     patience=cfg.patience,\n    #     eps=cfg.eps,\n    #     verbose=True\n    # )\n\n    # scheduler_oclr = None\n    scheduler_rlrop = None\n    \n    L, A, LR, BL, BA, _, _, _, _ = fit(\n        model=model, \n        optimizer=optimizer, \n        scheduler_oclr=scheduler_oclr,\n        scheduler_rlrop=scheduler_rlrop,\n        epochs=cfg.epochs, \n        early_stopping_patience=cfg.early_stopping, \n        dataloaders=dataloaders, \n        device=cfg.device,\n        save_path=cfg.save_path,\n        fold=fold,\n        verbose=True\n    )\n    \n    breaker()\n    show_loss_graphs(L)\n    breaker()\n    show_accuracy_graphs(A)\n    breaker()\n    if scheduler_oclr:\n        show_lr_graph(LR)\n        breaker()\n    \n    BLs.append(BL)\n    BAs.append(BA)\n    \n    fold += 1","metadata":{"execution":{"iopub.execute_input":"2022-10-13T13:22:01.042811Z","iopub.status.busy":"2022-10-13T13:22:01.042269Z","iopub.status.idle":"2022-10-13T16:12:09.215468Z","shell.execute_reply":"2022-10-13T16:12:09.214200Z"},"papermill":{"duration":10208.18048,"end_time":"2022-10-13T16:12:09.217469","exception":false,"start_time":"2022-10-13T13:22:01.036989","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Best Model**","metadata":{"papermill":{"duration":0.011164,"end_time":"2022-10-13T16:12:09.240608","exception":false,"start_time":"2022-10-13T16:12:09.229444","status":"completed"},"tags":[]}},{"cell_type":"code","source":"BL = np.inf\nfor i in range(len(BLs)):\n    if BLs[i][\"valid\"] < BL:\n        BL = BLs[i][\"valid\"]\n        best_loss_index = i\n\n        \nBA = 0.0\nfor i in range(len(BAs)):\n    if BAs[i][\"valid\"] > BA:\n        BA = BAs[i][\"valid\"]\n        best_accs_index = i\n\nbreaker()\nprint(f\"Best Loss Model Fold     : {best_loss_index + 1}\")\nprint(f\"Best Accuracy Model Fold : {best_accs_index + 1}\")\nbreaker()","metadata":{"execution":{"iopub.execute_input":"2022-10-13T16:12:09.264886Z","iopub.status.busy":"2022-10-13T16:12:09.263990Z","iopub.status.idle":"2022-10-13T16:12:09.273002Z","shell.execute_reply":"2022-10-13T16:12:09.272082Z"},"papermill":{"duration":0.023212,"end_time":"2022-10-13T16:12:09.274974","exception":false,"start_time":"2022-10-13T16:12:09.251762","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]}]}