{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"sourceType":"competition"}],"dockerImageVersionId":30746,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os                                         # To work with OS commands\nimport pandas as pd\nfrom PIL import Image                             # To read Images\nfrom termcolor import colored                     # To Colorfull output\nfrom datetime import datetime                     # To calculate durations\n\nimport numpy as np                                # To work with Numpy arrays\nimport matplotlib.pyplot as plt                   # To Visualization\nimport seaborn as sns                             # To Visualization\n\nimport torch                                      # To work with TORCH framework\nimport torch.nn as nn                             # To work with Neural Networks\nimport torchvision                                # To work with image datasets\nimport torchvision.transforms as transforms       # To create data transforms\nfrom torch.utils.data import Dataset, DataLoader,random_split\n\nfrom torchvision.models import efficientnet_b5, EfficientNet_B5_Weights # Pretrained model with its weights\n\nfrom sklearn.metrics import confusion_matrix, classification_report # To Evaluate the result","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.032270Z","iopub.execute_input":"2024-08-18T13:58:53.032964Z","iopub.status.idle":"2024-08-18T13:58:53.040031Z","shell.execute_reply.started":"2024-08-18T13:58:53.032935Z","shell.execute_reply":"2024-08-18T13:58:53.039009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_path = '/kaggle/input/cassava-leaf-disease-classification/train.csv'\ntrain_csv = pd.read_csv(train_path);\ntrain_csv.head()","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.042131Z","iopub.execute_input":"2024-08-18T13:58:53.042472Z","iopub.status.idle":"2024-08-18T13:58:53.076535Z","shell.execute_reply.started":"2024-08-18T13:58:53.042436Z","shell.execute_reply":"2024-08-18T13:58:53.075698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_dir = '/kaggle/input/cassava-leaf-disease-classification/train_images'","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.077996Z","iopub.execute_input":"2024-08-18T13:58:53.078604Z","iopub.status.idle":"2024-08-18T13:58:53.082718Z","shell.execute_reply.started":"2024-08-18T13:58:53.078569Z","shell.execute_reply":"2024-08-18T13:58:53.081818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transform = transforms.Compose(\n    [        \n        transforms.ToTensor(),                         # Convert images to tensor\n        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # Normalize R, G, B channels\n    ]\n)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.084171Z","iopub.execute_input":"2024-08-18T13:58:53.084557Z","iopub.status.idle":"2024-08-18T13:58:53.092927Z","shell.execute_reply.started":"2024-08-18T13:58:53.084522Z","shell.execute_reply":"2024-08-18T13:58:53.092026Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = '/kaggle/input/cassava-leaf-disease-classification/train_images/1005200906.jpg'\n\n# Open the image file\nwith Image.open(image_path) as img:\n    # Get the size of the image\n    width, height = img.size\n    print(f\"Image size: {width} x {height}\")","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.094992Z","iopub.execute_input":"2024-08-18T13:58:53.095269Z","iopub.status.idle":"2024-08-18T13:58:53.114280Z","shell.execute_reply.started":"2024-08-18T13:58:53.095246Z","shell.execute_reply":"2024-08-18T13:58:53.113428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ImageDataset(Dataset):\n    def __init__(self, csv_file, root_dir, transform=None):\n        # Read the CSV file\n        self.data = pd.read_csv(csv_file)\n        # Set the directory where images are stored\n        self.root_dir = root_dir\n        # Apply any image transformations if provided\n        self.transform = transform\n    \n    def __len__(self):\n        # Return the total number of images in the dataset\n        return len(self.data)\n    \n    def __getitem__(self, idx):\n        # Get the image name from the CSV file\n        img_name = os.path.join(self.root_dir, self.data.iloc[idx, 0])\n        # Open the image using PIL\n        image = Image.open(img_name).convert('RGB')\n        # Get the corresponding label from the CSV file\n        label = self.data.iloc[idx, 1]\n        \n        # Apply any transformations to the image\n        if self.transform:\n            image = self.transform(image)\n        \n        # Return the image and its label\n        return image, label","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.115290Z","iopub.execute_input":"2024-08-18T13:58:53.115565Z","iopub.status.idle":"2024-08-18T13:58:53.122724Z","shell.execute_reply.started":"2024-08-18T13:58:53.115541Z","shell.execute_reply":"2024-08-18T13:58:53.121828Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(type(train_path))  # Should be <class 'str'>","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.123903Z","iopub.execute_input":"2024-08-18T13:58:53.124434Z","iopub.status.idle":"2024-08-18T13:58:53.136088Z","shell.execute_reply.started":"2024-08-18T13:58:53.124403Z","shell.execute_reply":"2024-08-18T13:58:53.135309Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(train_path)       # Should be the file path as a string","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.137173Z","iopub.execute_input":"2024-08-18T13:58:53.137457Z","iopub.status.idle":"2024-08-18T13:58:53.145123Z","shell.execute_reply.started":"2024-08-18T13:58:53.137434Z","shell.execute_reply":"2024-08-18T13:58:53.144277Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CassavaDataset(Dataset):\n    def __init__(self, csv_file, root_dir, transform=None):\n        # Read the CSV file\n        self.data = pd.read_csv(csv_file)\n        # Set the directory where images are stored\n        self.root_dir = root_dir\n        # Apply any image transformations if provided\n        self.transform = transform\n    \n    def __len__(self):\n        # Return the total number of images in the dataset\n        return len(self.data)\n    \n    def __getitem__(self, idx):\n        # Get the image name from the CSV file\n        img_name = os.path.join(self.root_dir, self.data.iloc[idx, 0])\n        # Open the image using PIL\n        image = Image.open(img_name).convert('RGB')\n        # Get the corresponding label from the CSV file\n        label = self.data.iloc[idx, 1]\n        \n        # Apply any transformations to the image\n        if self.transform:\n            image = self.transform(image)\n        \n        # Return the image and its label\n        return image, label","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.242000Z","iopub.execute_input":"2024-08-18T13:58:53.242697Z","iopub.status.idle":"2024-08-18T13:58:53.250657Z","shell.execute_reply.started":"2024-08-18T13:58:53.242660Z","shell.execute_reply":"2024-08-18T13:58:53.249578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset = CassavaDataset(csv_file=train_path, root_dir=base_dir, transform=transform)\n\nprint(\"Images added with labels Now creating new dataset\")\n\n\n","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.252431Z","iopub.execute_input":"2024-08-18T13:58:53.252773Z","iopub.status.idle":"2024-08-18T13:58:53.276356Z","shell.execute_reply.started":"2024-08-18T13:58:53.252738Z","shell.execute_reply":"2024-08-18T13:58:53.275422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Lets divide into train and valid data","metadata":{}},{"cell_type":"code","source":"BATCH_SIZE=32\n# Define the split ratio\ntrain_size = int(0.8 * len(dataset))  # 80% for training\nval_size = len(dataset) - train_size  # Remaining 20% for validation\n\n# Split the dataset\ntrain_dataset, val_dataset = random_split(dataset, [train_size, val_size])\n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False)\n\nprint(\"Dataloaders are done\")","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.277630Z","iopub.execute_input":"2024-08-18T13:58:53.277995Z","iopub.status.idle":"2024-08-18T13:58:53.288129Z","shell.execute_reply.started":"2024-08-18T13:58:53.277962Z","shell.execute_reply":"2024-08-18T13:58:53.287274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image, label = dataset[3]\n# image is the tensor form and label is the index no of the species\nprint(image.shape)\nprint(label)\nimage","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.290486Z","iopub.execute_input":"2024-08-18T13:58:53.291015Z","iopub.status.idle":"2024-08-18T13:58:53.314326Z","shell.execute_reply.started":"2024-08-18T13:58:53.290980Z","shell.execute_reply":"2024-08-18T13:58:53.313292Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# To visualize the image, convert the tensor to a numpy array and plot it\nimage_np = image.permute(1, 2, 0).numpy()  # Convert from (C, H, W) to (H, W, C) for visualization\n\nplt.imshow(image_np)\nplt.title(f\"Label: {label}\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.315731Z","iopub.execute_input":"2024-08-18T13:58:53.316487Z","iopub.status.idle":"2024-08-18T13:58:53.741208Z","shell.execute_reply.started":"2024-08-18T13:58:53.316442Z","shell.execute_reply":"2024-08-18T13:58:53.740347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import json\n\n# Load the label to disease mapping\nwith open('/kaggle/input/cassava-leaf-disease-classification/label_num_to_disease_map.json', 'r') as file:\n    label_map = json.load(file)\n\n# Print to check the contents\nprint(label_map)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.742444Z","iopub.execute_input":"2024-08-18T13:58:53.742734Z","iopub.status.idle":"2024-08-18T13:58:53.748487Z","shell.execute_reply.started":"2024-08-18T13:58:53.742709Z","shell.execute_reply":"2024-08-18T13:58:53.747574Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# lets 1st Fetch 1 Batch from train_loader(contain 32 Images and Labels)\nfor images, labels in train_loader :\n    break\n    \n# Plot images and their labels in 1 batch (32 images and labels)\nplt.subplots(4, 8, figsize=(20, 10))\nplt.suptitle('Images and Labels in 1 batch', fontsize=20, fontweight='bold')\n\nfor i in range(BATCH_SIZE) :\n    ax = plt.subplot(4, 8, i+1)\n    img = torch.permute(images[i], (1, 2, 0))\n    #Many visualization libraries, such as matplotlib expect images to be in the format [Height, Width, Channels]\n    #But PyTorch’s default image tensor format is [Channels, Height, Width] (i.e., [C, H, W]),\n    plt.imshow(img)\n    plt.axis('off')\n    # Convert label to class name using label_map\n    label_name = label_map.get(str(labels[i].item()), 'Unknown')  # Ensure keys are strings\n    plt.title(label_name)\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:53.749607Z","iopub.execute_input":"2024-08-18T13:58:53.749919Z","iopub.status.idle":"2024-08-18T13:58:58.558963Z","shell.execute_reply.started":"2024-08-18T13:58:53.749895Z","shell.execute_reply":"2024-08-18T13:58:58.557997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Using efficientnet_model.Then only uncomment these","metadata":{}},{"cell_type":"code","source":"# # Load efficientnet_b5 weight (best weight)\n# weights = EfficientNet_B5_Weights.DEFAULT   # I have imported this..Check the beginning of this file\n\n# # This function prepare images to feed into efficientnet_b5 model\n# preprocess = weights.transforms()\n\n# # Load efficientnet_b5 model with best weights\n# efficientnet_model = efficientnet_b5(weights=weights)\n\n# # Freezing parameters means that during training, these parameters will not be updated (i.e., their gradients will not be computed).\n# # This is typically done when you want to use a pretrained model as a feature extractor and do not want to modify the pretrained weights.\n# for param in efficientnet_model.parameters() :\n#     param.requires_grad = False\n\n# # Then Unfreeze some of last layers of model\n# # By unfreezing the last layers, you allow their weights to be updated during training.\n# # ONLY unfreeze LAST LAYER when using a pretrained model !!!!\n# for param in efficientnet_model.features[6].parameters() :   # refers to the seventh block\n#     param.requires_grad = True","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:58.560240Z","iopub.execute_input":"2024-08-18T13:58:58.560552Z","iopub.status.idle":"2024-08-18T13:58:58.565430Z","shell.execute_reply.started":"2024-08-18T13:58:58.560527Z","shell.execute_reply":"2024-08-18T13:58:58.564434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Using Vgg16 model ","metadata":{}},{"cell_type":"code","source":"from torchvision.models import vgg16, VGG16_Weights\n\n# Load the pre-trained VGG16 weights\nweights = VGG16_Weights.DEFAULT\n\n# This function prepares images to feed into VGG16\npreprocess = weights.transforms()\n\n# Load VGG16 model with pre-trained weights\nvgg16_model = vgg16(weights=weights)\n\nfor name, param in vgg16_model.named_parameters():\n    print(name)\nfor name, param in vgg16_model.named_parameters():\n    print(f\"{name}: requires_grad = {param.requires_grad}\")\n\n\n# Freeze all layers except the final fully connected layer\nfor name, param in vgg16_model.named_parameters():\n    if \"classifier.6\" in name:  # Unfreeze the final fully connected layer\n        param.requires_grad = True\n    else:\n        param.requires_grad = False\n\nfor name, param in vgg16_model.named_parameters():\n    print(f\"{name}: requires_grad = {param.requires_grad}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:58:58.566608Z","iopub.execute_input":"2024-08-18T13:58:58.566913Z","iopub.status.idle":"2024-08-18T13:59:00.251007Z","shell.execute_reply.started":"2024-08-18T13:58:58.566886Z","shell.execute_reply":"2024-08-18T13:59:00.250024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### I named it as CNN model","metadata":{}},{"cell_type":"code","source":"# Number of classes\nnum_classes =5\n\nclass CNN(nn.Module) :\n    def __init__(self, num_classes) :\n        super(CNN, self).__init__()\n        ####  CONVs\n        self.conv_layers = vgg16_model   # UPDATE YOUR MODEL !!!!!!!!!!!!!!!!!!!!!!!\n        ####  Dense\n        self.dense_layers = nn.Sequential(\n            nn.Linear(1000, 256),  # UPDATE !efficientnet_model has 1000 classes in its layer thats why we take from there\n            nn.ReLU(),\n            nn.Linear(256, 64),\n            nn.ReLU(),\n            nn.Linear(64, 5)    # The number 5 corresponds to the number of output classes\n        )\n    def forward(self, X) :\n        out = self.conv_layers(X)\n         # the output tensor is typically multi-dimensional (e.g., [batch_size, channels, height, width]). out.size(0): This is the batch size\n        out = out.view(out.size(0), -1)    # used to flatten the output tensor\n        out = self.dense_layers(out)\n        return out\n\n\nmodel = CNN(num_classes)\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters())  # default => torch.optim.Adam (Adaptive Moment Estimation): lr=0.001\n\n#print(model)\nprint(\"Model created !!\")","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:59:00.254628Z","iopub.execute_input":"2024-08-18T13:59:00.255387Z","iopub.status.idle":"2024-08-18T13:59:00.267262Z","shell.execute_reply.started":"2024-08-18T13:59:00.255360Z","shell.execute_reply":"2024-08-18T13:59:00.266418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\nprint(device)\n\nmodel.to(device)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:59:00.268300Z","iopub.execute_input":"2024-08-18T13:59:00.268572Z","iopub.status.idle":"2024-08-18T13:59:00.428219Z","shell.execute_reply.started":"2024-08-18T13:59:00.268542Z","shell.execute_reply":"2024-08-18T13:59:00.427339Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"torch.manual_seed(32)\n\nnum_epochs = 1\nprint( \"LETS START !! \")\n# below, train_losses is initialized as a NumPy array with zeros.\n#Predefined Size: This method creates a fixed-size array with a size of num_epochs,\n#which is useful if you want to use array indexing for storing loss values.\n\ntrain_losses = np.zeros(num_epochs)  # 0 0 0 0 0 ..stored in this format\nval_losses = np.zeros(num_epochs)\ntrain_accs = np.zeros(num_epochs)\nval_accs = np.zeros(num_epochs)\n# Initialize batch counter\nbatch_counter = 0\nval_counter = 0\n    \nfor it in range(num_epochs) :\n\n    # Define some variabels to store metrics in each epoch\n    train_loss = []\n    val_loss = []\n    n_correct = 0\n    n_total = 0\n    # Change mode to TRAIN\n    model.train()\n    t0 = datetime.now()\n\n    # Fetch images from DataLoader\n    for images, labels in train_loader :\n        batch_counter += 1 \n        # Move data to GPU\n        images = images.to(device)\n        labels = labels.to(device)\n        \n        # Zero Grad optimizer\n        optimizer.zero_grad()\n\n        # Forward pass\n        y_pred = model(images)\n        loss = criterion(y_pred, labels)\n\n        # Backward pass\n        loss.backward()\n        optimizer.step()\n\n        # Train loss\n        train_loss.append(loss.item())\n\n        # Train Accuracy Calculate\n        _, prediction = torch.max(y_pred, 1)\n        n_correct += (prediction==labels).sum().item()\n        n_total += labels.shape[0]\n        \n        if batch_counter % 50 == 0:\n            print(f'Epoch [{it + 1}/{num_epochs}], Batch [{batch_counter}/{len(train_loader)}] -> '\n                  f'Train Loss: {np.mean(train_loss):.4f}, Train Acc: {n_correct / n_total:.4f}')\n\n    train_loss = np.mean(train_loss)\n    train_losses[it] = train_loss\n    train_accs[it] = n_correct / n_total\n    print(\"train_loader done..starting val_loader\")\n    # Validation\n    for images, labels in val_loader :\n        val_counter += 1 \n         # Move data to GPU\n        images = images.to(device)\n        labels = labels.to(device)\n        # Forward padd\n        y_pred = model(images)\n        loss = criterion(y_pred, labels)\n\n        # Validation Loss\n        val_loss.append(loss.item())\n\n        # Validation Accuracy\n        _, prediction = torch.max(y_pred, 1)\n        n_correct += (prediction==labels).sum().item()\n        n_total += labels.shape[0]\n        if val_counter % 30 == 0:\n            print(f'Epoch [{it + 1}/{num_epochs}], Batch [{val_counter}/{len(val_loader)}] -> '\n                  f'Val Loss: {np.mean(val_loss):.4f}, Val Acc: {n_correct / n_total:.4f}')\n\n    val_loss = np.mean(val_loss)\n    val_losses[it] = val_loss\n    val_accs[it] = n_correct / n_total\n\n    dt = datetime.now() - t0\n    # Print the result of  each epochs\n    print(f'Epoch [{it+1}/{num_epochs}] -> '\n          f' Train Loss:{train_loss:.4f}, Train Acc:{train_accs[it]:.4f} '  #  using indexing WOW !!\n          f' |Val Loss:{val_loss:.4f},   Val Acc:{val_accs[it]:.4f}  | | Duration : {dt}')\n    batch_counter=0\n    val_counter=0","metadata":{"execution":{"iopub.status.busy":"2024-08-18T13:59:00.429450Z","iopub.execute_input":"2024-08-18T13:59:00.429739Z","iopub.status.idle":"2024-08-18T14:13:18.487724Z","shell.execute_reply.started":"2024-08-18T13:59:00.429716Z","shell.execute_reply":"2024-08-18T14:13:18.486764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ax = plt.subplots(1, 2, figsize=(10, 5))\n# Left plots\nax[0].plot(train_accs, label='Train Accuracy')\nax[0].plot(val_accs, label='Validation Accuracy')\nax[0].set_title('Accuracy')\nax[0].legend()\n\n\nax[1].plot(train_losses, label='Train Loss')\nax[1].plot(val_losses, label='Validation Loss')\nax[1].set_title('Losses')\nax[1].legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-18T14:13:18.488918Z","iopub.execute_input":"2024-08-18T14:13:18.489184Z","iopub.status.idle":"2024-08-18T14:13:18.882333Z","shell.execute_reply.started":"2024-08-18T14:13:18.489162Z","shell.execute_reply":"2024-08-18T14:13:18.881426Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"MODEL Trrained now lets predict !\")","metadata":{"execution":{"iopub.status.busy":"2024-08-18T14:13:18.883498Z","iopub.execute_input":"2024-08-18T14:13:18.883772Z","iopub.status.idle":"2024-08-18T14:13:18.888461Z","shell.execute_reply.started":"2024-08-18T14:13:18.883748Z","shell.execute_reply":"2024-08-18T14:13:18.887547Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Save only the model's state dictionary\n# torch.save(model.state_dict(), 'my_VGG16_model.pth')\n\ntorch.save(model, 'my_VGG16_full_model.pth')\n\nprint(\"Model saved successfully after training!\")","metadata":{"execution":{"iopub.status.busy":"2024-08-18T14:13:18.889519Z","iopub.execute_input":"2024-08-18T14:13:18.889765Z","iopub.status.idle":"2024-08-18T14:13:19.885447Z","shell.execute_reply.started":"2024-08-18T14:13:18.889744Z","shell.execute_reply":"2024-08-18T14:13:19.884484Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}