{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Setup: Importing Libraries\n",
        "In this cell, we import the necessary libraries for model training and tokenization. \n",
        "We use `torch` for handling the neural network and `sklearn` for splitting the dataset."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/aivm/blob/main/examples/2b-fine-tuning-lenet5.ipynb)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "6oSxXkJN2eUV",
        "outputId": "52fc3dcc-3c1d-4a1b-dff3-4480d41c4299"
      },
      "outputs": [],
      "source": [
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.nn.functional as F\n",
        "import torch.optim as optim\n",
        "import matplotlib.pyplot as plt\n",
        "import torch\n",
        "from sklearn.model_selection import train_test_split\n",
        "from datasets import load_dataset\n",
        "\n",
        "\n",
        "if torch.cuda.is_available():\n",
        "    print(f\"GPU: {torch.cuda.get_device_name(0)} is available.\")\n",
        "else:\n",
        "    print(\"No GPU available. Training will run on CPU.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qG58IfExeNtw"
      },
      "source": [
        "# Load Dataset\n",
        "This section loads the dataset, which consists of images of cats and dogs. The dataset is used for training and testing the neural network."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "qrX2xajFeNtw",
        "outputId": "c0999ff2-8966-495d-9446-b608054ba030"
      },
      "outputs": [],
      "source": [
        "ds = load_dataset(\"microsoft/cats_vs_dogs\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 372
        },
        "id": "Ye6SN1ofMcxU",
        "outputId": "cc87c473-ea4e-404e-9d88-d22050cdc882"
      },
      "outputs": [],
      "source": [
        "# Plot images from the dataset\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "def plot_images(dataset, num_images=4):\n",
        "  \"\"\"Plots a specified number of images from the dataset.\"\"\"\n",
        "  fig, axs = plt.subplots(1, num_images, figsize=(15, 5))\n",
        "  for i in range(num_images):\n",
        "    image = dataset['train'][i]['image']\n",
        "    image = np.array(image)\n",
        "    axs[i].imshow(image)\n",
        "    axs[i].axis('off')\n",
        "    axs[i].set_title(f\"Label: {dataset['train'][i]['labels']}\")\n",
        "  plt.show()\n",
        "\n",
        "\n",
        "plot_images(ds)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5RF81PKjeNtx"
      },
      "source": [
        "# Prepare the Dataset\n",
        "In this step, we prepare the dataset by applying transformations such as resizing the images, converting them to grayscale, and normalizing the pixel values. This ensures that the input data is standardized for the model."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 49,
          "referenced_widgets": [
            "295c57243289480cb77de11941a1609d",
            "026965338a824584850e349b11f9a500",
            "7ffeb47e213146b093de0dcc14552ee4",
            "abcda6b06bec4560939324adad750a90",
            "e3e96b49d75d49139c2cf09a6c9ef2e6",
            "ba65c08cceea47c69831981430e6d4dd",
            "08a69539ccdd4cda829f72ec9773caf4",
            "a670d57a50474999a9ec98393bdc7da4",
            "66769dda32194d7d9db2175443f7dfdb",
            "0e556b96e9a24a2da1e332afeae6ea94",
            "548e8b43993f4addbfe464a054ddf3c3"
          ]
        },
        "id": "pJjvSWdmPClb",
        "outputId": "ec2b3dd6-ce20-4fd0-e13c-ea631d7f566b"
      },
      "outputs": [],
      "source": [
        "import torch\n",
        "from torch.utils.data import DataLoader\n",
        "from torchvision import transforms\n",
        "from PIL import Image\n",
        "\n",
        "# Define transformation: Resize to 28x28 and convert to grayscale\n",
        "transform = transforms.Compose([\n",
        "    transforms.ToPILImage(),\n",
        "    transforms.Resize((28, 28)),  # Resize to 28x28\n",
        "    transforms.Grayscale(num_output_channels=1),  # Convert to grayscale\n",
        "    transforms.ToTensor(),  # Convert image to Tensor\n",
        "    transforms.Normalize((0.5,), (1.0,))\n",
        "])\n",
        "\n",
        "# Apply transformation to the dataset\n",
        "def transform_dataset(example):\n",
        "    # Apply the transform on the 'image' field\n",
        "    image = example['image']\n",
        "    image = transform(image)\n",
        "    example['image'] = image\n",
        "    return example\n",
        "\n",
        "# Apply transform to the dataset\n",
        "ds = ds.with_format(\"torch\")\n",
        "ds = ds.map(transform_dataset)\n",
        "\n",
        "ds['train'] = ds['train'].shuffle(seed=42)\n",
        "ds = ds['train'].train_test_split(test_size=0.1)\n",
        "\n",
        "train_loader = torch.utils.data.DataLoader([(x, y) for x,y in zip(ds['train']['image'], ds['train']['labels'])], batch_size=32)\n",
        "test_loader = torch.utils.data.DataLoader([(x, y) for x,y in zip(ds['test']['image'], ds['test']['labels'])], batch_size=32)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 312
        },
        "id": "90VNTJcVkVKY",
        "outputId": "96a88165-6936-421a-f864-08c831c02b09"
      },
      "outputs": [],
      "source": [
        "# plot the images after preprocessing\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "def plot_transformed_images(dataset, num_images=4):\n",
        "  \"\"\"Plots a specified number of images from the transformed dataset.\"\"\"\n",
        "  fig, axs = plt.subplots(1, num_images, figsize=(15, 5))\n",
        "  for i in range(num_images):\n",
        "    image = dataset['train'][i]['image'].permute(1, 2, 0).numpy()  # Move channel dimension to the end\n",
        "    axs[i].imshow(image, cmap='gray')  # Display grayscale image\n",
        "    axs[i].axis('off')\n",
        "    axs[i].set_title(f\"Label: {dataset['train'][i]['labels']}\")\n",
        "  plt.show()\n",
        "\n",
        "\n",
        "plot_transformed_images(ds)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4vnsfZLUeNtx"
      },
      "source": [
        "# Define the LeNet5 Model\n",
        "This section defines the LeNet5 model architecture. It includes convolutional and pooling layers for feature extraction, followed by fully connected layers for classification."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "UvrVbHRpk97j",
        "outputId": "13c72594-1b00-43d1-b817-71b523316b78"
      },
      "outputs": [],
      "source": [
        "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
        "print(device)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "id": "LpEWYeoHeNtx"
      },
      "outputs": [],
      "source": [
        "class LeNet5(nn.Module):\n",
        "    def __init__(self):\n",
        "        super().__init__()\n",
        "        self.feature = nn.Sequential(\n",
        "            #1\n",
        "            nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=2),   # 28*28->32*32-->28*28\n",
        "            nn.ReLU(),\n",
        "            nn.AvgPool2d(kernel_size=2, stride=2),  # 14*14\n",
        "\n",
        "            #2\n",
        "            nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1),  # 10*10\n",
        "            nn.ReLU(),\n",
        "            nn.AvgPool2d(kernel_size=2, stride=2),  # 5*5\n",
        "\n",
        "        )\n",
        "        self.flattener = nn.Flatten()\n",
        "        self.classifier = nn.Sequential(\n",
        "            nn.Flatten(),\n",
        "            nn.Linear(in_features=16*5*5, out_features=120),\n",
        "            nn.ReLU(),\n",
        "            nn.Linear(in_features=120, out_features=84),\n",
        "            nn.ReLU(),\n",
        "            nn.Linear(in_features=84, out_features=10),\n",
        "        )\n",
        "\n",
        "    def forward(self, x):\n",
        "        return self.classifier(self.feature(x))\n",
        "\n",
        "\n",
        "network = LeNet5()\n",
        "network.to(device)\n",
        "optimizer = optim.Adam(network.parameters(), lr=1e-3)\n",
        "\n",
        "# Instantiate a torch loss function\n",
        "loss_fn = nn.CrossEntropyLoss()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Training the LeNet5 Model\n",
        "In this section, we implement the training loop for the LeNet5 model. The model is trained over multiple epochs where:\n",
        "\n",
        "1. **Forward Pass**: The input images are passed through the model to make predictions.\n",
        "2. **Loss Calculation**: The predicted outputs are compared with the true labels using a loss function (e.g., CrossEntropy Loss).\n",
        "3. **Backward Pass**: The gradients are computed through backpropagation to adjust the weights of the model.\n",
        "4. **Optimization Step**: An optimizer (e.g., SGD or Adam) updates the weights of the model based on the computed gradients to minimize the loss.\n",
        "This process is repeated over several batches of the training dataset to improve the model's performance."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "\n",
        "def train(epoch):\n",
        "    network.train()\n",
        "    for batch_idx, (data, target) in enumerate(train_loader):\n",
        "        data, target = data.to(device), target.to(device)\n",
        "        optimizer.zero_grad()\n",
        "        logits = network(data)\n",
        "        loss = loss_fn(logits, target)\n",
        "        loss.backward()\n",
        "        optimizer.step()\n",
        "        if batch_idx % 100 == 0:\n",
        "            print('Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}'.format(epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item()))\n",
        "    network.eval()\n",
        "    train_loss = 0\n",
        "    correct = 0\n",
        "    with torch.no_grad():\n",
        "        for data, target in train_loader:\n",
        "            data, target = data.to(device), target.to(device)\n",
        "            #print(data.shape)\n",
        "            logits = network(data)\n",
        "            train_loss += loss_fn(logits, target).item()\n",
        "            pred = logits.data.max(1, keepdim=True)[1]\n",
        "            correct += pred.eq(target.data.view_as(pred)).sum()\n",
        "    train_loss /= len(train_loader.dataset)\n",
        "    print('\\nTraining set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\\n'.format(\n",
        "    train_loss, correct, len(train_loader.dataset),\n",
        "    100. * correct / len(train_loader.dataset)))\n",
        "      #torch.save(network.state_dict(), '/results/model.pth')\n",
        "      #torch.save(optimizer.state_dict(), '/results/optimizer.pth')\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Model Evaluation and Inference\n",
        "After the model is trained, we evaluate its performance on a test set. This involves:\n",
        "\n",
        "1. **Switch to Evaluation Mode**: The model is set to evaluation mode, disabling any layers that behave differently during training (like Dropout or BatchNorm).\n",
        "2. **Forward Pass for Inference**: The test data is passed through the trained model to generate predictions.\n",
        "3. **Accuracy Calculation**: The predictions are compared to the actual labels to calculate accuracy, giving insight into how well the model generalizes to unseen data."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "\n",
        "def test():\n",
        "  network.eval()\n",
        "  test_loss = 0\n",
        "  correct = 0\n",
        "  with torch.no_grad():\n",
        "    for data, target in test_loader:\n",
        "      data, target = data.to(device), target.to(device)\n",
        "      output = network(data)\n",
        "      test_loss += F.nll_loss(output, target, size_average=False).item()\n",
        "      pred = output.data.max(1, keepdim=True)[1]\n",
        "      correct += pred.eq(target.data.view_as(pred)).sum()\n",
        "  test_loss /= len(test_loader.dataset)\n",
        "  print('\\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\\n'.format(\n",
        "    test_loss, correct, len(test_loader.dataset),\n",
        "    100. * correct / len(test_loader.dataset)))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "UgxSxjjReNtx",
        "outputId": "fa151247-5b76-4566-f3c6-f21744cdfbf7"
      },
      "outputs": [],
      "source": [
        "N_EPOCHS = 5\n",
        "for epoch in range(1, N_EPOCHS + 1):\n",
        "  train(epoch)\n",
        "  test()\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "torch.save(network.to('cpu').state_dict(), \"./cats_vs_dogs_lenet5.pth\")"
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "gpuType": "T4",
      "provenance": []
    },
    "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.12.6"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "026965338a824584850e349b11f9a500": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "HTMLModel",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_ba65c08cceea47c69831981430e6d4dd",
            "placeholder": "​",
            "style": "IPY_MODEL_08a69539ccdd4cda829f72ec9773caf4",
            "value": "Map: 100%"
          }
        },
        "08a69539ccdd4cda829f72ec9773caf4": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "DescriptionStyleModel",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        },
        "0e556b96e9a24a2da1e332afeae6ea94": {
          "model_module": "@jupyter-widgets/base",
          "model_module_version": "1.2.0",
          "model_name": "LayoutModel",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "295c57243289480cb77de11941a1609d": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "HBoxModel",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HBoxModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HBoxView",
            "box_style": "",
            "children": [
              "IPY_MODEL_026965338a824584850e349b11f9a500",
              "IPY_MODEL_7ffeb47e213146b093de0dcc14552ee4",
              "IPY_MODEL_abcda6b06bec4560939324adad750a90"
            ],
            "layout": "IPY_MODEL_e3e96b49d75d49139c2cf09a6c9ef2e6"
          }
        },
        "548e8b43993f4addbfe464a054ddf3c3": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "DescriptionStyleModel",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        },
        "66769dda32194d7d9db2175443f7dfdb": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "ProgressStyleModel",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "ProgressStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "bar_color": null,
            "description_width": ""
          }
        },
        "7ffeb47e213146b093de0dcc14552ee4": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "FloatProgressModel",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "FloatProgressModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "ProgressView",
            "bar_style": "success",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_a670d57a50474999a9ec98393bdc7da4",
            "max": 23410,
            "min": 0,
            "orientation": "horizontal",
            "style": "IPY_MODEL_66769dda32194d7d9db2175443f7dfdb",
            "value": 23410
          }
        },
        "a670d57a50474999a9ec98393bdc7da4": {
          "model_module": "@jupyter-widgets/base",
          "model_module_version": "1.2.0",
          "model_name": "LayoutModel",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "abcda6b06bec4560939324adad750a90": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "HTMLModel",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_0e556b96e9a24a2da1e332afeae6ea94",
            "placeholder": "​",
            "style": "IPY_MODEL_548e8b43993f4addbfe464a054ddf3c3",
            "value": " 23410/23410 [01:41&lt;00:00, 310.73 examples/s]"
          }
        },
        "ba65c08cceea47c69831981430e6d4dd": {
          "model_module": "@jupyter-widgets/base",
          "model_module_version": "1.2.0",
          "model_name": "LayoutModel",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "e3e96b49d75d49139c2cf09a6c9ef2e6": {
          "model_module": "@jupyter-widgets/base",
          "model_module_version": "1.2.0",
          "model_name": "LayoutModel",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        }
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
