{"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":"!pip install scipy","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:46:55.294399Z","iopub.execute_input":"2022-08-18T19:46:55.295811Z","iopub.status.idle":"2022-08-18T19:47:06.315951Z","shell.execute_reply.started":"2022-08-18T19:46:55.295699Z","shell.execute_reply":"2022-08-18T19:47:06.314819Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n\nfrom torch.nn import Module\nfrom torch.nn import Conv2d\nfrom torch.nn import Linear\nfrom torch.nn import MaxPool2d\nfrom torch.nn import ReLU\nfrom torch.nn import LogSoftmax\nfrom torch.nn import Dropout\nfrom torch import flatten, tensor\n\n# import the necessary packages\nfrom sklearn.metrics import classification_report\nfrom torch.utils.data import random_split\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision.transforms import ToTensor\nfrom torch.optim import Adam\nfrom torch import nn\nfrom collections import defaultdict\nfrom scipy.signal import convolve2d\nfrom skimage.measure import block_reduce\n\nimport argparse\nimport torch\nimport time","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:58:15.442136Z","iopub.execute_input":"2022-08-18T19:58:15.442644Z","iopub.status.idle":"2022-08-18T19:58:15.453375Z","shell.execute_reply.started":"2022-08-18T19:58:15.442612Z","shell.execute_reply":"2022-08-18T19:58:15.452308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\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":"2022-08-18T19:47:09.048461Z","iopub.execute_input":"2022-08-18T19:47:09.049091Z","iopub.status.idle":"2022-08-18T19:47:09.057677Z","shell.execute_reply.started":"2022-08-18T19:47:09.049062Z","shell.execute_reply":"2022-08-18T19:47:09.056557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainDataframe = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\ntestDataframe = pd.read_csv('/kaggle/input/digit-recognizer/test.csv')","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:47:09.060681Z","iopub.execute_input":"2022-08-18T19:47:09.061445Z","iopub.status.idle":"2022-08-18T19:47:13.502667Z","shell.execute_reply.started":"2022-08-18T19:47:09.061408Z","shell.execute_reply":"2022-08-18T19:47:13.501433Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def print_image_raw(pixels, label, prediction=None, ax=plt):\n    ax.imshow(pixels)\n    ax.annotate(f\"L:{label}\", (0,0), c='white',size=20, ha='left', va='top')\n    if prediction is not None:\n        ax.annotate(f\"P:{prediction}\", (pixels.shape[0]-1, 0), c='white',size=20, ha='right', va='top')\n\ndef print_image(data,ax=plt, col_start=1):\n    img = data[data.columns[col_start:]].iloc[0].values\n    side_len = int(len(img) ** (1/2))\n    print_image_raw(\n        np.array(img).reshape(side_len, side_len), \n        data.iloc[0]['label'], \n        data.iloc[0]['predicted'] if 'predicted' in data.columns else None,\n        ax = ax\n    )\n    \nprint_image(trainDataframe.sample())","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:47:13.504221Z","iopub.execute_input":"2022-08-18T19:47:13.504570Z","iopub.status.idle":"2022-08-18T19:47:13.720838Z","shell.execute_reply.started":"2022-08-18T19:47:13.504532Z","shell.execute_reply":"2022-08-18T19:47:13.719777Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RecognitionModel(Module):\n    def __init__(self, classes):\n        # call the parent constructor\n        super(RecognitionModel, self).__init__()\n        # initialize first set of CONV => RELU => POOL layers\n        self.conv1 = Conv2d(in_channels=1, out_channels=20,\n            kernel_size=(5, 5)).to(device)\n        self.relu1 = ReLU().to(device)\n        self.maxpool1 = MaxPool2d(kernel_size=(2, 2), stride=(2, 2)).to(device)\n        # initialize second set of CONV => RELU => POOL layers\n        self.conv2 = Conv2d(in_channels=20, out_channels=50,\n            kernel_size=(5, 5)).to(device)\n        self.relu2 = ReLU().to(device)\n        self.maxpool2 = MaxPool2d(kernel_size=(2, 2), stride=(2, 2)).to(device)\n        # initialize first (and only) set of FC => RELU layers\n        self.fc1 = Linear(in_features=800, out_features=500).to(device)\n        self.dropout = Dropout(p=0.5)\n        self.relu3 = ReLU().to(device)\n        # initialize our softmax classifier\n        self.fc2 = Linear(in_features=500, out_features=classes).to(device)\n        self.logSoftmax = LogSoftmax(dim=1).to(device)\n        \n    \n    def forward(self, x):\n        # pass the input through our first set of CONV => RELU =>\n        # POOL layers\n        x = self.conv1(x)\n        x = self.relu1(x)\n        x = self.maxpool1(x)\n        # pass the output from the previous layer through the second\n        # set of CONV => RELU => POOL layers\n        x = self.conv2(x)\n        x = self.relu2(x)\n        x = self.maxpool2(x)\n        # flatten the output from the previous layer and pass it\n        # through our only set of FC => RELU layers\n        x = flatten(x, 1)\n        x = self.fc1(x)\n        x = self.relu3(x)\n        x = self.dropout(x)\n        # pass the output to our softmax classifier to get our output\n        # predictions\n        x = self.fc2(x)\n        output = self.logSoftmax(x)\n        # return the output predictions\n        return output","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:03:18.680939Z","iopub.execute_input":"2022-08-18T20:03:18.681475Z","iopub.status.idle":"2022-08-18T20:03:18.696583Z","shell.execute_reply.started":"2022-08-18T20:03:18.681415Z","shell.execute_reply":"2022-08-18T20:03:18.695415Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# define training hyperparameters\nINIT_LR = 1e-3\nBATCH_SIZE = 64\nEPOCHS = 50\n# define the train and val splits\nTRAIN_SPLIT = 0.75\nVAL_SPLIT = 1 - TRAIN_SPLIT\n# set the device we will be using to train the model\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:03:19.414923Z","iopub.execute_input":"2022-08-18T20:03:19.415349Z","iopub.status.idle":"2022-08-18T20:03:19.421775Z","shell.execute_reply.started":"2022-08-18T20:03:19.415311Z","shell.execute_reply":"2022-08-18T20:03:19.420665Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class MINSTDataset(Dataset):\n \n    def __init__(self,df):\n        wide_train_data = df[df.columns[1:]].values\n        squared_data = wide_train_data.reshape(len(wide_train_data), 1, int(np.sqrt(wide_train_data.shape[1])), int(np.sqrt(wide_train_data.shape[1])))\n\n\n        self.x=torch.tensor(squared_data,dtype=torch.float).to(device)\n        self.y=torch.tensor(df['label'],dtype=torch.long).to(device)\n        \n        self.classes = self.y.unique()\n        \n\n    def __len__(self):\n        return len(self.y)\n   \n    def __getitem__(self,idx):\n        return self.x[idx],self.y[idx]","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:03:19.764760Z","iopub.execute_input":"2022-08-18T20:03:19.765695Z","iopub.status.idle":"2022-08-18T20:03:19.773583Z","shell.execute_reply.started":"2022-08-18T20:03:19.765661Z","shell.execute_reply":"2022-08-18T20:03:19.772417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainDataset =  MINSTDataset(trainDataframe)\nnumTrainSamples = int(len(trainDataset) * TRAIN_SPLIT)\nnumValSamples = int(len(trainDataset) * VAL_SPLIT)\n(trainData, valData) = random_split(trainDataset,\n    [numTrainSamples, numValSamples],\n    generator=torch.Generator().manual_seed(42))","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:03:20.136371Z","iopub.execute_input":"2022-08-18T20:03:20.136723Z","iopub.status.idle":"2022-08-18T20:03:20.372286Z","shell.execute_reply.started":"2022-08-18T20:03:20.136693Z","shell.execute_reply":"2022-08-18T20:03:20.371384Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# initialize the train, validation, and test data loaders\ntrainDataLoader = DataLoader(trainData, shuffle=True,\n    batch_size=BATCH_SIZE)\nvalDataLoader = DataLoader(valData, batch_size=BATCH_SIZE)\n# calculate steps per epoch for training and validation set\ntrainSteps = len(trainDataLoader.dataset) // BATCH_SIZE\nvalSteps = len(valDataLoader.dataset) // BATCH_SIZE","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:03:21.960485Z","iopub.execute_input":"2022-08-18T20:03:21.960835Z","iopub.status.idle":"2022-08-18T20:03:21.967811Z","shell.execute_reply.started":"2022-08-18T20:03:21.960805Z","shell.execute_reply":"2022-08-18T20:03:21.966868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# initialize the LeNet model\nmodel = RecognitionModel(\n    classes=tensor(len(trainData.dataset.classes)).to(device)\n)\n# initialize our optimizer and loss function\nopt = Adam(model.parameters(), lr=INIT_LR)\nlossFn = nn.NLLLoss()\n# initialize a dictionary to store training history\nH = {\n    \"train_loss\": [],\n    \"train_acc\": [],\n    \"val_loss\": [],\n    \"val_acc\": []\n}\n# measure how long training is going to take\nprint(\"[INFO] training the network...\")\nstartTime = time.time()","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:03:23.622138Z","iopub.execute_input":"2022-08-18T20:03:23.623361Z","iopub.status.idle":"2022-08-18T20:03:23.637776Z","shell.execute_reply.started":"2022-08-18T20:03:23.623316Z","shell.execute_reply":"2022-08-18T20:03:23.636645Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# loop over our epochs\nfor e in range(0, EPOCHS):\n    # set the model in training mode\n    model.train()\n    # initialize the total training and validation loss\n    totalTrainLoss = 0\n    totalValLoss = 0\n    # initialize the number of correct predictions in the training\n    # and validation step\n    trainCorrect = 0\n    valCorrect = 0\n    # loop over the training set\n    for (x, y) in trainDataLoader:\n        # send the input to the device\n        (x, y) = (x.to(device), y.to(device))\n        # perform a forward pass and calculate the training loss\n        pred = model(x)\n        loss = lossFn(pred, y)\n        # zero out the gradients, perform the backpropagation step,\n        # and update the weights\n        opt.zero_grad()\n        loss.backward()\n        opt.step()\n        # add the loss to the total training loss so far and\n        # calculate the number of correct predictions\n        totalTrainLoss += loss\n        trainCorrect += (pred.argmax(1) == y).type(\n            torch.float).sum().item()\n        \n    with torch.no_grad():\n        # set the model in evaluation mode\n        model.eval()\n        valCorrect = 0\n        # loop over the validation set\n        for (x, y) in valDataLoader:\n            # send the input to the device\n            (x, y) = (x.to(device), y.to(device))\n            # make the predictions and calculate the validation loss\n            pred = model(x)\n            totalValLoss += lossFn(pred, y)\n            # calculate the number of correct predictions\n            valCorrect += (pred.argmax(1) == y).type(\n                torch.float).sum().item() \n            \n        print(valCorrect / len(valDataLoader.dataset))\n            \n            \n            ","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:03:25.328208Z","iopub.execute_input":"2022-08-18T20:03:25.328582Z","iopub.status.idle":"2022-08-18T20:04:39.039220Z","shell.execute_reply.started":"2022-08-18T20:03:25.328552Z","shell.execute_reply":"2022-08-18T20:04:39.037991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wrong_predictions = pd.DataFrame()\ncorrect_predictions = pd.DataFrame()\nvalCorrect = 0 \n\n# switch off autograd for evaluation\nwith torch.no_grad():\n        # set the model in evaluation mode\n        model.eval()\n        # loop over the validation set\n        for (x, y) in valDataLoader:\n            # send the input to the device\n            (x, y) = (x.to(device), y.to(device))\n            # make the predictions and calculate the validation loss\n            pred = model(x)\n            totalValLoss += lossFn(pred, y)\n            # calculate the number of correct predictions\n            valCorrect += (pred.argmax(1) == y).type(\n                torch.float).sum().item()\n            \n            wrong_indicies = pred.argmax(1) != y\n            correct_indicies = pred.argmax(1) == y\n            if sum(wrong_indicies) > 0:  \n                wrong_images = x[wrong_indicies].cpu().numpy()\n                wrong_images = wrong_images.reshape(len(wrong_images), -1)\n                pixels = defaultdict(lambda: [])\n                for image in wrong_images:\n                    for idx, pixel in enumerate(image):\n                        pixels[f'pixel{idx}'].append(pixel)\n                wrong_predictions = wrong_predictions.append(pd.DataFrame(data={\n                    'predicted': pred[wrong_indicies].argmax(1).to(torch.int).cpu(),\n                    'label': y[wrong_indicies].cpu(),\n                    **pixels,\n                }))\n            if sum(correct_indicies) > 0:  \n                correct_images = x[correct_indicies].cpu().numpy()\n                correct_images = correct_images.reshape(len(correct_images), -1)\n                pixels = defaultdict(lambda: [])\n                for image in correct_images:\n                    for idx, pixel in enumerate(image):\n                        pixels[f'pixel{idx}'].append(pixel)\n                correct_predictions = correct_predictions.append(pd.DataFrame(data={\n                    'predicted': pred[correct_indicies].argmax(1).to(torch.int).cpu(),\n                    'label': y[correct_indicies].cpu(),\n                    **pixels,\n                }))\n            \nwrong_predictions = wrong_predictions.reset_index(drop=True).astype(int)\ncorrect_predictions = correct_predictions.reset_index(drop=True).astype(int)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:08:22.431854Z","iopub.execute_input":"2022-08-18T20:08:22.432315Z","iopub.status.idle":"2022-08-18T20:08:34.533801Z","shell.execute_reply.started":"2022-08-18T20:08:22.432284Z","shell.execute_reply":"2022-08-18T20:08:34.532789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(5,5,figsize=(16,9))\nfor ax in axs.reshape(-1):\n    print_image(\n        correct_predictions.sample(), \n        col_start=2,\n        ax=ax\n    )\n    ax.axis('off')\n","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:08:34.535849Z","iopub.execute_input":"2022-08-18T20:08:34.536412Z","iopub.status.idle":"2022-08-18T20:08:35.850146Z","shell.execute_reply.started":"2022-08-18T20:08:34.536373Z","shell.execute_reply":"2022-08-18T20:08:35.849045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(5,5,figsize=(16,9))\n\nfor ax in axs.reshape(-1):\n    print_image(\n        wrong_predictions.sample(), \n        col_start=2,\n        ax=ax\n    )\n    ax.axis('off')","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:08:35.851564Z","iopub.execute_input":"2022-08-18T20:08:35.852019Z","iopub.status.idle":"2022-08-18T20:08:36.785739Z","shell.execute_reply.started":"2022-08-18T20:08:35.851984Z","shell.execute_reply":"2022-08-18T20:08:36.784794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\t# calculate the average training and validation loss\n\tavgTrainLoss = totalTrainLoss / trainSteps\n\tavgValLoss = totalValLoss / valSteps\n\t# calculate the training and validation accuracy\n\ttrainCorrect = trainCorrect / len(trainDataLoader.dataset)\n\tvalCorrect = valCorrect / len(valDataLoader.dataset)\n\t# update our training history\n\tH[\"train_loss\"].append(avgTrainLoss.cpu().detach().numpy())\n\tH[\"train_acc\"].append(trainCorrect)\n\tH[\"val_loss\"].append(avgValLoss.cpu().detach().numpy())\n\tH[\"val_acc\"].append(valCorrect)\n\t# print the model training and validation information\n\tprint(\"[INFO] EPOCH: {}/{}\".format(e + 1, EPOCHS))\n\tprint(\"Train loss: {:.6f}, Train accuracy: {:.4f}\".format(\n\t\tavgTrainLoss, trainCorrect))\n\tprint(\"Val loss: {:.6f}, Val accuracy: {:.4f}\\n\".format(\n\t\tavgValLoss, valCorrect))","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:08:36.788208Z","iopub.execute_input":"2022-08-18T20:08:36.788866Z","iopub.status.idle":"2022-08-18T20:08:36.804603Z","shell.execute_reply.started":"2022-08-18T20:08:36.788826Z","shell.execute_reply":"2022-08-18T20:08:36.803392Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def print_conv_weights(conv_model):\n\n    weights = conv_model.weight.cpu().detach().numpy()\n    weights = weights.reshape(len(weights), 5, 5)\n\n    fig, axs = plt.subplots(5,4,figsize=(16,9))\n\n    for idx, ax in enumerate(axs.reshape(-1)):\n        data = weights[idx]\n        ax.imshow(weights[idx], cmap='gray',  vmin=data.min(), vmax=data.max())\n        ax.axis('off')\n        \n\n        \nprint_conv_weights(model.conv1)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:09:21.146929Z","iopub.execute_input":"2022-08-18T20:09:21.147493Z","iopub.status.idle":"2022-08-18T20:09:21.805827Z","shell.execute_reply.started":"2022-08-18T20:09:21.147459Z","shell.execute_reply":"2022-08-18T20:09:21.804784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with torch.no_grad():\n    \n    example_image = trainData.dataset.x[3]\n\n    plt.imshow(example_image.cpu().reshape(28,28))\n    plt.axis('off')\n    plt.show()\n    weights = model.conv1.weight\n    weights = weights.reshape(len(weights), 5, 5)\n\n    fig, axs = plt.subplots(5,4,figsize=(16,9))\n    result = model.conv1(example_image)\n    result = model.relu1(result)\n    result = model.maxpool1(result)\n    for ax, channel in zip(axs.reshape(-1),result):\n        ax.imshow(channel.cpu().numpy())\n        ax.axis('off')\n    \n    plt.show()\n        \n    fig, axs = plt.subplots(5,4,figsize=(16,9))    \n    result = model.conv2(result)\n    result = model.relu2(result)\n    result = model.maxpool2(result)\n    for ax, channel in zip(axs.reshape(-1),result):\n        ax.imshow(channel.cpu().numpy())\n        ax.axis('off')\n        \n    \n","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:09:41.501416Z","iopub.execute_input":"2022-08-18T20:09:41.501790Z","iopub.status.idle":"2022-08-18T20:09:43.653855Z","shell.execute_reply.started":"2022-08-18T20:09:41.501742Z","shell.execute_reply":"2022-08-18T20:09:43.652816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_raw_data= testDataframe[testDataframe.columns[0:]].values\ntest_values = tensor(test_raw_data).reshape(len(test_raw_data), 1, int(test_raw_data.shape[1] ** (1/2)), int(test_raw_data.shape[1] ** (1/2)))","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:10:14.999728Z","iopub.execute_input":"2022-08-18T20:10:15.000712Z","iopub.status.idle":"2022-08-18T20:10:15.183093Z","shell.execute_reply.started":"2022-08-18T20:10:15.000675Z","shell.execute_reply":"2022-08-18T20:10:15.181930Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = model.forward(test_values.to(device).type(torch.float32))\noutputs = predictions.argmax(dim=1)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:10:16.706841Z","iopub.execute_input":"2022-08-18T20:10:16.707222Z","iopub.status.idle":"2022-08-18T20:10:16.756015Z","shell.execute_reply.started":"2022-08-18T20:10:16.707191Z","shell.execute_reply":"2022-08-18T20:10:16.755080Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result = pd.DataFrame(data={\n    \"ImageId\": list(testDataframe.index + 1),\n    \"Label\":outputs.tolist()\n}\n)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:10:17.865032Z","iopub.execute_input":"2022-08-18T20:10:17.865595Z","iopub.status.idle":"2022-08-18T20:10:17.888386Z","shell.execute_reply.started":"2022-08-18T20:10:17.865562Z","shell.execute_reply":"2022-08-18T20:10:17.887494Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T20:10:27.122433Z","iopub.execute_input":"2022-08-18T20:10:27.123094Z","iopub.status.idle":"2022-08-18T20:10:27.180208Z","shell.execute_reply.started":"2022-08-18T20:10:27.123035Z","shell.execute_reply":"2022-08-18T20:10:27.179222Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}