{"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":117682,"databundleVersionId":14443416,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":13812215,"sourceType":"datasetVersion","datasetId":8794891},{"sourceId":658188,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":487686,"modelId":503106}],"dockerImageVersionId":31192,"isInternetEnabled":false,"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-11-23T20:14:22.964060Z","iopub.execute_input":"2025-11-23T20:14:22.964349Z","iopub.status.idle":"2025-11-23T20:14:26.036986Z","shell.execute_reply.started":"2025-11-23T20:14:22.964331Z","shell.execute_reply":"2025-11-23T20:14:26.036114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile inference_module.py\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport numpy as np\nimport tifffile as tiff # <--- Debe importar imagecodecs para funcionar\nimport os\nimport subprocess\nimport sys\nfrom typing import List, Tuple\n\n# --- I. ARQUITECTURAS DEL PIPELINE DE CODIFICACIÓN ---\nD_SIZE = 128\nHIDDEN_SIZE = 128\nVOXEL_DEPTH = 64\nPATCH_SIZE_2D = 64 \n\nclass VoxelEncoder3D(nn.Module):\n    def __init__(self, voxel_depth, d_size):\n        super(VoxelEncoder3D, self).__init__()\n        self.encoder = nn.Sequential(\n            nn.Conv3d(1, 16, kernel_size=3, padding=1), nn.LeakyReLU(), nn.MaxPool3d(2),\n            nn.Conv3d(16, 32, kernel_size=3, padding=1), nn.LeakyReLU(), nn.MaxPool3d(2),\n        )\n        linear_in_features = 32 * (voxel_depth//4) * (PATCH_SIZE_2D//4) * (PATCH_SIZE_2D//4)\n        if voxel_depth == 64 and PATCH_SIZE_2D == 64:\n             linear_in_features = 131072 \n        \n        self.compress = nn.Linear(linear_in_features, d_size)\n        \n    def forward(self, x):\n        x = self.encoder(x)\n        x = x.view(x.size(0), -1)\n        embedding = self.compress(x)\n        embedding = torch.tanh(embedding)\n        return embedding\n\nclass MaskDecoder2D(nn.Module):\n    def __init__(self, d_in, h_out, w_out):\n        super(MaskDecoder2D, self).__init__()\n        self.linear_upscale = nn.Linear(d_in, 32 * 16 * 16) \n        \n        self.decoder = nn.Sequential(\n            nn.ConvTranspose2d(32, 16, kernel_size=4, stride=2, padding=1),\n            nn.LeakyReLU(),\n            nn.ConvTranspose2d(16, 1, kernel_size=4, stride=2, padding=1),\n        )\n\n    def forward(self, x):\n        if x.dim() == 1: x = x.unsqueeze(0)\n        x = self.linear_upscale(x)\n        x = x.view(x.size(0), 32, 16, 16) \n        output_mask = self.decoder(x)\n        return output_mask.squeeze(1)\n\n# --- II. ARQUITECTURA DIALÉCTICA ---\nclass AttentionDialecticalLayer(nn.Module):\n    def __init__(self, d_model, top_k_contexts=2):\n        super(AttentionDialecticalLayer, self).__init__()\n        self.d_model = d_model\n        self.top_k = top_k_contexts \n        self.to_qk = nn.Linear(d_model, d_model * 2) \n        self.to_v = nn.Linear(d_model, d_model)\n        \n    def forward(self, subject_embedding, batch_embeddings):\n        q = self.to_qk(subject_embedding.unsqueeze(0)).chunk(2, dim=-1)[0]\n        k, v = self.to_qk(batch_embeddings).chunk(2, dim=-1)\n        \n        scores = torch.matmul(q, k.transpose(0, 1)) / (self.d_model ** 0.5) \n        \n        k_val = min(self.top_k, k.size(0))\n        topk_scores, topk_indices = torch.topk(scores, k=k_val, dim=-1)\n        mask = torch.zeros_like(scores, dtype=torch.bool).scatter_(-1, topk_indices, True)\n        masked_scores = scores.masked_fill(~mask, float('-inf'))\n        \n        attn_weights = F.softmax(masked_scores, dim=-1)\n        \n        context_vector = torch.matmul(attn_weights, v)\n        return context_vector.squeeze(0)\n\nclass DialecticalNet(nn.Module):\n    def __init__(self, d_in, d_hidden, patch_size_2d):\n        super(DialecticalNet, self).__init__()\n        self.f_dialectical = nn.Sequential(\n            nn.Linear(d_in, d_hidden),\n            nn.ReLU(),\n            nn.Linear(d_hidden, d_hidden)\n        )\n        self.f_utility = MaskDecoder2D(d_hidden, patch_size_2d, patch_size_2d)\n        self.f_synthesis = nn.Linear(d_hidden, d_in)\n        self.attention_layer = AttentionDialecticalLayer(d_in, top_k_contexts=2)\n\n    def forward(self, C_curr, batch_embeddings):\n        C_curr_event = self.attention_layer(C_curr, batch_embeddings)\n        C_analysis_input = C_curr + C_curr_event\n        C_molded = self.f_dialectical(C_analysis_input)\n        prediction = self.f_utility(C_molded)\n        V_syn = self.f_synthesis(C_molded)\n        V_syn = torch.tanh(V_syn)\n        return C_molded, prediction, V_syn\n\n# --- LÓGICA DE INSTALACIÓN FINAL (Ruta fija) ---\ndef install_offline_dependencies(whl_full_path: str, package_name: str):\n    \"\"\"Instala el paquete directamente desde la ruta completa del archivo .whl.\"\"\"\n    print(f\"⌛ Instalando la dependencia {package_name} directamente...\")\n\n    if not os.path.exists(whl_full_path):\n        print(f\"🚨 ERROR CRÍTICO: El archivo .whl no existe en la ruta: {whl_full_path}\")\n        raise FileNotFoundError(\"Ruta de .whl incorrecta o archivo faltante. Deteniendo la inferencia.\")\n\n    try:\n       \n        # Intento 3: Forzar la instalación con --user (o --break-system-packages si es necesario)\n        subprocess.run(\n            [sys.executable, \"-m\", \"pip\", \"install\", \"--no-deps\", \"--force-reinstall\", \"--user\", whl_full_path],\n            check=True,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            text=True\n        )\n        \n        # Importación forzada para asegurar que el módulo está registrado\n        import imagecodecs\n        # Intentar forzar la re-inicialización de tifffile para que vea los codecs\n        try:\n            # tifffile.tifffile.IMREAD_CODECS = tifffile.tifffile._load_codecs()\n            pass # Ya que reimportamos arriba, debería ser suficiente\n        except Exception:\n            pass\n        \n        print(f\"✅ {package_name} instalado y registrado con éxito desde la ruta fija.\")\n    except subprocess.CalledProcessError as e:\n        print(f\"🚨 ERROR CRÍTICO al instalar {package_name}. Incompatibilidad de Python/OS.\")\n        print(f\"Detalle del error: {e.stderr}\")\n        raise e\n    except ImportError as e:\n        print(f\"🚨 ADVERTENCIA: La instalación tuvo éxito, pero el módulo {package_name} no se registró.\")\n        raise Exception(f\"Error de registro de librería: {e}\")\n\n# --- 0. CONFIGURACIONES GLOBALES DE INFERENCIA ---\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nKAGGLE_DATA_PATH = '/kaggle/input/vesuvius-challenge-surface-detection'\nTEST_IMAGE_ID = '1407735'\n\n# 🚀 RUTA FINAL Y DEFINITIVA CONFIRMADA (Usando CP311) 🚀\nIMAGECODECS_WHL_FULL_PATH = '/kaggle/input/imagecodecs-2025/imagecodecs-2025.11.11-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl' \n\n# RUTA DE CARGA DEL MODELO \nMODEL_CHECKPOINT_DIR = '/kaggle/input/modelo-vesubio1/pytorch/default/2' \nCHECKPOINT_FILENAME = \"dialectical_net_checkpoint.pth\"\nCHECKPOINT_FULL_PATH = os.path.join(MODEL_CHECKPOINT_DIR, CHECKPOINT_FILENAME)\n\n\ndef load_checkpoint(filename):\n    \"\"\"Carga el estado del modelo DialecticalNet y el VoxelEncoder.\"\"\"\n    print(f\"⌛ Cargando pesos desde la ruta: {filename}...\")\n    try:\n        checkpoint = torch.load(filename, map_location=DEVICE)\n    except FileNotFoundError:\n        print(f\"\\n🚨 ERROR: No se encontró el archivo de checkpoint en la ruta: {filename}\")\n        raise\n    \n    voxel_encoder = VoxelEncoder3D(VOXEL_DEPTH, D_SIZE).to(DEVICE)\n    model_rnd = DialecticalNet(D_SIZE, HIDDEN_SIZE, PATCH_SIZE_2D).to(DEVICE)\n    \n    voxel_encoder.load_state_dict(checkpoint['voxel_encoder_state_dict'])\n    model_rnd.load_state_dict(checkpoint['model_state_dict'])\n    voxel_encoder.eval()\n    model_rnd.eval()\n    \n    print(f\"✅ Checkpoint cargado con éxito. Modelos listos para la inferencia.\")\n    return model_rnd, voxel_encoder\n\n\ndef create_submission_mask(model, encoder_3d, test_image_id, data_path, patch_size_2d, device):\n    \"\"\"Genera la máscara de predicción por parches.\"\"\"\n    test_volume_path = os.path.join(data_path, 'test_images', f'{test_image_id}.tif')\n    \n    print(f\"\\nComenzando inferencia con el volumen de prueba: {test_image_id}...\")\n    \n    try:\n        # Esto debería funcionar si la importación en install_offline_dependencies fue exitosa\n        full_voxel_volume = tiff.imread(test_volume_path) \n    except Exception as e:\n        print(f\"\\n🚨 ERROR: Falló la carga del TIF. Confirma que imagecodecs está instalado correctamente.\")\n        raise Exception(f\"Error de carga de TIF: {e}\")\n    \n    D, H, W = full_voxel_volume.shape\n    final_mask_volume = np.zeros((H, W), dtype=np.uint8)\n    \n    step = patch_size_2d\n    patch_coords = []\n    for y in range(0, H, step):\n        for x in range(0, W, step):\n            patch_coords.append((y, x))\n            \n    with torch.no_grad():\n        for y_start, x_start in patch_coords:\n            \n            y_end_slice = min(y_start + patch_size_2d, H)\n            x_end_slice = min(x_start + patch_size_2d, W)\n            \n            X_raw_patch_np = full_voxel_volume[\n                0:VOXEL_DEPTH,\n                y_start : y_end_slice,\n                x_start : x_end_slice\n            ]\n            \n            if X_raw_patch_np.shape[1] != patch_size_2d or X_raw_patch_np.shape[2] != patch_size_2d:\n                padding_h = patch_size_2d - X_raw_patch_np.shape[1]\n                padding_w = patch_size_2d - X_raw_patch_np.shape[2]\n                X_raw_patch_np = np.pad(X_raw_patch_np, ((0, 0), (0, padding_h), (0, padding_w)), mode='constant')\n                \n            X_raw_patch = torch.from_numpy(X_raw_patch_np).float().unsqueeze(0).unsqueeze(0).to(device)\n\n            X_curr = encoder_3d(X_raw_patch)\n            \n            # La inferencia no necesita el lote completo, solo el embedding actual\n            C_molded, mask_prediction, V_syn = model(X_curr.squeeze(0), X_curr) \n            \n            final_mask_patch = (mask_prediction.sigmoid() > 0.5).squeeze(0).cpu().numpy().astype(np.uint8)\n            y_end = y_end_slice\n            x_end = x_end_slice\n            mask_to_insert = final_mask_patch[:y_end-y_start, :x_end-x_start]\n            final_mask_volume[y_start:y_end, x_start:x_end] = mask_to_insert\n\n    OUTPUT_DIR = '/kaggle/working/'\n    output_filename_base = f\"submission_{test_image_id}.tif\"\n    output_full_path = os.path.join(OUTPUT_DIR, output_filename_base)\n    os.makedirs(OUTPUT_DIR, exist_ok=True)\n    \n    tiff.imwrite(output_full_path, final_mask_volume.astype(np.uint8))\n    print(f\"DEBUG: Archivo guardado en ruta: {output_full_path}\") # <--- ¡Nuevo DEBUG!\n    return output_full_path # Retorna la ruta completa\n    \n\n    return output_filename\n\n# --- BLOQUE PRINCIPAL DE EJECUCIÓN (Arrancador) ---\nif __name__ == '__main__':\n    \n    print(\"--- INICIANDO MÓDULO DE INFERENCIA INDEPENDIENTE ---\")\n    \n    # 1. INSTALACIÓN DE DEPENDENCIAS OFFLINE\n    try:\n        install_offline_dependencies(IMAGECODECS_WHL_FULL_PATH, 'imagecodecs')\n    except Exception as e:\n        print(f\"❌ ERROR DE INSTALACIÓN CRÍTICO. Deteniendo la ejecución: {e}\")\n        sys.exit(1)\n\n    try:\n        # 2. Cargar los modelos entrenados\n        model_rnd, voxel_encoder = load_checkpoint(filename=CHECKPOINT_FULL_PATH)\n        \n        # 3. Ejecutar la inferencia y generar el archivo de envío\n        output_file = create_submission_mask(\n            model=model_rnd,\n            encoder_3d=voxel_encoder,\n            test_image_id=TEST_IMAGE_ID,\n            data_path=KAGGLE_DATA_PATH,\n            patch_size_2d=PATCH_SIZE_2D, \n            device=DEVICE\n        )\n        \n        print(f\"✅ Proceso de inferencia completado. Archivo de envío generado: {output_file}\")\n        \n    except Exception as e:\n        print(f\"❌ Ocurrió un error inesperado durante la inferencia: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-24T12:06:03.794678Z","iopub.execute_input":"2025-11-24T12:06:03.795021Z","iopub.status.idle":"2025-11-24T12:06:03.806521Z","shell.execute_reply.started":"2025-11-24T12:06:03.794999Z","shell.execute_reply":"2025-11-24T12:06:03.805906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python inference_module.py","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-24T12:06:12.136395Z","iopub.execute_input":"2025-11-24T12:06:12.136705Z","iopub.status.idle":"2025-11-24T12:06:17.936234Z","shell.execute_reply.started":"2025-11-24T12:06:12.136677Z","shell.execute_reply":"2025-11-24T12:06:17.935497Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport zipfile\n\n# Definición de nombres y rutas\nORIGINAL_TIF_PATH = '/kaggle/working/submission_1407735.tif' # Ruta completa garantizada\nREQUIRED_TIF_NAME = '1407735.tif'                           # Nombre interno que Kaggle espera\nSUBMISSION_ZIP_NAME = 'submission.zip'\n\nprint(f\"⌛ Creando archivo de envío final...\")\n\n# 1. Verificar existencia y crear el ZIP\nif os.path.exists(ORIGINAL_TIF_PATH):\n    \n    # Creamos el ZIP\n    with zipfile.ZipFile(SUBMISSION_ZIP_NAME, 'w', compression=zipfile.ZIP_DEFLATED) as zf:\n        # Añadimos el archivo. arcname asegura que el nombre interno sea 1407735.tif.\n        zf.write(ORIGINAL_TIF_PATH, arcname=REQUIRED_TIF_NAME)\n        \n    print(f\"✅ Archivo de envío final creado: {SUBMISSION_ZIP_NAME}\")\n    \n    # Opcional: Eliminar el TIF grande para liberar espacio\n    os.remove(ORIGINAL_TIF_PATH)\n    print(f\"🗑️ TIF original eliminado: {ORIGINAL_TIF_PATH}\")\n    \nelse:\n    print(f\"❌ ERROR CRÍTICO FINAL: El archivo {ORIGINAL_TIF_PATH} no se encuentra. Algo salió mal en el último paso de inferencia.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-24T12:07:23.287091Z","iopub.execute_input":"2025-11-24T12:07:23.287416Z","iopub.status.idle":"2025-11-24T12:07:23.295917Z","shell.execute_reply.started":"2025-11-24T12:07:23.287388Z","shell.execute_reply":"2025-11-24T12:07:23.295275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!find / -name \"submission_1407735.tif\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-24T12:07:27.791688Z","iopub.execute_input":"2025-11-24T12:07:27.792041Z","iopub.status.idle":"2025-11-24T12:07:34.280051Z","shell.execute_reply.started":"2025-11-24T12:07:27.792017Z","shell.execute_reply":"2025-11-24T12:07:34.279201Z"}},"outputs":[],"execution_count":null}]}