{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":107469,"databundleVersionId":13058354,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"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","trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:53:47.008271Z","iopub.execute_input":"2025-08-22T11:53:47.008453Z","iopub.status.idle":"2025-08-22T11:53:52.407017Z","shell.execute_reply.started":"2025-08-22T11:53:47.008435Z","shell.execute_reply":"2025-08-22T11:53:52.406417Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nfrom pathlib import Path\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nfrom PIL import Image\nimport cv2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:54:27.947232Z","iopub.execute_input":"2025-08-22T11:54:27.947919Z","iopub.status.idle":"2025-08-22T11:54:28.108542Z","shell.execute_reply.started":"2025-08-22T11:54:27.947895Z","shell.execute_reply":"2025-08-22T11:54:28.107790Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install ultralytics -q\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:54:35.295342Z","iopub.execute_input":"2025-08-22T11:54:35.295803Z","iopub.status.idle":"2025-08-22T11:55:45.035426Z","shell.execute_reply.started":"2025-08-22T11:54:35.295775Z","shell.execute_reply":"2025-08-22T11:55:45.034647Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from ultralytics import YOLO\nimport yaml","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:55:45.037012Z","iopub.execute_input":"2025-08-22T11:55:45.037247Z","iopub.status.idle":"2025-08-22T11:55:48.029579Z","shell.execute_reply.started":"2025-08-22T11:55:45.037223Z","shell.execute_reply":"2025-08-22T11:55:48.028863Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"📁 Available input data:\")\nfor item in os.listdir('/kaggle/input/'):\n    print(f\"  - {item}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:55:48.030420Z","iopub.execute_input":"2025-08-22T11:55:48.030816Z","iopub.status.idle":"2025-08-22T11:55:48.035393Z","shell.execute_reply.started":"2025-08-22T11:55:48.030789Z","shell.execute_reply":"2025-08-22T11:55:48.034652Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"starter_data_path = \"/kaggle/input/multi-class-object-detection-challenge/Starter_Dataset\"\ntest_data_path = \"/kaggle/input/multi-class-object-detection-challenge/testImages/images\"\nyolo_config_path = \"/kaggle/input/multi-class-object-detection-challenge/yolo_params.yaml\"\nprint(f\"\\n📊 Data paths:\")\nprint(f\"  🏋️ Training data: {starter_data_path}\")\nprint(f\"  🎯 Test images: {test_data_path}\")\nprint(f\"  ⚙️ YOLO config: {yolo_config_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:57:21.181278Z","iopub.execute_input":"2025-08-22T11:57:21.181562Z","iopub.status.idle":"2025-08-22T11:57:21.186367Z","shell.execute_reply.started":"2025-08-22T11:57:21.181541Z","shell.execute_reply":"2025-08-22T11:57:21.185694Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_image(image_path, title=\"\", figsize=(10, 6)):\n    \"\"\"Display image inline in notebook\"\"\"\n    if os.path.exists(image_path):\n        plt.figure(figsize=figsize)\n        img = mpimg.imread(image_path)\n        plt.imshow(img)\n        plt.title(title)\n        plt.axis('off')\n        plt.show()\n    else:\n        print(f\"❌ Image not found: {image_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:57:50.774776Z","iopub.execute_input":"2025-08-22T11:57:50.775286Z","iopub.status.idle":"2025-08-22T11:57:50.779833Z","shell.execute_reply.started":"2025-08-22T11:57:50.775264Z","shell.execute_reply":"2025-08-22T11:57:50.778975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_images_grid(image_paths, titles=None, figsize=(15, 10), cols=2):\n    \"\"\"Display multiple images in a grid\"\"\"\n    if not image_paths:\n        return","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:57:58.523526Z","iopub.execute_input":"2025-08-22T11:57:58.523825Z","iopub.status.idle":"2025-08-22T11:57:58.527660Z","shell.execute_reply.started":"2025-08-22T11:57:58.523802Z","shell.execute_reply":"2025-08-22T11:57:58.526964Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_images_grid(image_paths, titles=None, figsize=(15, 10), cols=2):\n    \"\"\"Display multiple images in a grid\"\"\"\n    if not image_paths:\n        return\n    \n    rows = (len(image_paths) + cols - 1) // cols\n    fig, axes = plt.subplots(rows, cols, figsize=figsize)\n    \n    if rows == 1:\n        axes = [axes] if cols == 1 else axes\n    else:\n        axes = axes.flatten()\n    \n    for i, image_path in enumerate(image_paths):\n        if os.path.exists(image_path):\n            img = mpimg.imread(image_path)\n            axes[i].imshow(img)\n            axes[i].set_title(titles[i] if titles else os.path.basename(image_path))\n            axes[i].axis('off')\n        else:\n            axes[i].text(0.5, 0.5, 'Image not found', ha='center', va='center')\n            axes[i].axis('off')\n    \n    # Hide empty subplots\n    for i in range(len(image_paths), len(axes)):\n        axes[i].axis('off')\n    \n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:58:17.790931Z","iopub.execute_input":"2025-08-22T11:58:17.791485Z","iopub.status.idle":"2025-08-22T11:58:17.797165Z","shell.execute_reply.started":"2025-08-22T11:58:17.791458Z","shell.execute_reply":"2025-08-22T11:58:17.796358Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def visualize_sample_data():\n    \"\"\"Show sample training and test images\"\"\"\n    print(\"🖼️ Sample Training Images:\")\n    \n    # Show sample training images\n    train_img_path = os.path.join(starter_data_path, \"train\", \"images\")\n    if os.path.exists(train_img_path):\n        train_images = [f for f in os.listdir(train_img_path)[:4] \n                       if f.lower().endswith(('.jpg', '.jpeg', '.png'))]\n        train_paths = [os.path.join(train_img_path, img) for img in train_images]\n        show_images_grid(train_paths, titles=[f\"Training: {img}\" for img in train_images])\n    \n    print(\"🎯 Sample Test Images:\")\n    \n    # Show sample test images\n    if os.path.exists(test_data_path):\n        test_images = [f for f in os.listdir(test_data_path)[:4] \n                      if f.lower().endswith(('.jpg', '.jpeg', '.png'))]\n        test_paths = [os.path.join(test_data_path, img) for img in test_images]\n        show_images_grid(test_paths, titles=[f\"Test: {img}\" for img in test_images])\n\nvisualize_sample_data()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:58:29.345169Z","iopub.execute_input":"2025-08-22T11:58:29.345436Z","iopub.status.idle":"2025-08-22T11:58:50.130720Z","shell.execute_reply.started":"2025-08-22T11:58:29.345414Z","shell.execute_reply":"2025-08-22T11:58:50.129922Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_working_yolo_config():\n    \"\"\"Create a working YOLO config with absolute paths\"\"\"\n    \n    # First, try to use the provided config if it exists and is valid\n    if os.path.exists(yolo_config_path):\n        try:\n            with open(yolo_config_path, 'r') as f:\n                config = yaml.safe_load(f)\n            \n            # Check if the config has valid paths\n            if 'train' in config and config['train'] is not None:\n                # Convert relative paths to absolute if needed\n                if not os.path.isabs(config['train']):\n                    config['train'] = os.path.join(starter_data_path, config['train'])\n                if 'val' in config and config['val'] and not os.path.isabs(config['val']):\n                    config['val'] = os.path.join(starter_data_path, config['val'])\n                \n                # Update the path to absolute\n                config['path'] = starter_data_path\n                \n                # Save the corrected config\n                working_config_file = \"working_dataset.yaml\"\n                with open(working_config_file, 'w') as f:\n                    yaml.dump(config, f)\n                \n                print(f\"✅ Using corrected provided config: {working_config_file}\")\n                return working_config_file\n                \n        except Exception as e:\n            print(f\"⚠️ Error reading provided config: {e}\")\n    \n    # Create our own config if the provided one doesn't work\n    print(\"🔧 Creating custom YOLO config...\")\n    \n    # Define absolute paths\n    train_path = os.path.join(starter_data_path, \"train\", \"images\")\n    val_path = os.path.join(starter_data_path, \"val\", \"images\")\n    \n    # Verify these paths exist\n    if not os.path.exists(train_path):\n        print(f\"❌ Training images not found at: {train_path}\")\n        # Try alternative structure\n        alt_train_path = os.path.join(starter_data_path, \"images\", \"train\")\n        if os.path.exists(alt_train_path):\n            train_path = alt_train_path\n            val_path = os.path.join(starter_data_path, \"images\", \"val\")\n            print(f\"✅ Found alternative structure: {train_path}\")\n        else:\n            print(\"❌ Cannot find training images in expected locations\")\n            return None\n    \n    config = {\n        'path': starter_data_path,\n        'train': train_path,\n        'val': val_path,\n        'test': '',\n        'names': {\n            0: 'cheerios',\n            1: 'soup'\n        },\n        'nc': 2\n    }\n    \n    config_file = \"custom_dataset.yaml\"\n    with open(config_file, 'w') as f:\n        yaml.dump(config, f, default_flow_style=False)\n    \n    print(f\"✅ Created custom YOLO config: {config_file}\")\n    \n    # Verify the paths exist\n    if os.path.exists(config['train']) and os.path.exists(config['val']):\n        train_count = len([f for f in os.listdir(config['train']) if f.lower().endswith(('.jpg', '.jpeg', '.png'))])\n        val_count = len([f for f in os.listdir(config['val']) if f.lower().endswith(('.jpg', '.jpeg', '.png'))])\n        print(f\"   ✅ Found {train_count} training images\")\n        print(f\"   ✅ Found {val_count} validation images\")\n    \n    return config_file\n\n# Create working config\nconfig_file = create_working_yolo_config()\n\nif config_file is None:\n    print(\"❌ Could not create valid config - stopping here\")\n    exit()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T11:59:36.923937Z","iopub.execute_input":"2025-08-22T11:59:36.924213Z","iopub.status.idle":"2025-08-22T11:59:36.953564Z","shell.execute_reply.started":"2025-08-22T11:59:36.924192Z","shell.execute_reply":"2025-08-22T11:59:36.953037Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_and_visualize_model(config_path):\n    \"\"\"Train model and show results inline\"\"\"\n    \n    print(\"🏋️ Starting training...\")\n    \n    # Load YOLOv8 nano\n    model = YOLO(\"yolov8n.pt\")\n    \n    try:\n        # Train the model\n        results = model.train(\n            data=config_path,\n            epochs=25,\n            imgsz=640,\n            batch=16,\n            patience=8,\n            save=True,\n            verbose=True,\n            project=\"runs\",\n            name=\"first_model\",\n            plots=True,\n            val=True,\n            save_period=5,\n            device=0 if os.system('nvidia-smi') == 0 else 'cpu'\n        )\n        \n        print(\"✅ Training complete!\")\n        \n        # Now visualize the results inline!\n        results_dir = \"runs/first_model\"\n        \n        print(\"\\n📊 TRAINING RESULTS VISUALIZATION:\")\n        print(\"=\"*50)\n        \n        # 1. Show training curves\n        results_plot = os.path.join(results_dir, \"results.png\")\n        if os.path.exists(results_plot):\n            print(\"📈 Training Curves:\")\n            show_image(results_plot, \"Training Progress Over Time\", figsize=(12, 8))\n        \n        # 2. Show confusion matrix\n        confusion_plots = [\n            os.path.join(results_dir, \"confusion_matrix.png\"),\n            os.path.join(results_dir, \"confusion_matrix_normalized.png\")\n        ]\n        valid_confusion = [p for p in confusion_plots if os.path.exists(p)]\n        if valid_confusion:\n            print(\"🎯 Model Performance:\")\n            show_images_grid(valid_confusion, \n                           titles=[\"Confusion Matrix\", \"Normalized Confusion Matrix\"],\n                           figsize=(12, 6))\n        \n        # 3. Show PR curves\n        curve_plots = [\n            os.path.join(results_dir, \"BoxPR_curve.png\"),\n            os.path.join(results_dir, \"BoxF1_curve.png\")\n        ]\n        valid_curves = [p for p in curve_plots if os.path.exists(p)]\n        if valid_curves:\n            print(\"📊 Performance Curves:\")\n            show_images_grid(valid_curves,\n                           titles=[\"Precision-Recall Curve\", \"F1 Score Curve\"],\n                           figsize=(12, 6))\n        \n        # 4. Show label distribution\n        labels_plot = os.path.join(results_dir, \"labels.jpg\")\n        if os.path.exists(labels_plot):\n            print(\"🏷️ Label Distribution:\")\n            show_image(labels_plot, \"Dataset Label Analysis\", figsize=(10, 6))\n        \n        # 5. Show training batch samples\n        print(\"🖼️ Training Batch Samples:\")\n        train_batches = [\n            os.path.join(results_dir, f\"train_batch{i}.jpg\") \n            for i in range(3) \n            if os.path.exists(os.path.join(results_dir, f\"train_batch{i}.jpg\"))\n        ]\n        if train_batches:\n            show_images_grid(train_batches[:2], \n                           titles=[f\"Training Batch {i}\" for i in range(len(train_batches[:2]))],\n                           figsize=(15, 8))\n        \n        # 6. Show validation predictions vs ground truth\n        print(\"✅ Validation Results (Predictions vs Ground Truth):\")\n        val_images = []\n        val_titles = []\n        \n        for i in range(2):  # Show first 2 validation batches\n            labels_path = os.path.join(results_dir, f\"val_batch{i}_labels.jpg\")\n            pred_path = os.path.join(results_dir, f\"val_batch{i}_pred.jpg\")\n            \n            if os.path.exists(labels_path):\n                val_images.append(labels_path)\n                val_titles.append(f\"Ground Truth Batch {i}\")\n            if os.path.exists(pred_path):\n                val_images.append(pred_path)\n                val_titles.append(f\"Predictions Batch {i}\")\n        \n        if val_images:\n            show_images_grid(val_images, titles=val_titles, figsize=(15, 12), cols=2)\n        \n        return os.path.join(results_dir, \"weights\", \"best.pt\")\n        \n    except Exception as e:\n        print(f\"❌ Training failed: {e}\")\n        return \"yolov8n.pt\"\n\n# Train and visualize\nif config_file:\n    model_path = train_and_visualize_model(config_file)\n    print(f\"🎯 Model ready at: {model_path}\")\nelse:\n    model_path = \"yolov8n.pt\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T12:00:22.526220Z","iopub.execute_input":"2025-08-22T12:00:22.526940Z","iopub.status.idle":"2025-08-22T12:20:58.351677Z","shell.execute_reply.started":"2025-08-22T12:00:22.526914Z","shell.execute_reply":"2025-08-22T12:20:58.350775Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\ndef make_predictions_with_visualization(model_path, test_folder):\n    \"\"\"Generate predictions and show sample results\"\"\"\n    \n    print(\"🔮 Making predictions...\")\n    \n    if not os.path.exists(test_folder):\n        print(f\"❌ Test folder not found: {test_folder}\")\n        return []\n    \n    # Load model\n    model = YOLO(model_path)\n    \n    predictions = []\n    test_images = [f for f in os.listdir(test_folder) \n                   if f.lower().endswith(('.jpg', '.jpeg', '.png'))]\n    \n    print(f\"📊 Predicting on {len(test_images)} images...\")\n    \n    # Store some sample predictions to visualize\n    sample_predictions = []\n    \n    for i, image_file in enumerate(test_images):\n        if i % 50 == 0:\n            print(f\"  Processed {i}/{len(test_images)} images\")\n        \n        try:\n            image_path = os.path.join(test_folder, image_file)\n            image_id = os.path.splitext(image_file)[0]\n            \n            # Run prediction\n            results = model.predict(image_path, conf=0.25, verbose=False)\n            \n            # Store first few for visualization\n            if len(sample_predictions) < 4:\n                sample_predictions.append((image_path, results))\n            \n            # Format predictions\n            pred_parts = []\n            \n            for result in results:\n                if result.boxes is not None and len(result.boxes) > 0:\n                    boxes = result.boxes\n                    for j in range(len(boxes)):\n                        cls = int(boxes.cls[j])\n                        conf = float(boxes.conf[j])\n                        x_center, y_center, width, height = boxes.xywhn[j]\n                        \n                        pred_parts.append(f\"{cls} {conf:.6f} {float(x_center):.6f} {float(y_center):.6f} {float(width):.6f} {float(height):.6f}\")\n            \n            pred_string = \" \".join(pred_parts)\n            predictions.append([image_id, pred_string])\n            \n        except Exception as e:\n            print(f\"⚠️ Error processing {image_file}: {e}\")\n            image_id = os.path.splitext(image_file)[0]\n            predictions.append([image_id, \"\"])\n    \n    # Visualize sample predictions\n    print(\"\\n🎯 Sample Predictions on Test Images:\")\n    print(\"=\"*50)\n    \n    if sample_predictions:\n        fig, axes = plt.subplots(2, 2, figsize=(15, 12))\n        axes = axes.flatten()\n        \n        for idx, (image_path, results) in enumerate(sample_predictions):\n            if idx >= 4:\n                break\n                \n            # Load and display image\n            img = cv2.imread(image_path)\n            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n            \n            # Draw predictions\n            if results[0].boxes is not None:\n                for box in results[0].boxes:\n                    # Get box coordinates (xyxy format)\n                    x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()\n                    conf = box.conf[0].cpu().numpy()\n                    cls = int(box.cls[0].cpu().numpy())\n                    \n                    # Draw rectangle\n                    cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)\n                    \n                    # Add label\n                    class_names = ['cheerios', 'soup']\n                    label = f\"{class_names[cls]}: {conf:.2f}\"\n                    cv2.putText(img, label, (int(x1), int(y1-10)), \n                               cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)\n            \n            axes[idx].imshow(img)\n            axes[idx].set_title(f\"Prediction {idx+1}: {os.path.basename(image_path)}\")\n            axes[idx].axis('off')\n        \n        plt.tight_layout()\n        plt.show()\n    \n    print(\"✅ Predictions complete!\")\n    return predictions\n\n# Make predictions with visualization\nif os.path.exists(test_data_path):\n    predictions = make_predictions_with_visualization(model_path, test_data_path)\n    print(f\"📊 Made predictions for {len(predictions)} images\")\nelse:\n    print(\"❌ Can't find test images\")\n    predictions = []\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T12:31:55.978809Z","iopub.execute_input":"2025-08-22T12:31:55.979154Z","iopub.status.idle":"2025-08-22T12:34:05.863402Z","shell.execute_reply.started":"2025-08-22T12:31:55.979118Z","shell.execute_reply":"2025-08-22T12:34:05.862422Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_and_analyze_submission(predictions, filename=\"submission.csv\"):\n    \"\"\"Create submission and show analysis\"\"\"\n    \n    if not predictions:\n        print(\"❌ No predictions to create submission\")\n        return None\n    \n    df = pd.DataFrame(predictions, columns=[\"image_id\", \"prediction_string\"])\n    df.to_csv(filename, index=False)\n    \n    print(f\"📝 Submission saved as: {filename}\")\n    print(f\"Shape: {df.shape}\")\n    \n    # Analyze submission\n    non_empty = df[df['prediction_string'] != ''].shape[0]\n    empty = df[df['prediction_string'] == ''].shape[0]\n    \n    print(f\"\\n📊 Submission Analysis:\")\n    print(f\"   Images with detections: {non_empty}\")\n    print(f\"   Images with no detections: {empty}\")\n    print(f\"   Detection rate: {non_empty/len(df)*100:.1f}%\")\n    \n    # Show distribution of predictions\n    pred_lengths = df['prediction_string'].str.split().str.len().fillna(0)\n    \n    plt.figure(figsize=(10, 6))\n    plt.subplot(1, 2, 1)\n    plt.hist(pred_lengths, bins=20, alpha=0.7)\n    plt.title('Distribution of Predictions per Image')\n    plt.xlabel('Number of Detections')\n    plt.ylabel('Count')\n    \n    plt.subplot(1, 2, 2)\n    detection_status = ['No Detections', 'Has Detections']\n    detection_counts = [empty, non_empty]\n    plt.pie(detection_counts, labels=detection_status, autopct='%1.1f%%')\n    plt.title('Detection Coverage')\n    \n    plt.tight_layout()\n    plt.show()\n    \n    # Show first few predictions\n    print(\"\\nFirst few predictions:\")\n    display(df.head())\n    \n    return df\n\nif predictions:\n    submission_df = create_and_analyze_submission(predictions)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-22T12:35:51.459832Z","iopub.execute_input":"2025-08-22T12:35:51.460502Z","iopub.status.idle":"2025-08-22T12:35:51.752131Z","shell.execute_reply.started":"2025-08-22T12:35:51.460472Z","shell.execute_reply":"2025-08-22T12:35:51.751469Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}