{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":11848,"databundleVersionId":862157}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Import Libraries","metadata":{}},{"cell_type":"code","source":"!pip install numpy pandas matplotlib seaborn scikit-learn opencv-python pillow tqdm\n!pip install torch torchvision torchaudio\n!pip install tensorflow\n!pip install pennylane\n!pip install qiskit\n!pip install pennylane-qiskit plotly kaggle","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:15:47.734888Z","iopub.execute_input":"2026-04-30T04:15:47.735204Z","iopub.status.idle":"2026-04-30T04:16:23.328951Z","shell.execute_reply.started":"2026-04-30T04:15:47.735176Z","shell.execute_reply":"2026-04-30T04:16:23.328023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader\n\nfrom torchvision import datasets, transforms\n\nimport pennylane as qml\nfrom pennylane import numpy as pnp\n\nfrom sklearn.metrics import classification_report, confusion_matrix","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:17:30.031013Z","iopub.execute_input":"2026-04-30T04:17:30.032091Z","iopub.status.idle":"2026-04-30T04:17:41.455058Z","shell.execute_reply.started":"2026-04-30T04:17:30.032049Z","shell.execute_reply":"2026-04-30T04:17:41.454464Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nDATA_PATH = \"/kaggle/input/competitions/histopathologic-cancer-detection\"\n\nprint(os.listdir(DATA_PATH))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:47:36.165461Z","iopub.execute_input":"2026-04-30T04:47:36.166326Z","iopub.status.idle":"2026-04-30T04:47:36.171420Z","shell.execute_reply.started":"2026-04-30T04:47:36.166277Z","shell.execute_reply":"2026-04-30T04:47:36.170759Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Configuration\n","metadata":{}},{"cell_type":"code","source":"import os\n\nDATA_PATH = \"/kaggle/input/competitions/histopathologic-cancer-detection\"\n\nTRAIN_DIR = os.path.join(DATA_PATH, \"train\")\nTEST_DIR = os.path.join(DATA_PATH, \"test\")\nLABELS_PATH = os.path.join(DATA_PATH, \"train_labels.csv\")\n\nIMAGE_SIZE = 96\nBATCH_SIZE = 32\nEPOCHS = 5\n\nDEVICE = \"cuda\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:48:20.787722Z","iopub.execute_input":"2026-04-30T04:48:20.788261Z","iopub.status.idle":"2026-04-30T04:48:20.792307Z","shell.execute_reply.started":"2026-04-30T04:48:20.788232Z","shell.execute_reply":"2026-04-30T04:48:20.791748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(os.listdir(TRAIN_DIR)))\nprint(len(os.listdir(TEST_DIR)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:48:32.001058Z","iopub.execute_input":"2026-04-30T04:48:32.001366Z","iopub.status.idle":"2026-04-30T04:48:38.003521Z","shell.execute_reply.started":"2026-04-30T04:48:32.001321Z","shell.execute_reply":"2026-04-30T04:48:38.002897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndf = pd.read_csv(LABELS_PATH)\nprint(df.head())\nprint(df['label'].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:48:59.781048Z","iopub.execute_input":"2026-04-30T04:48:59.781544Z","iopub.status.idle":"2026-04-30T04:49:00.018133Z","shell.execute_reply.started":"2026-04-30T04:48:59.781483Z","shell.execute_reply":"2026-04-30T04:49:00.017304Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Dataset class","metadata":{}},{"cell_type":"code","source":"import os\nimport pandas as pd\nfrom PIL import Image\nfrom torch.utils.data import Dataset\n\nclass HistopathDataset(Dataset):\n    def __init__(self, dataframe, img_dir, transform=None):\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        img_id = self.dataframe.loc[idx, \"id\"]\n        label = self.dataframe.loc[idx, \"label\"]\n\n        img_path = os.path.join(self.img_dir, img_id + \".tif\")\n        image = Image.open(img_path).convert(\"RGB\")\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:49:57.500672Z","iopub.execute_input":"2026-04-30T04:49:57.501483Z","iopub.status.idle":"2026-04-30T04:49:57.507126Z","shell.execute_reply.started":"2026-04-30T04:49:57.501450Z","shell.execute_reply":"2026-04-30T04:49:57.506487Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train / Validation Split","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ndf = pd.read_csv(LABELS_PATH)\n\ntrain_df, val_df = train_test_split(\n    df,\n    test_size=0.2,\n    stratify=df[\"label\"],\n    random_state=42\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:50:33.828704Z","iopub.execute_input":"2026-04-30T04:50:33.829010Z","iopub.status.idle":"2026-04-30T04:50:34.121850Z","shell.execute_reply.started":"2026-04-30T04:50:33.828982Z","shell.execute_reply":"2026-04-30T04:50:34.121154Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Transforms","metadata":{}},{"cell_type":"code","source":"import torchvision.transforms as transforms\n\ntrain_transform = transforms.Compose([\n    transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.ToTensor()\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n    transforms.ToTensor()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:51:39.732635Z","iopub.execute_input":"2026-04-30T04:51:39.732946Z","iopub.status.idle":"2026-04-30T04:51:39.737373Z","shell.execute_reply.started":"2026-04-30T04:51:39.732919Z","shell.execute_reply":"2026-04-30T04:51:39.736778Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# DataLoaders","metadata":{}},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\ntrain_dataset = HistopathDataset(train_df, TRAIN_DIR, train_transform)\nval_dataset = HistopathDataset(val_df, TRAIN_DIR, val_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:51:50.487814Z","iopub.execute_input":"2026-04-30T04:51:50.488325Z","iopub.status.idle":"2026-04-30T04:51:50.502950Z","shell.execute_reply.started":"2026-04-30T04:51:50.488294Z","shell.execute_reply":"2026-04-30T04:51:50.502261Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# VISUALIZE IMAGES","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom PIL import Image\nimport os\n\nsample_df = df.sample(6, random_state=42).reset_index(drop=True)\n\nplt.figure(figsize=(10, 6))\n\nfor i in range(6):\n    img_id = sample_df.loc[i, \"id\"]\n    label = sample_df.loc[i, \"label\"]\n\n    img_path = os.path.join(TRAIN_DIR, img_id + \".tif\")\n\n    # safety check\n    if not os.path.exists(img_path):\n        continue\n\n    image = Image.open(img_path).convert(\"RGB\")\n\n    plt.subplot(2, 3, i + 1)\n    plt.imshow(image)\n    plt.title(f\"Label: {label}\")\n    plt.axis(\"off\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:55:20.096956Z","iopub.execute_input":"2026-04-30T04:55:20.097481Z","iopub.status.idle":"2026-04-30T04:55:20.820956Z","shell.execute_reply.started":"2026-04-30T04:55:20.097430Z","shell.execute_reply":"2026-04-30T04:55:20.820173Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# CLASSICAL CNN MODEL","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass SimpleCNN(nn.Module):\n    def __init__(self):\n        super(SimpleCNN, self).__init__()\n\n        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)\n        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)\n        self.conv3 = nn.Conv2d(64, 128, 3, padding=1)\n\n        self.pool = nn.MaxPool2d(2, 2)\n        self.dropout = nn.Dropout(0.3)\n\n        # assuming IMAGE_SIZE = 96\n        self.fc1 = nn.Linear(128 * 12 * 12, 256)\n        self.fc2 = nn.Linear(256, 1)\n\n    def forward(self, x):\n        x = self.pool(F.relu(self.conv1(x)))  # 96 -> 48\n        x = self.pool(F.relu(self.conv2(x)))  # 48 -> 24\n        x = self.pool(F.relu(self.conv3(x)))  # 24 -> 12\n\n        x = x.view(x.size(0), -1)\n\n        x = F.relu(self.fc1(x))\n        x = self.dropout(x)\n        x = torch.sigmoid(self.fc2(x))\n\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:56:05.918609Z","iopub.execute_input":"2026-04-30T04:56:05.918936Z","iopub.status.idle":"2026-04-30T04:56:05.926158Z","shell.execute_reply.started":"2026-04-30T04:56:05.918909Z","shell.execute_reply":"2026-04-30T04:56:05.925399Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Loss & Optimizer","metadata":{}},{"cell_type":"code","source":"import torch\n\nmodel = SimpleCNN().to(DEVICE)\n\ncriterion = torch.nn.BCELoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:57:33.470382Z","iopub.execute_input":"2026-04-30T04:57:33.470921Z","iopub.status.idle":"2026-04-30T04:57:33.518125Z","shell.execute_reply.started":"2026-04-30T04:57:33.470887Z","shell.execute_reply":"2026-04-30T04:57:33.517581Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# TRAINING LOOP","metadata":{}},{"cell_type":"code","source":"# -------------------\n# TRAIN FUNCTION\n# -------------------\ndef train_one_epoch(model, loader, optimizer, criterion):\n    model.train()\n\n    total_loss = 0\n    correct = 0\n    total = 0\n\n    for images, labels in loader:\n        images = images.to(DEVICE)\n        labels = labels.float().unsqueeze(1).to(DEVICE)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n\n        preds = (outputs > 0.5).float()\n        correct += (preds == labels).sum().item()\n        total += labels.size(0)\n\n    acc = correct / total\n    return total_loss / len(loader), acc\n\n# -------------------\n# EVAL FUNCTION\n# -------------------\ndef evaluate(model, loader, criterion):\n    model.eval()\n\n    total_loss = 0\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n        for images, labels in loader:\n            images = images.to(DEVICE)\n            labels = labels.float().unsqueeze(1).to(DEVICE)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            total_loss += loss.item()\n\n            preds = (outputs > 0.5).float()\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n\n    acc = correct / total\n    return total_loss / len(loader), acc\n\n# -------------------\n# TRAIN LOOP\n# -------------------\nfor epoch in range(EPOCHS):\n    train_loss, train_acc = train_one_epoch(model, train_loader, optimizer, criterion)\n    val_loss, val_acc = evaluate(model, val_loader, criterion)\n\n    print(f\"Epoch {epoch+1}/{EPOCHS}\")\n    print(f\"Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f}\")\n    print(f\"Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T04:59:06.783055Z","iopub.execute_input":"2026-04-30T04:59:06.783634Z","iopub.status.idle":"2026-04-30T05:26:53.196032Z","shell.execute_reply.started":"2026-04-30T04:59:06.783604Z","shell.execute_reply":"2026-04-30T05:26:53.195175Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# EVALUATION","metadata":{}},{"cell_type":"code","source":"import torch\nfrom sklearn.metrics import confusion_matrix, classification_report\n\nmodel.eval()\n\n# -------------------\n# 1. BASIC METRICS\n# -------------------\nval_loss, val_acc = evaluate(model, val_loader, criterion)\n\nprint(\"===== BASIC METRICS =====\")\nprint(f\"Validation Loss: {val_loss:.4f}\")\nprint(f\"Validation Accuracy: {val_acc:.4f}\")\n\n\n# -------------------\n# 2. COLLECT PREDICTIONS\n# -------------------\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():\n    for images, labels in val_loader:\n        images = images.to(DEVICE)\n\n        outputs = model(images)\n        preds = (outputs > 0.5).int().cpu().numpy()\n\n        all_preds.extend(preds.flatten())\n        all_labels.extend(labels.numpy())\n\n\n# -------------------\n# 3. CONFUSION MATRIX\n# -------------------\ncm = confusion_matrix(all_labels, all_preds)\n\nprint(\"\\n===== CONFUSION MATRIX =====\")\nprint(cm)\n\n\n# -------------------\n# 4. CLASSIFICATION REPORT\n# -------------------\nreport = classification_report(all_labels, all_preds)\n\nprint(\"\\n===== CLASSIFICATION REPORT =====\")\nprint(report)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:27:48.193722Z","iopub.execute_input":"2026-04-30T05:27:48.194050Z","iopub.status.idle":"2026-04-30T05:29:15.850773Z","shell.execute_reply.started":"2026-04-30T05:27:48.194018Z","shell.execute_reply":"2026-04-30T05:29:15.849949Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Quantum device","metadata":{}},{"cell_type":"code","source":"n_qubits = 4\n\ndev = qml.device(\"default.qubit\", wires=n_qubits)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:33:37.345575Z","iopub.execute_input":"2026-04-30T05:33:37.346341Z","iopub.status.idle":"2026-04-30T05:33:37.350461Z","shell.execute_reply.started":"2026-04-30T05:33:37.346307Z","shell.execute_reply":"2026-04-30T05:33:37.349847Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Quantum circuit","metadata":{"execution":{"iopub.status.busy":"2026-04-30T05:33:55.465948Z","iopub.execute_input":"2026-04-30T05:33:55.466270Z","iopub.status.idle":"2026-04-30T05:33:55.470244Z","shell.execute_reply.started":"2026-04-30T05:33:55.466242Z","shell.execute_reply":"2026-04-30T05:33:55.469558Z"}}},{"cell_type":"code","source":"@qml.qnode(dev, interface=\"torch\")\ndef quantum_circuit(inputs, weights):\n    qml.templates.AngleEmbedding(inputs, wires=range(n_qubits))\n    qml.templates.BasicEntanglerLayers(weights, wires=range(n_qubits))\n    return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:34:21.319757Z","iopub.execute_input":"2026-04-30T05:34:21.320357Z","iopub.status.idle":"2026-04-30T05:34:21.325035Z","shell.execute_reply.started":"2026-04-30T05:34:21.320327Z","shell.execute_reply":"2026-04-30T05:34:21.324287Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Quantum layer wrapper","metadata":{}},{"cell_type":"code","source":"weight_shapes = {\"weights\": (2, n_qubits)}\n\nqlayer = qml.qnn.TorchLayer(quantum_circuit, weight_shapes)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:34:55.919054Z","iopub.execute_input":"2026-04-30T05:34:55.919640Z","iopub.status.idle":"2026-04-30T05:34:55.924407Z","shell.execute_reply.started":"2026-04-30T05:34:55.919610Z","shell.execute_reply":"2026-04-30T05:34:55.923866Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Hybrid QCNN model","metadata":{}},{"cell_type":"code","source":"class HybridQCNN(nn.Module):\n    def __init__(self):\n        super(HybridQCNN, self).__init__()\n\n        # classical CNN part\n        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)\n        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)\n        self.conv3 = nn.Conv2d(64, 128, 3, padding=1)\n\n        self.pool = nn.MaxPool2d(2, 2)\n\n        # compress to small feature vector for quantum layer\n        self.fc1 = nn.Linear(128 * 12 * 12, 16)\n\n        # quantum layer (4-qubit input)\n        self.q_layer = qlayer\n\n        # final classifier\n        self.fc2 = nn.Linear(n_qubits, 1)\n\n    def forward(self, x):\n        x = self.pool(F.relu(self.conv1(x)))\n        x = self.pool(F.relu(self.conv2(x)))\n        x = self.pool(F.relu(self.conv3(x)))\n\n        x = x.view(x.size(0), -1)\n        x = F.relu(self.fc1(x))\n\n    # keep only required qubits\n        x = x[:, :n_qubits]\n\n        x = self.q_layer(x)   # quantum layer output (already tensor)\n\n        x = torch.sigmoid(self.fc2(x))\n\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:38:15.224838Z","iopub.execute_input":"2026-04-30T05:38:15.225502Z","iopub.status.idle":"2026-04-30T05:38:15.232336Z","shell.execute_reply.started":"2026-04-30T05:38:15.225463Z","shell.execute_reply":"2026-04-30T05:38:15.231543Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Loss + optimizer","metadata":{}},{"cell_type":"code","source":"model = HybridQCNN().to(DEVICE)\n\ncriterion = nn.BCELoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:38:19.141576Z","iopub.execute_input":"2026-04-30T05:38:19.142177Z","iopub.status.idle":"2026-04-30T05:38:19.152326Z","shell.execute_reply.started":"2026-04-30T05:38:19.142146Z","shell.execute_reply":"2026-04-30T05:38:19.151570Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_one_epoch(model, loader, optimizer, criterion):\n    model.train()\n\n    total_loss = 0\n    correct = 0\n    total = 0\n\n    for images, labels in loader:\n        images = images.to(DEVICE)\n        labels = labels.float().unsqueeze(1).to(DEVICE)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n\n        preds = (outputs > 0.5).float()\n        correct += (preds == labels).sum().item()\n        total += labels.size(0)\n\n    acc = correct / total\n    return total_loss / len(loader), acc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:38:20.059158Z","iopub.execute_input":"2026-04-30T05:38:20.059549Z","iopub.status.idle":"2026-04-30T05:38:20.066667Z","shell.execute_reply.started":"2026-04-30T05:38:20.059496Z","shell.execute_reply":"2026-04-30T05:38:20.065253Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate(model, loader, criterion):\n    model.eval()\n\n    total_loss = 0\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n        for images, labels in loader:\n            images = images.to(DEVICE)\n            labels = labels.float().unsqueeze(1).to(DEVICE)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            total_loss += loss.item()\n\n            preds = (outputs > 0.5).float()\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n\n    acc = correct / total\n    return total_loss / len(loader), acc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:38:22.646161Z","iopub.execute_input":"2026-04-30T05:38:22.646878Z","iopub.status.idle":"2026-04-30T05:38:22.652032Z","shell.execute_reply.started":"2026-04-30T05:38:22.646839Z","shell.execute_reply":"2026-04-30T05:38:22.651370Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 5  # start small (QCNN is slow)\n\nfor epoch in range(EPOCHS):\n    train_loss, train_acc = train_one_epoch(model, train_loader, optimizer, criterion)\n    val_loss, val_acc = evaluate(model, val_loader, criterion)\n\n    print(f\"Epoch {epoch+1}/{EPOCHS}\")\n    print(f\"Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f}\")\n    print(f\"Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T05:38:23.479426Z","iopub.execute_input":"2026-04-30T05:38:23.480145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}