{"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":13836,"databundleVersionId":1718836,"sourceType":"competition"},{"sourceId":14169030,"sourceType":"datasetVersion","datasetId":9031649},{"sourceId":14169174,"sourceType":"datasetVersion","datasetId":9031722}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import timm\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:02.224690Z","iopub.execute_input":"2025-12-18T03:23:02.225119Z","iopub.status.idle":"2025-12-18T03:23:15.667552Z","shell.execute_reply.started":"2025-12-18T03:23:02.225092Z","shell.execute_reply":"2025-12-18T03:23:15.666895Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create 5 Stratified Folds (siempre los mismos)\nNUM_FOLDS = 5\n\n# ====================================================\n# Elegir un fold (del 0 al 4)\n# ====================================================\nFOLD = 3  #\nEPOCHS = 15 # esto no lo muevan!\nIMG_SIZE = 512\nMODEL_TINY = 'convnext_tiny'\nMODEL_DENSENET169 = 'densenet169'\nMODEL_EFFICIENTNET_B3 = 'efficientnet_b3'\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:15.668953Z","iopub.execute_input":"2025-12-18T03:23:15.669196Z","iopub.status.idle":"2025-12-18T03:23:15.673409Z","shell.execute_reply.started":"2025-12-18T03:23:15.669177Z","shell.execute_reply":"2025-12-18T03:23:15.672704Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# Set All Seeds for Reproducibility\n# ====================================================\n\nimport random\nimport numpy as np\nimport torch\n\n\ndef set_seed(seed=42):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    #For deterministic behavior (slightly slower but reproducible)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n\nset_seed(42)\n\ndef seed_worker(worker_id):\n    worker_seed = torch.initial_seed() % 2**32\n    np.random.seed(worker_seed)\n    random.seed(worker_seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:15.674158Z","iopub.execute_input":"2025-12-18T03:23:15.674415Z","iopub.status.idle":"2025-12-18T03:23:15.701490Z","shell.execute_reply.started":"2025-12-18T03:23:15.674393Z","shell.execute_reply":"2025-12-18T03:23:15.700869Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# Cassava Leaf Disease Detection using EfficientNet B3\n# 80:20 Train-Validation Split\n# ====================================================\n\nimport os\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom sklearn.model_selection import StratifiedKFold\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport timm\n\n# ====================================================\n# Step 1: Load Dataset\n# ====================================================\n\n# Load CSV\n\n# Load CSV\ndf = pd.read_csv('/kaggle/input/cassava-leaf-disease-classification/train.csv')\ndf['filepath'] = df['image_id'].apply(\n    lambda x: os.path.join('/kaggle/input/cassava-leaf-disease-classification/train_images', x)\n)\n\n\ndf[\"fold\"] = -1\n\nskf = StratifiedKFold(n_splits=NUM_FOLDS, shuffle=True, random_state=42)\nfor fold, (_, val_idx) in enumerate(skf.split(df, df['label'])):\n    df.loc[val_idx, 'fold'] = fold\n\nprint(df['fold'].value_counts())\n\n\ntrain_df = df[df['fold'] != FOLD].reset_index(drop=True)\nval_df   = df[df['fold'] == FOLD].reset_index(drop=True)\n\nprint(f\"Using Fold {FOLD}\")\nprint(\"Train samples:\", len(train_df), \"Validation samples:\", len(val_df))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:15.702327Z","iopub.execute_input":"2025-12-18T03:23:15.702591Z","iopub.status.idle":"2025-12-18T03:23:16.605473Z","shell.execute_reply.started":"2025-12-18T03:23:15.702567Z","shell.execute_reply":"2025-12-18T03:23:16.604727Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# Step 2: Data Transforms & Dataset Class\n# ====================================================\n\n\n\ntrain_transform = transforms.Compose([\n    transforms.RandomResizedCrop(IMG_SIZE),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(20),\n    transforms.ColorJitter(\n        brightness=0.2,     \n        contrast=0.2,       \n        saturation=0.2,     \n        hue=0.1\n    ),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406],\n                         [0.229, 0.224, 0.225])\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406],\n                         [0.229, 0.224, 0.225])\n])\n\nclass CassavaDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df.reset_index(drop=True)\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        path = self.df.loc[idx, 'filepath']\n        label = self.df.loc[idx, 'label']\n        image = Image.open(path).convert(\"RGB\")\n        if self.transform:\n            image = self.transform(image)\n        return image, label\n\n# Dataloaders\ntrain_ds = CassavaDataset(train_df, train_transform)\nval_ds = CassavaDataset(val_df, val_transform)\n\ntrain_loader = DataLoader(train_ds, batch_size=32, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_ds, batch_size=32, shuffle=False, num_workers=2)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:16.607392Z","iopub.execute_input":"2025-12-18T03:23:16.607704Z","iopub.status.idle":"2025-12-18T03:23:16.617238Z","shell.execute_reply.started":"2025-12-18T03:23:16.607659Z","shell.execute_reply":"2025-12-18T03:23:16.616467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:16.618146Z","iopub.execute_input":"2025-12-18T03:23:16.618466Z","iopub.status.idle":"2025-12-18T03:23:16.719479Z","shell.execute_reply.started":"2025-12-18T03:23:16.618446Z","shell.execute_reply":"2025-12-18T03:23:16.718636Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_tiny_loaded_model(dcnn_model:str, fl=False):\n    model = timm.create_model(dcnn_model, pretrained=True)\n    in_features = model.head.fc.in_features\n    model.head.fc = nn.Sequential(\n        nn.Linear(in_features, 512),\n        nn.ReLU(),\n        nn.Dropout(0.2),\n        nn.Linear(512, NUM_CLASSES)\n    )\n    if torch.cuda.device_count() > 1:\n        model = nn.DataParallel(model)\n\n    model = model.to(device)\n    if fl:\n        model.load_state_dict(torch.load(f\"/kaggle/input/cassava-fl/FL/best_{dcnn_model}_{FOLD}.pth\"))\n    else:\n        model.load_state_dict(torch.load(f\"/kaggle/input/cassava-cw/CW/best_{dcnn_model}_{FOLD}.pth\"))\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:16.720525Z","iopub.execute_input":"2025-12-18T03:23:16.720854Z","iopub.status.idle":"2025-12-18T03:23:16.736407Z","shell.execute_reply.started":"2025-12-18T03:23:16.720827Z","shell.execute_reply":"2025-12-18T03:23:16.735630Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_loaded_model(dcnn_model:str, fl=False):\n    model = timm.create_model(dcnn_model, pretrained=True)\n    model.classifier = nn.Sequential(\n        nn.Linear(model.classifier.in_features, 512),\n        nn.ReLU(),\n        nn.Dropout(0.2),\n        nn.Linear(512, NUM_CLASSES)\n    )\n    if torch.cuda.device_count() > 1:\n        model = nn.DataParallel(model)\n    model = model.to(device)\n    if fl:\n        model.load_state_dict(torch.load(f\"/kaggle/input/cassava-fl/FL/best_{dcnn_model}_{FOLD}.pth\"))\n    else:\n        model.load_state_dict(torch.load(f\"/kaggle/input/cassava-cw/CW/best_{dcnn_model}_{FOLD}.pth\"))\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:16.737324Z","iopub.execute_input":"2025-12-18T03:23:16.737631Z","iopub.status.idle":"2025-12-18T03:23:16.756170Z","shell.execute_reply.started":"2025-12-18T03:23:16.737603Z","shell.execute_reply":"2025-12-18T03:23:16.755485Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"NUM_CLASSES = df['label'].nunique()\nmodel_tyny_fl = create_tiny_loaded_model(MODEL_TINY, fl=True)\nmodel_tyny_cw = create_tiny_loaded_model(MODEL_TINY)\n\nmodel_densenet169_fl = create_loaded_model(MODEL_DENSENET169, fl=True)\nmodel_densenet169_cw = create_loaded_model(MODEL_DENSENET169)\n\nmodel_efficientnetb3_fl = create_loaded_model(MODEL_EFFICIENTNET_B3, fl=True)\nmodel_efficientnetb3_cw = create_loaded_model(MODEL_EFFICIENTNET_B3)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:16.756989Z","iopub.execute_input":"2025-12-18T03:23:16.757265Z","iopub.status.idle":"2025-12-18T03:23:28.797638Z","shell.execute_reply.started":"2025-12-18T03:23:16.757238Z","shell.execute_reply":"2025-12-18T03:23:28.797049Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ModelResults:\n    def __init__(self, n_classes, all_preds, all_labels, all_probs):\n        self.n_classes = n_classes\n        self.all_preds = all_preds\n        self.all_labels = all_labels\n        self.all_probs = all_probs\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:28.798617Z","iopub.execute_input":"2025-12-18T03:23:28.798931Z","iopub.status.idle":"2025-12-18T03:23:28.803003Z","shell.execute_reply.started":"2025-12-18T03:23:28.798913Z","shell.execute_reply":"2025-12-18T03:23:28.802262Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport numpy as np\nfrom tqdm import tqdm\nimport albumentations as A\n\ntta_albu = [\n    A.Compose([]),\n    A.Compose([A.HorizontalFlip(p=1)]),\n    A.Compose([A.VerticalFlip(p=1)]),\n    A.Compose([A.Rotate(limit=(90, 90), p=1)]),\n]\n\ndef apply_albu_tta(images, tta):\n    imgs_np = images.permute(0, 2, 3, 1).cpu().numpy()\n    imgs_aug = [tta(image=img.astype(np.float32))[\"image\"] for img in imgs_np]\n    imgs_aug = np.stack(imgs_aug)\n    imgs_aug = torch.from_numpy(imgs_aug).permute(0, 3, 1, 2).float()\n    return imgs_aug.to(images.device)\n\ntta_transforms = [lambda x, t=tta: apply_albu_tta(x, t) for tta in tta_albu]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:28.803854Z","iopub.execute_input":"2025-12-18T03:23:28.804460Z","iopub.status.idle":"2025-12-18T03:23:29.560649Z","shell.execute_reply.started":"2025-12-18T03:23:28.804442Z","shell.execute_reply":"2025-12-18T03:23:29.560096Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# Step 8: Confusion Matrix + ROC-AUC + Metrics per Class\n# ====================================================\nfrom sklearn.metrics import (\n    confusion_matrix, roc_curve, auc, precision_score, recall_score, f1_score,\n    classification_report\n)\nfrom sklearn.preprocessing import label_binarize\nimport seaborn as sns\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom itertools import cycle\nimport pandas as pd\n\ndef evaluate_model(model, tta_transforms=None):\n    \"\"\"\n    Evalúa un modelo con Test-Time Augmentation (TTA) opcional usando autocast (mixed precision).\n    \"\"\"\n    model.eval()\n    model = model.module if hasattr(model, \"module\") else model\n    #if torch.cuda.device_count() > 1:\n    #    model = torch.nn.DataParallel(model)\n\n    model = model.to(device)\n\n    all_preds, all_labels, all_probs = [], [], []\n\n    # Usa autocast para acelerar operaciones en GPU\n    with torch.no_grad():\n        for images, labels in tqdm(val_loader, desc=\"Evaluating (with autocast)\"):\n            images = images.to(device, non_blocking=True)\n\n            # --- Aplica TTA ---\n            if tta_transforms is not None:\n                probs_tta = []\n                for t in tta_transforms:\n                    imgs_aug = t(images)\n                    with torch.cuda.amp.autocast():\n                        outputs = model(imgs_aug)\n                        probs = torch.softmax(outputs, dim=1)\n                    probs_tta.append(probs)\n                probs_mean = torch.stack(probs_tta).mean(dim=0)\n            else:\n                with torch.cuda.amp.autocast():\n                    outputs = model(images)\n                    probs_mean = torch.softmax(outputs, dim=1)\n\n            preds = probs_mean.argmax(1).cpu().numpy()\n            all_probs.extend(probs_mean.cpu().numpy())\n            all_preds.extend(preds)\n            all_labels.extend(labels.numpy())\n\n    all_labels = np.array(all_labels)\n    all_preds = np.array(all_preds)\n    all_probs = np.array(all_probs)\n    n_classes = len(np.unique(all_labels))\n\n    return ModelResults(n_classes, all_preds, all_labels, all_probs)\n\n\ndef generate_overall_metrics(results):\n    cm = confusion_matrix(results.all_labels, results.all_preds)\n    acc = np.trace(cm) / np.sum(cm)\n    \n    plt.figure(figsize=(8,6))\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')\n    plt.title(f'Confusion Matrix (Fold {FOLD})\\nAccuracy: {acc:.4f}')\n    plt.xlabel('Predicted Label')\n    plt.ylabel('True Label')\n    plt.show()\n    \n    precisions = precision_score(results.all_labels, results.all_preds, average=None, zero_division=0)\n    recalls = recall_score(results.all_labels, results.all_preds, average=None, zero_division=0)\n    f1s = f1_score(results.all_labels, results.all_preds, average=None, zero_division=0)\n    accuracies = cm.diagonal() / cm.sum(axis=1)\n    \n    metrics_df = pd.DataFrame({\n        \"Class\": np.arange(results.n_classes),\n        \"Precision\": precisions,\n        \"Recall\": recalls,\n        \"F1-Score\": f1s,\n        \"Accuracy\": accuracies,\n    })\n    \n    display(metrics_df.style.background_gradient(cmap=\"Blues\", subset=[\"Precision\",\"Recall\",\"F1-Score\",\"Accuracy\"])\n                            .format({\"Precision\": \"{:.3f}\", \"Recall\": \"{:.3f}\", \"F1-Score\": \"{:.3f}\", \"Accuracy\": \"{:.3f}\"}))\n    \n    # ====================================================\n    # 4️⃣ Report global averages\n    # ====================================================\n    print(\"\\nOverall Metrics:\")\n    print(classification_report(results.all_labels, results.all_preds, digits=3))\n    print(f\"Global Accuracy: {acc:.3f}\")\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:29.561439Z","iopub.execute_input":"2025-12-18T03:23:29.561631Z","iopub.status.idle":"2025-12-18T03:23:29.787500Z","shell.execute_reply.started":"2025-12-18T03:23:29.561615Z","shell.execute_reply":"2025-12-18T03:23:29.786866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"results_tiny = evaluate_model(model_tyny, tta_transforms=tta_transforms)\nresults_densenet169 = evaluate_model(model_densenet169, tta_transforms=tta_transforms) \nresults_densenet201 = evaluate_model(model_densenet201, tta_transforms=tta_transforms) \nresults_efficientnet_b3 = evaluate_model(model_efficientnet_b3, tta_transforms=tta_transforms) \nresults_efficientnet_b4 = evaluate_model(model_efficientnet_b4, tta_transforms=tta_transforms) \"\"\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:29.788370Z","iopub.execute_input":"2025-12-18T03:23:29.788852Z","iopub.status.idle":"2025-12-18T03:23:29.794448Z","shell.execute_reply.started":"2025-12-18T03:23:29.788825Z","shell.execute_reply":"2025-12-18T03:23:29.793916Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results_tiny_fl = evaluate_model(model_tyny_fl)\nresults_tiny_cw = evaluate_model(model_tyny_cw)\nresults_densenet169_fl = evaluate_model(model_densenet169_fl) \nresults_densenet169_cw = evaluate_model(model_densenet169_cw) \nresults_efficientnetb3_fl = evaluate_model(model_efficientnetb3_fl) \nresults_efficientnetb3_cw = evaluate_model(model_efficientnetb3_cw) \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:23:29.797156Z","iopub.execute_input":"2025-12-18T03:23:29.797383Z","iopub.status.idle":"2025-12-18T03:28:06.589483Z","shell.execute_reply.started":"2025-12-18T03:23:29.797365Z","shell.execute_reply":"2025-12-18T03:28:06.588520Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"results_tiny_cw\")\ngenerate_overall_metrics(results_tiny_cw)\nprint(\"results_tiny_fl\")\ngenerate_overall_metrics(results_tiny_fl)\nprint(\"results_densenet169_cw\")\ngenerate_overall_metrics(results_densenet169_cw)\nprint(\"results_densenet169_fl\")\ngenerate_overall_metrics(results_densenet169_fl)\nprint(\"results_efficientnetb3_cw\")\ngenerate_overall_metrics(results_efficientnetb3_cw)\nprint(\"results_efficientnetb3_fl\")\ngenerate_overall_metrics(results_efficientnetb3_fl)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:06.590836Z","iopub.execute_input":"2025-12-18T03:28:06.591229Z","iopub.status.idle":"2025-12-18T03:28:08.677023Z","shell.execute_reply.started":"2025-12-18T03:28:06.591192Z","shell.execute_reply":"2025-12-18T03:28:08.676210Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"generate_overall_metrics(results_tiny_no_tta)\ngenerate_overall_metrics(results_densenet169_no_tta)\ngenerate_overall_metrics(results_densenet201_no_tta)\ngenerate_overall_metrics(results_efficientnet_b3_no_tta)\ngenerate_overall_metrics(results_efficientnet_b4_no_tta)\"\"\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:08.677867Z","iopub.execute_input":"2025-12-18T03:28:08.678388Z","iopub.status.idle":"2025-12-18T03:28:08.683452Z","shell.execute_reply.started":"2025-12-18T03:28:08.678359Z","shell.execute_reply":"2025-12-18T03:28:08.682795Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"all_results = [\n#    results_tiny_cw, \n    results_tiny_fl, \n    results_densenet169_cw, \n    results_densenet169_fl, \n#    results_efficientnetb3_cw, \n    results_efficientnetb3_fl, \n]\n\n# Ensamble simple: promedio de probabilidades\nall_probs = np.array([r.all_probs for r in all_results])  # shape = [n_models, n_samples, n_classes]\nensemble_probs = np.mean(all_probs, axis=0)               # promedio a través de modelos\nensemble_preds = np.argmax(ensemble_probs, axis=1)\n\n# Evaluación final\nensemble_results = ModelResults(\n    n_classes=results_tiny_cw.n_classes,\n    all_preds=ensemble_preds,\n    all_labels=results_tiny_cw.all_labels,  # todas comparten las mismas labels\n    all_probs=ensemble_probs\n)\n\ngenerate_overall_metrics(ensemble_results)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:08.684125Z","iopub.execute_input":"2025-12-18T03:28:08.684375Z","iopub.status.idle":"2025-12-18T03:28:08.954379Z","shell.execute_reply.started":"2025-12-18T03:28:08.684358Z","shell.execute_reply":"2025-12-18T03:28:08.953449Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"all_results_no_tta = [\n    results_tiny_no_tta, \n    results_densenet169_no_tta, \n    results_densenet201_no_tta, \n    results_efficientnet_b3_no_tta, \n    results_efficientnet_b4_no_tta\n]\n\n# Ensamble simple: promedio de probabilidades\nall_probs = np.array([r.all_probs for r in all_results_no_tta])  # shape = [n_models, n_samples, n_classes]\nensemble_probs = np.mean(all_probs, axis=0)               # promedio a través de modelos\nensemble_preds = np.argmax(ensemble_probs, axis=1)\n\n# Evaluación final\nensemble_results_no_tta = ModelResults(\n    n_classes=results_tiny_no_tta.n_classes,\n    all_preds=ensemble_preds,\n    all_labels=results_tiny_no_tta.all_labels,  # todas comparten las mismas labels\n    all_probs=ensemble_probs\n)\n\ngenerate_overall_metrics(ensemble_results_no_tta)\"\"\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:08.955341Z","iopub.execute_input":"2025-12-18T03:28:08.955772Z","iopub.status.idle":"2025-12-18T03:28:08.960903Z","shell.execute_reply.started":"2025-12-18T03:28:08.955742Z","shell.execute_reply":"2025-12-18T03:28:08.960220Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.linear_model import LogisticRegression\nimport numpy as np\n\ndef stacking_weights_by_class(all_results, y_true):\n    \"\"\"\n    Calcula pesos por modelo (por clase) usando un meta-modelo logístico.\n    Retorna:\n      - w_mean: vector de pesos promedio global por modelo\n      - W: matriz completa de pesos [n_models, n_classes]\n    \"\"\"\n    # all_results: lista de ModelResults (cada uno con .all_probs)\n    # y_true: etiquetas verdaderas\n    \n    P = np.array([r.all_probs for r in all_results])  # [n_models, n_samples, n_classes]\n    n_models, n_samples, n_classes = P.shape\n    \n    W = np.zeros((n_models, n_classes))  # pesos modelo x clase\n\n    for c in range(n_classes):\n        # Probabilidades de todos los modelos para la clase c\n        X = P[:, :, c].T  # [n_samples, n_models]\n        y = (y_true == c).astype(int)  # etiquetas binarias: 1 si clase c, 0 si no\n        \n        meta = LogisticRegression(max_iter=1000)\n        meta.fit(X, y)\n        \n        # Guardar pesos absolutos normalizados por clase\n        w_c = np.abs(meta.coef_)[0]\n        w_c /= np.sum(w_c)\n        W[:, c] = w_c\n\n    # Promedio global de los pesos sobre todas las clases\n    w_mean = W.mean(axis=1)\n    w_mean /= np.sum(w_mean)\n    \n    return np.round(w_mean, 4), np.round(W, 4)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:08.961569Z","iopub.execute_input":"2025-12-18T03:28:08.961776Z","iopub.status.idle":"2025-12-18T03:28:09.082062Z","shell.execute_reply.started":"2025-12-18T03:28:08.961759Z","shell.execute_reply":"2025-12-18T03:28:09.081193Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_true = results_tiny_cw.all_labels\nw_mean, W =  stacking_weights_by_class(all_results, y_true)\nprint(\"📊 Pesos globales por modelo:\")\nprint(w_mean)\nprint(\"✅ Suma de pesos:\", w_mean.sum())\n\nprint(\"\\n📈 Matriz de pesos por clase (modelo x clase):\")\nprint(W)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:09.083023Z","iopub.execute_input":"2025-12-18T03:28:09.083240Z","iopub.status.idle":"2025-12-18T03:28:09.583325Z","shell.execute_reply.started":"2025-12-18T03:28:09.083225Z","shell.execute_reply":"2025-12-18T03:28:09.582698Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"all_probs = np.array([r.all_probs for r in all_results])\nensemble_probs = np.tensordot(w_mean, all_probs, axes=(0, 0))\nensemble_preds = np.argmax(ensemble_probs, axis=1)\n\nensemble_results = ModelResults(\n    n_classes=all_results[0].n_classes,\n    all_preds=ensemble_preds,\n    all_labels=y_true,\n    all_probs=ensemble_probs\n)\n\ngenerate_overall_metrics(ensemble_results)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:09.583947Z","iopub.execute_input":"2025-12-18T03:28:09.584193Z","iopub.status.idle":"2025-12-18T03:28:09.890928Z","shell.execute_reply.started":"2025-12-18T03:28:09.584171Z","shell.execute_reply":"2025-12-18T03:28:09.890157Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"y_true = results_tiny_no_tta.all_labels\nw_mean, W =  stacking_weights_by_class(all_results_no_tta, y_true)\nprint(\"📊 Pesos globales por modelo:\")\nprint(w_mean)\nprint(\"✅ Suma de pesos:\", w_mean.sum())\n\nprint(\"\\n📈 Matriz de pesos por clase (modelo x clase):\")\nprint(W)\n\"\"\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:09.891851Z","iopub.execute_input":"2025-12-18T03:28:09.892162Z","iopub.status.idle":"2025-12-18T03:28:09.897740Z","shell.execute_reply.started":"2025-12-18T03:28:09.892138Z","shell.execute_reply":"2025-12-18T03:28:09.896961Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"all_probs = np.array([r.all_probs for r in all_results_no_tta])\nensemble_probs = np.tensordot(w_mean, all_probs, axes=(0, 0))\nensemble_preds = np.argmax(ensemble_probs, axis=1)\n\nensemble_results = ModelResults(\n    n_classes=all_results_no_tta[0].n_classes,\n    all_preds=ensemble_preds,\n    all_labels=y_true,\n    all_probs=ensemble_probs\n)\n\ngenerate_overall_metrics(ensemble_results)\"\"\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T03:28:09.898604Z","iopub.execute_input":"2025-12-18T03:28:09.898964Z","iopub.status.idle":"2025-12-18T03:28:09.916130Z","shell.execute_reply.started":"2025-12-18T03:28:09.898929Z","shell.execute_reply":"2025-12-18T03:28:09.915441Z"}},"outputs":[],"execution_count":null}]}