{"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":"# =====================================================\n# BÀI 7: VISION TRANSFORMER - CODE FULL ĐÃ FIX SẠCH OUTPUT\n# =====================================================\n\nimport os\nimport time\nimport gc\nimport warnings\nimport torch\nimport pandas as pd\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision\nimport torchvision.transforms as transforms\nimport torchvision.models as models\nfrom torch.utils.data import DataLoader\nimport matplotlib.pyplot as plt\n\nwarnings.filterwarnings(\"ignore\")\n\n# =====================================================\n# CẤU HÌNH OUTPUT\n# =====================================================\n\nOUTPUT_DIR = \"/kaggle/working/Bai7_KetQua\" if os.path.exists(\"/kaggle/working\") else \"./Bai7_KetQua\"\nos.makedirs(OUTPUT_DIR, exist_ok=True)\n\nprint(\"Kết quả sẽ lưu tại:\", OUTPUT_DIR)\n\n# =====================================================\n# CẤU HÌNH CHUNG\n# =====================================================\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Thiết bị đang dùng:\", device)\n\nIMG_SIZE = 224\nBATCH_SIZE = 32\n\n# Tất cả các bài có train đều chạy 10 epoch\nEPOCHS_ALL = 10\n\n# Chống spam DataLoader trong Kaggle/Jupyter\nNUM_WORKERS = 0\nPIN_MEMORY = False\n\nsummary_results = []\n\n# =====================================================\n# HÀM LƯU KẾT QUẢ\n# =====================================================\n\ndef save_txt(filename, content):\n    path = os.path.join(OUTPUT_DIR, filename)\n    with open(path, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\ndef add_summary(bai, noi_dung, ket_qua):\n    summary_results.append({\n        \"Bài\": bai,\n        \"Nội dung\": noi_dung,\n        \"Kết quả\": ket_qua\n    })\n\n    df = pd.DataFrame(summary_results)\n    df.to_csv(os.path.join(OUTPUT_DIR, \"tong_hop_ket_qua.csv\"), index=False)\n\ndef save_model(model, filename, accuracy=None, classes=None):\n    path = os.path.join(OUTPUT_DIR, filename)\n\n    torch.save(\n        {\n            \"model_state_dict\": model.state_dict(),\n            \"accuracy\": accuracy,\n            \"classes\": classes,\n            \"image_size\": IMG_SIZE\n        },\n        path\n    )\n\n    print(\"Đã lưu model:\", path)\n\ndef make_loader(dataset, batch_size=32, shuffle=False):\n    return DataLoader(\n        dataset,\n        batch_size=batch_size,\n        shuffle=shuffle,\n        num_workers=0,\n        pin_memory=False\n    )\n\n# =====================================================\n# BÀI 1: TẢI CIFAR-10\n# =====================================================\n\nprint(\"\\nBÀI 1: TẢI DATASET CIFAR-10\")\n\ntransform_train = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomCrop(IMG_SIZE, padding=4),\n    transforms.ToTensor()\n])\n\ntransform_test = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor()\n])\n\ntrain_dataset = torchvision.datasets.CIFAR10(\n    root=\"./data\",\n    train=True,\n    download=True,\n    transform=transform_train\n)\n\ntest_dataset = torchvision.datasets.CIFAR10(\n    root=\"./data\",\n    train=False,\n    download=True,\n    transform=transform_test\n)\n\nclasses = train_dataset.classes\n\ntrain_loader = make_loader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True\n)\n\ntest_loader = make_loader(\n    test_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\nprint(\"Số ảnh train:\", len(train_dataset))\nprint(\"Số ảnh test:\", len(test_dataset))\nprint(\"Số lớp:\", len(classes))\nprint(\"Tên các lớp:\", classes)\n\nbai1_text = f\"\"\"\nBÀI 1: TẢI DATASET CIFAR-10\n\nSố ảnh train: {len(train_dataset)}\nSố ảnh test: {len(test_dataset)}\nSố lớp: {len(classes)}\nTên các lớp: {classes}\n\"\"\"\n\nsave_txt(\"bai1_dataset_info.txt\", bai1_text)\n\nadd_summary(\n    \"Bài 1\",\n    \"Tải dataset CIFAR-10\",\n    f\"Train: {len(train_dataset)}, Test: {len(test_dataset)}, Classes: {len(classes)}\"\n)\n\n# =====================================================\n# BÀI 2: HIỂN THỊ ẢNH\n# =====================================================\n\nprint(\"\\nBÀI 2: HIỂN THỊ MỘT SỐ ẢNH\")\n\nimages, labels = next(iter(train_loader))\n\nplt.figure(figsize=(12, 6))\n\nfor i in range(8):\n    img = images[i].permute(1, 2, 0)\n    plt.subplot(2, 4, i + 1)\n    plt.imshow(img)\n    plt.title(classes[labels[i]])\n    plt.axis(\"off\")\n\nplt.savefig(\n    os.path.join(OUTPUT_DIR, \"bai2_hien_thi_anh_dataset.png\"),\n    dpi=200,\n    bbox_inches=\"tight\"\n)\n\nplt.show()\n\nadd_summary(\n    \"Bài 2\",\n    \"Hiển thị một số ảnh trong dataset\",\n    \"Đã lưu bai2_hien_thi_anh_dataset.png\"\n)\n\n# =====================================================\n# HÀM TẠO MODEL ViT-B/16\n# =====================================================\n\ndef create_vit_b16(num_classes):\n    try:\n        model = models.vit_b_16(weights=models.ViT_B_16_Weights.DEFAULT)\n        pretrained_loaded = True\n    except:\n        model = models.vit_b_16(weights=None)\n        pretrained_loaded = False\n        print(\"Không tải được pretrained weights, dùng weights=None\")\n\n    model.heads.head = nn.Linear(\n        model.heads.head.in_features,\n        num_classes\n    )\n\n    # Khi có pretrained thì đóng băng backbone để train nhanh, ổn định\n    if pretrained_loaded:\n        for name, param in model.named_parameters():\n            if \"heads.head\" not in name:\n                param.requires_grad = False\n\n    return model.to(device)\n\n# =====================================================\n# HÀM TẠO MODEL ViT-B/32\n# =====================================================\n\ndef create_vit_b32(num_classes):\n    try:\n        model = models.vit_b_32(weights=models.ViT_B_32_Weights.DEFAULT)\n        pretrained_loaded = True\n    except:\n        model = models.vit_b_32(weights=None)\n        pretrained_loaded = False\n        print(\"Không tải được pretrained weights, dùng weights=None\")\n\n    model.heads.head = nn.Linear(\n        model.heads.head.in_features,\n        num_classes\n    )\n\n    if pretrained_loaded:\n        for name, param in model.named_parameters():\n            if \"heads.head\" not in name:\n                param.requires_grad = False\n\n    return model.to(device)\n\n# =====================================================\n# HÀM TRAIN MODEL - OUTPUT GỌN, KHÔNG TQDM, KHÔNG SPAM\n# =====================================================\n\ndef train_model(model, train_loader, epochs=10, lr=0.0001, run_name=\"Train\"):\n    model = model.to(device)\n    criterion = nn.CrossEntropyLoss()\n\n    optimizer = optim.Adam(\n        filter(lambda p: p.requires_grad, model.parameters()),\n        lr=lr\n    )\n\n    loss_list = []\n    start_time = time.time()\n\n    scaler = torch.cuda.amp.GradScaler(enabled=(device.type == \"cuda\"))\n\n    print(f\"\\n{run_name}\")\n\n    for epoch in range(epochs):\n        model.train()\n        running_loss = 0\n\n        for images, labels in train_loader:\n            images = images.to(device, non_blocking=True)\n            labels = labels.to(device, non_blocking=True)\n\n            optimizer.zero_grad()\n\n            with torch.cuda.amp.autocast(enabled=(device.type == \"cuda\")):\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n\n            scaler.scale(loss).backward()\n            scaler.step(optimizer)\n            scaler.update()\n\n            running_loss += loss.item()\n\n        avg_loss = running_loss / len(train_loader)\n        loss_list.append(avg_loss)\n\n        print(f\"Epoch {epoch + 1:02d}/{epochs} - Loss: {avg_loss:.4f}\", flush=True)\n\n    total_time = time.time() - start_time\n\n    return loss_list, total_time\n\n# =====================================================\n# HÀM TÍNH ACCURACY - KHÔNG SPAM\n# =====================================================\n\ndef evaluate_model(model, test_loader):\n    model.eval()\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n        for images, labels in test_loader:\n            images = images.to(device, non_blocking=True)\n            labels = labels.to(device, non_blocking=True)\n\n            outputs = model(images)\n            _, predicted = torch.max(outputs, 1)\n\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n\n    accuracy = 100 * correct / total\n    return accuracy\n\n# =====================================================\n# BÀI 3: HUẤN LUYỆN VISION TRANSFORMER 10 EPOCH\n# =====================================================\n\nprint(\"\\nBÀI 3: HUẤN LUYỆN ViT-B/16\")\n\nmodel_vit_b16 = create_vit_b16(num_classes=10)\n\nloss_vit_b16, time_vit_b16 = train_model(\n    model_vit_b16,\n    train_loader,\n    epochs=EPOCHS_ALL,\n    lr=0.0001,\n    run_name=\"Bài 3 - ViT-B/16 - CIFAR-10\"\n)\n\nbai3_text = f\"\"\"\nBÀI 3: HUẤN LUYỆN VISION TRANSFORMER\n\nModel: ViT-B/16\nDataset: CIFAR-10\nSố epoch: {EPOCHS_ALL}\nOptimizer: Adam\nLearning rate: 0.0001\nLoss cuối: {loss_vit_b16[-1]:.4f}\nThời gian train: {time_vit_b16:.2f}s\n\"\"\"\n\nsave_txt(\"bai3_train_vit_b16.txt\", bai3_text)\n\nadd_summary(\n    \"Bài 3\",\n    f\"Huấn luyện ViT-B/16 trong {EPOCHS_ALL} epoch\",\n    f\"Loss cuối: {loss_vit_b16[-1]:.4f}, Time: {time_vit_b16:.2f}s\"\n)\n\n# =====================================================\n# BÀI 4: TÍNH ACCURACY\n# =====================================================\n\nprint(\"\\nBÀI 4: TÍNH ACCURACY\")\n\nacc_vit_b16 = evaluate_model(model_vit_b16, test_loader)\n\nprint(f\"Accuracy ViT-B/16 trên CIFAR-10: {acc_vit_b16:.2f}%\")\n\nbai4_text = f\"\"\"\nBÀI 4: TÍNH ACCURACY\n\nModel: ViT-B/16\nDataset: CIFAR-10\nAccuracy: {acc_vit_b16:.2f}%\nTime: {time_vit_b16:.2f}s\n\"\"\"\n\nsave_txt(\"bai4_accuracy_vit_b16.txt\", bai4_text)\n\nadd_summary(\n    \"Bài 4\",\n    \"Tính accuracy trên tập test\",\n    f\"ViT-B/16 Accuracy: {acc_vit_b16:.2f}%\"\n)\n\nsave_model(\n    model_vit_b16,\n    \"bai4_vit_b16_cifar10.pth\",\n    accuracy=acc_vit_b16,\n    classes=classes\n)\n\n# =====================================================\n# BÀI 5: THỬ LEARNING RATE - 10 EPOCH\n# =====================================================\n\nprint(\"\\nBÀI 5: THỬ LEARNING RATE\")\n\nlearning_rates = [0.0001, 0.0003, 0.001]\nlr_results = {}\n\nfor lr in learning_rates:\n    model = create_vit_b16(num_classes=10)\n\n    losses, train_time = train_model(\n        model,\n        train_loader,\n        epochs=EPOCHS_ALL,\n        lr=lr,\n        run_name=f\"Bài 5 - ViT-B/16 - Learning rate {lr}\"\n    )\n\n    accuracy = evaluate_model(model, test_loader)\n\n    lr_results[lr] = {\n        \"loss\": losses[-1],\n        \"accuracy\": accuracy,\n        \"time\": train_time\n    }\n\n    add_summary(\n        \"Bài 5\",\n        f\"Thử learning rate {lr}\",\n        f\"Loss: {losses[-1]:.4f}, Accuracy: {accuracy:.2f}%, Time: {train_time:.2f}s\"\n    )\n\n    del model\n    torch.cuda.empty_cache()\n    gc.collect()\n\nlr_df = pd.DataFrame([\n    {\n        \"learning_rate\": lr,\n        \"loss\": result[\"loss\"],\n        \"accuracy\": result[\"accuracy\"],\n        \"time\": result[\"time\"]\n    }\n    for lr, result in lr_results.items()\n])\n\nlr_df.to_csv(\n    os.path.join(OUTPUT_DIR, \"bai5_learning_rate_results.csv\"),\n    index=False\n)\n\n# =====================================================\n# BÀI 6: THỬ BATCH SIZE - 10 EPOCH\n# =====================================================\n\nprint(\"\\nBÀI 6: THỬ BATCH SIZE\")\n\nbatch_sizes = [16, 32, 64]\nbatch_results = {}\n\nfor batch_size in batch_sizes:\n    train_loader_bs = make_loader(\n        train_dataset,\n        batch_size=batch_size,\n        shuffle=True\n    )\n\n    test_loader_bs = make_loader(\n        test_dataset,\n        batch_size=batch_size,\n        shuffle=False\n    )\n\n    model = create_vit_b16(num_classes=10)\n\n    losses, train_time = train_model(\n        model,\n        train_loader_bs,\n        epochs=EPOCHS_ALL,\n        lr=0.0001,\n        run_name=f\"Bài 6 - ViT-B/16 - Batch size {batch_size}\"\n    )\n\n    accuracy = evaluate_model(model, test_loader_bs)\n\n    batch_results[batch_size] = {\n        \"loss\": losses[-1],\n        \"accuracy\": accuracy,\n        \"time\": train_time\n    }\n\n    add_summary(\n        \"Bài 6\",\n        f\"Thử batch size {batch_size}\",\n        f\"Loss: {losses[-1]:.4f}, Accuracy: {accuracy:.2f}%, Time: {train_time:.2f}s\"\n    )\n\n    del model\n    torch.cuda.empty_cache()\n    gc.collect()\n\nbatch_df = pd.DataFrame([\n    {\n        \"batch_size\": bs,\n        \"loss\": result[\"loss\"],\n        \"accuracy\": result[\"accuracy\"],\n        \"time\": result[\"time\"]\n    }\n    for bs, result in batch_results.items()\n])\n\nbatch_df.to_csv(\n    os.path.join(OUTPUT_DIR, \"bai6_batch_size_results.csv\"),\n    index=False\n)\n\n# =====================================================\n# BÀI 7: SIMPLE CNN - 10 EPOCH\n# =====================================================\n\nprint(\"\\nBÀI 7: SIMPLE CNN\")\n\nclass SimpleCNN(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        self.conv = nn.Sequential(\n            nn.Conv2d(3, 32, 3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(32, 64, 3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2)\n        )\n\n        self.fc = nn.Linear(64 * 56 * 56, 10)\n\n    def forward(self, x):\n        x = self.conv(x)\n        x = x.view(x.size(0), -1)\n        x = self.fc(x)\n        return x\n\ncnn_model = SimpleCNN().to(device)\n\nloss_cnn, time_cnn = train_model(\n    cnn_model,\n    train_loader,\n    epochs=EPOCHS_ALL,\n    lr=0.0001,\n    run_name=\"Bài 7 - Simple CNN - CIFAR-10\"\n)\n\nacc_cnn = evaluate_model(cnn_model, test_loader)\n\nprint(f\"Accuracy Simple CNN trên CIFAR-10: {acc_cnn:.2f}%\")\n\nbai7_df = pd.DataFrame([\n    {\n        \"model\": \"ViT-B/16\",\n        \"dataset\": \"CIFAR-10\",\n        \"accuracy\": acc_vit_b16,\n        \"time\": time_vit_b16\n    },\n    {\n        \"model\": \"Simple CNN\",\n        \"dataset\": \"CIFAR-10\",\n        \"accuracy\": acc_cnn,\n        \"time\": time_cnn\n    }\n])\n\nbai7_df.to_csv(\n    os.path.join(OUTPUT_DIR, \"bai7_so_sanh_vit_cnn.csv\"),\n    index=False\n)\n\nadd_summary(\n    \"Bài 7\",\n    \"So sánh ViT-B/16 và CNN\",\n    f\"ViT: {acc_vit_b16:.2f}%, CNN: {acc_cnn:.2f}%\"\n)\n\ndel cnn_model\ntorch.cuda.empty_cache()\ngc.collect()\n\n# =====================================================\n# BÀI 8: THỬ CIFAR-100 - 10 EPOCH\n# =====================================================\n\nprint(\"\\nBÀI 8: THỬ CIFAR-100\")\n\ntrain_dataset_100 = torchvision.datasets.CIFAR100(\n    root=\"./data\",\n    train=True,\n    download=True,\n    transform=transform_train\n)\n\ntest_dataset_100 = torchvision.datasets.CIFAR100(\n    root=\"./data\",\n    train=False,\n    download=True,\n    transform=transform_test\n)\n\ntrain_loader_100 = make_loader(\n    train_dataset_100,\n    batch_size=BATCH_SIZE,\n    shuffle=True\n)\n\ntest_loader_100 = make_loader(\n    test_dataset_100,\n    batch_size=BATCH_SIZE,\n    shuffle=False\n)\n\nmodel_cifar100 = create_vit_b16(num_classes=100)\n\nloss_cifar100, time_cifar100 = train_model(\n    model_cifar100,\n    train_loader_100,\n    epochs=EPOCHS_ALL,\n    lr=0.0001,\n    run_name=\"Bài 8 - ViT-B/16 - CIFAR-100\"\n)\n\nacc_cifar100 = evaluate_model(model_cifar100, test_loader_100)\n\nprint(f\"Accuracy ViT-B/16 trên CIFAR-100: {acc_cifar100:.2f}%\")\n\nbai8_df = pd.DataFrame([\n    {\n        \"model\": \"ViT-B/16\",\n        \"dataset\": \"CIFAR-100\",\n        \"loss\": loss_cifar100[-1],\n        \"accuracy\": acc_cifar100,\n        \"time\": time_cifar100\n    }\n])\n\nbai8_df.to_csv(\n    os.path.join(OUTPUT_DIR, \"bai8_cifar100_result.csv\"),\n    index=False\n)\n\nadd_summary(\n    \"Bài 8\",\n    \"Thử dataset CIFAR-100\",\n    f\"Loss: {loss_cifar100[-1]:.4f}, Accuracy: {acc_cifar100:.2f}%, Time: {time_cifar100:.2f}s\"\n)\n\ndel model_cifar100\ntorch.cuda.empty_cache()\ngc.collect()\n\n# =====================================================\n# BÀI 9: THỬ ViT-B/32 - 10 EPOCH\n# =====================================================\n\nprint(\"\\nBÀI 9: THỬ ViT-B/32\")\n\nmodel_vit_b32 = create_vit_b32(num_classes=10)\n\nloss_vit_b32, time_vit_b32 = train_model(\n    model_vit_b32,\n    train_loader,\n    epochs=EPOCHS_ALL,\n    lr=0.0001,\n    run_name=\"Bài 9 - ViT-B/32 - CIFAR-10\"\n)\n\nacc_vit_b32 = evaluate_model(model_vit_b32, test_loader)\n\nprint(f\"Accuracy ViT-B/32 trên CIFAR-10: {acc_vit_b32:.2f}%\")\n\nbai9_df = pd.DataFrame([\n    {\n        \"model\": \"ViT-B/16\",\n        \"dataset\": \"CIFAR-10\",\n        \"accuracy\": acc_vit_b16,\n        \"time\": time_vit_b16\n    },\n    {\n        \"model\": \"ViT-B/32\",\n        \"dataset\": \"CIFAR-10\",\n        \"accuracy\": acc_vit_b32,\n        \"time\": time_vit_b32\n    }\n])\n\nbai9_df.to_csv(\n    os.path.join(OUTPUT_DIR, \"bai9_so_sanh_vit_b16_vit_b32.csv\"),\n    index=False\n)\n\nadd_summary(\n    \"Bài 9\",\n    \"Thử model ViT-B/32\",\n    f\"ViT-B/16: {acc_vit_b16:.2f}%, ViT-B/32: {acc_vit_b32:.2f}%\"\n)\n\ndel model_vit_b32\ntorch.cuda.empty_cache()\ngc.collect()\n\n# =====================================================\n# BÀI 10: ẢNH DỰ ĐOÁN SAI\n# =====================================================\n\nprint(\"\\nBÀI 10: HIỂN THỊ ẢNH DỰ ĐOÁN SAI\")\n\ndef show_wrong_predictions(model, test_loader, classes, max_images=8):\n    model.eval()\n\n    wrong_images = []\n    true_labels = []\n    pred_labels = []\n\n    with torch.no_grad():\n        for images, labels in test_loader:\n            images_device = images.to(device, non_blocking=True)\n            labels_device = labels.to(device, non_blocking=True)\n\n            outputs = model(images_device)\n            _, predicted = torch.max(outputs, 1)\n\n            for i in range(len(labels)):\n                if predicted[i] != labels_device[i]:\n                    wrong_images.append(images[i])\n                    true_labels.append(labels[i].item())\n                    pred_labels.append(predicted[i].item())\n\n                if len(wrong_images) >= max_images:\n                    break\n\n            if len(wrong_images) >= max_images:\n                break\n\n    plt.figure(figsize=(14, 6))\n\n    for i in range(len(wrong_images)):\n        img = wrong_images[i].permute(1, 2, 0)\n\n        plt.subplot(2, 4, i + 1)\n        plt.imshow(img)\n        plt.title(\n            \"True: \" + classes[true_labels[i]] +\n            \"\\nPred: \" + classes[pred_labels[i]]\n        )\n        plt.axis(\"off\")\n\n    plt.savefig(\n        os.path.join(OUTPUT_DIR, \"bai10_wrong_predictions.png\"),\n        dpi=200,\n        bbox_inches=\"tight\"\n    )\n\n    plt.show()\n\nshow_wrong_predictions(\n    model=model_vit_b16,\n    test_loader=test_loader,\n    classes=classes,\n    max_images=8\n)\n\nadd_summary(\n    \"Bài 10\",\n    \"Phân tích các trường hợp mô hình dự đoán sai\",\n    \"Đã lưu ảnh bai10_wrong_predictions.png\"\n)\n\n# =====================================================\n# BẢNG KẾT QUẢ TỔNG HỢP\n# =====================================================\n\nprint(\"\\n================ BẢNG KẾT QUẢ TỔNG HỢP ================\")\n\nsummary_df = pd.DataFrame(summary_results)\n\nprint(summary_df.to_string(index=False))\n\nsummary_df.to_csv(\n    os.path.join(OUTPUT_DIR, \"tong_hop_ket_qua.csv\"),\n    index=False\n)\n\nprint(\"\\nĐã lưu bảng tổng hợp vào:\", os.path.join(OUTPUT_DIR, \"tong_hop_ket_qua.csv\"))\nprint(\"Hoàn thành toàn bộ Bài 1 -> Bài 10.\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-06-14T12:02:45.158930Z","iopub.execute_input":"2026-06-14T12:02:45.159679Z"}},"outputs":[],"execution_count":null}]}