{"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":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"phase - 1","metadata":{}},{"cell_type":"code","source":"# !pip install --upgrade jupyter ipywidgets\n# !jupyter nbextension enable --py widgetsnbextension","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom tqdm.notebook import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_light_curves(lc_df, meta_df, max_seq_len=162):\n    \"\"\"\n    Preprocess PLAsTiCC light curves based on 3DSubM steps.\n    Returns: Dictionary {object_id: sequence}, padded to same length.\n    \"\"\"\n\n    # 1. Add Gaussian noise to flux\n    lc_df['flux'] += np.random.normal(\n        loc=0, scale=(lc_df['flux_err'] * 2 / 3)\n    )\n\n    # 2. Compute flux scaler\n    flux_max = lc_df['flux'].max()\n    flux_min = lc_df['flux'].min()\n    flux_scaler = 1 / np.log2(flux_max - flux_min + 1)  # add 1 to avoid log(0)\n\n    # 3. Normalize flux\n    lc_df['flux'] *= flux_scaler\n\n    # 4. Replace MJD with delta time (per object)\n    lc_df['mjd'] = lc_df.groupby('object_id')['mjd'].transform(lambda x: x - x.min())\n\n    # 5. Group observations by object and aggregate per-night flux\n    grouped_data = {}\n    for obj_id, group in tqdm(lc_df.groupby('object_id'), desc=\"Processing objects\"):\n        # Create 2D sequence: [time_steps x 6 passbands]\n        sequence = []\n        current_night = -10  # dummy value to group by nights (>=8 hr apart)\n        night_buffer = {}\n        \n        for _, row in group.sort_values('mjd').iterrows():\n            if row['mjd'] - current_night > 0.33:  # new night if >8 hours\n                if night_buffer:\n                    sequence.append([night_buffer.get(pb, np.nan) for pb in range(6)])\n                night_buffer = {}\n                current_night = row['mjd']\n            night_buffer[row['passband']] = row['flux']\n\n        if night_buffer:\n            sequence.append([night_buffer.get(pb, np.nan) for pb in range(6)])\n\n        # Interpolate missing passbands in each time step\n        sequence = pd.DataFrame(sequence).interpolate(axis=1, limit_direction='both')\n\n        # Replace NaNs with 0\n        sequence = sequence.fillna(0).values.tolist()\n\n        # Pad or trim to fixed length\n        if len(sequence) < max_seq_len:\n            pad = [[0]*6] * (max_seq_len - len(sequence))\n            sequence.extend(pad)\n        else:\n            sequence = sequence[:max_seq_len]\n\n        grouped_data[obj_id] = np.array(sequence)\n\n    return grouped_data  # <-- Ensure this is typed with a normal space","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_lc = pd.read_csv(\"/kaggle/input/PLAsTiCC-2018/training_set.csv\")\ntrain_meta = pd.read_csv(\"/kaggle/input/PLAsTiCC-2018/training_set_metadata.csv\")\n\n# Only use 85% for Phase 1\nfrom sklearn.model_selection import train_test_split\nobject_ids = train_meta['object_id'].unique()\ntrain_ids, val_ids = train_test_split(object_ids, test_size=0.15, random_state=42)\n\n# Filter light curve data\ntrain_lc_split = train_lc[train_lc['object_id'].isin(train_ids)]\nval_lc_split = train_lc[train_lc['object_id'].isin(val_ids)]\n\n# Run preprocessing\nX_train_seq = preprocess_light_curves(train_lc_split, train_meta)\nX_val_seq = preprocess_light_curves(val_lc_split, train_meta)  # Fixed space","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.utils import to_categorical\n\n# Merge metadata to get class labels\ntrain_meta = pd.read_csv(\"/kaggle/input/PLAsTiCC-2018/training_set_metadata.csv\")\ntrain_meta = train_meta.set_index('object_id')\n\n# Filter labels only for the IDs present in our split\ny_train = [train_meta.loc[obj_id]['target'] for obj_id in X_train_seq.keys()]\ny_val = [train_meta.loc[obj_id]['target'] for obj_id in X_val_seq.keys()]\n\n# Convert to arrays\nX_train = np.array(list(X_train_seq.values()))\nX_val = np.array(list(X_val_seq.values()))\n\n# Map target classes to 0–14 range\nunique_classes = sorted(train_meta['target'].unique())\nclass_to_index = {c: i for i, c in enumerate(unique_classes)}\ny_train = np.array([class_to_index[y] for y in y_train])\ny_val = np.array([class_to_index[y] for y in y_val])\n\n# One-hot encode the labels\ny_train = to_categorical(y_train, num_classes=15)\ny_val = to_categorical(y_val, num_classes=15)  # <-- Fixed! (normal space)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import GRU, Bidirectional, Dropout, Dense, GlobalMaxPooling1D\n\nmodel = Sequential([\n    Bidirectional(GRU(256, return_sequences=True), input_shape=(162, 6)),\n    Dropout(0.1),\n    Bidirectional(GRU(64, return_sequences=True)),\n    GlobalMaxPooling1D(),\n    Dense(128, activation='tanh'),\n    Dropout(0.1),\n    Dense(15, activation='softmax')\n])\n\nmodel.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping\n\n# Optional: stop early if validation accuracy stops improving\nearly_stop = EarlyStopping(\n    monitor='val_accuracy', \n    patience=3, \n    restore_best_weights=True\n)\n\n# Train the GRU model\nhistory = model.fit(\n    X_train, y_train,\n    validation_data=(X_val, y_val),\n    epochs=20,\n    batch_size=64,\n    # callbacks=[early_stop],\n    verbose=1\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Plot accuracy\nplt.plot(history.history['accuracy'], label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Model Accuracy\")\nplt.legend()\nplt.grid(True)\nplt.show()\n\n# Plot loss\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Validation Loss')\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.title(\"Model Loss\")\nplt.legend()\nplt.grid(True)\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report, confusion_matrix\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport numpy as np\n\n# Predict probabilities on validation set\nval_probs = model.predict(X_val)\n\n# Convert to class predictions\nval_preds = np.argmax(val_probs, axis=1)\nval_true = np.argmax(y_val, axis=1)\n\n# Map class indices back to original class labels\nindex_to_class = {v: k for k, v in class_to_index.items()}\nval_preds_labels = [index_to_class[i] for i in val_preds]\nval_true_labels = [index_to_class[i] for i in val_true]\n\n# Classification report\nprint(\"Classification Report:\")\nprint(classification_report(val_true_labels, val_preds_labels))\n\n# Confusion matrix\ncm = confusion_matrix(val_true_labels, val_preds_labels, labels=sorted(index_to_class.values()))\n\nplt.figure(figsize=(12, 10))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=sorted(index_to_class.values()), yticklabels=sorted(index_to_class.values()))\nplt.xlabel(\"Predicted Class\")\nplt.ylabel(\"True Class\")\nplt.title(\"Confusion Matrix\")\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Saving Model","metadata":{}},{"cell_type":"code","source":"model.save(\"gru_model_phase1.h5\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Phase - 2","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom tqdm.notebook import tqdm\n# import tensorflow as tf\nimport tensorflow as tf\nimport torch\nfrom tensorflow.keras.models import load_model\nimport os\n\n# Load model\nmodel = load_model(\"gru_model_phase1.h5\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Map model class indices to actual class labels from training\nindex_to_class = {\n    0: 6, 1: 15, 2: 16, 3: 42, 4: 52, 5: 53, 6: 62, 7: 64, 8: 65,\n    9: 67, 10: 88, 11: 90, 12: 92, 13: 95, 14: 99\n}","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_light_curves_test(lc_df, max_seq_len=162):\n    lc_df['flux'] += np.random.normal(loc=0, scale=(lc_df['flux_err'] * 2 / 3))\n    flux_max = lc_df['flux'].max()\n    flux_min = lc_df['flux'].min()\n    flux_scaler = 1 / np.log2(flux_max - flux_min + 1)\n    lc_df['flux'] *= flux_scaler\n    lc_df['mjd'] = lc_df.groupby('object_id')['mjd'].transform(lambda x: x - x.min())\n\n    grouped_data = {}\n    for obj_id, group in lc_df.groupby('object_id'):\n        sequence = []\n        current_night = -10\n        night_buffer = {}\n        for _, row in group.sort_values('mjd').iterrows():\n            if row['mjd'] - current_night > 0.33:\n                if night_buffer:\n                    sequence.append([night_buffer.get(pb, np.nan) for pb in range(6)])\n                night_buffer = {}\n                current_night = row['mjd']\n            night_buffer[row['passband']] = row['flux']\n\n        if night_buffer:\n            sequence.append([night_buffer.get(pb, np.nan) for pb in range(6)])\n\n        sequence = pd.DataFrame(sequence).interpolate(axis=1, limit_direction='both')\n        sequence = sequence.fillna(0).values.tolist()\n\n        if len(sequence) < max_seq_len:\n            pad = [[0]*6] * (max_seq_len - len(sequence))\n            sequence.extend(pad)\n        else:\n            sequence = sequence[:max_seq_len]\n\n        grouped_data[obj_id] = np.array(sequence)\n    return grouped_data","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport tensorflow as tf\nfrom tqdm import tqdm\nimport numpy as np\nimport pandas as pd\n\n# Assuming your model and preprocess function are already defined\n# model = ... (load your trained model)\n# def preprocess_light_curves_test(df): ...\n\n# Directory containing test batches\nbatch_dir = \"/kaggle/input/PLAsTiCC-2018/\"\nobject_preds = {}\nbatch_files = [f\"test_set_batch{i}.csv\" for i in range(1, 6)]\n\nfor batch_file in tqdm(batch_files, desc=\"Processing batches\"):\n    path = os.path.join(batch_dir, batch_file)\n    batch_df = pd.read_csv(path)\n\n    test_seq_dict = preprocess_light_curves_test(batch_df)\n    if not test_seq_dict:\n        continue\n\n    X_test = np.array(list(test_seq_dict.values()))\n    preds = model.predict(X_test, batch_size=256)  # Optimized for T4\n\n    for i, oid in enumerate(test_seq_dict.keys()):\n        object_preds[oid] = preds[i]\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}