{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":39763,"databundleVersionId":11756775,"sourceType":"competition"}],"dockerImageVersionId":31041,"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},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndt = [\"/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis2_1_0.npy\",\n     \"/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis4_1_0.npy\",\n     \"/kaggle/input/waveform-inversion/train_samples/CurveFault_B/seis6_1_0.npy\",\n     \"/kaggle/input/waveform-inversion/train_samples/CurveFault_B/seis8_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/CurveVel_A/data/data1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/CurveVel_A/data/data2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/CurveVel_B/data/data1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/CurveVel_B/data/data2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatFault_A/seis2_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatFault_A/seis4_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatFault_B/seis6_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatFault_B/seis8_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatVel_A/data/data1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatVel_A/data/data2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatVel_B/data/data1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatVel_B/data/data2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/Style_A/data/data1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/Style_A/data/data2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/Style_B/data/data1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/Style_B/data/data2.npy\"  \n      \n     ]\n\nmd = [\"/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel2_1_0.npy\",\n     \"/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel4_1_0.npy\",\n     \"/kaggle/input/waveform-inversion/train_samples/CurveFault_B/vel6_1_0.npy\",\n     \"/kaggle/input/waveform-inversion/train_samples/CurveFault_B/vel8_1_0.npy\",\n     \"/kaggle/input/waveform-inversion/train_samples/CurveVel_A/model/model1.npy\",\n     \"/kaggle/input/waveform-inversion/train_samples/CurveVel_A/model/model2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/CurveVel_B/model/model1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/CurveVel_B/model/model2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatFault_A/vel2_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatFault_A/vel4_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatFault_B/vel6_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatFault_B/vel8_1_0.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatVel_A/model/model1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatVel_A/model/model2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatVel_B/model/model1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/FlatVel_B/model/model2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/Style_A/model/model1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/Style_A/model/model2.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/Style_B/model/model1.npy\",\n      \"/kaggle/input/waveform-inversion/train_samples/Style_B/model/model2.npy\"\n         \n     ]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T03:56:50.133874Z","iopub.execute_input":"2025-06-22T03:56:50.134186Z","iopub.status.idle":"2025-06-22T03:56:50.533312Z","shell.execute_reply.started":"2025-06-22T03:56:50.134164Z","shell.execute_reply":"2025-06-22T03:56:50.532469Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a DataFrame with two columns named 'dt' and 'md'\ndf = pd.DataFrame({\n    'dt': dt,  # Empty column for dt\n    'md': md   # Empty column for md\n})\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T03:56:52.558947Z","iopub.execute_input":"2025-06-22T03:56:52.559361Z","iopub.status.idle":"2025-06-22T03:56:52.575572Z","shell.execute_reply.started":"2025-06-22T03:56:52.559335Z","shell.execute_reply":"2025-06-22T03:56:52.574674Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df[:5].shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T03:56:53.768058Z","iopub.execute_input":"2025-06-22T03:56:53.76887Z","iopub.status.idle":"2025-06-22T03:56:53.774435Z","shell.execute_reply.started":"2025-06-22T03:56:53.768843Z","shell.execute_reply":"2025-06-22T03:56:53.773611Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\ndef check_column_shape_consistency(df, columns=['dt', 'md']):\n    for col in columns:\n        shapes = set()\n        for i, path in enumerate(df[col]):\n            try:\n                arr = np.load(path, mmap_mode='r')\n                shapes.add(arr.shape)\n            except Exception as e:\n                print(f\"❌ Error loading {col} at index {i}: {e}\")\n                return False\n        if len(shapes) == 1:\n            print(f\"✅ All '{col}' files have the same shape: {shapes.pop()}\")\n        else:\n            print(f\"❌ Mismatch in shapes for column '{col}': {shapes}\")\n            return False\n    return True\n\n# Example usage:\ncheck_column_shape_consistency(df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T03:57:02.958387Z","iopub.execute_input":"2025-06-22T03:57:02.95868Z","iopub.status.idle":"2025-06-22T03:57:03.689477Z","shell.execute_reply.started":"2025-06-22T03:57:02.958659Z","shell.execute_reply":"2025-06-22T03:57:03.688656Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# import numpy as np\n# import torch\n# from torch.utils.data import Dataset\n\n# class SeismicInversionDataset(Dataset):\n#     \"\"\"\n#     Loads seismic waveform (dt) and velocity map (md) .npy files,\n#     flattens 500-sample chunks across all files into a single dataset,\n#     and supports configurable batch sizes later during DataLoader creation.\n#     \"\"\"\n#     def __init__(self, df, transform=None, device='cpu'):\n#         self.seismic_data = []\n#         self.velocity_maps = []\n#         self.transform = transform\n#         self.device = device\n    \n#         print(f\"📦 Loading {len(df)} data pairs...\")\n    \n#         for i, row in df.iterrows():\n#             dt_path = row['dt']\n#             md_path = row['md']\n    \n#             print(f\"  [{i+1}/{len(df)}] Loading:\")\n#             print(f\"     ├─ dt: {os.path.basename(dt_path)}\")\n#             print(f\"     └─ md: {os.path.basename(md_path)}\")\n    \n#             dt_array = np.load(dt_path, mmap_mode='r')  # shape: (500, 5, 1000, 70)\n#             md_array = np.load(md_path, mmap_mode='r')  # shape: (500, 1, 1000, 70)\n    \n#             assert dt_array.shape[0] == md_array.shape[0] == 500\n    \n#             self.seismic_data.append(dt_array)\n#             self.velocity_maps.append(md_array)\n    \n#         self.seismic_data = np.vstack(self.seismic_data)\n#         self.velocity_maps = np.vstack(self.velocity_maps)\n#         self.N = self.seismic_data.shape[0]\n    \n#         print(f\"✅ Loaded {self.N} samples total.\")\n\n    \n#     def __len__(self):\n#         return self.N\n\n#     def __getitem__(self, idx):\n#         x = self.seismic_data[idx]     # shape: (5, 1000, 70)\n#         y = self.velocity_maps[idx]    # shape: (1, 1000, 70)\n\n#         x = torch.from_numpy(x).float()\n#         y = torch.from_numpy(y).float()\n\n#         if self.transform:\n#             x, y = self.transform(x, y)\n        \n#         return x, y\n\nimport os\nimport numpy as np\nimport torch\nfrom torch.utils.data import Dataset, DataLoader, random_split\n\nclass SeismicInversionDataset(Dataset):\n    \"\"\"\n    Loads seismic waveform (dt) and velocity map (md) .npy files,\n    flattens 500-sample chunks across all files into a single dataset,\n    and supports configurable batch sizes later during DataLoader creation.\n    \"\"\"\n    def __init__(self, df, transform=None, device='cpu'):\n        self.seismic_data = []\n        self.velocity_maps = []\n        self.transform = transform\n        self.device = device\n    \n        print(f\"📦 Loading {len(df)} data pairs...\")\n    \n        for i, row in df.iterrows():\n            dt_path = row['dt']\n            md_path = row['md']\n    \n            print(f\"  [{i+1}/{len(df)}] Loading:\")\n            print(f\"     ├─ dt: {os.path.basename(dt_path)}\")\n            print(f\"     └─ md: {os.path.basename(md_path)}\")\n    \n            dt_array = np.load(dt_path, mmap_mode='r')  # shape: (500, 5, 1000, 70)\n            md_array = np.load(md_path, mmap_mode='r')  # shape: (500, 1, 1000, 70)\n    \n            assert dt_array.shape[0] == md_array.shape[0] == 500\n    \n            self.seismic_data.append(dt_array)\n            self.velocity_maps.append(md_array)\n    \n        self.seismic_data = np.vstack(self.seismic_data)\n        self.velocity_maps = np.vstack(self.velocity_maps)\n        self.N = self.seismic_data.shape[0]\n    \n        print(f\"✅ Loaded {self.N} samples total.\")\n    \n    def __len__(self):\n        return self.N\n        \n    def __getitem__(self, idx):\n        x = self.seismic_data[idx]     # shape: (5, 1000, 70)\n        y = self.velocity_maps[idx]    # shape: (1, 1000, 70)\n        x = torch.from_numpy(x).float()\n        y = torch.from_numpy(y).float()\n        if self.transform:\n            x, y = self.transform(x, y)\n        \n        return x, y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T03:57:10.067754Z","iopub.execute_input":"2025-06-22T03:57:10.068071Z","iopub.status.idle":"2025-06-22T03:57:16.537273Z","shell.execute_reply.started":"2025-06-22T03:57:10.068047Z","shell.execute_reply":"2025-06-22T03:57:16.536424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create dataset and split into train/val/test (70/20/10)\ndataset = SeismicInversionDataset(df, device='cuda')\n\ntrain_size = int(0.7 * len(dataset))\nval_size = int(0.2 * len(dataset))\ntest_size = len(dataset) - train_size - val_size\n\ntrain_dataset, val_dataset, test_dataset = random_split(\n    dataset, [train_size, val_size, test_size],\n    generator=torch.Generator().manual_seed(42)  # For reproducible splits\n)\n\n# Create DataLoaders for each split\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=4)\nval_loader = DataLoader(val_dataset, batch_size=16, shuffle=False, num_workers=4)\ntest_loader = DataLoader(test_dataset, batch_size=16, shuffle=False, num_workers=4)\n\nprint(f\"Split sizes - Train: {len(train_dataset)}, Val: {len(val_dataset)}, Test: {len(test_dataset)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T03:57:24.324075Z","iopub.execute_input":"2025-06-22T03:57:24.325134Z","iopub.status.idle":"2025-06-22T04:00:28.979507Z","shell.execute_reply.started":"2025-06-22T03:57:24.325102Z","shell.execute_reply":"2025-06-22T04:00:28.978704Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Physics Informed ResNeXt InversionNet for Seismic Inversion\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader\nimport pandas as pd\nimport numpy as np\n# from SeismicInversionDataset import SeismicInversionDataset  # Import your dataset class\n\nclass PhysicsInformedResNeXtInversionNet(nn.Module):\n    \"\"\"\n    Physics Informed ResNeXt-based InversionNet for seismic inversion\n    Input: (batch, 5, 1000, 70) - seismic waveforms\n    Output: (batch, 1, 70, 70) - velocity maps\n    \n    Incorporates:\n    - ResNeXt50 or ResNeXt101 backbone\n    - Wave equation physics\n    - Velocity range constraints\n    - Smoothness regularization\n    - Geological constraints\n    \"\"\"\n    def __init__(self, model_name='resnext50_32x4d', velocity_range=(1500, 5000), dt=0.001, dx=25.0):\n        super(PhysicsInformedResNeXtInversionNet, self).__init__()\n        \n        # Physical parameters\n        self.velocity_min = velocity_range[0]  # Minimum velocity (m/s)\n        self.velocity_max = velocity_range[1]  # Maximum velocity (m/s)\n        self.dt = dt  # Time step (s)\n        self.dx = dx  # Spatial step (m)\n        \n        # Load pretrained ResNeXt model from torch.hub\n        print(f\"🔄 Loading pretrained {model_name} from torch.hub...\")\n        if model_name == 'resnext50_32x4d':\n            self.backbone = torch.hub.load('pytorch/vision:v0.10.0', 'resnext50_32x4d', pretrained=True)\n            feature_dim = 2048  # ResNeXt50 output dimension\n        elif model_name == 'resnext101_32x8d':\n            self.backbone = torch.hub.load('pytorch/vision:v0.10.0', 'resnext101_32x8d', pretrained=True)\n            feature_dim = 2048  # ResNeXt101 output dimension\n        else:\n            raise ValueError(f\"Unsupported model: {model_name}\")\n        \n        # Remove the final classification layer (fc)\n        self.feature_extractor = nn.Sequential(*list(self.backbone.children())[:-1])\n        \n        # Input adaptation layer to convert 5 channels to 3 channels (RGB expected by ResNeXt)\n        self.input_adapter = nn.Sequential(\n            nn.Conv2d(5, 3, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(3)\n        )\n        \n        # Physics-aware decoder network\n        self.decoder = nn.Sequential(\n            # Global average pooling is already applied by ResNeXt\n            nn.Linear(feature_dim, 1024),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            \n            nn.Linear(1024, 2048),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            \n            # Output raw velocity predictions\n            nn.Linear(2048, 70 * 70),\n        )\n        \n        # Physics-constrained refinement layers\n        self.physics_refiner = nn.Sequential(\n            nn.Conv2d(1, 64, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(64),\n            \n            nn.Conv2d(64, 32, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(32),\n            \n            nn.Conv2d(32, 16, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(16),\n            \n            nn.Conv2d(16, 1, kernel_size=1)\n        )\n        \n        # Learnable physics parameters\n        self.physics_weight = nn.Parameter(torch.tensor(1.0))\n        self.smoothness_weight = nn.Parameter(torch.tensor(0.05))  # Reduced for better balance\n        self.geological_weight = nn.Parameter(torch.tensor(0.1))\n        \n    def apply_velocity_constraints(self, velocity):\n        \"\"\"Apply physical velocity constraints using sigmoid activation\"\"\"\n        # Normalize to [0, 1] then scale to [v_min, v_max]\n        velocity_normalized = torch.sigmoid(velocity)\n        velocity_constrained = (self.velocity_min + \n                              velocity_normalized * (self.velocity_max - self.velocity_min))\n        return velocity_constrained\n    \n    def compute_wave_equation_residual(self, velocity, waveforms):\n        \"\"\"\n        Compute wave equation residual for physics loss\n        2D acoustic wave equation: ∂²p/∂t² = v²(∇²p)\n        \"\"\"\n        batch_size = velocity.size(0)\n        \n        # Compute spatial derivatives using finite differences\n        # Second derivative in x-direction\n        d2p_dx2 = torch.zeros_like(velocity)\n        d2p_dx2[:, :, 1:-1, :] = (velocity[:, :, 2:, :] - 2*velocity[:, :, 1:-1, :] + \n                                  velocity[:, :, :-2, :]) / (self.dx**2)\n        \n        # Second derivative in z-direction\n        d2p_dz2 = torch.zeros_like(velocity)\n        d2p_dz2[:, :, :, 1:-1] = (velocity[:, :, :, 2:] - 2*velocity[:, :, :, 1:-1] + \n                                  velocity[:, :, :, :-2]) / (self.dx**2)\n        \n        # Laplacian\n        laplacian = d2p_dx2 + d2p_dz2\n        \n        # Simplified wave equation residual\n        wave_residual = torch.mean(torch.abs(laplacian))\n        \n        return wave_residual\n    \n    def compute_smoothness_loss(self, velocity):\n        \"\"\"Compute total variation loss for smoothness regularization\"\"\"\n        # Horizontal differences\n        h_diff = torch.abs(velocity[:, :, 1:, :] - velocity[:, :, :-1, :])\n        # Vertical differences\n        v_diff = torch.abs(velocity[:, :, :, 1:] - velocity[:, :, :, :-1])\n        \n        smoothness_loss = torch.mean(h_diff) + torch.mean(v_diff)\n        return smoothness_loss\n    \n    def compute_geological_constraints(self, velocity):\n        \"\"\"Apply geological constraints (velocity should increase with depth)\"\"\"\n        # Velocity should generally increase with depth (z-direction)\n        depth_gradient = velocity[:, :, 1:, :] - velocity[:, :, :-1, :]\n        # Penalize negative gradients (velocity decreasing with depth)\n        geological_loss = torch.mean(torch.relu(-depth_gradient))\n        return geological_loss\n    \n    def forward(self, x):\n        # x shape: (batch, 5, 1000, 70)\n        batch_size = x.size(0)\n        \n        # Store input waveforms for physics loss\n        input_waveforms = x.clone()\n        \n        # Adapt input channels from 5 to 3 (RGB)\n        x = self.input_adapter(x)  # (batch, 3, 1000, 70)\n        \n        # Resize to standard ImageNet size for ResNeXt (224x224)\n        x = nn.functional.interpolate(x, size=(224, 224), mode='bilinear', align_corners=False)\n        \n        # Extract features using ResNeXt backbone\n        features = self.feature_extractor(x)  # (batch, 2048, 1, 1)\n        \n        # Flatten features\n        features = features.view(batch_size, -1)  # (batch, 2048)\n        \n        # Decode to velocity map\n        raw_velocity = self.decoder(features)  # (batch, 70*70)\n        \n        # Reshape to 2D map\n        raw_velocity = raw_velocity.view(batch_size, 1, 70, 70)  # (batch, 1, 70, 70)\n        \n        # Apply velocity constraints\n        velocity_constrained = self.apply_velocity_constraints(raw_velocity)\n        \n        # Physics-aware refinement\n        velocity_refined = self.physics_refiner(velocity_constrained)\n        \n        # Final velocity map with constraints\n        final_velocity = self.apply_velocity_constraints(velocity_refined)\n        \n        return final_velocity\n    \n    def physics_loss(self, predicted_velocity, input_waveforms, targets):\n        \"\"\"\n        Compute total physics-informed loss\n        \"\"\"\n        # Data fitting loss (L1)\n        data_loss = nn.functional.l1_loss(predicted_velocity, targets)\n        \n        # Physics-based losses\n        wave_residual = self.compute_wave_equation_residual(predicted_velocity, input_waveforms)\n        smoothness_loss = self.compute_smoothness_loss(predicted_velocity)\n        geological_loss = self.compute_geological_constraints(predicted_velocity)\n        \n        # Velocity constraint loss (ensure values are within physical bounds)\n        velocity_constraint_loss = torch.mean(\n            torch.relu(predicted_velocity - self.velocity_max) + \n            torch.relu(self.velocity_min - predicted_velocity)\n        )\n        \n        # Combine all losses with learnable weights\n        total_loss = (data_loss + \n                     self.physics_weight * wave_residual +\n                     self.smoothness_weight * smoothness_loss +\n                     self.geological_weight * geological_loss +\n                     10.0 * velocity_constraint_loss)\n        \n        return total_loss, {\n            'data_loss': data_loss.item(),\n            'wave_residual': wave_residual.item(),\n            'smoothness_loss': smoothness_loss.item(),\n            'geological_loss': geological_loss.item(),\n            'velocity_constraint_loss': velocity_constraint_loss.item()\n        }\n\nclass PhysicsLoss(nn.Module):\n    \"\"\"Separate physics loss module for cleaner code organization\"\"\"\n    \n    def __init__(self, model):\n        super(PhysicsLoss, self).__init__()\n        self.model = model\n        \n    def forward(self, predictions, targets, input_waveforms):\n        \"\"\"Compute combined physics-informed loss\"\"\"\n        return self.model.physics_loss(predictions, input_waveforms, targets)\n\ndef test_seismic_cnn():\n    # data = {\n    #     'dt': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis4_1_0.npy'],\n    #     'md': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel4_1_0.npy']\n    # }\n    # df = pd.DataFrame(data) \n    \n    # Initialize dataset and dataloader\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    print(f\"🖥️  Using device: {device}\")\n    \n    dataset = SeismicInversionDataset(df, device=device)\n    dataloader = DataLoader(dataset, batch_size=16, shuffle=True)\n    \n    # Initialize Physics Informed ResNeXt model\n    print(\"🏗️  Initializing Physics Informed ResNeXt InversionNet...\")\n    \n    # Physical parameters for seismic inversion\n    velocity_range = (1500, 5000)  # Typical seismic velocities (m/s)\n    dt = 0.001  # Time sampling (1ms)\n    dx = 25.0   # Spatial sampling (25m)\n    \n    # Choose model: 'resnext50_32x4d' or 'resnext101_32x8d'\n    model_name = 'resnext50_32x4d'  # Change to 'resnext101_32x8d' for larger model\n    \n    model = PhysicsInformedResNeXtInversionNet(\n        model_name=model_name,\n        velocity_range=velocity_range,\n        dt=dt,\n        dx=dx\n    ).to(device)\n    \n    # Use physics-informed loss\n    physics_criterion = PhysicsLoss(model)\n    \n    # Optimizer with different learning rates\n    resnext_params = []\n    decoder_params = []\n    physics_params = []\n    \n    for name, param in model.named_parameters():\n        if 'feature_extractor' in name or 'backbone' in name:\n            resnext_params.append(param)\n        elif any(weight_name in name for weight_name in ['physics_weight', 'smoothness_weight', 'geological_weight']):\n            physics_params.append(param)\n        else:\n            decoder_params.append(param)\n    \n    optimizer = optim.Adam([\n        {'params': resnext_params, 'lr': 1e-5},     # Lower LR for pretrained ResNeXt\n        {'params': decoder_params, 'lr': 1e-3},     # Higher LR for new layers\n        {'params': physics_params, 'lr': 1e-4}      # Medium LR for physics parameters\n    ])\n    \n    # Learning rate scheduler\n    scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', patience=3, factor=0.5)\n    \n    print(f\"\\n🏗️  Model Architecture: {model_name}\")\n    print(f\"📊 ResNeXt backbone: Pretrained on ImageNet\")\n    \n    # Count parameters\n    total_params = sum(p.numel() for p in model.parameters())\n    trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n    resnext_params_count = sum(p.numel() for p in model.feature_extractor.parameters())\n    \n    print(f\"\\n📊 Parameter Count:\")\n    print(f\"    Total parameters: {total_params:,}\")\n    print(f\"    Trainable parameters: {trainable_params:,}\")\n    print(f\"    ResNeXt backbone parameters: {resnext_params_count:,}\")\n    print(f\"🌊 Velocity range: {velocity_range[0]}-{velocity_range[1]} m/s\")\n    print(f\"⏱️  Time step: {dt} s\")\n    print(f\"📏 Spatial step: {dx} m\")\n    \n    # Training loop with physics-informed loss\n    num_epochs = 10\n    print(f\"\\n🚀 Starting Physics Informed ResNeXt training for {num_epochs} epochs...\")\n    \n    model.train()\n    freeze_epochs = 3  # Freeze ResNeXt for first few epochs\n    \n    # Initially freeze ResNeXt backbone\n    for param in model.feature_extractor.parameters():\n        param.requires_grad = False\n    \n    for epoch in range(num_epochs):\n        # Optionally unfreeze ResNeXt after initial training\n        if epoch == freeze_epochs:\n            print(f\"🔓 Unfreezing ResNeXt backbone at epoch {epoch+1}\")\n            for param in model.feature_extractor.parameters():\n                param.requires_grad = True\n        \n        epoch_loss = 0.0\n        epoch_losses = {\n            'data_loss': 0.0,\n            'wave_residual': 0.0,\n            'smoothness_loss': 0.0,\n            'geological_loss': 0.0,\n            'velocity_constraint_loss': 0.0\n        }\n        num_batches = 0\n        \n        for batch_idx, (inputs, targets) in enumerate(dataloader):\n            inputs = inputs.to(device)\n            targets = targets.to(device)\n            \n            # Forward pass\n            optimizer.zero_grad()\n            outputs = model(inputs)\n            \n            # Compute physics-informed loss\n            loss, loss_components = physics_criterion(outputs, targets, inputs)\n            \n            # Backward pass\n            loss.backward()\n            \n            # Gradient clipping for stability\n            torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)\n            \n            optimizer.step()\n            \n            epoch_loss += loss.item()\n            num_batches += 1\n            \n            # Accumulate loss components\n            for key, value in loss_components.items():\n                epoch_losses[key] += value\n            \n            # Log batch progress\n            if batch_idx % 10 == 0:\n                print(f\"  Epoch [{epoch+1}/{num_epochs}], Batch [{batch_idx+1}]\")\n                print(f\"    Total Loss: {loss.item():.6f}\")\n                print(f\"    Data Loss: {loss_components['data_loss']:.6f}\")\n                print(f\"    Physics Loss: {loss_components['wave_residual']:.6f}\")\n                print(f\"    Smoothness: {loss_components['smoothness_loss']:.6f}\")\n                print(f\"    Velocity Range: [{outputs.min():.1f}, {outputs.max():.1f}] m/s\")\n        \n        avg_loss = epoch_loss / num_batches\n        \n        # Average loss components\n        for key in epoch_losses:\n            epoch_losses[key] /= num_batches\n        \n        print(f\"\\n📈 Epoch [{epoch+1}/{num_epochs}] Summary:\")\n        print(f\"    Average Total Loss: {avg_loss:.6f}\")\n        print(f\"    Data Loss: {epoch_losses['data_loss']:.6f}\")\n        print(f\"    Wave Residual: {epoch_losses['wave_residual']:.6f}\")\n        print(f\"    Smoothness Loss: {epoch_losses['smoothness_loss']:.6f}\")\n        print(f\"    Geological Loss: {epoch_losses['geological_loss']:.6f}\")\n        print(f\"    Velocity Constraint Loss: {epoch_losses['velocity_constraint_loss']:.6f}\")\n        print(f\"    Physics Weight: {model.physics_weight.item():.4f}\")\n        print(f\"    Smoothness Weight: {model.smoothness_weight.item():.4f}\")\n        print(f\"    Geological Weight: {model.geological_weight.item():.4f}\")\n        print(\"-\" * 70)\n        \n        # Update learning rate\n        scheduler.step(avg_loss)\n    \n    print(\"✅ Physics Informed ResNeXt training completed!\")\n    \n    # Save model\n    model_path = f'/kaggle/working/physics_informed_{model_name}_inversionnet.pth'\n    torch.save({\n        'model_state_dict': model.state_dict(),\n        'model_name': model_name,\n        'velocity_range': velocity_range,\n        'dt': dt,\n        'dx': dx,\n    }, model_path)\n    print(f\"💾 Model saved to: {model_path}\")\n    \n    # Test with a single batch\n    print(\"\\n🧪 Testing Physics Informed ResNeXt model...\")\n    \n    model.eval()\n    with torch.no_grad():\n        for inputs, targets in dataloader:\n            inputs = inputs.to(device)\n            targets = targets.to(device)\n            outputs = model(inputs)\n            \n            # Compute final physics metrics\n            final_loss, loss_components = physics_criterion(outputs, targets, inputs)\n            \n            print(f\"Input shape: {inputs.shape}\")\n            print(f\"Target shape: {targets.shape}\")\n            print(f\"Output shape: {outputs.shape}\")\n            print(f\"Output velocity range: [{outputs.min():.1f}, {outputs.max():.1f}] m/s\")\n            print(f\"Target velocity range: [{targets.min():.1f}, {targets.max():.1f}] m/s\")\n            print(f\"Final physics loss: {final_loss.item():.6f}\")\n            \n            # Check if velocities are within physical bounds\n            within_bounds = torch.all((outputs >= velocity_range[0]) & (outputs <= velocity_range[1]))\n            print(f\"All velocities within physical bounds: {within_bounds}\")\n            break\n\nif __name__ == \"__main__\":\n    test_seismic_cnn()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T04:01:30.160972Z","iopub.execute_input":"2025-06-22T04:01:30.161682Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import csv\nimport time\nimport glob\nfrom tqdm import tqdm\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\n\ndef format_time(seconds):\n    \"\"\"Format seconds into human readable time\"\"\"\n    if seconds < 60:\n        return f\"{seconds:.1f}s\"\n    elif seconds < 3600:\n        minutes = seconds // 60\n        remaining_seconds = seconds % 60\n        return f\"{int(minutes)}m {remaining_seconds:.1f}s\"\n    else:\n        hours = seconds // 3600\n        remaining_minutes = (seconds % 3600) // 60\n        remaining_seconds = seconds % 60\n        return f\"{int(hours)}h {int(remaining_minutes)}m {remaining_seconds:.1f}s\"\n\nclass PhysicsInformedResNeXtInversionNet(nn.Module):\n    \"\"\"\n    Physics Informed ResNeXt-based InversionNet for seismic inversion\n    Input: (batch, 5, 1000, 70) - seismic waveforms\n    Output: (batch, 1, 70, 70) - velocity maps\n    \"\"\"\n    def __init__(self, model_name='resnext50_32x4d', velocity_range=(1500, 5000), dt=0.001, dx=25.0):\n        super(PhysicsInformedResNeXtInversionNet, self).__init__()\n        \n        # Physical parameters\n        self.velocity_min = velocity_range[0]\n        self.velocity_max = velocity_range[1]\n        self.dt = dt\n        self.dx = dx\n        \n        # Load pretrained ResNeXt model from torch.hub\n        if model_name == 'resnext50_32x4d':\n            self.backbone = torch.hub.load('pytorch/vision:v0.10.0', 'resnext50_32x4d', pretrained=True)\n            feature_dim = 2048\n        elif model_name == 'resnext101_32x8d':\n            self.backbone = torch.hub.load('pytorch/vision:v0.10.0', 'resnext101_32x8d', pretrained=True)\n            feature_dim = 2048\n        else:\n            raise ValueError(f\"Unsupported model: {model_name}\")\n        \n        # Remove the final classification layer\n        self.feature_extractor = nn.Sequential(*list(self.backbone.children())[:-1])\n        \n        # Input adaptation layer\n        self.input_adapter = nn.Sequential(\n            nn.Conv2d(5, 3, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(3)\n        )\n        \n        # Physics-aware decoder network\n        self.decoder = nn.Sequential(\n            nn.Linear(feature_dim, 1024),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(1024, 2048),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(2048, 70 * 70),\n        )\n        \n        # Physics-constrained refinement layers\n        self.physics_refiner = nn.Sequential(\n            nn.Conv2d(1, 64, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(64),\n            nn.Conv2d(64, 32, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(32),\n            nn.Conv2d(32, 16, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(16),\n            nn.Conv2d(16, 1, kernel_size=1)\n        )\n        \n        # Learnable physics parameters\n        self.physics_weight = nn.Parameter(torch.tensor(1.0))\n        self.smoothness_weight = nn.Parameter(torch.tensor(0.05))\n        self.geological_weight = nn.Parameter(torch.tensor(0.1))\n        \n    def apply_velocity_constraints(self, velocity):\n        \"\"\"Apply physical velocity constraints using sigmoid activation\"\"\"\n        velocity_normalized = torch.sigmoid(velocity)\n        velocity_constrained = (self.velocity_min + \n                              velocity_normalized * (self.velocity_max - self.velocity_min))\n        return velocity_constrained\n    \n    def forward(self, x):\n        # x shape: (batch, 5, 1000, 70)\n        batch_size = x.size(0)\n        \n        # Adapt input channels from 5 to 3 (RGB)\n        x = self.input_adapter(x)  # (batch, 3, 1000, 70)\n        \n        # Resize to standard ImageNet size for ResNeXt (224x224)\n        x = nn.functional.interpolate(x, size=(224, 224), mode='bilinear', align_corners=False)\n        \n        # Extract features using ResNeXt backbone\n        features = self.feature_extractor(x)  # (batch, 2048, 1, 1)\n        \n        # Flatten features\n        features = features.view(batch_size, -1)  # (batch, 2048)\n        \n        # Decode to velocity map\n        raw_velocity = self.decoder(features)  # (batch, 70*70)\n        \n        # Reshape to 2D map\n        raw_velocity = raw_velocity.view(batch_size, 1, 70, 70)  # (batch, 1, 70, 70)\n        \n        # Apply velocity constraints\n        velocity_constrained = self.apply_velocity_constraints(raw_velocity)\n        \n        # Physics-aware refinement\n        velocity_refined = self.physics_refiner(velocity_constrained)\n        \n        # Final velocity map with constraints\n        final_velocity = self.apply_velocity_constraints(velocity_refined)\n        \n        return final_velocity\n\nclass TestDataset(Dataset):\n    \"\"\"Dataset class for test data loading\"\"\"\n    def __init__(self, test_files):\n        self.test_files = test_files\n        \n    def __len__(self):\n        return len(self.test_files)\n    \n    def __getitem__(self, idx):\n        # Load test seismic data\n        file_path = self.test_files[idx]\n        seismic_data = np.load(file_path)  # Shape should be (5, 1000, 70)\n        \n        # Extract OID from filename\n        oid = file_path.split('/')[-1].replace('.npy', '')\n        \n        # Convert to tensor and ensure correct shape\n        seismic_tensor = torch.from_numpy(seismic_data).float()\n        \n        # Ensure shape is (5, 1000, 70)\n        if seismic_tensor.shape != (5, 1000, 70):\n            raise ValueError(f\"Expected shape (5, 1000, 70), got {seismic_tensor.shape}\")\n        \n        return seismic_tensor, oid\n\ndef load_trained_model(model_path, device):\n    \"\"\"Load the trained Physics Informed ResNeXt model\"\"\"\n    print(f\"🔄 Loading trained model from: {model_path}\")\n    \n    # Load checkpoint\n    checkpoint = torch.load(model_path, map_location=device)\n    \n    # Extract model parameters\n    model_name = checkpoint.get('model_name', 'resnext50_32x4d')\n    velocity_range = checkpoint.get('velocity_range', (1500, 5000))\n    dt = checkpoint.get('dt', 0.001)\n    dx = checkpoint.get('dx', 25.0)\n    \n    # Initialize model\n    model = PhysicsInformedResNeXtInversionNet(\n        model_name=model_name,\n        velocity_range=velocity_range,\n        dt=dt,\n        dx=dx\n    ).to(device)\n    \n    # Load trained weights\n    model.load_state_dict(checkpoint['model_state_dict'])\n    model.eval()\n    \n    print(f\"✅ Model loaded successfully!\")\n    print(f\"    Model: {model_name}\")\n    print(f\"    Velocity range: {velocity_range[0]}-{velocity_range[1]} m/s\")\n    \n    return model\n\ndef generate_submission():\n    \"\"\"Generate submission file using trained Physics Informed ResNeXt model\"\"\"\n    \n    # Configuration\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    batch_size = 8  # Adjust based on GPU memory\n    num_workers = 4\n    \n    print(f\"🖥️  Using device: {device}\")\n    \n    # Model path (update this to your actual model path)\n    model_path = \"/kaggle/working/physics_informed_resnext50_32x4d_inversionnet.pth\"\n    \n    # Load trained model\n    model = load_trained_model(model_path, device)\n    \n    # Load sample submission for format reference\n    try:\n        sample_submission = pd.read_csv(\"/kaggle/input/waveform-inversion/sample_submission.csv\")\n        print(f\"📄 Sample submission loaded: {len(sample_submission)} rows\")\n    except FileNotFoundError:\n        print(\"⚠️  Sample submission not found, proceeding without format check\")\n        sample_submission = None\n    \n    # Get test files\n    test_files = sorted(glob.glob(\"/kaggle/input/waveform-inversion/test/*.npy\"))\n    print(f\"📁 Found {len(test_files)} test files\")\n    \n    if len(test_files) == 0:\n        print(\"❌ No test files found! Check the test data path.\")\n        return\n    \n    # Define output columns (every other x position from 1 to 69, step=2)\n    x_cols = [f\"x_{i}\" for i in range(1, 70, 2)]  # x_1, x_3, x_5, ..., x_69\n    fieldnames = [\"oid_ypos\"] + x_cols\n    \n    print(f\"📊 Output format: {len(x_cols)} x-columns per y-position\")\n    print(f\"    X columns: {x_cols[:5]}...{x_cols[-5:]}\")\n    \n    # Create test dataset and dataloader\n    test_dataset = TestDataset(test_files)\n    test_dataloader = DataLoader(\n        test_dataset,\n        batch_size=batch_size,\n        shuffle=False,  # Keep order for submission\n        num_workers=num_workers,\n        pin_memory=True if device.type == 'cuda' else False\n    )\n    \n    print(f\"🔄 Starting inference on {len(test_dataset)} test samples...\")\n    print(f\"    Batch size: {batch_size}\")\n    print(f\"    Total batches: {len(test_dataloader)}\")\n    \n    # Start inference and submission generation\n    row_count = 0\n    t0 = time.time()\n    \n    with open(\"submission.csv\", \"wt\", newline=\"\") as csvfile:\n        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)\n        writer.writeheader()\n        \n        with torch.inference_mode():\n            # Use autocast for faster inference on GPU\n            autocast_context = torch.autocast(device.type) if device.type == 'cuda' else torch.no_grad()\n            \n            with autocast_context:\n                for batch_idx, (inputs, oids_test) in enumerate(tqdm(test_dataloader, desc=\"Processing batches\")):\n                    inputs = inputs.to(device, non_blocking=True)\n                    \n                    # Forward pass through model\n                    outputs = model(inputs)  # Shape: (batch_size, 1, 70, 70)\n                    \n                    # Extract velocity maps\n                    y_preds = outputs[:, 0].cpu().numpy()  # Shape: (batch_size, 70, 70)\n                    \n                    # Process each sample in the batch\n                    for y_pred, oid_test in zip(y_preds, oids_test):\n                        # For each y position (depth level)\n                        for y_pos in range(70):\n                            # Extract values at specified x positions (1, 3, 5, ..., 69)\n                            row_data = {}\n                            for col_idx, x_pos in enumerate(range(1, 70, 2)):\n                                row_data[f\"x_{x_pos}\"] = float(y_pred[y_pos, x_pos])\n                            \n                            # Create row identifier\n                            row_data[\"oid_ypos\"] = f\"{oid_test}_y_{y_pos}\"\n                            \n                            # Write row\n                            writer.writerow(row_data)\n                            row_count += 1\n                    \n                    # Flush buffer periodically for large files\n                    if row_count % 100_000 == 0:\n                        csvfile.flush()\n                        elapsed = time.time() - t0\n                        print(f\"    Processed {row_count:,} rows in {format_time(elapsed)}\")\n    \n    # Final timing\n    total_time = time.time() - t0\n    print(f\"\\n✅ Submission generation completed!\")\n    print(f\"    Total rows: {row_count:,}\")\n    print(f\"    Total time: {format_time(total_time)}\")\n    print(f\"    Average time per sample: {total_time/len(test_dataset):.3f}s\")\n    print(f\"💾 Submission saved to: submission.csv\")\n    \n    # Validate submission format\n    try:\n        submission_df = pd.read_csv(\"submission.csv\")\n        print(f\"\\n📊 Submission validation:\")\n        print(f\"    Shape: {submission_df.shape}\")\n        print(f\"    Columns: {list(submission_df.columns)}\")\n        print(f\"    Sample rows:\")\n        print(submission_df.head())\n        \n        # Check for expected number of rows (num_test_files * 70 y_positions)\n        expected_rows = len(test_files) * 70\n        if len(submission_df) == expected_rows:\n            print(f\"✅ Row count matches expected: {expected_rows}\")\n        else:\n            print(f\"⚠️  Row count mismatch: got {len(submission_df)}, expected {expected_rows}\")\n            \n    except Exception as e:\n        print(f\"⚠️  Could not validate submission: {e}\")\n\nif __name__ == \"__main__\":\n    generate_submission()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Physics Informed Neural Network with SwinV2 for Seismic Inversion\n# import torch\n# import torch.nn as nn\n# import torch.optim as optim\n# from torch.utils.data import DataLoader\n# import pandas as pd\n# import numpy as np\n# import torchvision.models as models\n# from torchvision.models import Swin_V2_T_Weights\n# # from SeismicInversionDataset import SeismicInversionDataset  # Import your dataset class\n\n# class PhysicsInformedSwinV2InversionNet(nn.Module):\n#     \"\"\"\n#     Physics Informed SwinV2-based InversionNet for seismic inversion\n#     Input: (batch, 5, 1000, 70) - seismic waveforms\n#     Output: (batch, 1, 70, 70) - velocity maps\n    \n#     Incorporates:\n#     - Wave equation physics\n#     - Velocity range constraints\n#     - Smoothness regularization\n#     \"\"\"\n#     def __init__(self, pretrained=True, velocity_range=(1500, 5000), dt=0.001, dx=25.0):\n#         super(PhysicsInformedSwinV2InversionNet, self).__init__()\n        \n#         # Physical parameters\n#         self.velocity_min = velocity_range[0]  # Minimum velocity (m/s)\n#         self.velocity_max = velocity_range[1]  # Maximum velocity (m/s)\n#         self.dt = dt  # Time step (s)\n#         self.dx = dx  # Spatial step (m)\n        \n#         # Input adaptation layer\n#         self.input_adapter = nn.Sequential(\n#             nn.Conv2d(5, 3, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(3)\n#         )\n        \n#         # Load pretrained SwinV2 as feature extractor\n#         if pretrained:\n#             weights = Swin_V2_T_Weights.IMAGENET1K_V1\n#             swin_model = models.swin_v2_t(weights=weights)\n#         else:\n#             swin_model = models.swin_v2_t(weights=None)\n        \n#         # Use SwinV2 as feature extractor (remove classifier)\n#         self.feature_extractor = nn.Sequential(*list(swin_model.children())[:-1])\n        \n#         # Physics-aware decoder network\n#         self.decoder = nn.Sequential(\n#             nn.Linear(768, 1024),  # 768 is swin_v2_t output dimension\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n            \n#             nn.Linear(1024, 2048),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n            \n#             # Output raw velocity predictions\n#             nn.Linear(2048, 70 * 70),\n#         )\n        \n#         # Physics-constrained refinement layers\n#         self.physics_refiner = nn.Sequential(\n#             nn.Conv2d(1, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             nn.Conv2d(32, 64, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(64),\n            \n#             nn.Conv2d(64, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             nn.Conv2d(32, 1, kernel_size=1)\n#         )\n        \n#         # Learnable physics parameters\n#         self.physics_weight = nn.Parameter(torch.tensor(1.0))\n#         # self.smoothness_weight = nn.Parameter(torch.tensor(0.01))  # Instead of 0.1\n#         smoothness_weight_adaptive = 0.1 * torch.exp(-epoch/10)\n\n        \n#     def apply_velocity_constraints(self, velocity):\n#         \"\"\"Apply physical velocity constraints using sigmoid activation\"\"\"\n#         # Normalize to [0, 1] then scale to [v_min, v_max]\n#         velocity_normalized = torch.sigmoid(velocity)\n#         velocity_constrained = (self.velocity_min + \n#                               velocity_normalized * (self.velocity_max - self.velocity_min))\n#         return velocity_constrained\n    \n#     def compute_wave_equation_residual(self, velocity, waveforms):\n#         \"\"\"\n#         Compute wave equation residual for physics loss\n#         2D acoustic wave equation: ∂²p/∂t² = v²(∇²p)\n#         \"\"\"\n#         batch_size = velocity.size(0)\n        \n#         # Compute spatial derivatives using finite differences\n#         # Second derivative in x-direction\n#         d2p_dx2 = torch.zeros_like(velocity)\n#         d2p_dx2[:, :, 1:-1, :] = (velocity[:, :, 2:, :] - 2*velocity[:, :, 1:-1, :] + \n#                                   velocity[:, :, :-2, :]) / (self.dx**2)\n        \n#         # Second derivative in z-direction\n#         d2p_dz2 = torch.zeros_like(velocity)\n#         d2p_dz2[:, :, :, 1:-1] = (velocity[:, :, :, 2:] - 2*velocity[:, :, :, 1:-1] + \n#                                   velocity[:, :, :, :-2]) / (self.dx**2)\n        \n#         # Laplacian\n#         laplacian = d2p_dx2 + d2p_dz2\n        \n#         # Simplified wave equation residual\n#         # In practice, you would use the actual seismic waveforms and time derivatives\n#         wave_residual = torch.mean(torch.abs(laplacian))\n        \n#         return wave_residual\n    \n#     def compute_smoothness_loss(self, velocity):\n#         \"\"\"Compute total variation loss for smoothness regularization\"\"\"\n#         # Horizontal differences\n#         h_diff = torch.abs(velocity[:, :, 1:, :] - velocity[:, :, :-1, :])\n#         # Vertical differences\n#         v_diff = torch.abs(velocity[:, :, :, 1:] - velocity[:, :, :, :-1])\n        \n#         smoothness_loss = torch.mean(h_diff) + torch.mean(v_diff)\n#         return smoothness_loss\n    \n#     def compute_geological_constraints(self, velocity):\n#         \"\"\"Apply geological constraints (velocity should increase with depth)\"\"\"\n#         # Velocity should generally increase with depth (z-direction)\n#         depth_gradient = velocity[:, :, 1:, :] - velocity[:, :, :-1, :]\n#         # Penalize negative gradients (velocity decreasing with depth)\n#         geological_loss = torch.mean(torch.relu(-depth_gradient))\n#         return geological_loss\n    \n#     def forward(self, x):\n#         # x shape: (batch, 5, 1000, 70)\n#         batch_size = x.size(0)\n        \n#         # Store input waveforms for physics loss\n#         input_waveforms = x.clone()\n        \n#         # Adapt input channels\n#         x = self.input_adapter(x)  # (batch, 3, 1000, 70)\n        \n#         # Resize to standard image size for SwinV2\n#         x = nn.functional.interpolate(x, size=(224, 224), mode='bilinear', align_corners=False)\n        \n#         # Extract features using SwinV2\n#         features = self.feature_extractor(x)  # (batch, 768)\n        \n#         # Decode to velocity map\n#         raw_velocity = self.decoder(features)  # (batch, 70*70)\n        \n#         # Reshape to 2D map\n#         raw_velocity = raw_velocity.view(batch_size, 1, 70, 70)  # (batch, 1, 70, 70)\n        \n#         # Apply velocity constraints\n#         velocity_constrained = self.apply_velocity_constraints(raw_velocity)\n        \n#         # Physics-aware refinement\n#         velocity_refined = self.physics_refiner(velocity_constrained)\n        \n#         # Final velocity map with constraints\n#         final_velocity = self.apply_velocity_constraints(velocity_refined)\n        \n#         return final_velocity\n    \n#     def physics_loss(self, predicted_velocity, input_waveforms, targets):\n#         \"\"\"\n#         Compute total physics-informed loss\n#         \"\"\"\n#         # Data fitting loss (MSE or L1)\n#         data_loss = nn.functional.l1_loss(predicted_velocity, targets)\n        \n#         # Physics-based losses\n#         wave_residual = self.compute_wave_equation_residual(predicted_velocity, input_waveforms)\n#         smoothness_loss = self.compute_smoothness_loss(predicted_velocity)\n#         geological_loss = self.compute_geological_constraints(predicted_velocity)\n        \n#         # Velocity constraint loss (ensure values are within physical bounds)\n#         velocity_constraint_loss = torch.mean(\n#             torch.relu(predicted_velocity - self.velocity_max) + \n#             torch.relu(self.velocity_min - predicted_velocity)\n#         )\n        \n#         # Combine all losses\n#         total_loss = (data_loss + \n#                      self.physics_weight * wave_residual +\n#                      self.smoothness_weight * smoothness_loss +\n#                      0.1 * geological_loss +\n#                      10.0 * velocity_constraint_loss)\n        \n#         return total_loss, {\n#             'data_loss': data_loss.item(),\n#             'wave_residual': wave_residual.item(),\n#             'smoothness_loss': smoothness_loss.item(),\n#             'geological_loss': geological_loss.item(),\n#             'velocity_constraint_loss': velocity_constraint_loss.item()\n#         }\n\n# class PhysicsLoss(nn.Module):\n#     \"\"\"Separate physics loss module for cleaner code organization\"\"\"\n    \n#     def __init__(self, model, alpha_physics=1.0, alpha_smooth=0.1, alpha_geo=0.1):\n#         super(PhysicsLoss, self).__init__()\n#         self.model = model\n#         self.alpha_physics = alpha_physics\n#         self.alpha_smooth = alpha_smooth\n#         self.alpha_geo = alpha_geo\n#         self.mse_loss = nn.MSELoss()\n#         self.l1_loss = nn.L1Loss()\n        \n#     def forward(self, predictions, targets, input_waveforms):\n#         \"\"\"Compute combined physics-informed loss\"\"\"\n#         return self.model.physics_loss(predictions, input_waveforms, targets)\n\n# def test_seismic_cnn():\n#     data = {\n#         'dt': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis4_1_0.npy'],\n#         'md': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel4_1_0.npy']\n#     }\n#     df = pd.DataFrame(data)\n    \n#     # Initialize dataset and dataloader\n#     device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n#     print(f\"🖥️  Using device: {device}\")\n    \n#     dataset = SeismicInversionDataset(df, device=device)\n#     dataloader = DataLoader(dataset, batch_size=4, shuffle=True)\n    \n#     # Initialize Physics Informed model\n#     print(\"🏗️  Initializing Physics Informed SwinV2 InversionNet...\")\n    \n#     # Physical parameters for seismic inversion\n#     velocity_range = (1500, 5000)  # Typical seismic velocities (m/s)\n#     dt = 0.001  # Time sampling (1ms)\n#     dx = 25.0   # Spatial sampling (25m)\n    \n#     model = PhysicsInformedSwinV2InversionNet(\n#         pretrained=True, \n#         velocity_range=velocity_range,\n#         dt=dt,\n#         dx=dx\n#     ).to(device)\n    \n#     # Use physics-informed loss\n#     physics_criterion = PhysicsLoss(model)\n    \n#     # Optimizer with different learning rates\n#     swin_params = []\n#     decoder_params = []\n#     physics_params = []\n    \n#     for name, param in model.named_parameters():\n#         if 'feature_extractor' in name:\n#             swin_params.append(param)\n#         elif 'physics_weight' in name or 'smoothness_weight' in name:\n#             physics_params.append(param)\n#         else:\n#             decoder_params.append(param)\n    \n#     optimizer = optim.Adam([\n#         {'params': swin_params, 'lr': 1e-5},      # Lower LR for pretrained features\n#         {'params': decoder_params, 'lr': 1e-3},   # Higher LR for new layers\n#         {'params': physics_params, 'lr': 1e-4}    # Medium LR for physics parameters\n#     ])\n    \n#     # Learning rate scheduler\n#     scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', patience=3, factor=0.5)\n    \n#     print(f\"\\n🏗️  Model Architecture:\")\n#     print(model)\n    \n#     # Count parameters\n#     total_params = sum(p.numel() for p in model.parameters())\n#     trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n#     print(f\"\\n📊 Total parameters: {total_params:,}\")\n#     print(f\"📊 Trainable parameters: {trainable_params:,}\")\n#     print(f\"🌊 Velocity range: {velocity_range[0]}-{velocity_range[1]} m/s\")\n#     print(f\"⏱️  Time step: {dt} s\")\n#     print(f\"📏 Spatial step: {dx} m\")\n    \n#     # Training loop with physics-informed loss\n#     num_epochs = 10\n#     print(f\"\\n🚀 Starting Physics Informed training for {num_epochs} epochs...\")\n    \n#     model.train()\n#     freeze_epochs = 3\n    \n#     for epoch in range(num_epochs):\n#         # Optionally unfreeze SwinV2 after initial training\n#         if epoch == freeze_epochs:\n#             print(f\"🔓 Unfreezing SwinV2 backbone at epoch {epoch+1}\")\n#             for param in model.feature_extractor.parameters():\n#                 param.requires_grad = True\n        \n#         epoch_loss = 0.0\n#         epoch_losses = {\n#             'data_loss': 0.0,\n#             'wave_residual': 0.0,\n#             'smoothness_loss': 0.0,\n#             'geological_loss': 0.0,\n#             'velocity_constraint_loss': 0.0\n#         }\n#         num_batches = 0\n        \n#         for batch_idx, (inputs, targets) in enumerate(dataloader):\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n            \n#             # Forward pass\n#             optimizer.zero_grad()\n#             outputs = model(inputs)\n            \n#             # Compute physics-informed loss\n#             loss, loss_components = physics_criterion(outputs, targets, inputs)\n            \n#             # Backward pass\n#             loss.backward()\n            \n#             # Gradient clipping for stability\n#             torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)\n            \n#             optimizer.step()\n            \n#             epoch_loss += loss.item()\n#             num_batches += 1\n            \n#             # Accumulate loss components\n#             for key, value in loss_components.items():\n#                 epoch_losses[key] += value\n            \n#             # Log batch progress\n#             if batch_idx % 10 == 0:\n#                 print(f\"  Epoch [{epoch+1}/{num_epochs}], Batch [{batch_idx+1}]\")\n#                 print(f\"    Total Loss: {loss.item():.6f}\")\n#                 print(f\"    Data Loss: {loss_components['data_loss']:.6f}\")\n#                 print(f\"    Physics Loss: {loss_components['wave_residual']:.6f}\")\n#                 print(f\"    Smoothness: {loss_components['smoothness_loss']:.6f}\")\n#                 print(f\"    Velocity Range: [{outputs.min():.1f}, {outputs.max():.1f}] m/s\")\n        \n#         avg_loss = epoch_loss / num_batches\n        \n#         # Average loss components\n#         for key in epoch_losses:\n#             epoch_losses[key] /= num_batches\n        \n#         print(f\"\\n📈 Epoch [{epoch+1}/{num_epochs}] Summary:\")\n#         print(f\"    Average Total Loss: {avg_loss:.6f}\")\n#         print(f\"    Data Loss: {epoch_losses['data_loss']:.6f}\")\n#         print(f\"    Wave Residual: {epoch_losses['wave_residual']:.6f}\")\n#         print(f\"    Smoothness Loss: {epoch_losses['smoothness_loss']:.6f}\")\n#         print(f\"    Geological Loss: {epoch_losses['geological_loss']:.6f}\")\n#         print(f\"    Velocity Constraint Loss: {epoch_losses['velocity_constraint_loss']:.6f}\")\n#         print(f\"    Physics Weight: {model.physics_weight.item():.4f}\")\n#         print(f\"    Smoothness Weight: {model.smoothness_weight.item():.4f}\")\n#         print(\"-\" * 70)\n        \n#         # Update learning rate\n#         scheduler.step(avg_loss)\n    \n#     print(\"✅ Physics Informed training completed!\")\n    \n#     # Save model\n#     model_path = '/kaggle/working/physics_informed_swinv2_inversionnet.pth'\n#     torch.save({\n#         'model_state_dict': model.state_dict(),\n#         'velocity_range': velocity_range,\n#         'dt': dt,\n#         'dx': dx,\n#     }, model_path)\n#     print(f\"💾 Model saved to: {model_path}\")\n    \n#     # Test with a single batch\n#     print(\"\\n🧪 Testing Physics Informed model...\")\n    \n#     model.eval()\n#     with torch.no_grad():\n#         for inputs, targets in dataloader:\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n#             outputs = model(inputs)\n            \n#             # Compute final physics metrics\n#             final_loss, loss_components = physics_criterion(outputs, targets, inputs)\n            \n#             print(f\"Input shape: {inputs.shape}\")\n#             print(f\"Target shape: {targets.shape}\")\n#             print(f\"Output shape: {outputs.shape}\")\n#             print(f\"Output velocity range: [{outputs.min():.1f}, {outputs.max():.1f}] m/s\")\n#             print(f\"Target velocity range: [{targets.min():.1f}, {targets.max():.1f}] m/s\")\n#             print(f\"Final physics loss: {final_loss.item():.6f}\")\n            \n#             # Check if velocities are within physical bounds\n#             within_bounds = torch.all((outputs >= velocity_range[0]) & (outputs <= velocity_range[1]))\n#             print(f\"All velocities within physical bounds: {within_bounds}\")\n#             break\n\n# if __name__ == \"__main__\":\n#     test_seismic_cnn()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # from dataset import SeismicInversionDataset\n# from torch.utils.data import DataLoader\n\n# dataset = SeismicInversionDataset(df, device='cuda')\n# train_loader = DataLoader(dataset, batch_size=16, shuffle=True, num_workers=4)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # config.py\n# import os\n# import torch\n\n# class Config:\n#     \"\"\"\n#     Configuration class for the Waveform Inversion project.\n#     Stores paths, model parameters, training hyperparameters, etc.\n#     \"\"\"\n#     # --- Data Paths ---\n#     # Base directory for the Kaggle dataset\n#     DATA_ROOT = \"/kaggle/input/waveform-inversion\" \n#     TRAIN_DT_DIR = os.path.join(DATA_ROOT, \"train_samples_dt\")\n#     TRAIN_MD_DIR = os.path.join(DATA_ROOT, \"train_velocity_maps\") # Assuming this contains velocity maps for training\n#     TEST_DT_DIR = os.path.join(DATA_ROOT, \"test_samples_dt\")\n#     # For test data, we won't have MD. Predictions will be saved for submission.\n\n#     # --- Model Parameters ---\n#     IN_CHANNELS = 5  # Number of input channels (num_sources)\n#     OUT_CHANNELS = 1 # Number of output channels (for the 2D velocity map)\n\n#     # Input shape after preprocessing: (N, C, D, H, W) -> (N, 5, 1000, 70, 1)\n#     IMG_DEPTH = 1000 # Time steps / Depth dimension of seismic data\n#     IMG_HEIGHT = 70  # Receiver dimension of seismic data (interpreted as Height)\n#     IMG_WIDTH = 1    # Dummy dimension added to make input 5D (interpreted as Width)\n\n#     # Target shape after preprocessing: (N, C_out, D_out, H_out, W_out) -> (N, 1, 1, 70, 70)\n#     # The U-Net will reduce D (1000 -> 1) and expand W (1 -> 70)\n#     TARGET_DEPTH = 1\n#     TARGET_HEIGHT = 70\n#     TARGET_WIDTH = 70\n\n#     # --- Training Parameters ---\n#     BATCH_SIZE = 2 # Adjusted for memory, 1000 depth is large. Consider gradient accumulation if too small.\n#     NUM_EPOCHS = 50\n#     LEARNING_RATE = 1e-4\n#     NUM_WORKERS = 4 # Number of DataLoader workers. Set to 0 for easier debugging.\n#     GRADIENT_ACCUMULATION_STEPS = 1 # Set to >1 to simulate larger batch size without consuming more VRAM\n\n#     # --- Scheduler Parameters ---\n#     T_MAX = NUM_EPOCHS # For CosineAnnealingLR. This is typically the number of iterations or epochs.\n\n#     # --- Other Settings ---\n#     DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n#     CHECKPOINT_DIR = \"./checkpoints\"\n#     PREDICTION_DIR = \"./predictions\"\n#     LOG_INTERVAL = 10 # Log training progress every N batches\n#     SAVE_INTERVAL = 5 # Save model checkpoint every N epochs\n\n#     def __init__(self):\n#         \"\"\"Initializes configuration and creates necessary directories.\"\"\"\n#         os.makedirs(self.CHECKPOINT_DIR, exist_ok=True)\n#         os.makedirs(self.PREDICTION_DIR, exist_ok=True)\n#         print(f\"Using device: {self.DEVICE}\")\n\n# # --- End of config.py ---\n\n\n# # dataset.py\n# import os\n# import numpy as np\n# import torch\n# from torch.utils.data import Dataset\n# import pandas as pd # Required for processing DataFrame\n# from config import Config # Import the configuration\n\n# class SeismicInversionDataset(Dataset):\n#     \"\"\"\n#     Loads seismic waveform (dt) and velocity map (md) .npy files,\n#     flattens 500-sample chunks across all files into a single dataset.\n    \n#     This dataset prepares data for a 3D U-Net with specific input/output transformations:\n#     - Input (x): Raw (5, 1000, 70) data is transformed to (5, 1000, 70, 1) by unsqueezing a dummy 4th dimension.\n#       This corresponds to (Channels, Depth, Height, Width=1) for a 3D convolutional network.\n#     - Target (y): Raw (70, 70) label data is transformed to (1, 1, 70, 70) by adding a channel\n#       and a dummy depth dimension. This corresponds to (Channels=1, Depth=1, Height=70, Width=70).\n#     \"\"\"\n#     def __init__(self, df: pd.DataFrame, transform=None):\n#         \"\"\"\n#         Initializes the dataset.\n\n#         Args:\n#             df (pd.DataFrame): DataFrame containing 'dt' and 'md' columns with paths to .npy files.\n#             transform (callable, optional): Optional transform to be applied on a sample.\n#         \"\"\"\n#         self.seismic_data_paths = df['dt'].tolist()\n#         self.velocity_map_paths = df['md'].tolist()\n#         self.transform = transform\n\n#         self.loaded_seismic_data = []  # To store numpy memmap arrays for dt files\n#         self.loaded_velocity_maps = [] # To store numpy memmap arrays for md files\n#         self.sample_indices = []       # Stores (file_idx, sample_in_file_idx) mappings for flat access\n\n#         print(f\"📦 Initializing dataset from {len(df)} data pairs...\")\n\n#         for file_idx, (dt_path, md_path) in enumerate(zip(self.seismic_data_paths, self.velocity_map_paths)):\n#             print(f\"  [{file_idx+1}/{len(df)}] Loading:\")\n#             print(f\"     ├─ dt: {os.path.basename(dt_path)}\")\n#             print(f\"     └─ md: {os.path.basename(md_path)}\")\n\n#             # Load .npy files with mmap_mode='r' for memory efficiency\n#             # dt_array expected shape from problem description: (500, 5, 1000, 70)\n#             # md_array expected shape from problem description: (500, 70, 70)\n#             dt_array = np.load(dt_path, mmap_mode='r')\n#             md_array = np.load(md_path, mmap_mode='r')\n\n#             # Basic sanity checks on loaded shapes\n#             if not (dt_array.shape[0] == md_array.shape[0] == 500):\n#                  raise ValueError(\n#                      f\"Mismatched or unexpected sample count for {os.path.basename(dt_path)} and {os.path.basename(md_path)}. \"\n#                      f\"Expected 500 samples per file, got {dt_array.shape[0]} and {md_array.shape[0]}.\"\n#                  )\n#             if not (dt_array.shape[1:] == (Config.IN_CHANNELS, Config.IMG_DEPTH, Config.IMG_HEIGHT)):\n#                 print(f\"Warning: dt_array sample shape {dt_array.shape[1:]} deviates from expected \"\n#                       f\"(C={Config.IN_CHANNELS}, D={Config.IMG_DEPTH}, H={Config.IMG_HEIGHT}). \"\n#                       f\"Proceeding assuming (C, D, H).\")\n#             if not (md_array.shape[1:] == (Config.TARGET_HEIGHT, Config.TARGET_WIDTH)):\n#                 print(f\"Warning: md_array sample shape {md_array.shape[1:]} deviates from expected \"\n#                       f\"(H={Config.TARGET_HEIGHT}, W={Config.TARGET_WIDTH}). \"\n#                       f\"Proceeding assuming (H, W).\")\n\n#             self.loaded_seismic_data.append(dt_array)\n#             self.loaded_velocity_maps.append(md_array)\n\n#             # Map each of the 500 samples within the file to a global index\n#             for sample_in_file_idx in range(dt_array.shape[0]):\n#                 self.sample_indices.append((file_idx, sample_in_file_idx))\n\n#         self.N = len(self.sample_indices)\n#         print(f\"✅ Initialized dataset with {self.N} total samples.\")\n\n#     def __len__(self):\n#         \"\"\"Returns the total number of individual samples in the dataset.\"\"\"\n#         return self.N\n\n#     def __getitem__(self, idx):\n#         \"\"\"\n#         Retrieves a single data sample and its corresponding label.\n\n#         Args:\n#             idx (int): The index of the sample to retrieve.\n\n#         Returns:\n#             tuple: (x, y) where x is the input tensor and y is the target tensor.\n#                    x shape: (C, D, H, W) -> (5, 1000, 70, 1)\n#                    y shape: (C_out, D_out, H_out, W_out) -> (1, 1, 70, 70)\n#         \"\"\"\n#         file_idx, sample_in_file_idx = self.sample_indices[idx]\n\n#         # Load input (seismic data)\n#         # x_raw shape: (5, 1000, 70) from disk\n#         x_raw = self.loaded_seismic_data[file_idx][sample_in_file_idx]\n#         x = torch.from_numpy(x_raw).float()\n#         # Add a dummy 4th dimension (Width=1) to make it (C, D, H, W) compatible with 3D Conv\n#         x = x.unsqueeze(-1) # shape: (5, 1000, 70, 1)\n\n#         # Load target (velocity map)\n#         # y_raw shape: (70, 70) from disk\n#         y_raw = self.loaded_velocity_maps[file_idx][sample_in_file_idx]\n#         y = torch.from_numpy(y_raw).float()\n#         # Add channel and dummy depth dimensions to make it (C_out, D_out, H_out, W_out)\n#         y = y.unsqueeze(0).unsqueeze(0) # shape: (1, 1, 70, 70)\n\n#         if self.transform:\n#             x, y = self.transform(x, y)\n\n#         return x, y\n\n# # --- Example Usage (for testing this chunk) ---\n# if __name__ == '__main__':\n#     # Create a dummy DataFrame and dummy .npy files for testing\n#     def create_dummy_data(num_files=1):\n#         dummy_data_dir = \"./data/dummy_train\"\n#         os.makedirs(dummy_data_dir, exist_ok=True)\n        \n#         dt_paths = []\n#         md_paths = []\n\n#         print(f\"Creating {num_files} dummy data file pairs in {dummy_data_dir}...\")\n#         for i in range(num_files):\n#             dt_filename = f\"train_dt_{i}.npy\"\n#             md_filename = f\"train_md_{i}.npy\"\n#             dt_path = os.path.join(dummy_data_dir, dt_filename)\n#             md_path = os.path.join(dummy_data_dir, md_filename)\n\n#             # Simulate shapes from competition:\n#             # dt: (500, 5, 1000, 70) -> per file\n#             # md: (500, 70, 70)     -> per file\n#             np.save(dt_path, np.random.rand(500, 5, 1000, 70).astype(np.float32))\n#             np.save(md_path, np.random.rand(500, 70, 70).astype(np.float32))\n\n#             dt_paths.append(dt_path)\n#             md_paths.append(md_path)\n        \n#         return pd.DataFrame({'dt': dt_paths, 'md': md_paths})\n\n#     # Initialize Config\n#     cfg = Config()\n#     print(f\"\\nConfig Device: {cfg.DEVICE}\")\n\n#     # Test Dataset and DataLoader\n#     print(\"\\n--- Testing SeismicInversionDataset ---\")\n#     dummy_df = create_dummy_data(num_files=1) # Create 1 dummy file pair for test\n#     dataset = SeismicInversionDataset(dummy_df)\n\n#     print(f\"Total samples in dataset: {len(dataset)}\")\n    \n#     # Check a single sample\n#     x_sample, y_sample = dataset[0]\n#     print(f\"Shape of x_sample (input to model): {x_sample.shape}\") # Expected: (5, 1000, 70, 1)\n#     print(f\"Shape of y_sample (target): {y_sample.shape}\") # Expected: (1, 1, 70, 70)\n\n#     # Test DataLoader\n#     from torch.utils.data import DataLoader\n#     # Set num_workers=0 for easier debugging, change to cfg.NUM_WORKERS for training\n#     data_loader = DataLoader(dataset, batch_size=cfg.BATCH_SIZE, shuffle=True, num_workers=0) \n    \n#     print(f\"\\n--- Testing DataLoader with batch_size={cfg.BATCH_SIZE} ---\")\n#     for batch_idx, (batch_x, batch_y) in enumerate(data_loader):\n#         print(f\"Batch {batch_idx+1}:\")\n#         print(f\"  batch_x shape: {batch_x.shape}\") # Expected: (BATCH_SIZE, 5, 1000, 70, 1)\n#         print(f\"  batch_y shape: {batch_y.shape}\") # Expected: (BATCH_SIZE, 1, 1, 70, 70)\n#         break # Just check the first batch\n\n#     print(\"\\nDataset and DataLoader test complete.\")\n#     print(\"Please delete the './data/dummy_train' directory manually if not needed.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import torch\n# import torch.nn as nn\n# import torch.optim as optim\n# from torch.utils.data import DataLoader\n# import pandas as pd\n# import numpy as np\n# # from SeismicInversionDataset import SeismicInversionDataset  # Import your dataset class\n\n# class BasicSeismicCNN(nn.Module):\n#     \"\"\"\n#     Basic CNN for seismic inversion\n#     Input: (batch, 5, 1000, 70) - seismic waveforms\n#     Output: (batch, 1, 70, 70) - velocity maps\n#     \"\"\"\n#     def __init__(self):\n#         super(BasicSeismicCNN, self).__init__()\n        \n#         self.conv_layers = nn.Sequential(\n#             # First conv block\n#             nn.Conv2d(5, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             # Second conv block\n#             nn.Conv2d(32, 64, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(64),\n            \n#             # Third conv block\n#             nn.Conv2d(64, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             # Output layer\n#             nn.Conv2d(32, 1, kernel_size=1)\n#         )\n        \n#         # Adaptive pooling to ensure exact output size\n#         self.adaptive_pool = nn.AdaptiveAvgPool2d((70, 70))\n    \n#     def forward(self, x):\n#         x = self.conv_layers(x)\n#         x = self.adaptive_pool(x)\n#         return x\n\n# def test_seismic_cnn():\n#     # Create dummy dataframe (replace with your actual data paths)\n#     data = {\n#         'dt': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis4_1_0.npy'],  # Replace with actual paths\n#         'md': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel4_1_0.npy']   # Replace with actual paths\n#     }\n#     df = pd.DataFrame(data)\n    \n#     # Initialize dataset and dataloader\n#     device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n#     print(f\"🖥️  Using device: {device}\")\n    \n#     dataset = SeismicInversionDataset(df, device=device)\n#     dataloader = DataLoader(dataset, batch_size=4, shuffle=True)\n    \n#     # Initialize model, loss, and optimizer\n#     model = BasicSeismicCNN().to(device)\n#     criterion = nn.L1Loss()\n#     optimizer = optim.Adam(model.parameters(), lr=0.001)\n    \n#     print(f\"\\n🏗️  Model Architecture:\")\n#     print(model)\n    \n#     # Count parameters\n#     total_params = sum(p.numel() for p in model.parameters())\n#     trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n#     print(f\"\\n📊 Total parameters: {total_params:,}\")\n#     print(f\"📊 Trainable parameters: {trainable_params:,}\")\n    \n#     # Training loop\n#     num_epochs = 10\n#     print(f\"\\n🚀 Starting training for {num_epochs} epochs...\")\n    \n#     model.train()\n#     for epoch in range(num_epochs):\n#         epoch_loss = 0.0\n#         num_batches = 0\n        \n#         for batch_idx, (inputs, targets) in enumerate(dataloader):\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n            \n#             # Forward pass\n#             optimizer.zero_grad()\n#             outputs = model(inputs)\n#             loss = criterion(outputs, targets)\n            \n#             # Backward pass\n#             loss.backward()\n#             optimizer.step()\n            \n#             epoch_loss += loss.item()\n#             num_batches += 1\n            \n#             # Log batch progress\n#             if batch_idx % 10 == 0:\n#                 print(f\"  Epoch [{epoch+1}/{num_epochs}], Batch [{batch_idx+1}], Loss: {loss.item():.6f}\")\n        \n#         avg_loss = epoch_loss / num_batches\n#         print(f\"📈 Epoch [{epoch+1}/{num_epochs}] - Average Loss: {avg_loss:.6f}\")\n#         print(\"-\" * 50)\n    \n#     print(\"✅ Training completed!\")\n    \n#     # Save model\n#     model_path = '/kaggle/working/seismic_cnn_model.pth'\n#     torch.save(model.state_dict(), model_path)\n#     print(f\"💾 Model saved to: {model_path}\")\n#     # Test with a single batch\n#     print(\"\\n🧪 Testing model output shapes...\")\n    \n#     model.eval()\n#     with torch.no_grad():\n#         for inputs, targets in dataloader:\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n#             outputs = model(inputs)\n            \n#             print(f\"Input shape: {inputs.shape}\")\n#             print(f\"Target shape: {targets.shape}\")\n#             print(f\"Output shape: {outputs.shape}\")\n#             print(f\"Output range: [{outputs.min():.4f}, {outputs.max():.4f}]\")\n#             break\n\n# if __name__ == \"__main__\":\n#     test_seismic_cnn()\n\n# InversionNet\n# import torch\n# import torch.nn as nn\n# import torch.optim as optim\n# from torch.utils.data import DataLoader\n# import pandas as pd\n# import numpy as np\n# from SeismicInversionDataset import SeismicInversionDataset  # Import your dataset class\n\n# class InversionNet(nn.Module):\n#     \"\"\"\n#     InversionNet for seismic inversion\n#     Input: (batch, 5, 1000, 70) - seismic waveforms\n#     Output: (batch, 1, 70, 70) - velocity maps\n#     \"\"\"\n#     def __init__(self):\n#         super(InversionNet, self).__init__()\n        \n#         self.conv_layers = nn.Sequential(\n#             # First conv block\n#             nn.Conv2d(5, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             # Second conv block\n#             nn.Conv2d(32, 64, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(64),\n            \n#             # Third conv block\n#             nn.Conv2d(64, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             # Output layer\n#             nn.Conv2d(32, 1, kernel_size=1)\n#         )\n        \n#         # Adaptive pooling to ensure exact output size\n#         self.adaptive_pool = nn.AdaptiveAvgPool2d((70, 70))\n    \n#     def forward(self, x):\n#         x = self.conv_layers(x)\n#         x = self.adaptive_pool(x)\n#         return x\n\n# def test_seismic_cnn():\n#     data = {\n#         'dt': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis4_1_0.npy'],  # Replace with actual paths\n#         'md': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel4_1_0.npy']   # Replace with actual paths\n#     }\n#     # df = pd.DataFrame(data)\n    \n#     # Initialize dataset and dataloader\n#     device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n#     print(f\"🖥️  Using device: {device}\")\n    \n#     dataset = SeismicInversionDataset(df, device=device)\n#     dataloader = DataLoader(dataset, batch_size=4, shuffle=True)\n    \n#     # Initialize model, loss, and optimizer\n#     model = InversionNet().to(device)\n#     criterion = nn.L1Loss()\n#     optimizer = optim.Adam(model.parameters(), lr=0.001)\n    \n#     print(f\"\\n🏗️  Model Architecture:\")\n#     print(model)\n    \n#     # Count parameters\n#     total_params = sum(p.numel() for p in model.parameters())\n#     trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n#     print(f\"\\n📊 Total parameters: {total_params:,}\")\n#     print(f\"📊 Trainable parameters: {trainable_params:,}\")\n    \n#     # Training loop\n#     num_epochs = 10 ###############Epochs######################\n#     print(f\"\\n🚀 Starting training for {num_epochs} epochs...\")\n    \n#     model.train()\n#     for epoch in range(num_epochs):\n#         epoch_loss = 0.0\n#         num_batches = 0\n        \n#         for batch_idx, (inputs, targets) in enumerate(dataloader):\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n            \n#             # Forward pass\n#             optimizer.zero_grad()\n#             outputs = model(inputs)\n#             loss = criterion(outputs, targets)\n            \n#             # Backward pass\n#             loss.backward()\n#             optimizer.step()\n            \n#             epoch_loss += loss.item()\n#             num_batches += 1\n            \n#             # Log batch progress\n#             if batch_idx % 10 == 0:\n#                 print(f\"  Epoch [{epoch+1}/{num_epochs}], Batch [{batch_idx+1}], Loss: {loss.item():.6f}\")\n        \n#         avg_loss = epoch_loss / num_batches\n#         print(f\"📈 Epoch [{epoch+1}/{num_epochs}] - Average Loss: {avg_loss:.6f}\")\n#         print(\"-\" * 50)\n    \n#     print(\"✅ Training completed!\")\n    \n#     # Save model\n#     model_path = '/kaggle/working/seismic_inversionnet_model.pth'\n#     torch.save(model.state_dict(), model_path)\n#     print(f\"💾 Model saved to: {model_path}\")\n#     # Test with a single batch\n#     print(\"\\n🧪 Testing model output shapes...\")\n    \n#     model.eval()\n#     with torch.no_grad():\n#         for inputs, targets in val_loader:\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n#             outputs = model(inputs)\n            \n#             print(f\"Input shape: {inputs.shape}\")\n#             print(f\"Target shape: {targets.shape}\")\n#             print(f\"Output shape: {outputs.shape}\")\n#             print(f\"Output range: [{outputs.min():.4f}, {outputs.max():.4f}]\")\n#             break\n\n# if __name__ == \"__main__\":\n#     test_seismic_cnn()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # InversionNet\n# import torch\n# import torch.nn as nn\n# import torch.optim as optim\n# from torch.utils.data import DataLoader\n# import pandas as pd\n# import numpy as np\n# # from SeismicInversionDataset import SeismicInversionDataset  # Import your dataset class\n\n# class InversionNet(nn.Module):\n#     \"\"\"\n#     InversionNet for seismic inversion\n#     Input: (batch, 5, 1000, 70) - seismic waveforms\n#     Output: (batch, 1, 70, 70) - velocity maps\n#     \"\"\"\n#     def __init__(self):\n#         super(InversionNet, self).__init__()\n        \n#         self.conv_layers = nn.Sequential(\n#             # First conv block\n#             nn.Conv2d(5, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             # Second conv block\n#             nn.Conv2d(32, 64, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(64),\n            \n#             # Third conv block\n#             nn.Conv2d(64, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             # Output layer\n#             nn.Conv2d(32, 1, kernel_size=1)\n#         )\n        \n#         # Adaptive pooling to ensure exact output size\n#         self.adaptive_pool = nn.AdaptiveAvgPool2d((70, 70))\n    \n#     def forward(self, x):\n#         x = self.conv_layers(x)\n#         x = self.adaptive_pool(x)\n#         return x\n\n# def test_seismic_cnn():\n#     data = {\n#         'dt': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis4_1_0.npy'],  # Replace with actual paths\n#         'md': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel4_1_0.npy']   # Replace with actual paths\n#     }\n#     # df = pd.DataFrame(data)\n    \n#     # Initialize dataset and dataloader\n#     device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n#     print(f\"🖥️  Using device: {device}\")\n    \n#     dataset = SeismicInversionDataset(df, device=device)\n#     dataloader = DataLoader(dataset, batch_size=4, shuffle=True)\n    \n#     # Initialize model, loss, and optimizer\n#     model = InversionNet().to(device)\n#     criterion = nn.L1Loss()\n#     optimizer = optim.Adam(model.parameters(), lr=0.001)\n    \n#     print(f\"\\n🏗️  Model Architecture:\")\n#     print(model)\n    \n#     # Count parameters\n#     total_params = sum(p.numel() for p in model.parameters())\n#     trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n#     print(f\"\\n📊 Total parameters: {total_params:,}\")\n#     print(f\"📊 Trainable parameters: {trainable_params:,}\")\n    \n#     # Training loop\n#     num_epochs = 10 ###############Epochs######################\n#     print(f\"\\n🚀 Starting training for {num_epochs} epochs...\")\n    \n#     model.train()\n#     for epoch in range(num_epochs):\n#         epoch_loss = 0.0\n#         num_batches = 0\n        \n#         for batch_idx, (inputs, targets) in enumerate(dataloader):\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n            \n#             # Forward pass\n#             optimizer.zero_grad()\n#             outputs = model(inputs)\n#             loss = criterion(outputs, targets)\n            \n#             # Backward pass\n#             loss.backward()\n#             optimizer.step()\n            \n#             epoch_loss += loss.item()\n#             num_batches += 1\n            \n#             # Log batch progress\n#             if batch_idx % 10 == 0:\n#                 print(f\"  Epoch [{epoch+1}/{num_epochs}], Batch [{batch_idx+1}], Loss: {loss.item():.6f}\")\n        \n#         avg_loss = epoch_loss / num_batches\n#         print(f\"📈 Epoch [{epoch+1}/{num_epochs}] - Average Loss: {avg_loss:.6f}\")\n#         print(\"-\" * 50)\n    \n#     print(\"✅ Training completed!\")\n    \n#     # Save model\n#     model_path = '/kaggle/working/seismic_inversionnet_model.pth'\n#     torch.save(model.state_dict(), model_path)\n#     print(f\"💾 Model saved to: {model_path}\")\n#     # Test with a single batch\n#     print(\"\\n🧪 Testing model output shapes...\")\n    \n#     model.eval()\n#     with torch.no_grad():\n#         for inputs, targets in val_loader:\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n#             outputs = model(inputs)\n            \n#             print(f\"Input shape: {inputs.shape}\")\n#             print(f\"Target shape: {targets.shape}\")\n#             print(f\"Output shape: {outputs.shape}\")\n#             print(f\"Output range: [{outputs.min():.4f}, {outputs.max():.4f}]\")\n#             break\n\n# if __name__ == \"__main__\":\n#     test_seismic_cnn()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # InversionNet with SwinV2 Backbone\n# import torch\n# import torch.nn as nn\n# import torch.optim as optim\n# from torch.utils.data import DataLoader\n# import pandas as pd\n# import numpy as np\n# import torchvision.models as models\n# from torchvision.models import Swin_V2_T_Weights\n# # from SeismicInversionDataset import SeismicInversionDataset  # Import your dataset class\n\n# class SwinV2InversionNet(nn.Module):\n#     \"\"\"\n#     SwinV2-based InversionNet for seismic inversion\n#     Input: (batch, 5, 1000, 70) - seismic waveforms\n#     Output: (batch, 1, 70, 70) - velocity maps\n#     \"\"\"\n#     def __init__(self, pretrained=True):\n#         super(SwinV2InversionNet, self).__init__()\n        \n#         # Load pretrained SwinV2 tiny model\n#         if pretrained:\n#             weights = Swin_V2_T_Weights.IMAGENET1K_V1\n#             self.swin_backbone = models.swin_v2_t(weights=weights)\n#         else:\n#             self.swin_backbone = models.swin_v2_t(weights=None)\n        \n#         # Remove the classification head\n#         self.swin_features = nn.Sequential(*list(self.swin_backbone.children())[:-2])\n        \n#         # Input adaptation layer to convert 5 channels to 3 channels (RGB expected by SwinV2)\n#         self.input_adapter = nn.Conv2d(5, 3, kernel_size=1, bias=False)\n        \n#         # Get the feature dimension from SwinV2 (768 for swin_v2_t)\n#         swin_feature_dim = 768\n        \n#         # Decoder layers to convert SwinV2 features to velocity maps\n#         self.decoder = nn.Sequential(\n#             # First upsampling block\n#             nn.ConvTranspose2d(swin_feature_dim, 512, kernel_size=4, stride=2, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(512),\n            \n#             # Second upsampling block\n#             nn.ConvTranspose2d(512, 256, kernel_size=4, stride=2, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(256),\n            \n#             # Third upsampling block\n#             nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(128),\n            \n#             # Fourth upsampling block\n#             nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(64),\n            \n#             # Fifth upsampling block\n#             nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             # Final output layer\n#             nn.Conv2d(32, 1, kernel_size=3, padding=1)\n#         )\n        \n#         # Adaptive pooling to ensure exact output size\n#         self.adaptive_pool = nn.AdaptiveAvgPool2d((70, 70))\n        \n#         # Initialize the input adapter with appropriate weights\n#         nn.init.kaiming_normal_(self.input_adapter.weight, mode='fan_out', nonlinearity='relu')\n    \n#     def forward(self, x):\n#         # x shape: (batch, 5, 1000, 70)\n        \n#         # Adapt input channels from 5 to 3\n#         x = self.input_adapter(x)  # (batch, 3, 1000, 70)\n        \n#         # SwinV2 expects square images, so we need to handle the rectangular input\n#         # We'll use adaptive pooling to make it square, then resize back\n#         original_shape = x.shape[-2:]  # (1000, 70)\n        \n#         # Make input square for SwinV2 (use the larger dimension)\n#         max_dim = max(original_shape)\n#         x_square = nn.functional.adaptive_avg_pool2d(x, (max_dim, max_dim))\n        \n#         # Pass through SwinV2 backbone\n#         features = self.swin_features(x_square)  # Extract features\n        \n#         # Get the last feature map from SwinV2\n#         # SwinV2 outputs a tuple, we need the features\n#         if isinstance(features, tuple):\n#             features = features[0]\n        \n#         # Reshape features if needed (SwinV2 might flatten the spatial dimensions)\n#         if len(features.shape) == 3:  # (batch, seq_len, embed_dim)\n#             batch_size, seq_len, embed_dim = features.shape\n#             # Assuming square feature maps\n#             feat_size = int(seq_len ** 0.5)\n#             features = features.transpose(1, 2).view(batch_size, embed_dim, feat_size, feat_size)\n        \n#         # Pass through decoder\n#         x = self.decoder(features)\n        \n#         # Ensure exact output size\n#         x = self.adaptive_pool(x)  # (batch, 1, 70, 70)\n        \n#         return x\n\n# # Alternative simpler version using feature extraction\n# class SimpleSwinV2InversionNet(nn.Module):\n#     \"\"\"\n#     Simplified SwinV2-based InversionNet for seismic inversion\n#     Input: (batch, 5, 1000, 70) - seismic waveforms\n#     Output: (batch, 1, 70, 70) - velocity maps\n#     \"\"\"\n#     def __init__(self, pretrained=True):\n#         super(SimpleSwinV2InversionNet, self).__init__()\n        \n#         # Input adaptation layer\n#         self.input_adapter = nn.Sequential(\n#             nn.Conv2d(5, 3, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(3)\n#         )\n        \n#         # Load pretrained SwinV2 as feature extractor\n#         if pretrained:\n#             weights = Swin_V2_T_Weights.IMAGENET1K_V1\n#             swin_model = models.swin_v2_t(weights=weights)\n#         else:\n#             swin_model = models.swin_v2_t(weights=None)\n        \n#         # Use SwinV2 as feature extractor (remove classifier)\n#         self.feature_extractor = nn.Sequential(*list(swin_model.children())[:-1])\n        \n#         # Freeze SwinV2 parameters (optional - you can unfreeze for fine-tuning)\n#         for param in self.feature_extractor.parameters():\n#             param.requires_grad = False\n        \n#         # Decoder network\n#         self.decoder = nn.Sequential(\n#             # Fully connected layers to process SwinV2 features\n#             nn.Linear(768, 1024),  # 768 is swin_v2_t output dimension\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n            \n#             nn.Linear(1024, 2048),\n#             nn.ReLU(),\n#             nn.Dropout(0.2),\n            \n#             # Reshape to spatial dimensions\n#             nn.Linear(2048, 70 * 70),  # Target output size\n#         )\n        \n#         # Final reshape and refinement\n#         self.final_conv = nn.Sequential(\n#             nn.Conv2d(1, 32, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(32),\n            \n#             nn.Conv2d(32, 16, kernel_size=3, padding=1),\n#             nn.ReLU(),\n#             nn.BatchNorm2d(16),\n            \n#             nn.Conv2d(16, 1, kernel_size=1)\n#         )\n    \n#     def forward(self, x):\n#         # x shape: (batch, 5, 1000, 70)\n#         batch_size = x.size(0)\n        \n#         # Adapt input channels\n#         x = self.input_adapter(x)  # (batch, 3, 1000, 70)\n        \n#         # Resize to standard image size for SwinV2 (224x224 is common)\n#         x = nn.functional.interpolate(x, size=(224, 224), mode='bilinear', align_corners=False)\n        \n#         # Extract features using SwinV2\n#         features = self.feature_extractor(x)  # (batch, 768)\n        \n#         # Decode to velocity map\n#         x = self.decoder(features)  # (batch, 70*70)\n        \n#         # Reshape to 2D map\n#         x = x.view(batch_size, 1, 70, 70)  # (batch, 1, 70, 70)\n        \n#         # Final refinement\n#         x = self.final_conv(x)\n        \n#         return x\n\n# def test_seismic_cnn():\n#     data = {\n#         'dt': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis4_1_0.npy'],  # Replace with actual paths\n#         'md': ['/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel2_1_0.npy', '/kaggle/input/waveform-inversion/train_samples/CurveFault_A/vel4_1_0.npy']   # Replace with actual paths\n#     }\n#     df = pd.DataFrame(data)\n    \n#     # Initialize dataset and dataloader\n#     device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n#     print(f\"🖥️  Using device: {device}\")\n    \n#     dataset = SeismicInversionDataset(df, device=device)\n#     dataloader = DataLoader(dataset, batch_size=4, shuffle=True)\n    \n#     # Initialize model with SwinV2 backbone\n#     print(\"🏗️  Initializing SwinV2-based InversionNet...\")\n#     model = SimpleSwinV2InversionNet(pretrained=True).to(device)\n    \n#     # Alternative: use the more complex version\n#     # model = SwinV2InversionNet(pretrained=True).to(device)\n    \n#     criterion = nn.L1Loss()\n    \n#     # Use different learning rates for different parts\n#     # Lower learning rate for pretrained SwinV2 features, higher for decoder\n#     swin_params = []\n#     decoder_params = []\n    \n#     for name, param in model.named_parameters():\n#         if 'feature_extractor' in name:\n#             swin_params.append(param)\n#         else:\n#             decoder_params.append(param)\n    \n#     optimizer = optim.Adam([\n#         {'params': swin_params, 'lr': 1e-5},  # Lower LR for pretrained features\n#         {'params': decoder_params, 'lr': 1e-3}  # Higher LR for new layers\n#     ])\n    \n#     print(f\"\\n🏗️  Model Architecture:\")\n#     print(model)\n    \n#     # Count parameters\n#     total_params = sum(p.numel() for p in model.parameters())\n#     trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n#     print(f\"\\n📊 Total parameters: {total_params:,}\")\n#     print(f\"📊 Trainable parameters: {trainable_params:,}\")\n    \n#     # Training loop\n#     num_epochs = 10 ###############Epochs######################\n#     print(f\"\\n🚀 Starting training for {num_epochs} epochs...\")\n    \n#     model.train()\n#     # Unfreeze SwinV2 for fine-tuning after a few epochs (optional)\n#     freeze_epochs = 3\n    \n#     for epoch in range(num_epochs):\n#         # Optionally unfreeze SwinV2 after initial training\n#         if epoch == freeze_epochs:\n#             print(f\"🔓 Unfreezing SwinV2 backbone at epoch {epoch+1}\")\n#             for param in model.feature_extractor.parameters():\n#                 param.requires_grad = True\n        \n#         epoch_loss = 0.0\n#         num_batches = 0\n        \n#         for batch_idx, (inputs, targets) in enumerate(dataloader):\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n            \n#             # Forward pass\n#             optimizer.zero_grad()\n#             outputs = model(inputs)\n#             loss = criterion(outputs, targets)\n            \n#             # Backward pass\n#             loss.backward()\n#             optimizer.step()\n            \n#             epoch_loss += loss.item()\n#             num_batches += 1\n            \n#             # Log batch progress\n#             if batch_idx % 10 == 0:\n#                 print(f\"  Epoch [{epoch+1}/{num_epochs}], Batch [{batch_idx+1}], Loss: {loss.item():.6f}\")\n        \n#         avg_loss = epoch_loss / num_batches\n#         print(f\"📈 Epoch [{epoch+1}/{num_epochs}] - Average Loss: {avg_loss:.6f}\")\n#         print(\"-\" * 50)\n    \n#     print(\"✅ Training completed!\")\n    \n#     # Save model\n#     model_path = '/kaggle/working/swinv2_seismic_inversionnet_model.pth'\n#     torch.save(model.state_dict(), model_path)\n#     print(f\"💾 Model saved to: {model_path}\")\n    \n#     # Test with a single batch\n#     print(\"\\n🧪 Testing model output shapes...\")\n    \n#     model.eval()\n#     with torch.no_grad():\n#         for inputs, targets in dataloader:\n#             inputs = inputs.to(device)\n#             targets = targets.to(device)\n#             outputs = model(inputs)\n            \n#             print(f\"Input shape: {inputs.shape}\")\n#             print(f\"Target shape: {targets.shape}\")\n#             print(f\"Output shape: {outputs.shape}\")\n#             print(f\"Output range: [{outputs.min():.4f}, {outputs.max():.4f}]\")\n#             break\n\n# if __name__ == \"__main__\":\n#     test_seismic_cnn()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}