{"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":10384,"databundleVersionId":120379,"sourceType":"competition"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"training_data = pd.read_csv('/kaggle/input/PLAsTiCC-2018/training_set.csv')\n\nmeta = pd.read_csv('/kaggle/input/PLAsTiCC-2018/training_set_metadata.csv')\n\n\n# #print(training_data.head(5))\n# print(meta.head(3))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\n\nif torch.cuda.is_available():\n    device = torch.device(\"cuda\")\n    print(\"✅ GPU is available.\")\n    print(f\"Using device: {torch.cuda.get_device_name(0)}\")\nelse:\n    device = torch.device(\"cpu\")\n    print(\"❌ GPU not available. Using CPU.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T23:53:00.584847Z","iopub.execute_input":"2025-05-26T23:53:00.585124Z","iopub.status.idle":"2025-05-26T23:53:03.425706Z","shell.execute_reply.started":"2025-05-26T23:53:00.585094Z","shell.execute_reply":"2025-05-26T23:53:03.424872Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Simple EDA on dataset**","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Load datasets\nts_df = pd.read_csv('/kaggle/input/PLAsTiCC-2018/training_set.csv')\nmeta_df = pd.read_csv('/kaggle/input/PLAsTiCC-2018/training_set_metadata.csv')\n\n# Merge to bring target and metadata together\nmerged_df = ts_df.merge(meta_df, on='object_id')\n\n# # Check the structure\n# print(meta_df.head())\n# print(meta_df.info())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T23:53:34.797045Z","iopub.execute_input":"2025-05-26T23:53:34.797467Z","iopub.status.idle":"2025-05-26T23:53:37.637063Z","shell.execute_reply.started":"2025-05-26T23:53:34.797441Z","shell.execute_reply":"2025-05-26T23:53:37.636475Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Group by target and get means of metadata features\ngrouped = meta_df.groupby('target').mean(numeric_only=True)\n#print(grouped.T)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T23:54:09.646967Z","iopub.execute_input":"2025-05-26T23:54:09.647655Z","iopub.status.idle":"2025-05-26T23:54:09.653529Z","shell.execute_reply.started":"2025-05-26T23:54:09.64763Z","shell.execute_reply":"2025-05-26T23:54:09.652866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# One-hot encode categorical features (if needed) for correlation\nmeta_numeric = meta_df.select_dtypes(include=[np.number])\ncorr_matrix = meta_numeric.corr()\n\n# Visualize correlation of features with target\nplt.figure(figsize=(10, 6))\nsns.heatmap(corr_matrix[['target']].sort_values(by='target', ascending=False), annot=True, cmap='coolwarm')\nplt.title('Correlation of Metadata Features with Target')\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T23:54:13.917247Z","iopub.execute_input":"2025-05-26T23:54:13.917522Z","iopub.status.idle":"2025-05-26T23:54:14.214851Z","shell.execute_reply.started":"2025-05-26T23:54:13.917498Z","shell.execute_reply":"2025-05-26T23:54:14.214024Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"key_features = ['hostgal_specz', 'hostgal_photoz', 'distmod', 'gal_l', 'gal_b']\n\nfor col in key_features:\n    plt.figure(figsize=(12, 4))\n    sns.boxplot(x='target', y=col, data=meta_df)\n    plt.title(f'Distribution of {col} by Target Class')\n    plt.xticks(rotation=45)\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T23:54:21.224136Z","iopub.execute_input":"2025-05-26T23:54:21.224408Z","iopub.status.idle":"2025-05-26T23:54:22.578376Z","shell.execute_reply.started":"2025-05-26T23:54:21.224386Z","shell.execute_reply":"2025-05-26T23:54:22.577412Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(6, 4))\nsns.countplot(x='hostgal_photoz', data=meta_df, hue='target')\nplt.title('hostgal_photoz vs Target Classes')\nplt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T23:54:30.948274Z","iopub.execute_input":"2025-05-26T23:54:30.949107Z","iopub.status.idle":"2025-05-26T23:56:12.183958Z","shell.execute_reply.started":"2025-05-26T23:54:30.949071Z","shell.execute_reply":"2025-05-26T23:56:12.183151Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Import libraries\nimport numpy as np\nimport pandas as pd\nimport os\nfrom sklearn.model_selection import StratifiedShuffleSplit\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.metrics import log_loss, accuracy_score, f1_score\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.nn.utils.rnn import pad_sequence\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"Using device: {device}\")\n\nTRAIN_LIGHT_CURVES = '/kaggle/input/PLAsTiCC-2018/training_set.csv'\nTRAIN_META = '/kaggle/input/PLAsTiCC-2018/training_set_metadata.csv'\n\n# Load metadata\nmeta_df = pd.read_csv(TRAIN_META)\nprint(f\"Loaded metadata: {meta_df.shape[0]} objects, {meta_df.shape[1]} features\")\n# Encode target classes as integers\nle = LabelEncoder()\nmeta_df['target'] = le.fit_transform(meta_df['target'])\nnum_classes = len(le.classes_)\nprint(f\"Number of classes: {num_classes}\")\n\nlc_df = pd.read_csv(TRAIN_LIGHT_CURVES)\nprint(f\"Loaded light curves: {lc_df.shape[0]} rows\")\nprint(lc_df.head())\n\n\nprint(meta_df.head())","metadata":{"trusted":true,"execution":{"execution_failed":"2025-05-27T09:23:17.603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import time\n\n# for i in range(5):\n#     time.sleep(10)  # sleep 10 seconds\n#     print(\"Still working...\")\n\ntest=pd.read_csv('/kaggle/input/PLAsTiCC-2018/training_set_metadata.csv')\nprint(test.columns)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T00:10:56.588366Z","iopub.execute_input":"2025-05-27T00:10:56.588729Z","iopub.status.idle":"2025-05-27T00:10:56.6078Z","shell.execute_reply.started":"2025-05-27T00:10:56.588701Z","shell.execute_reply":"2025-05-27T00:10:56.607101Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Merge metadata into light-curve data on object_id\ndata = lc_df.merge(meta_df, on='object_id', how='left')\n\n# Handle missing values: drop rows where flux or flux_err is NaN\ndata = data.dropna(subset=['flux', 'flux_err'])\ndata = data.reset_index(drop=True)\nprint(f\"After dropping missing values: {data.shape[0]} rows\")\n\n# Optionally, filter out objects with very few observations\nmin_observations = 5\ncounts = data['object_id'].value_counts()\nvalid_ids = counts[counts >= min_observations].index\ndata = data[data['object_id'].isin(valid_ids)]\nprint(f\"After filtering objects with >= {min_observations} points: {len(valid_ids)} objects\")\n\n# Sort data by object_id and time (mjd) for sequential processing so model will process in temporal order\ndata = data.sort_values(['object_id', 'mjd']).reset_index(drop=True)\nprint(data.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T00:10:56.698962Z","iopub.execute_input":"2025-05-27T00:10:56.699239Z","iopub.status.idle":"2025-05-27T00:10:56.760755Z","shell.execute_reply.started":"2025-05-27T00:10:56.699212Z","shell.execute_reply":"2025-05-27T00:10:56.75975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compute time offset from first observation per object\ndata['t_offset'] = data.groupby('object_id')['mjd'].transform(lambda x: x - x.iloc[0])\n\n# Select relevant features\nfeature_cols = ['passband', 'flux', 'flux_err', 'detected', 't_offset']\n# Normalize continuous features (flux, flux_err, t_offset) globally\n# (In practice, compute these on training data and apply same scaling to test)\ncont_cols = ['flux', 'flux_err', 't_offset']\nfor col in cont_cols:\n    mean = data[col].mean()\n    std = data[col].std()\n    data[col] = (data[col] - mean) / std\nprint(\"Feature columns:\", feature_cols)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:30:14.100623Z","iopub.execute_input":"2025-05-26T11:30:14.100931Z","iopub.status.idle":"2025-05-26T11:30:15.327042Z","shell.execute_reply.started":"2025-05-26T11:30:14.100907Z","shell.execute_reply":"2025-05-26T11:30:15.32633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class LightCurveDataset(Dataset):\n    \"\"\"\n    PyTorch Dataset for PLAsTiCC light curves.\n    Each item is one object's sequence of observations and its class label.\n    \"\"\"\n    def __init__(self, data, max_seq_len=None):\n        \"\"\"\n        data: DataFrame containing merged light curves and metadata.\n        max_seq_len: optional cap on sequence length (for demonstration/memory).\n        \"\"\"\n        self.data = data\n        self.object_ids = data['object_id'].unique()\n        self.max_seq_len = max_seq_len\n        # Build an index mapping object_id to slice of data\n        self.groups = {obj: grp for obj, grp in data.groupby('object_id')}\n        \n    def __len__(self):\n        return len(self.object_ids)\n    \n    def __getitem__(self, idx):\n        obj_id = self.object_ids[idx]\n        grp = self.groups[obj_id]\n        # Extract features for this object sequence\n        # Convert to torch tensors\n        passband = torch.tensor(grp['passband'].values, dtype=torch.long)\n        flux = torch.tensor(grp['flux'].values, dtype=torch.float)\n        flux_err = torch.tensor(grp['flux_err'].values, dtype=torch.float)\n        detected = torch.tensor(grp['detected'].values, dtype=torch.float)\n        t_offset = torch.tensor(grp['t_offset'].values, dtype=torch.float)\n        # Stack continuous features\n        cont_feats = torch.stack([flux, flux_err, detected, t_offset], dim=1)  # shape [seq_len, 4]\n        # Label for object\n        label = torch.tensor(grp['target'].iloc[0], dtype=torch.long)\n        # If sequence is longer than max_seq_len (optional), truncate/pad accordingly\n        if self.max_seq_len is not None and cont_feats.size(0) > self.max_seq_len:\n            cont_feats = cont_feats[:self.max_seq_len]\n            passband = passband[:self.max_seq_len]\n        return cont_feats, passband, label\n\ndef collate_fn(batch):\n    \"\"\"\n    Collate function to pad sequences in a batch.\n    Returns padded continuous features, padded passbands, masks, and labels.\n    \"\"\"\n    cont_feats, passbands, labels = zip(*batch)\n    # Pad the continuous features (shape [seq_len, feat_dim])\n    cont_padded = pad_sequence(cont_feats, batch_first=True, padding_value=0.0)\n    # Pad passbands (1D sequences)\n    pass_padded = pad_sequence(passbands, batch_first=True, padding_value=0)\n    # Create attention mask (True for padded positions)\n    lengths = torch.tensor([len(x) for x in cont_feats])\n    max_len = lengths.max()\n    mask = torch.arange(max_len).expand(len(lengths), max_len) >= lengths.unsqueeze(1)\n    labels = torch.tensor(labels, dtype=torch.long)\n    return cont_padded, pass_padded, mask, labels\n\n# Example: create dataset and dataloader (using subset for speed/demonstration)\nsample_ids = data['object_id'].unique()[:1000]  # use 1000 objects as a small demo\ndataset = LightCurveDataset(data[data['object_id'].isin(sample_ids)], max_seq_len=50)\ndataloader = DataLoader(dataset, batch_size=32, shuffle=True, collate_fn=collate_fn)\nfor cont_batch, pb_batch, mask_batch, lbl_batch in dataloader:\n    print(\"Batch shapes:\", cont_batch.shape, pb_batch.shape, mask_batch.shape, lbl_batch.shape)\n    break\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:30:18.894458Z","iopub.execute_input":"2025-05-26T11:30:18.895187Z","iopub.status.idle":"2025-05-26T11:30:19.091541Z","shell.execute_reply.started":"2025-05-26T11:30:18.895164Z","shell.execute_reply":"2025-05-26T11:30:19.090774Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Stratified split of object IDs\nall_ids = meta_df['object_id'].values\nall_labels = meta_df['target'].values\nsplitter = StratifiedShuffleSplit(n_splits=1, test_size=0.15, random_state=42)\ntrain_idx, val_idx = next(splitter.split(all_ids, all_labels))\ntrain_ids = all_ids[train_idx]\nval_ids = all_ids[val_idx]\n\n# Create train/val dataframes\ntrain_data = data[data['object_id'].isin(train_ids)]\nval_data = data[data['object_id'].isin(val_ids)]\n\nprint(f\"Training objects: {len(train_ids)}, validation objects: {len(val_ids)}\")\n\n# Create Datasets and DataLoaders\ntrain_dataset = LightCurveDataset(train_data)\nval_dataset = LightCurveDataset(val_data)\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, collate_fn=collate_fn)\nval_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, collate_fn=collate_fn)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:30:24.344824Z","iopub.execute_input":"2025-05-26T11:30:24.345361Z","iopub.status.idle":"2025-05-26T11:30:24.90676Z","shell.execute_reply.started":"2025-05-26T11:30:24.345337Z","shell.execute_reply":"2025-05-26T11:30:24.906194Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TimeSeriesTransformer(nn.Module):\n    \"\"\"\n    Transformer model for multivariate time-series classification.\n    \"\"\"\n    def __init__(self, num_passbands, d_model=64, nhead=4, num_layers=2, num_classes=15):\n        super().__init__()\n        self.d_model = d_model\n        # Embedding for passband (categorical)\n        self.passband_embed = nn.Embedding(num_passbands, d_model)\n        # Linear projection for continuous features (flux, flux_err, detected, time)\n        self.cont_proj = nn.Linear(4, d_model)\n        # Positional encoding (fixed sinusoidal)\n        self.pos_encoder = PositionalEncoding(d_model)\n        # Transformer encoder layers\n        encoder_layer = nn.TransformerEncoderLayer(d_model=d_model, nhead=nhead, batch_first=True)\n        self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)\n        # Classification head\n        self.classifier = nn.Linear(d_model, num_classes)\n    \n    def forward(self, cont_feats, passbands, src_key_padding_mask):\n        \"\"\"\n        cont_feats: [batch, seq_len, 4] continuous features\n        passbands: [batch, seq_len] integer passband indices\n        src_key_padding_mask: [batch, seq_len] bool mask (True for PAD)\n        \"\"\"\n        # Embed passband and project continuous features\n        x_pb = self.passband_embed(passbands)            # [batch, seq_len, d_model]\n        x_cont = self.cont_proj(cont_feats)             # [batch, seq_len, d_model]\n        x = x_pb + x_cont\n        # Add positional encoding\n        x = self.pos_encoder(x)\n        # Transformer expects src_key_padding_mask with True at PAD positions\n        x = self.transformer(x, src_key_padding_mask=src_key_padding_mask)\n        # Global average pooling (only over non-pad positions)\n        # We mask out padded positions when averaging\n        mask = ~src_key_padding_mask.unsqueeze(2)  # [batch, seq_len, 1]\n        x = (x * mask).sum(1) / mask.sum(1)       # [batch, d_model]\n        logits = self.classifier(x)  # [batch, num_classes]\n        return logits\n\nclass PositionalEncoding(nn.Module):\n    \"\"\"\n    Sinusoidal positional encoding for Transformer.\n    \"\"\"\n    def __init__(self, d_model, max_len=500):\n        super().__init__()\n        # Create constant 'pe' matrix with values\n        pe = torch.zeros(max_len, d_model)\n        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)\n        div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(np.log(10000.0) / d_model))\n        pe[:, 0::2] = torch.sin(position * div_term)\n        pe[:, 1::2] = torch.cos(position * div_term)\n        pe = pe.unsqueeze(0)  # shape [1, max_len, d_model]\n        self.register_buffer('pe', pe)\n    \n    def forward(self, x):\n        # x shape: [batch, seq_len, d_model]\n        seq_len = x.size(1)\n        x = x + self.pe[:, :seq_len]\n        return x\n\n# Instantiate model\nmodel = TimeSeriesTransformer(num_passbands=6, d_model=64, nhead=4, num_layers=2, num_classes=num_classes).to(device)\nprint(model)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:30:29.49722Z","iopub.execute_input":"2025-05-26T11:30:29.497796Z","iopub.status.idle":"2025-05-26T11:30:29.719887Z","shell.execute_reply.started":"2025-05-26T11:30:29.497777Z","shell.execute_reply":"2025-05-26T11:30:29.719172Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Loss and optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n\ndef evaluate(model, data_loader):\n    \"\"\"\n    Evaluate the model on a DataLoader. Returns (log_loss, accuracy, f1).\n    \"\"\"\n    model.eval()\n    all_logits = []\n    all_labels = []\n    with torch.no_grad():\n        for cont_batch, pb_batch, mask_batch, lbl_batch in data_loader:\n            cont_batch = cont_batch.to(device)\n            pb_batch = pb_batch.to(device)\n            mask_batch = mask_batch.to(device)\n            labels = lbl_batch.to(device)\n            logits = model(cont_batch, pb_batch, mask_batch)\n            all_logits.append(logits.cpu().numpy())\n            all_labels.append(labels.cpu().numpy())\n    all_logits = np.concatenate(all_logits)\n    all_labels = np.concatenate(all_labels)\n    probs = np.exp(all_logits) / np.exp(all_logits).sum(axis=1, keepdims=True)\n    ll = log_loss(all_labels, probs)\n    acc = accuracy_score(all_labels, probs.argmax(axis=1))\n    f1 = f1_score(all_labels, probs.argmax(axis=1), average='weighted')\n    return ll, acc, f1\n\n# Training loop\nnum_epochs = 15\nfor epoch in range(num_epochs):\n    model.train()\n    epoch_loss = 0.0\n    for cont_batch, pb_batch, mask_batch, lbl_batch in train_loader:\n        cont_batch = cont_batch.to(device)\n        pb_batch = pb_batch.to(device)\n        mask_batch = mask_batch.to(device)\n        labels = lbl_batch.to(device)\n        \n        optimizer.zero_grad()\n        logits = model(cont_batch, pb_batch, mask_batch)\n        loss = criterion(logits, labels)\n        loss.backward()\n        optimizer.step()\n        epoch_loss += loss.item() * cont_batch.size(0)\n    \n    avg_loss = epoch_loss / len(train_dataset)\n    val_ll, val_acc, val_f1 = evaluate(model, val_loader)\n    print(f\"Epoch {epoch+1}/{num_epochs}: Train Loss={avg_loss:.4f}, Val LogLoss={val_ll:.4f}, \"\n          f\"Val Acc={val_acc:.4f}, Val F1={val_f1:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:30:35.405391Z","iopub.execute_input":"2025-05-26T11:30:35.406016Z","iopub.status.idle":"2025-05-26T11:32:38.528611Z","shell.execute_reply.started":"2025-05-26T11:30:35.40599Z","shell.execute_reply":"2025-05-26T11:32:38.527739Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Combine train and val for final training\nfull_dataset = LightCurveDataset(data)\nfull_loader = DataLoader(full_dataset, batch_size=64, shuffle=True, collate_fn=collate_fn)\n# Reinitialize or keep the model (optionally reset weights)\nmodel_full = TimeSeriesTransformer(num_passbands=6, d_model=64, nhead=4, num_layers=2, num_classes=num_classes).to(device)\noptimizer_full = torch.optim.Adam(model_full.parameters(), lr=1e-3)\nnum_epochs_full = 15\n\nfor epoch in range(num_epochs_full):\n    model_full.train()\n    for cont_batch, pb_batch, mask_batch, lbl_batch in full_loader:\n        cont_batch = cont_batch.to(device)\n        pb_batch = pb_batch.to(device)\n        mask_batch = mask_batch.to(device)\n        labels = lbl_batch.to(device)\n        optimizer_full.zero_grad()\n        logits = model_full(cont_batch, pb_batch, mask_batch)\n        loss = criterion(logits, labels)\n        loss.backward()\n        optimizer_full.step()\n    print(f\"Completed epoch {epoch+1}/{num_epochs_full} on full training data.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:33:12.23883Z","iopub.execute_input":"2025-05-26T11:33:12.239599Z","iopub.status.idle":"2025-05-26T11:35:22.415903Z","shell.execute_reply.started":"2025-05-26T11:33:12.239577Z","shell.execute_reply":"2025-05-26T11:35:22.415072Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Load test metadata for object ids (and any needed features)\n# TEST_LIGHT_CURVES = '/kaggle/input/PLAsTiCC-2018/test_set_sample.csv'\n# TEST_META = '/kaggle/input/PLAsTiCC-2018/test_set_metadata.csv'\n# full_test_data='/kaggle/input/PLAsTiCC-2018'\n\n# test_meta = pd.read_csv(TEST_META)\n# test_meta = test_meta.sort_values('object_id').reset_index(drop=True)\n\n# # Function to process a chunk of the test light-curves file\n# def process_test_chunk(chunk, model):\n#     \"\"\"\n#     Given a chunk of test light curves, group by object and run inference.\n#     Returns a DataFrame with object_id and predicted probabilities.\n#     \"\"\"\n#     chunk = chunk.merge(test_meta[['object_id']], on='object_id', how='left')\n#     chunk['t_offset'] = chunk.groupby('object_id')['mjd'].transform(lambda x: x - x.iloc[0])\n#     # Normalize using the same statistics as training (we assume means and stds saved)\n#     for col in cont_cols:\n#         chunk[col] = (chunk[col] - mean) / std\n#     chunk = chunk.sort_values(['object_id', 'mjd'])\n#     results = []\n#     for obj_id, grp in chunk.groupby('object_id'):\n#         # Build tensors for this object\n#         passband = torch.tensor(grp['passband'].values, dtype=torch.long).unsqueeze(0).to(device)\n#         flux = torch.tensor(grp['flux'].values, dtype=torch.float).unsqueeze(1)\n#         flux_err = torch.tensor(grp['flux_err'].values, dtype=torch.float).unsqueeze(1)\n#         detected = torch.tensor(grp['detected'].values, dtype=torch.float).unsqueeze(1)\n#         t_offset = torch.tensor(grp['t_offset'].values, dtype=torch.float).unsqueeze(1)\n#         cont_feats = torch.cat([flux, flux_err, detected, t_offset], dim=1).unsqueeze(0).to(device)\n#         mask = torch.zeros((1, cont_feats.size(1)), dtype=torch.bool).to(device)  # no padding within object\n#         with torch.no_grad():\n#             logits = model_full(cont_feats, passband, mask)\n#             probs = nn.functional.softmax(logits, dim=1).cpu().numpy().flatten()\n#         results.append((obj_id, probs))\n#     return results\n\n# # Iterate over test set in chunks to avoid memory overflow\n# test_predictions = {}\n# chunk_size = 10**6  # adjust as per environment memory\n# for chunk in pd.read_csv(TEST_LIGHT_CURVES, chunksize=chunk_size):\n#     preds = process_test_chunk(chunk, model_full)\n#     for obj_id, probs in preds:\n#         test_predictions[obj_id] = probs\n\n\n# import numpy as np\n\n# # Prepare submission DataFrame\n# submission = pd.DataFrame(\n#     [(obj_id,) + tuple(probs) for obj_id, probs in test_predictions.items()],\n#     columns=['object_id'] + [f'class_{int(c)}' for c in le.classes_]\n# )\n\n# submission = submission.sort_values('object_id').reset_index(drop=True)\n\n# # Add missing classes if any (paranoia check, optional)\n# for cls in le.classes_:\n#     col = f'class_{int(cls)}'\n#     if col not in submission.columns:\n#         submission[col] = 0.0\n\n# # # 🔴 ADD class_99 column\n# # known_class_cols = [f'class_{int(c)}' for c in le.classes_]\n# # submission['class_99'] = 1.0 - submission[known_class_cols].max(axis=1)\n# # submission['class_99'] = submission['class_99'].clip(0.0001, 0.5)  # safe bounds\n\n# # # ✅ Reorder columns: object_id first, then all class_XX including class_99\n# # final_columns = ['object_id'] + sorted([col for col in submission.columns if col.startswith('class_')],\n# #                                        key=lambda x: int(x.split('_')[1]))\n# # submission = submission[final_columns]\n# # Save\n# submission.to_csv('/kaggle/working/submission1.csv', index=False)\n# print(\"✅ Final submission file saved with class_99.\")\n\n\n\n\n\n\n\nimport pandas as pd\nimport numpy as np\nimport torch\nfrom torch import nn\n\n# Load metadata (remains the same)\nTEST_META = '/kaggle/input/PLAsTiCC-2018/test_set_metadata.csv'\ntest_meta = pd.read_csv(TEST_META)\ntest_meta = test_meta.sort_values('object_id').reset_index(drop=True)\n\n# Normalization statistics (you’d load from training stage)\ncont_cols = ['flux', 'flux_err', 't_offset']\nmean_std_dict = {'flux': 0.0, 'flux_err': 1.0, 't_offset': 0.0}  # update these\nmean = 0.0  # example\nstd = 1.0  # example\n\n# Function to process a chunk\ndef process_test_chunk(chunk, model):\n    chunk = chunk.merge(test_meta[['object_id']], on='object_id', how='left')\n    chunk['t_offset'] = chunk.groupby('object_id')['mjd'].transform(lambda x: x - x.iloc[0])\n    \n    for col in cont_cols:\n        chunk[col] = (chunk[col] - mean) / std\n    \n    chunk = chunk.sort_values(['object_id', 'mjd'])\n    results = []\n    \n    for obj_id, grp in chunk.groupby('object_id'):\n        passband = torch.tensor(grp['passband'].values, dtype=torch.long).unsqueeze(0).to(device)\n        flux = torch.tensor(grp['flux'].values, dtype=torch.float).unsqueeze(1)\n        flux_err = torch.tensor(grp['flux_err'].values, dtype=torch.float).unsqueeze(1)\n        detected = torch.tensor(grp['detected'].values, dtype=torch.float).unsqueeze(1)\n        t_offset = torch.tensor(grp['t_offset'].values, dtype=torch.float).unsqueeze(1)\n        cont_feats = torch.cat([flux, flux_err, detected, t_offset], dim=1).unsqueeze(0).to(device)\n        mask = torch.zeros((1, cont_feats.size(1)), dtype=torch.bool).to(device)\n        \n        with torch.no_grad():\n            logits = model(cont_feats, passband, mask)\n            probs = nn.functional.softmax(logits, dim=1).cpu().numpy().flatten()\n        results.append((obj_id, probs))\n    \n    return results\n\n# ✅ Updated file list (no need to read one giant file anymore!)\nfile_list = [\n    'test_set_batch1.csv', 'test_set_batch2.csv', 'test_set_batch3.csv',\n    'test_set_batch4.csv', 'test_set_batch5.csv', 'test_set_batch6.csv',\n    'test_set_batch7.csv', 'test_set_batch8.csv', 'test_set_batch9.csv',\n    'test_set_batch10.csv', 'test_set_batch11.csv'\n]\n\ndata_dir = '/kaggle/input/PLAsTiCC-2018'\n\ntest_predictions = {}\n\nfor i, file_name in enumerate(file_list):\n    print(f\"\\n🔵 Processing {file_name} ({i+1}/{len(file_list)})...\")\n    chunk_path = f\"{data_dir}/{file_name}\"\n    \n    # Using chunksize if you’re worried about huge files\n    chunk_size = 10**6\n    for j, chunk in enumerate(pd.read_csv(chunk_path, chunksize=chunk_size)):\n        print(f\"  🟢 Processing chunk {j+1} of {file_name}\")\n        preds = process_test_chunk(chunk, model_full)\n        \n        for obj_id, probs in preds:\n            test_predictions[obj_id] = probs\n        \n        # Keep-alive print to avoid idle timeout\n        if j % 10 == 0:\n            print(f\"  ✅ Processed {j*chunk_size} rows so far for {file_name}\")\n\nprint(\"\\n✅ All test files processed!\")\n\n# Prepare submission DataFrame\nsubmission = pd.DataFrame(\n    [(obj_id,) + tuple(probs) for obj_id, probs in test_predictions.items()],\n    columns=['object_id'] + [f'class_{int(c)}' for c in le.classes_]\n)\n\nsubmission = submission.sort_values('object_id').reset_index(drop=True)\n\n# Add missing classes if any\nfor cls in le.classes_:\n    col = f'class_{int(cls)}'\n    if col not in submission.columns:\n        submission[col] = 0.0\n\n\n# 🔴 Compute class_99 using a hybrid approach\n\nknown_class_cols = [col for col in submission.columns if col.startswith('class_') and col != 'class_99']\n\n# 2️⃣ Product of complements\nclass99_product = (1.0 - submission[known_class_cols]).prod(axis=1)\n\n# 3️⃣ Weighted sum of specific classes (42, 52, 62, 95)\nrelevant_classes = [42, 52, 62, 95]\nweights = [0.25, 0.25, 0.25, 0.25]  # equal weights as starting point\nclass99_weighted = sum(\n    w * submission[f'class_{cls}'] for w, cls in zip(weights, relevant_classes))\n\n# 4️⃣ Hybrid: average of the above three\nsubmission['class_99'] = (\n    class99_maxgap + class99_product + class99_weighted) / 3.0\n\n# 🔴 Clip to avoid zero probs\nsubmission['class_99'] = submission['class_99'].clip(0.0001, 0.5)\n\n# 🔴 Re-normalize all probabilities to sum to 1\nall_class_cols = [col for col in submission.columns if col.startswith('class_')]\nsubmission[all_class_cols] = submission[all_class_cols].div(\n    submission[all_class_cols].sum(axis=1), axis=0)\n\n# ✅ Reorder columns: object_id first, then class_XX (sorted), including class_99\nfinal_columns = ['object_id'] + sorted(\n    [col for col in submission.columns if col.startswith('class_')],\n    key=lambda x: int(x.split('_')[1])\n)\nsubmission = submission[final_columns]\n\nprint(\"✅ Final submission DataFrame updated with robust class_99 handling.\")\n\n\n# Save\nsubmission.to_csv('/kaggle/working/submission2.csv', index=False)\nprint(\"✅ Final submission file saved with class_99!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:42:32.344906Z","iopub.execute_input":"2025-05-26T11:42:32.345618Z","iopub.status.idle":"2025-05-26T14:03:48.437515Z","shell.execute_reply.started":"2025-05-26T11:42:32.345594Z","shell.execute_reply":"2025-05-26T14:03:48.436848Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submit=pd.read_csv('/kaggle/working/submission2.csv')\nprint(submit.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T14:28:58.158971Z","iopub.execute_input":"2025-05-26T14:28:58.159266Z","iopub.status.idle":"2025-05-26T14:29:07.850731Z","shell.execute_reply.started":"2025-05-26T14:28:58.159247Z","shell.execute_reply":"2025-05-26T14:29:07.849969Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import FileLink\n\nFileLink('/kaggle/working/submission.csv')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T14:42:28.838235Z","iopub.execute_input":"2025-05-26T14:42:28.838522Z","iopub.status.idle":"2025-05-26T14:42:28.843972Z","shell.execute_reply.started":"2025-05-26T14:42:28.8385Z","shell.execute_reply":"2025-05-26T14:42:28.843199Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}