{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-05-22T21:59:19.005020Z","iopub.execute_input":"2023-05-22T21:59:19.005512Z","iopub.status.idle":"2023-05-22T21:59:19.042373Z","shell.execute_reply.started":"2023-05-22T21:59:19.005467Z","shell.execute_reply":"2023-05-22T21:59:19.040950Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check version of tensorflow\nimport tensorflow as tf\nprint(tf.__version__)","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:19.047417Z","iopub.execute_input":"2023-05-22T21:59:19.048469Z","iopub.status.idle":"2023-05-22T21:59:30.515028Z","shell.execute_reply.started":"2023-05-22T21:59:19.048423Z","shell.execute_reply":"2023-05-22T21:59:30.513554Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 0. EDA","metadata":{}},{"cell_type":"code","source":"# Load train information dataset\ntrain_df = pd.read_csv(\"/kaggle/input/happy-whale-and-dolphin/train.csv\")\ntrain_df ","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:30.516905Z","iopub.execute_input":"2023-05-22T21:59:30.518374Z","iopub.status.idle":"2023-05-22T21:59:30.702714Z","shell.execute_reply.started":"2023-05-22T21:59:30.518329Z","shell.execute_reply":"2023-05-22T21:59:30.701046Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check unique values of species\nprint(train_df.species.unique())\nprint(len(train_df.species.unique()))","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:30.707039Z","iopub.execute_input":"2023-05-22T21:59:30.707604Z","iopub.status.idle":"2023-05-22T21:59:30.733130Z","shell.execute_reply.started":"2023-05-22T21:59:30.707545Z","shell.execute_reply":"2023-05-22T21:59:30.731529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Modify incorrect labels\ntrain_df[\"species\"] = train_df.species.replace({\n    \"globis\": \"short_finned_pilot_whale\",\n    \"pilot_whale\": \"short_finned_pilot_whale\",\n    \"kiler_whale\": \"killer_whale\",\n    \"bottlenose_dolpin\": \"bottlenose_dolphin\",\n    'beluga' : 'beluga_whale'\n})\nprint(train_df)\n# Check the modified labels\nprint(train_df.species.unique())\nprint(len(train_df.species.unique()))","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:30.735120Z","iopub.execute_input":"2023-05-22T21:59:30.735870Z","iopub.status.idle":"2023-05-22T21:59:30.783792Z","shell.execute_reply.started":"2023-05-22T21:59:30.735821Z","shell.execute_reply":"2023-05-22T21:59:30.782693Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make train images file path\ntrain_images_path = \"/kaggle/input/happy-whale-and-dolphin/train_images\"\ntrain_df[\"image_path\"] = train_df[\"image\"].apply(lambda x: train_images_path + \"/\" + x)\ntrain_df","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:30.785812Z","iopub.execute_input":"2023-05-22T21:59:30.786477Z","iopub.status.idle":"2023-05-22T21:59:30.836720Z","shell.execute_reply.started":"2023-05-22T21:59:30.786435Z","shell.execute_reply":"2023-05-22T21:59:30.835208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 1. Make simplest model and check the accuracy","metadata":{}},{"cell_type":"code","source":"# Use PyTorch for making a model\nimport torch # PyTorch project\nfrom torch.utils.data import Dataset\nfrom torch.utils.data import DataLoader\nimport torchvision\nfrom torchvision import transforms\nimport cv2\nimport matplotlib.pyplot as plt\nimport torch.nn as nn # Library for neural network\nimport torch.nn.functional as F # Function for neural networt\nimport torch.optim as optim # Library for optimizer","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:30.848014Z","iopub.execute_input":"2023-05-22T21:59:30.848387Z","iopub.status.idle":"2023-05-22T21:59:34.100929Z","shell.execute_reply.started":"2023-05-22T21:59:30.848350Z","shell.execute_reply":"2023-05-22T21:59:34.099295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hold reproductivity\ntorch.manual_seed(0) #for torch\n\nnp.random.seed(0) #for numpy\n\ntorch.backends.cudnn.benchmark = False \ntorch.backends.cudnn.deterministic = True #for GPU","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:19:35.218592Z","iopub.execute_input":"2023-05-22T22:19:35.219240Z","iopub.status.idle":"2023-05-22T22:19:35.228293Z","shell.execute_reply.started":"2023-05-22T22:19:35.219186Z","shell.execute_reply":"2023-05-22T22:19:35.226732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make dataset from csv\nclass MyDataset(Dataset):\n    def __init__(self, df, size=224, transform=None):\n        self.img_paths = self._get_img_paths(df)#get list of file path\n        self.size = size\n        self.transform = transform\n        self.dictionary = self._get_dictionary(df)\n        self.classes = self._get_classes(df)#get list of classes\n\n    def __getitem__(self, index):\n        # Load image\n        # Crop a picture into squared one\n        size = self.size\n        path = self.img_paths[index]\n        img = cv2.imread(path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        h, w, c = img.shape\n        x = w if h > w else h\n        y = x\n        top = int((h - y) / 2)\n        bottom = top + y\n        left = int((w - x) / 2)\n        right = left + x\n        img = img[top:bottom, left:right]\n        # Resize the picture to target size\n        img = cv2.resize(img, dsize=(size, size))\n        \n        if self.transform is not None:\n            img = self.transform(img)\n        \n        # Load class\n        species = self.classes[index]\n        \n        # Load label\n        label = self.dictionary[species]   \n        \n        return img, species, label\n\n    def _get_img_paths(self, df):\n        img_paths = df.iloc[:, -1].to_list()\n        return img_paths\n    \n    def _get_dictionary(self, df):\n        classes = df[\"species\"].unique()\n        dictionary = {}\n        i = 0\n        for key in classes:\n            dictionary[key] = i\n            i += 1\n        return dictionary \n    \n    def _get_classes(self, df):\n        classes = df[\"species\"].to_list()\n        return classes\n        \n    def __len__(self):\n        return len(self.img_paths)\n    \n# Make transform\ntransform = transforms.Compose([transforms.ToTensor()])\n\n# Make dataset for top3 data on train_df\ndataset = MyDataset(train_df.loc[0:8], transform=transform)\n\n# Check the contents\nimage, species, label = dataset[1]\nprint(label)\nprint(species)\nnp_image = image.permute(1, 2, 0).numpy()\nplt.imshow(np_image)\ndataset.dictionary","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:35.181327Z","iopub.execute_input":"2023-05-22T21:59:35.181744Z","iopub.status.idle":"2023-05-22T21:59:35.832042Z","shell.execute_reply.started":"2023-05-22T21:59:35.181704Z","shell.execute_reply":"2023-05-22T21:59:35.830711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make dataloader \ndataloader = DataLoader(dataset, batch_size=9)\n\n# Check information per batch in dataloader\nfor batch_image, batch_species, batch_label in dataloader:\n    print(batch_image.shape)\n    print(type(batch_label))\n    print(type(batch_species))","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:35.834159Z","iopub.execute_input":"2023-05-22T21:59:35.834563Z","iopub.status.idle":"2023-05-22T21:59:36.799512Z","shell.execute_reply.started":"2023-05-22T21:59:35.834526Z","shell.execute_reply":"2023-05-22T21:59:36.797262Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(batch_image[1].shape)","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:36.801055Z","iopub.execute_input":"2023-05-22T21:59:36.801450Z","iopub.status.idle":"2023-05-22T21:59:36.810242Z","shell.execute_reply.started":"2023-05-22T21:59:36.801405Z","shell.execute_reply":"2023-05-22T21:59:36.809158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10, 10))\nfor i in range(9):\n    ax = plt.subplot(3, 3, i + 1)\n    plt.imshow(batch_image[i].permute(1, 2, 0).numpy())\n    plt.title(batch_species[i])\n    plt.axis(\"off\")","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:36.811577Z","iopub.execute_input":"2023-05-22T21:59:36.812472Z","iopub.status.idle":"2023-05-22T21:59:37.800362Z","shell.execute_reply.started":"2023-05-22T21:59:36.812417Z","shell.execute_reply":"2023-05-22T21:59:37.798912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Split train/validation data in train dataset\norigin_dataset = MyDataset(train_df.loc[0:99], transform=transform)\ntrain_dataset, val_dataset = torch.utils.data.random_split(dataset=origin_dataset, lengths=[70, 30], generator=torch.Generator().manual_seed(0))\nprint(\"train_dataset has {} data.\".format(len(train_dataset)))\nprint(\"val_dataset has {} data.\".format(len(val_dataset)))","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:37.818036Z","iopub.execute_input":"2023-05-22T21:59:37.818834Z","iopub.status.idle":"2023-05-22T21:59:37.835767Z","shell.execute_reply.started":"2023-05-22T21:59:37.818782Z","shell.execute_reply":"2023-05-22T21:59:37.833756Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make dataloaders\ntrain_dataloader = DataLoader(train_dataset, batch_size=10)\nval_dataloader = DataLoader(val_dataset, batch_size=10)","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:37.837577Z","iopub.execute_input":"2023-05-22T21:59:37.838140Z","iopub.status.idle":"2023-05-22T21:59:37.847835Z","shell.execute_reply.started":"2023-05-22T21:59:37.838076Z","shell.execute_reply":"2023-05-22T21:59:37.845997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make model\nclass Model(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.conv1 = nn.Conv2d(3, 10, kernel_size=3, padding=1)\n        # Input channel is 3 because of RGB\n        self.conv2 = nn.Conv2d(10, 20, kernel_size=3, padding=1)\n        self.bn1 = nn.BatchNorm2d(num_features=20)\n        \n        self.fc1 = nn.Linear(20*56*56, 128)\n        self.fc2 = nn.Linear(128, 26)\n    \n    def forward(self, x):\n        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))\n        x = F.max_pool2d(F.relu(self.bn1(self.conv2(x))), (2, 2))\n        \n        x = torch.flatten(x, 1)\n        x = F.relu(self.fc1(x))\n        x = F.softmax(self.fc2(x), dim=1)\n        return x\n    \nmodel = Model()","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:37.849844Z","iopub.execute_input":"2023-05-22T21:59:37.850365Z","iopub.status.idle":"2023-05-22T21:59:37.948786Z","shell.execute_reply.started":"2023-05-22T21:59:37.850324Z","shell.execute_reply":"2023-05-22T21:59:37.947394Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set loss function\ncriterion = nn.CrossEntropyLoss()\n\n# Set optimizer\noptimizer = optim.Adam(model.parameters(), lr=0.001)","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:37.950305Z","iopub.execute_input":"2023-05-22T21:59:37.951477Z","iopub.status.idle":"2023-05-22T21:59:37.958417Z","shell.execute_reply.started":"2023-05-22T21:59:37.951419Z","shell.execute_reply":"2023-05-22T21:59:37.956733Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train the model\nfor epoch in range(2):\n    running_loss = 0.0\n    for i, data in enumerate(train_dataloader):\n        inputs, species, labels = data\n        optimizer.zero_grad()\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n        if i % 7 == 6:\n            print(f\"[{epoch + 1}, {i + 1}] loss:{running_loss :.3f}\")\n            running_loss = 0.0","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:37.961481Z","iopub.execute_input":"2023-05-22T21:59:37.962149Z","iopub.status.idle":"2023-05-22T21:59:54.082985Z","shell.execute_reply.started":"2023-05-22T21:59:37.962070Z","shell.execute_reply":"2023-05-22T21:59:54.081750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check validation of the training\ncorrect = 0\ntotal = 0\n\nwith torch.no_grad():\n    for data in val_dataloader:\n        inputs, species, labels = data\n        outputs = model(inputs)\n        _, predictions = torch.max(outputs.data, 1) #extract labels of outputs\n        total += labels.size(0)\n        correct += (predictions == labels).sum().item()\n\nprint(f\"Accuracy of the network on the 30 test images: {100 * correct // total} %\")","metadata":{"execution":{"iopub.status.busy":"2023-05-22T21:59:54.084459Z","iopub.execute_input":"2023-05-22T21:59:54.085432Z","iopub.status.idle":"2023-05-22T21:59:56.838298Z","shell.execute_reply.started":"2023-05-22T21:59:54.085383Z","shell.execute_reply":"2023-05-22T21:59:56.837190Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check validation of the training per class\nclasses = train_df.species.unique()\ncorrect_pred = {classname: 0 for classname in classes}\ntotal_pred = {classname: 0 for classname in classes}\n\nwith torch.no_grad():\n    for data in val_dataloader:\n        inputs, species, labels = data\n        outputs = model(inputs)\n        _, predictions = torch.max(outputs, 1)\n        \n        for label, prediction in zip(labels, predictions):\n            if label == prediction:\n                correct_pred[classes[label]] += 1 #if answer is correct, count 1 on correct_pred \n            total_pred[classes[label]] += 1 #count 1 regardless of correctness\n\nfor classname, correct_count in correct_pred.items():\n    if total_pred[classname] == 0:\n        print(f\"Can not insist anything about {classname}.\")\n    else:\n        accuracy = 100 * float(correct_count) / total_pred[classname]\n        print(f\"Accuracy for classes: {classname} is {accuracy:.1f} %.\")","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:01:31.684683Z","iopub.execute_input":"2023-05-22T22:01:31.685227Z","iopub.status.idle":"2023-05-22T22:01:33.926566Z","shell.execute_reply.started":"2023-05-22T22:01:31.685184Z","shell.execute_reply":"2023-05-22T22:01:33.924835Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Increase the accuracy by increasing the data","metadata":{}},{"cell_type":"code","source":"# Split train/validation data in train dataset\norigin_dataset = MyDataset(train_df.loc[0:999], transform=transform)\ntrain_dataset, val_dataset = torch.utils.data.random_split(dataset=origin_dataset, lengths=[700, 300], generator=torch.Generator().manual_seed(0))\nprint(\"train_dataset has {} data.\".format(len(train_dataset)))\nprint(\"val_dataset has {} data.\".format(len(val_dataset)))","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:30:56.579364Z","iopub.execute_input":"2023-05-22T22:30:56.580895Z","iopub.status.idle":"2023-05-22T22:30:56.592540Z","shell.execute_reply.started":"2023-05-22T22:30:56.580842Z","shell.execute_reply":"2023-05-22T22:30:56.590912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make dataloaders\ntrain_dataloader = DataLoader(train_dataset, batch_size=50)\nval_dataloader = DataLoader(val_dataset, batch_size=50)","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:31:11.805234Z","iopub.execute_input":"2023-05-22T22:31:11.805786Z","iopub.status.idle":"2023-05-22T22:31:11.813032Z","shell.execute_reply.started":"2023-05-22T22:31:11.805739Z","shell.execute_reply":"2023-05-22T22:31:11.811487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make model\nclass Model(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.conv1 = nn.Conv2d(3, 10, kernel_size=3, padding=1)\n        # Input channel is 3 because of RGB\n        self.conv2 = nn.Conv2d(10, 20, kernel_size=3, padding=1)\n        self.bn1 = nn.BatchNorm2d(num_features=20)\n        \n        self.fc1 = nn.Linear(20*56*56, 128)\n        self.fc2 = nn.Linear(128, 26)\n    \n    def forward(self, x):\n        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))\n        x = F.max_pool2d(F.relu(self.bn1(self.conv2(x))), (2, 2))\n        \n        x = torch.flatten(x, 1)\n        x = F.relu(self.fc1(x))\n        x = F.softmax(self.fc2(x), dim=1)\n        return x\n    \nmodel = Model()","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:31:18.265427Z","iopub.execute_input":"2023-05-22T22:31:18.266813Z","iopub.status.idle":"2023-05-22T22:31:18.352579Z","shell.execute_reply.started":"2023-05-22T22:31:18.266752Z","shell.execute_reply":"2023-05-22T22:31:18.351044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set loss function\ncriterion = nn.CrossEntropyLoss()\n\n# Set optimizer\noptimizer = optim.Adam(model.parameters(), lr=0.001)","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:31:23.282726Z","iopub.execute_input":"2023-05-22T22:31:23.284132Z","iopub.status.idle":"2023-05-22T22:31:23.291035Z","shell.execute_reply.started":"2023-05-22T22:31:23.284075Z","shell.execute_reply":"2023-05-22T22:31:23.289080Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train the model\nfor epoch in range(2):\n    running_loss = 0.0\n    for i, data in enumerate(train_dataloader):\n        inputs, species, labels = data\n        optimizer.zero_grad()\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n        if i % 50 == 49:\n            print(f\"[{epoch + 1}, {i + 1}] loss:{running_loss :.3f}\")\n            running_loss = 0.0","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:32:47.815943Z","iopub.execute_input":"2023-05-22T22:32:47.816511Z","iopub.status.idle":"2023-05-22T22:35:09.900183Z","shell.execute_reply.started":"2023-05-22T22:32:47.816463Z","shell.execute_reply":"2023-05-22T22:35:09.898673Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check validation of the training\ncorrect = 0\ntotal = 0\n\nwith torch.no_grad():\n    for data in val_dataloader:\n        inputs, species, labels = data\n        outputs = model(inputs)\n        _, predictions = torch.max(outputs.data, 1) #extract labels of outputs\n        total += labels.size(0)\n        correct += (predictions == labels).sum().item()\n\nprint(f\"Accuracy of the network on the 300 test images: {100 * correct // total} %\")","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:35:18.293134Z","iopub.execute_input":"2023-05-22T22:35:18.293760Z","iopub.status.idle":"2023-05-22T22:35:46.046643Z","shell.execute_reply.started":"2023-05-22T22:35:18.293707Z","shell.execute_reply":"2023-05-22T22:35:46.044713Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check validation of the training per class\nclasses = train_df.species.unique()\ncorrect_pred = {classname: 0 for classname in classes}\ntotal_pred = {classname: 0 for classname in classes}\n\nwith torch.no_grad():\n    for data in val_dataloader:\n        inputs, species, labels = data\n        outputs = model(inputs)\n        _, predictions = torch.max(outputs, 1)\n        \n        for label, prediction in zip(labels, predictions):\n            if label == prediction:\n                correct_pred[classes[label]] += 1 #if answer is correct, count 1 on correct_pred \n            total_pred[classes[label]] += 1 #count 1 regardless of correctness\n\nfor classname, correct_count in correct_pred.items():\n    if total_pred[classname] == 0:\n        print(f\"Can not insist anything about {classname}.\")\n    else:\n        accuracy = 100 * float(correct_count) / total_pred[classname]\n        print(f\"Accuracy for classes: {classname} is {accuracy:.1f} %.\")","metadata":{"execution":{"iopub.status.busy":"2023-05-22T22:35:54.694550Z","iopub.execute_input":"2023-05-22T22:35:54.695485Z","iopub.status.idle":"2023-05-22T22:36:18.857124Z","shell.execute_reply.started":"2023-05-22T22:35:54.695405Z","shell.execute_reply":"2023-05-22T22:36:18.854786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}