{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport random\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\n\nimport matplotlib.pyplot as plt\n\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import DataLoader\nfrom torchvision.datasets import ImageFolder\nfrom torchvision import transforms\nfrom torchvision.models import resnet18\n\nfrom tqdm.auto import tqdm\n\nfrom sklearn.model_selection import train_test_split","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:25.177340Z","iopub.execute_input":"2026-07-05T20:23:25.177785Z","iopub.status.idle":"2026-07-05T20:23:25.183921Z","shell.execute_reply.started":"2026-07-05T20:23:25.177751Z","shell.execute_reply":"2026-07-05T20:23:25.183071Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nprint(device)\nprint(torch.cuda.get_device_name(0) if torch.cuda.is_available() else \"CPU\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:25.185158Z","iopub.execute_input":"2026-07-05T20:23:25.185404Z","iopub.status.idle":"2026-07-05T20:23:25.198883Z","shell.execute_reply.started":"2026-07-05T20:23:25.185383Z","shell.execute_reply":"2026-07-05T20:23:25.198101Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"seed = 42\n\nrandom.seed(seed)\nnp.random.seed(seed)\n\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed_all(seed)\n\ntorch.backends.cudnn.deterministic = True\ntorch.backends.cudnn.benchmark = False","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:25.199788Z","iopub.execute_input":"2026-07-05T20:23:25.200055Z","iopub.status.idle":"2026-07-05T20:23:25.211911Z","shell.execute_reply.started":"2026-07-05T20:23:25.200007Z","shell.execute_reply":"2026-07-05T20:23:25.211114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\n\ninput_path = Path(\"/kaggle/input\")\n\nfor path in input_path.iterdir():\n    print(path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:25.213346Z","iopub.execute_input":"2026-07-05T20:23:25.213628Z","iopub.status.idle":"2026-07-05T20:23:25.223312Z","shell.execute_reply.started":"2026-07-05T20:23:25.213593Z","shell.execute_reply":"2026-07-05T20:23:25.222521Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\n\ncompetition_path = Path(\"/kaggle/input/competitions\")\n\nfor path in competition_path.iterdir():\n    print(path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:25.224275Z","iopub.execute_input":"2026-07-05T20:23:25.224532Z","iopub.status.idle":"2026-07-05T20:23:25.235222Z","shell.execute_reply.started":"2026-07-05T20:23:25.224504Z","shell.execute_reply":"2026-07-05T20:23:25.234446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"competition = Path(\"/kaggle/input/competitions/galaxy-zoo-the-galaxy-challenge\")\n\nfor path in competition.iterdir():\n    print(path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:25.236238Z","iopub.execute_input":"2026-07-05T20:23:25.236674Z","iopub.status.idle":"2026-07-05T20:23:25.254620Z","shell.execute_reply.started":"2026-07-05T20:23:25.236641Z","shell.execute_reply":"2026-07-05T20:23:25.254099Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from zipfile import ZipFile\nfrom pathlib import Path\n\ncompetition_path = Path(\"/kaggle/input/competitions/galaxy-zoo-the-galaxy-challenge\")\nworking_path = Path(\"/kaggle/working/galaxy_zoo\")\n\nworking_path.mkdir(exist_ok=True)\n\nmodel_name = \"resnet18\"\n\ncheckpoint_dir = working_path / \"checkpoints\"\ncheckpoint_dir.mkdir(exist_ok=True)\n\ncheckpoint_path = checkpoint_dir / f\"{model_name}_best.pth\"\n\nwith ZipFile(competition_path / \"images_training_rev1.zip\") as zip_file:\n    zip_file.extractall(working_path)\n\nwith ZipFile(competition_path / \"training_solutions_rev1.zip\") as zip_file:\n    zip_file.extractall(working_path)\n\nprint(\"Done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:25.255417Z","iopub.execute_input":"2026-07-05T20:23:25.255685Z","iopub.status.idle":"2026-07-05T20:23:39.549267Z","shell.execute_reply.started":"2026-07-05T20:23:25.255655Z","shell.execute_reply":"2026-07-05T20:23:39.548309Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sorted(working_path.iterdir())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:39.550505Z","iopub.execute_input":"2026-07-05T20:23:39.550813Z","iopub.status.idle":"2026-07-05T20:23:39.556886Z","shell.execute_reply.started":"2026-07-05T20:23:39.550775Z","shell.execute_reply":"2026-07-05T20:23:39.556187Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels = pd.read_csv(working_path / \"training_solutions_rev1.csv\")\n\nlabels.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:39.559367Z","iopub.execute_input":"2026-07-05T20:23:39.559746Z","iopub.status.idle":"2026-07-05T20:23:39.808672Z","shell.execute_reply.started":"2026-07-05T20:23:39.559713Z","shell.execute_reply":"2026-07-05T20:23:39.808104Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(labels.shape)\n\nlabels.columns.tolist()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:39.809537Z","iopub.execute_input":"2026-07-05T20:23:39.809826Z","iopub.status.idle":"2026-07-05T20:23:39.816586Z","shell.execute_reply.started":"2026-07-05T20:23:39.809797Z","shell.execute_reply":"2026-07-05T20:23:39.815743Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels.iloc[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:39.817473Z","iopub.execute_input":"2026-07-05T20:23:39.817752Z","iopub.status.idle":"2026-07-05T20:23:39.830836Z","shell.execute_reply.started":"2026-07-05T20:23:39.817729Z","shell.execute_reply":"2026-07-05T20:23:39.830024Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels.describe().T","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:39.831645Z","iopub.execute_input":"2026-07-05T20:23:39.831979Z","iopub.status.idle":"2026-07-05T20:23:39.964972Z","shell.execute_reply.started":"2026-07-05T20:23:39.831956Z","shell.execute_reply":"2026-07-05T20:23:39.964342Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"targets = labels.drop(columns=\"GalaxyID\")\n\nprint(targets.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:39.966090Z","iopub.execute_input":"2026-07-05T20:23:39.966434Z","iopub.status.idle":"2026-07-05T20:23:39.973608Z","shell.execute_reply.started":"2026-07-05T20:23:39.966395Z","shell.execute_reply":"2026-07-05T20:23:39.972787Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"targets.sum(axis=1).describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:39.974631Z","iopub.execute_input":"2026-07-05T20:23:39.974955Z","iopub.status.idle":"2026-07-05T20:23:40.001885Z","shell.execute_reply.started":"2026-07-05T20:23:39.974919Z","shell.execute_reply":"2026-07-05T20:23:40.001214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\n\nimport torch\nfrom PIL import Image\nfrom torch.utils.data import Dataset\n\n\nclass GalaxyZooDataset(Dataset):\n\n    def __init__(self, dataframe, image_dir, transform=None):\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.image_dir = Path(image_dir)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        row = self.dataframe.iloc[idx]\n    \n        galaxy_id = int(row[\"GalaxyID\"])\n        image_path = self.image_dir / f\"{galaxy_id}.jpg\"\n    \n        image = Image.open(image_path).convert(\"RGB\")\n    \n        target = torch.tensor(\n            row.drop(\"GalaxyID\").to_numpy(dtype=np.float32),\n            dtype=torch.float32\n        )\n    \n        if self.transform is not None:\n            image = self.transform(image)\n    \n        return image, target","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.002779Z","iopub.execute_input":"2026-07-05T20:23:40.003076Z","iopub.status.idle":"2026-07-05T20:23:40.009744Z","shell.execute_reply.started":"2026-07-05T20:23:40.003022Z","shell.execute_reply":"2026-07-05T20:23:40.009105Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.010801Z","iopub.execute_input":"2026-07-05T20:23:40.011171Z","iopub.status.idle":"2026-07-05T20:23:40.020264Z","shell.execute_reply.started":"2026-07-05T20:23:40.011149Z","shell.execute_reply":"2026-07-05T20:23:40.019487Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataset = GalaxyZooDataset(\n    dataframe=labels,\n    image_dir=working_path / \"images_training_rev1\",\n    transform=transform,\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.021775Z","iopub.execute_input":"2026-07-05T20:23:40.022109Z","iopub.status.idle":"2026-07-05T20:23:40.037875Z","shell.execute_reply.started":"2026-07-05T20:23:40.022080Z","shell.execute_reply":"2026-07-05T20:23:40.037200Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(dataset))\n\nimage, target = dataset[0]\n\nprint(image.shape)\nprint(target.shape)\nprint(target.dtype)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.038655Z","iopub.execute_input":"2026-07-05T20:23:40.038908Z","iopub.status.idle":"2026-07-05T20:23:40.049382Z","shell.execute_reply.started":"2026-07-05T20:23:40.038887Z","shell.execute_reply":"2026-07-05T20:23:40.048758Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import random_split\n\ntrain_size = int(0.70 * len(dataset))\nval_size = int(0.15 * len(dataset))\ntest_size = len(dataset) - train_size - val_size\n\ntrain_dataset, val_dataset, test_dataset = random_split(\n    dataset,\n    [train_size, val_size, test_size],\n    generator=torch.Generator().manual_seed(seed)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.050364Z","iopub.execute_input":"2026-07-05T20:23:40.050668Z","iopub.status.idle":"2026-07-05T20:23:40.057929Z","shell.execute_reply.started":"2026-07-05T20:23:40.050647Z","shell.execute_reply":"2026-07-05T20:23:40.057315Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(train_dataset))\nprint(len(val_dataset))\nprint(len(test_dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.059619Z","iopub.execute_input":"2026-07-05T20:23:40.059829Z","iopub.status.idle":"2026-07-05T20:23:40.063988Z","shell.execute_reply.started":"2026-07-05T20:23:40.059807Z","shell.execute_reply":"2026-07-05T20:23:40.063412Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_workers = os.cpu_count()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.064852Z","iopub.execute_input":"2026-07-05T20:23:40.065151Z","iopub.status.idle":"2026-07-05T20:23:40.075283Z","shell.execute_reply.started":"2026-07-05T20:23:40.065120Z","shell.execute_reply":"2026-07-05T20:23:40.074450Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    train_dataset,\n    batch_size=batch_size,\n    shuffle=True,\n    num_workers=num_workers,\n    pin_memory=True,\n    persistent_workers=True,\n    prefetch_factor=2\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=batch_size,\n    shuffle=False,\n    num_workers=num_workers,\n    pin_memory=True,\n    persistent_workers=True,\n    prefetch_factor=2\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=batch_size,\n    shuffle=False,\n    num_workers=num_workers,\n    pin_memory=True,\n    persistent_workers=True,\n    prefetch_factor=2\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.076308Z","iopub.execute_input":"2026-07-05T20:23:40.076603Z","iopub.status.idle":"2026-07-05T20:23:40.087217Z","shell.execute_reply.started":"2026-07-05T20:23:40.076568Z","shell.execute_reply":"2026-07-05T20:23:40.086402Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images, targets = next(iter(train_loader))\n\nprint(images.shape)\nprint(targets.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.087925Z","iopub.execute_input":"2026-07-05T20:23:40.088196Z","iopub.status.idle":"2026-07-05T20:23:40.722762Z","shell.execute_reply.started":"2026-07-05T20:23:40.088164Z","shell.execute_reply":"2026-07-05T20:23:40.721757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_RATIO = 0.70\nVAL_RATIO = 0.15\nTEST_RATIO = 0.15\n\ntrain_df, temp_df = train_test_split(\n    labels,\n    train_size=TRAIN_RATIO,\n    random_state=seed,\n    shuffle=True\n)\n\nval_df, test_df = train_test_split(\n    temp_df,\n    train_size=VAL_RATIO / (VAL_RATIO + TEST_RATIO),\n    random_state=seed,\n    shuffle=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.736503Z","iopub.execute_input":"2026-07-05T20:23:40.740504Z","iopub.status.idle":"2026-07-05T20:23:40.848051Z","shell.execute_reply.started":"2026-07-05T20:23:40.740448Z","shell.execute_reply":"2026-07-05T20:23:40.847235Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])\n\neval_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:40.850687Z","iopub.execute_input":"2026-07-05T20:23:40.851022Z","iopub.status.idle":"2026-07-05T20:23:41.277431Z","shell.execute_reply.started":"2026-07-05T20:23:40.850988Z","shell.execute_reply":"2026-07-05T20:23:41.276525Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = GalaxyZooDataset(\n    dataframe=train_df,\n    image_dir=working_path / \"images_training_rev1\",\n    transform=train_transform\n)\n\nval_dataset = GalaxyZooDataset(\n    dataframe=val_df,\n    image_dir=working_path / \"images_training_rev1\",\n    transform=eval_transform\n)\n\ntest_dataset = GalaxyZooDataset(\n    dataframe=test_df,\n    image_dir=working_path / \"images_training_rev1\",\n    transform=eval_transform\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:41.279573Z","iopub.execute_input":"2026-07-05T20:23:41.279830Z","iopub.status.idle":"2026-07-05T20:23:41.309834Z","shell.execute_reply.started":"2026-07-05T20:23:41.279808Z","shell.execute_reply":"2026-07-05T20:23:41.309212Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_workers = os.cpu_count()\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=batch_size,\n    shuffle=True,\n    num_workers=num_workers,\n    pin_memory=True,\n    persistent_workers=True,\n    prefetch_factor=2\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=batch_size,\n    shuffle=False,\n    num_workers=num_workers,\n    pin_memory=True,\n    persistent_workers=True,\n    prefetch_factor=2\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=batch_size,\n    shuffle=False,\n    num_workers=num_workers,\n    pin_memory=True,\n    persistent_workers=True,\n    prefetch_factor=2\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:41.310774Z","iopub.execute_input":"2026-07-05T20:23:41.311270Z","iopub.status.idle":"2026-07-05T20:23:41.464944Z","shell.execute_reply.started":"2026-07-05T20:23:41.311235Z","shell.execute_reply":"2026-07-05T20:23:41.464075Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images, targets = next(iter(train_loader))\n\nprint(images.shape)\nprint(targets.shape)\n\nprint(images.min())\nprint(images.max())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:41.465879Z","iopub.execute_input":"2026-07-05T20:23:41.466210Z","iopub.status.idle":"2026-07-05T20:23:42.140806Z","shell.execute_reply.started":"2026-07-05T20:23:41.466185Z","shell.execute_reply":"2026-07-05T20:23:42.139920Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision.models import resnet18, ResNet18_Weights","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.143412Z","iopub.execute_input":"2026-07-05T20:23:42.146812Z","iopub.status.idle":"2026-07-05T20:23:42.159458Z","shell.execute_reply.started":"2026-07-05T20:23:42.146750Z","shell.execute_reply":"2026-07-05T20:23:42.153707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = resnet18(weights=ResNet18_Weights.DEFAULT)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.160291Z","iopub.execute_input":"2026-07-05T20:23:42.160538Z","iopub.status.idle":"2026-07-05T20:23:42.605277Z","shell.execute_reply.started":"2026-07-05T20:23:42.160509Z","shell.execute_reply":"2026-07-05T20:23:42.604578Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.606211Z","iopub.execute_input":"2026-07-05T20:23:42.609211Z","iopub.status.idle":"2026-07-05T20:23:42.619653Z","shell.execute_reply.started":"2026-07-05T20:23:42.609182Z","shell.execute_reply":"2026-07-05T20:23:42.618709Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(model.fc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.621683Z","iopub.execute_input":"2026-07-05T20:23:42.622130Z","iopub.status.idle":"2026-07-05T20:23:42.641764Z","shell.execute_reply.started":"2026-07-05T20:23:42.622093Z","shell.execute_reply":"2026-07-05T20:23:42.641240Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.fc = nn.Linear(\n    in_features=model.fc.in_features,\n    out_features=37\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.642658Z","iopub.execute_input":"2026-07-05T20:23:42.643970Z","iopub.status.idle":"2026-07-05T20:23:42.665059Z","shell.execute_reply.started":"2026-07-05T20:23:42.643945Z","shell.execute_reply":"2026-07-05T20:23:42.663531Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.666699Z","iopub.execute_input":"2026-07-05T20:23:42.667011Z","iopub.status.idle":"2026-07-05T20:23:42.699004Z","shell.execute_reply.started":"2026-07-05T20:23:42.666988Z","shell.execute_reply":"2026-07-05T20:23:42.698252Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(model.fc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.700272Z","iopub.execute_input":"2026-07-05T20:23:42.700538Z","iopub.status.idle":"2026-07-05T20:23:42.712847Z","shell.execute_reply.started":"2026-07-05T20:23:42.700517Z","shell.execute_reply":"2026-07-05T20:23:42.712041Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.BCEWithLogitsLoss()\noptimizer = torch.optim.AdamW(\n    model.parameters(),\n    lr=1e-4,\n    weight_decay=1e-4\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.713953Z","iopub.execute_input":"2026-07-05T20:23:42.714212Z","iopub.status.idle":"2026-07-05T20:23:42.719131Z","shell.execute_reply.started":"2026-07-05T20:23:42.714186Z","shell.execute_reply":"2026-07-05T20:23:42.718055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer,\n    mode=\"min\",\n    factor=0.1,\n    patience=3\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.720305Z","iopub.execute_input":"2026-07-05T20:23:42.720724Z","iopub.status.idle":"2026-07-05T20:23:42.732407Z","shell.execute_reply.started":"2026-07-05T20:23:42.720695Z","shell.execute_reply":"2026-07-05T20:23:42.731652Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"scaler = torch.amp.GradScaler(\"cuda\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.733352Z","iopub.execute_input":"2026-07-05T20:23:42.733610Z","iopub.status.idle":"2026-07-05T20:23:42.743676Z","shell.execute_reply.started":"2026-07-05T20:23:42.733580Z","shell.execute_reply":"2026-07-05T20:23:42.742874Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 30\n\nbest_val_loss = float(\"inf\")\n\npatience = 5\npatience_counter = 0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.744713Z","iopub.execute_input":"2026-07-05T20:23:42.744971Z","iopub.status.idle":"2026-07-05T20:23:42.754588Z","shell.execute_reply.started":"2026-07-05T20:23:42.744951Z","shell.execute_reply":"2026-07-05T20:23:42.753881Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = {\n    \"train_loss\": [],\n    \"val_loss\": [],\n    \"learning_rate\": []\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.755419Z","iopub.execute_input":"2026-07-05T20:23:42.756072Z","iopub.status.idle":"2026-07-05T20:23:42.765771Z","shell.execute_reply.started":"2026-07-05T20:23:42.756009Z","shell.execute_reply":"2026-07-05T20:23:42.765078Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm.auto import tqdm\n\ndef train_one_epoch(\n    model,\n    dataloader,\n    criterion,\n    optimizer,\n    scaler,\n    device\n):\n    model.train()\n\n    running_loss = 0.0\n\n    progress_bar = tqdm(\n        dataloader,\n        desc=\"Training\",\n        leave=False\n    )\n\n    for images, targets in progress_bar:\n\n        images = images.to(device, non_blocking=True)\n        targets = targets.to(device, non_blocking=True)\n\n        optimizer.zero_grad(set_to_none=True)\n\n        with torch.amp.autocast(\n            device_type=device.type,\n            enabled=device.type == \"cuda\"\n        ):\n            outputs = model(images)\n            loss = criterion(outputs, targets)\n\n        scaler.scale(loss).backward()\n\n        scaler.step(optimizer)\n\n        scaler.update()\n\n        running_loss += loss.item() * images.size(0)\n\n        progress_bar.set_postfix(\n            loss=f\"{loss.item():.4f}\"\n        )\n\n    epoch_loss = running_loss / len(dataloader.dataset)\n\n    return {\n    \"loss\": epoch_loss\n    }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.766776Z","iopub.execute_input":"2026-07-05T20:23:42.767141Z","iopub.status.idle":"2026-07-05T20:23:42.775429Z","shell.execute_reply.started":"2026-07-05T20:23:42.767112Z","shell.execute_reply":"2026-07-05T20:23:42.774716Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def validate(\n    model,\n    dataloader,\n    criterion,\n    device\n):\n    model.eval()\n\n    running_loss = 0.0\n\n    with torch.no_grad():\n\n        progress_bar = tqdm(\n            dataloader,\n            desc=\"Validation\",\n            leave=False\n        )\n\n        for images, targets in progress_bar:\n\n            images = images.to(device, non_blocking=True)\n            targets = targets.to(device, non_blocking=True)\n\n            with torch.amp.autocast(\n                device_type=device.type,\n                enabled=device.type == \"cuda\"\n            ):\n                outputs = model(images)\n                loss = criterion(outputs, targets)\n\n            running_loss += loss.item() * images.size(0)\n\n            progress_bar.set_postfix(\n                loss=f\"{loss.item():.4f}\"\n            )\n\n    epoch_loss = running_loss / len(dataloader.dataset)\n\n    return {\n    \"loss\": epoch_loss}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.776483Z","iopub.execute_input":"2026-07-05T20:23:42.777102Z","iopub.status.idle":"2026-07-05T20:23:42.790603Z","shell.execute_reply.started":"2026-07-05T20:23:42.777058Z","shell.execute_reply":"2026-07-05T20:23:42.789621Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def fit(\n    model,\n    train_loader,\n    val_loader,\n    criterion,\n    optimizer,\n    scheduler,\n    scaler,\n    epochs,\n    device\n):\n    history = {\n        \"train_loss\": [],\n        \"val_loss\": [],\n        \"learning_rate\": []\n    }\n\n    best_val_loss = float(\"inf\")\n\n    patience_counter = 0\n\n    for epoch in range(1, epochs + 1):\n\n        print(f\"\\nEpoch {epoch}/{epochs}\")\n\n        train_metrics = train_one_epoch(\n            model,\n            train_loader,\n            criterion,\n            optimizer,\n            scaler,\n            device\n        )\n\n        val_metrics = validate(\n            model,\n            val_loader,\n            criterion,\n            device\n        )\n\n        train_loss = train_metrics[\"loss\"]\n        val_loss = val_metrics[\"loss\"]\n\n        scheduler.step(val_loss)\n\n        learning_rate = optimizer.param_groups[0][\"lr\"]\n\n        history[\"train_loss\"].append(train_loss)\n        history[\"val_loss\"].append(val_loss)\n        history[\"learning_rate\"].append(learning_rate)\n\n        print(\n            f\"Train Loss: {train_loss:.4f} | \"\n            f\"Val Loss: {val_loss:.4f} | \"\n            f\"LR: {learning_rate:.2e}\"\n        )\n\n        if val_loss < best_val_loss:\n\n            best_val_loss = val_loss\n\n            patience_counter = 0\n\n            torch.save({\n                \"epoch\": epoch,\n                \"model_state_dict\": model.state_dict(),\n                \"optimizer_state_dict\": optimizer.state_dict(),\n                \"scheduler_state_dict\": scheduler.state_dict(),\n                \"best_val_loss\": best_val_loss,\n            }, checkpoint_path)\n\n        else:\n\n            patience_counter += 1\n\n            print(\n                f\"No improvement \"\n                f\"({patience_counter}/{patience})\"\n            )\n\n            if patience_counter >= patience:\n\n                print(\"Early stopping.\")\n\n                break\n\n    return history","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.791675Z","iopub.execute_input":"2026-07-05T20:23:42.792056Z","iopub.status.idle":"2026-07-05T20:23:42.804662Z","shell.execute_reply.started":"2026-07-05T20:23:42.792005Z","shell.execute_reply":"2026-07-05T20:23:42.803778Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"total_params = sum(p.numel() for p in model.parameters())\ntrainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n\nprint(f\"Total parameters     : {total_params:,}\")\nprint(f\"Trainable parameters : {trainable_params:,}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.806283Z","iopub.execute_input":"2026-07-05T20:23:42.806550Z","iopub.status.idle":"2026-07-05T20:23:42.819926Z","shell.execute_reply.started":"2026-07-05T20:23:42.806528Z","shell.execute_reply":"2026-07-05T20:23:42.819155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images, targets = next(iter(train_loader))\n\nimages = images.to(device)\n\nwith torch.no_grad():\n    outputs = model(images)\n\nprint(outputs.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:42.820933Z","iopub.execute_input":"2026-07-05T20:23:42.821289Z","iopub.status.idle":"2026-07-05T20:23:43.727578Z","shell.execute_reply.started":"2026-07-05T20:23:42.821261Z","shell.execute_reply":"2026-07-05T20:23:43.725861Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"targets = targets.to(device)\n\nloss = criterion(outputs, targets)\n\nprint(loss.item())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:43.728455Z","iopub.execute_input":"2026-07-05T20:23:43.728692Z","iopub.status.idle":"2026-07-05T20:23:43.759636Z","shell.execute_reply.started":"2026-07-05T20:23:43.728666Z","shell.execute_reply":"2026-07-05T20:23:43.759102Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = fit(\n    model=model,\n    train_loader=train_loader,\n    val_loader=val_loader,\n    criterion=criterion,\n    optimizer=optimizer,\n    scheduler=scheduler,\n    scaler=scaler,\n    epochs=epochs,\n    device=device\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T20:23:43.760339Z","iopub.execute_input":"2026-07-05T20:23:43.760591Z","iopub.status.idle":"2026-07-05T21:00:51.640359Z","shell.execute_reply.started":"2026-07-05T20:23:43.760563Z","shell.execute_reply":"2026-07-05T21:00:51.639429Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"checkpoint = torch.load(\n    checkpoint_path,\n    map_location=device\n)\n\nmodel.load_state_dict(checkpoint[\"model_state_dict\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:06:22.817982Z","iopub.execute_input":"2026-07-05T21:06:22.818962Z","iopub.status.idle":"2026-07-05T21:06:22.998956Z","shell.execute_reply.started":"2026-07-05T21:06:22.818919Z","shell.execute_reply":"2026-07-05T21:06:22.998042Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:06:33.771703Z","iopub.execute_input":"2026-07-05T21:06:33.772086Z","iopub.status.idle":"2026-07-05T21:06:33.778655Z","shell.execute_reply.started":"2026-07-05T21:06:33.772008Z","shell.execute_reply":"2026-07-05T21:06:33.778102Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate(\n    model,\n    dataloader,\n    criterion,\n    device\n):\n    model.eval()\n\n    running_loss = 0.0\n\n    predictions = []\n    targets = []\n\n    with torch.no_grad():\n\n        for images, labels in tqdm(dataloader):\n\n            images = images.to(device, non_blocking=True)\n            labels = labels.to(device, non_blocking=True)\n\n            outputs = model(images)\n\n            loss = criterion(outputs, labels)\n\n            running_loss += loss.item() * images.size(0)\n\n            predictions.append(\n                torch.sigmoid(outputs).cpu()\n            )\n\n            targets.append(\n                labels.cpu()\n            )\n\n    test_loss = running_loss / len(dataloader.dataset)\n\n    predictions = torch.cat(predictions)\n    targets = torch.cat(targets)\n\n    return test_loss, predictions, targets","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:06:53.741183Z","iopub.execute_input":"2026-07-05T21:06:53.741539Z","iopub.status.idle":"2026-07-05T21:06:53.747591Z","shell.execute_reply.started":"2026-07-05T21:06:53.741513Z","shell.execute_reply":"2026-07-05T21:06:53.746944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_loss, predictions, targets = evaluate(\n    model,\n    test_loader,\n    criterion,\n    device\n)\n\nprint(f\"Test loss: {test_loss:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:07:18.018926Z","iopub.execute_input":"2026-07-05T21:07:18.019701Z","iopub.status.idle":"2026-07-05T21:07:36.712801Z","shell.execute_reply.started":"2026-07-05T21:07:18.019669Z","shell.execute_reply":"2026-07-05T21:07:36.711941Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mae = torch.mean(\n    torch.abs(predictions - targets)\n)\n\nprint(f\"MAE: {mae:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:07:57.128573Z","iopub.execute_input":"2026-07-05T21:07:57.129295Z","iopub.status.idle":"2026-07-05T21:07:57.140085Z","shell.execute_reply.started":"2026-07-05T21:07:57.129245Z","shell.execute_reply":"2026-07-05T21:07:57.139129Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rmse = torch.sqrt(\n    torch.mean(\n        (predictions - targets) ** 2\n    )\n)\n\nprint(f\"RMSE: {rmse:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:08:06.547564Z","iopub.execute_input":"2026-07-05T21:08:06.548157Z","iopub.status.idle":"2026-07-05T21:08:06.560081Z","shell.execute_reply.started":"2026-07-05T21:08:06.548129Z","shell.execute_reply":"2026-07-05T21:08:06.559246Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nplt.figure(figsize=(8, 5))\n\nplt.plot(history[\"train_loss\"], label=\"Train\")\nplt.plot(history[\"val_loss\"], label=\"Validation\")\n\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.title(\"Training History\")\n\nplt.legend()\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:08:22.011135Z","iopub.execute_input":"2026-07-05T21:08:22.011523Z","iopub.status.idle":"2026-07-05T21:08:22.173298Z","shell.execute_reply.started":"2026-07-05T21:08:22.011497Z","shell.execute_reply":"2026-07-05T21:08:22.172462Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"per_output_mae = torch.mean(\n    torch.abs(predictions - targets),\n    dim=0\n)\n\nprint(per_output_mae)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:12:32.893243Z","iopub.execute_input":"2026-07-05T21:12:32.893678Z","iopub.status.idle":"2026-07-05T21:12:32.900911Z","shell.execute_reply.started":"2026-07-05T21:12:32.893650Z","shell.execute_reply":"2026-07-05T21:12:32.900150Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"question_names = labels.columns[1:]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:12:52.861615Z","iopub.execute_input":"2026-07-05T21:12:52.862168Z","iopub.status.idle":"2026-07-05T21:12:52.866418Z","shell.execute_reply.started":"2026-07-05T21:12:52.862137Z","shell.execute_reply":"2026-07-05T21:12:52.865471Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\nmae_df = pd.DataFrame({\n    \"Output\": question_names,\n    \"MAE\": per_output_mae.numpy()\n})\n\nmae_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:13:06.377977Z","iopub.execute_input":"2026-07-05T21:13:06.378843Z","iopub.status.idle":"2026-07-05T21:13:06.392104Z","shell.execute_reply.started":"2026-07-05T21:13:06.378798Z","shell.execute_reply":"2026-07-05T21:13:06.391447Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mae_df = mae_df.sort_values(\n    by=\"MAE\",\n    ascending=False\n)\n\nmae_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:13:25.526239Z","iopub.execute_input":"2026-07-05T21:13:25.526661Z","iopub.status.idle":"2026-07-05T21:13:25.540970Z","shell.execute_reply.started":"2026-07-05T21:13:25.526631Z","shell.execute_reply":"2026-07-05T21:13:25.540273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nplt.figure(figsize=(12, 6))\n\nplt.bar(\n    mae_df[\"Output\"],\n    mae_df[\"MAE\"]\n)\n\nplt.xticks(rotation=90)\n\nplt.ylabel(\"Mean Absolute Error\")\n\nplt.title(\"MAE for Each Galaxy Zoo Output\")\n\nplt.tight_layout()\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:13:40.990063Z","iopub.execute_input":"2026-07-05T21:13:40.990478Z","iopub.status.idle":"2026-07-05T21:13:41.291784Z","shell.execute_reply.started":"2026-07-05T21:13:40.990451Z","shell.execute_reply":"2026-07-05T21:13:41.291020Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"target_stats = pd.DataFrame({\n    \"Output\": labels.columns[1:],\n    \"Mean\": targets.mean(dim=0).numpy(),\n    \"Std\": targets.std(dim=0).numpy(),\n    \"MAE\": per_output_mae.numpy()\n})\n\ntarget_stats.sort_values(\"MAE\", ascending=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:15:05.571833Z","iopub.execute_input":"2026-07-05T21:15:05.572293Z","iopub.status.idle":"2026-07-05T21:15:05.593633Z","shell.execute_reply.started":"2026-07-05T21:15:05.572259Z","shell.execute_reply":"2026-07-05T21:15:05.592987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(7,7))\n\nplt.scatter(\n    target_stats[\"Std\"],\n    target_stats[\"MAE\"]\n)\n\nplt.xlabel(\"Target Standard Deviation\")\nplt.ylabel(\"MAE\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:15:23.292767Z","iopub.execute_input":"2026-07-05T21:15:23.293222Z","iopub.status.idle":"2026-07-05T21:15:23.434704Z","shell.execute_reply.started":"2026-07-05T21:15:23.293182Z","shell.execute_reply":"2026-07-05T21:15:23.433987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_mae = torch.mean(\n    torch.abs(predictions - targets),\n    dim=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:17:08.475374Z","iopub.execute_input":"2026-07-05T21:17:08.475790Z","iopub.status.idle":"2026-07-05T21:17:08.480768Z","shell.execute_reply.started":"2026-07-05T21:17:08.475761Z","shell.execute_reply":"2026-07-05T21:17:08.480107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sorted_indices = torch.argsort(sample_mae)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:17:18.283774Z","iopub.execute_input":"2026-07-05T21:17:18.284216Z","iopub.status.idle":"2026-07-05T21:17:18.293755Z","shell.execute_reply.started":"2026-07-05T21:17:18.284187Z","shell.execute_reply":"2026-07-05T21:17:18.293055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sorted_indices[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:17:43.988694Z","iopub.execute_input":"2026-07-05T21:17:43.989473Z","iopub.status.idle":"2026-07-05T21:17:43.995451Z","shell.execute_reply.started":"2026-07-05T21:17:43.989441Z","shell.execute_reply":"2026-07-05T21:17:43.994713Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sorted_indices[-1]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:17:51.564202Z","iopub.execute_input":"2026-07-05T21:17:51.564616Z","iopub.status.idle":"2026-07-05T21:17:51.570452Z","shell.execute_reply.started":"2026-07-05T21:17:51.564590Z","shell.execute_reply":"2026-07-05T21:17:51.569807Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_prediction(index):\n\n    image, target = test_dataset[index]\n\n    with torch.no_grad():\n\n        output = model(\n            image.unsqueeze(0).to(device)\n        )\n\n        prediction = torch.sigmoid(output).cpu().squeeze()\n\n    # Undo normalization\n    image = image.permute(1, 2, 0)\n\n    image = image * torch.tensor([0.229, 0.224, 0.225]) \\\n                  + torch.tensor([0.485, 0.456, 0.406])\n\n    image = image.clamp(0, 1)\n\n    plt.figure(figsize=(5,5))\n    plt.imshow(image)\n    plt.axis(\"off\")\n    plt.show()\n\n    print(\"Target:\")\n    print(target)\n\n    print()\n\n    print(\"Prediction:\")\n    print(prediction)\n\n    print()\n\n    print(\"MAE:\",\n          torch.mean(torch.abs(target - prediction)).item())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:18:14.926289Z","iopub.execute_input":"2026-07-05T21:18:14.926554Z","iopub.status.idle":"2026-07-05T21:18:14.933146Z","shell.execute_reply.started":"2026-07-05T21:18:14.926533Z","shell.execute_reply":"2026-07-05T21:18:14.932439Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_prediction(sorted_indices[0].item())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:18:30.375460Z","iopub.execute_input":"2026-07-05T21:18:30.376128Z","iopub.status.idle":"2026-07-05T21:18:30.539699Z","shell.execute_reply.started":"2026-07-05T21:18:30.376096Z","shell.execute_reply":"2026-07-05T21:18:30.538980Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_prediction(sorted_indices[-1].item())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-05T21:18:43.175930Z","iopub.execute_input":"2026-07-05T21:18:43.176772Z","iopub.status.idle":"2026-07-05T21:18:43.322401Z","shell.execute_reply.started":"2026-07-05T21:18:43.176739Z","shell.execute_reply":"2026-07-05T21:18:43.321375Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}