{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":87793,"databundleVersionId":11228175,"sourceType":"competition"}],"dockerImageVersionId":30919,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Stanford RNA 3D Folding Competition Notebook\n\nThis notebook is designed for the \"Stanford RNA 3D Folding\" Kaggle competition.\nIt covers:\n\n1. Data Exploration  \n2. Data Preprocessing  \n   - Sequence encoding  \n   - Label grouping and padding (with NaN handling)\n3. Model Building using a fast CNN architecture  \n4. Model Training with early stopping  \n5. Prediction on test set and submission file generation\n\n_Note: This notebook uses only the provided CSV files (no external internet access)._","metadata":{"_uuid":"ba75c96f-68e8-4eb7-8159-5d762f35366f","_cell_guid":"4cd7fda1-faf2-4318-8c84-a231a515b338","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"markdown","source":"## 1. Import Libraries","metadata":{"_uuid":"4974e6d3-499d-4e64-ab6f-eb6b1fd4ba99","_cell_guid":"2e35b03c-2c0b-49fe-945d-e632cd4e7eeb","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# TensorFlow/Keras for deep learning model\nimport tensorflow as tf\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Input, Embedding, Conv1D, BatchNormalization, Dropout\nfrom tensorflow.keras.preprocessing.sequence import pad_sequences\nfrom tensorflow.keras.callbacks import EarlyStopping\n\n# Set random seed for reproducibility\nnp.random.seed(42)\ntf.random.set_seed(42)","metadata":{"_uuid":"1c8a6414-5cce-4cf9-82dc-74c2b1937448","_cell_guid":"95be3c1b-d91d-4ff2-aeb1-16b50fa1936e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:32:57.228537Z","iopub.execute_input":"2025-02-27T19:32:57.228961Z","iopub.status.idle":"2025-02-27T19:32:57.238381Z","shell.execute_reply.started":"2025-02-27T19:32:57.228934Z","shell.execute_reply":"2025-02-27T19:32:57.237532Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Data Loading and Exploration\n\nWe load the CSV files provided in the competition:\n- `train_sequences.csv`\n- `train_labels.csv`\n- `validation_sequences.csv` & `validation_labels.csv`\n- `test_sequences.csv`\n- `sample_submission.csv`\n\n**Important:** We fill missing values in the labels data with 0 to avoid NaN issues during training.","metadata":{"_uuid":"7d1c7eed-82d4-46ea-859c-d7a7490d9852","_cell_guid":"08036a25-432d-4eca-bbfe-961e87c3f83a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Define file paths (Kaggle input paths)\nTRAIN_SEQ_PATH = '/kaggle/input/stanford-rna-3d-folding/train_sequences.csv'\nTRAIN_LABELS_PATH = '/kaggle/input/stanford-rna-3d-folding/train_labels.csv'\nVALID_SEQ_PATH = '/kaggle/input/stanford-rna-3d-folding/validation_sequences.csv'\nVALID_LABELS_PATH = '/kaggle/input/stanford-rna-3d-folding/validation_labels.csv'\nTEST_SEQ_PATH  = '/kaggle/input/stanford-rna-3d-folding/test_sequences.csv'\nSAMPLE_SUB_PATH = '/kaggle/input/stanford-rna-3d-folding/sample_submission.csv'\n\n# Load CSV files\ntrain_sequences = pd.read_csv(TRAIN_SEQ_PATH)\ntrain_labels = pd.read_csv(TRAIN_LABELS_PATH)\nvalid_sequences = pd.read_csv(VALID_SEQ_PATH)\nvalid_labels = pd.read_csv(VALID_LABELS_PATH)\ntest_sequences = pd.read_csv(TEST_SEQ_PATH)\nsample_submission = pd.read_csv(SAMPLE_SUB_PATH)\n\n# Fill missing values in labels with 0\ntrain_labels.fillna(0, inplace=True)\nvalid_labels.fillna(0, inplace=True)\n\n# Display basic info\nprint(\"Train Sequences Shape:\", train_sequences.shape)\nprint(\"Train Labels Shape:\", train_labels.shape)\nprint(\"Validation Sequences Shape:\", valid_sequences.shape)\nprint(\"Validation Labels Shape:\", valid_labels.shape)\nprint(\"Test Sequences Shape:\", test_sequences.shape)\n\n# Look at a few examples\nprint(\"\\nTrain Sequences Head:\")\nprint(train_sequences.head())\nprint(\"\\nTrain Labels Head:\")\nprint(train_labels.head())","metadata":{"_uuid":"bd1b8c77-f907-4a3b-af02-5141a285d2ac","_cell_guid":"9f130e5f-7f0d-424f-8634-f304c96c9437","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:32:57.241755Z","iopub.execute_input":"2025-02-27T19:32:57.242106Z","iopub.status.idle":"2025-02-27T19:32:57.512516Z","shell.execute_reply.started":"2025-02-27T19:32:57.242084Z","shell.execute_reply":"2025-02-27T19:32:57.511809Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Data Preprocessing\n\n### 3.1 Sequence Encoding\n\nWe map each nucleotide to an integer:\n- A: 1, C: 2, G: 3, U: 4  \nUnknown characters are mapped to 0.","metadata":{"_uuid":"7b3ae69d-4427-4c9f-931e-9ea1176f5886","_cell_guid":"f84ddf2c-b665-420f-a7ec-43cfe711c0a4","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"nucleotide_map = {'A': 1, 'C': 2, 'G': 3, 'U': 4}\n\ndef encode_sequence(seq):\n    \"\"\"Encodes a RNA sequence into a list of integers based on nucleotide_map.\"\"\"\n    return [nucleotide_map.get(ch, 0) for ch in seq]\n\n# Apply encoding to all sequence files\ntrain_sequences['encoded'] = train_sequences['sequence'].apply(encode_sequence)\nvalid_sequences['encoded'] = valid_sequences['sequence'].apply(encode_sequence)\ntest_sequences['encoded'] = test_sequences['sequence'].apply(encode_sequence)","metadata":{"_uuid":"2f38e291-5974-4c43-93a0-fb024c343ba0","_cell_guid":"3c95facf-6580-4e0d-bf92-9cfb9539a551","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:32:57.513850Z","iopub.execute_input":"2025-02-27T19:32:57.514099Z","iopub.status.idle":"2025-02-27T19:32:57.534450Z","shell.execute_reply.started":"2025-02-27T19:32:57.514080Z","shell.execute_reply":"2025-02-27T19:32:57.533720Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 3.2 Processing Label Data\n\nEach row in the labels CSV is for one residue, with an `ID` formatted as `target_id_resid`.\nWe group rows by `target_id` and sort by residue number.\nHere, we use the first structure (x_1, y_1, z_1) as our target coordinates.","metadata":{"_uuid":"fbc2c63d-cb94-493c-9d4f-e0cf078d07c1","_cell_guid":"c3189e08-66bf-4548-8f33-3422b765f88c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"def process_labels(labels_df):\n    \"\"\"\n    Processes a labels DataFrame by grouping rows by target_id.\n    Returns a dictionary mapping target_id to an array of coordinates (seq_len, 3).\n    \"\"\"\n    label_dict = {}\n    for idx, row in labels_df.iterrows():\n        # Split ID into target_id and residue number (assumes format \"targetid_resid\")\n        parts = row['ID'].split('_')\n        target_id = \"_\".join(parts[:-1])\n        resid = int(parts[-1])\n        # Extract the coordinates; they should be numeric (missing values already set to 0)\n        coord = np.array([row['x_1'], row['y_1'], row['z_1']], dtype=np.float32)\n        if target_id not in label_dict:\n            label_dict[target_id] = []\n        label_dict[target_id].append((resid, coord))\n    \n    # Sort residues by resid and stack coordinates\n    for key in label_dict:\n        sorted_coords = sorted(label_dict[key], key=lambda x: x[0])\n        coords = np.stack([c for r, c in sorted_coords])\n        label_dict[key] = coords\n    return label_dict\n\n# Process training and validation labels\ntrain_labels_dict = process_labels(train_labels)\nvalid_labels_dict = process_labels(valid_labels)","metadata":{"_uuid":"1d94dbc2-d120-4c82-9d70-711e6b4a68c1","_cell_guid":"afbd9f74-c685-4e0a-9262-005656f79ca9","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:32:57.535571Z","iopub.execute_input":"2025-02-27T19:32:57.535852Z","iopub.status.idle":"2025-02-27T19:33:04.746021Z","shell.execute_reply.started":"2025-02-27T19:32:57.535807Z","shell.execute_reply":"2025-02-27T19:33:04.745283Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 3.3 Creating Datasets and Padding\n\nWe match each target sequence with its corresponding coordinate labels.\nThen we pad sequences and coordinate arrays to a uniform length.\n\nPadded positions in coordinates are set to 0.","metadata":{"_uuid":"acfc3ec8-97ac-44cf-a2dc-d1a721b1c8d8","_cell_guid":"1508c42f-9b88-4934-be7d-c75fcc36195c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"def create_dataset(sequences_df, labels_dict):\n    \"\"\"\n    Creates a dataset from a sequences DataFrame and a labels dictionary.\n    Returns:\n        X: list of encoded sequences,\n        y: list of coordinate arrays,\n        target_ids: list of target ids.\n    \"\"\"\n    X, y, target_ids = [], [], []\n    for idx, row in sequences_df.iterrows():\n        tid = row['target_id']\n        if tid in labels_dict:\n            X.append(row['encoded'])\n            y.append(labels_dict[tid])\n            target_ids.append(tid)\n    return X, y, target_ids\n\n# Create training and validation datasets\nX_train, y_train, train_ids = create_dataset(train_sequences, train_labels_dict)\nX_valid, y_valid, valid_ids = create_dataset(valid_sequences, valid_labels_dict)\n\n# Determine maximum sequence length from training set\nmax_len = max(len(seq) for seq in X_train)\nprint(\"Maximum sequence length (train):\", max_len)\n\n# Pad the sequences (padding value = 0)\nX_train_pad = pad_sequences(X_train, maxlen=max_len, padding='post', value=0)\nX_valid_pad = pad_sequences(X_valid, maxlen=max_len, padding='post', value=0)\n\n# Function to pad coordinate arrays\ndef pad_coordinates(coord_array, max_len):\n    L = coord_array.shape[0]\n    if L < max_len:\n        pad_width = ((0, max_len - L), (0, 0))\n        return np.pad(coord_array, pad_width, mode='constant', constant_values=0)\n    else:\n        return coord_array\n\n# Pad coordinate arrays\ny_train_pad = np.array([pad_coordinates(arr, max_len) for arr in y_train])\ny_valid_pad = np.array([pad_coordinates(arr, max_len) for arr in y_valid])\n\n# Check for any NaN values in the targets\nprint(\"Any NaN in y_train_pad?\", np.isnan(y_train_pad).any())\nprint(\"Any NaN in y_valid_pad?\", np.isnan(y_valid_pad).any())\n\nprint(\"X_train_pad shape:\", X_train_pad.shape)\nprint(\"y_train_pad shape:\", y_train_pad.shape)","metadata":{"_uuid":"f00ec075-2695-461e-a423-b8b251c43acd","_cell_guid":"be76be8c-682f-418d-94d0-4f6bb519b344","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:33:04.746880Z","iopub.execute_input":"2025-02-27T19:33:04.747166Z","iopub.status.idle":"2025-02-27T19:33:04.878684Z","shell.execute_reply.started":"2025-02-27T19:33:04.747139Z","shell.execute_reply":"2025-02-27T19:33:04.877720Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Fast CNN Model Training\n\nIn this section, we build a faster CNN-based model.\nThe model uses:\n- An Embedding layer  \n- Two Conv1D blocks (with BatchNormalization and Dropout)  \n- A final Conv1D layer (kernel size 1) to output 3 coordinates per residue","metadata":{"_uuid":"8d832ed7-a808-47b1-9bd1-4ca8aa72b337","_cell_guid":"9616835f-b774-4de4-bb7d-99fffe07dc65","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Define hyperparameters for the CNN model\nvocab_size = max(nucleotide_map.values()) + 1  # +1 for padding token 0\nembedding_dim = 16\nnum_filters = 64\nkernel_size = 3\ndrop_rate = 0.2\n\n# Build the CNN model\ninput_seq_cnn = Input(shape=(max_len,), name='input_seq')\nx_cnn = Embedding(input_dim=vocab_size, output_dim=embedding_dim, mask_zero=True, name='embedding')(input_seq_cnn)\n\n# First convolutional block\nx_cnn = Conv1D(filters=num_filters, kernel_size=kernel_size, padding='same', activation='relu', name='conv1')(x_cnn)\nx_cnn = BatchNormalization(name='bn1')(x_cnn)\nx_cnn = Dropout(drop_rate, name='drop1')(x_cnn)\n\n# Second convolutional block\nx_cnn = Conv1D(filters=num_filters, kernel_size=kernel_size, padding='same', activation='relu', name='conv2')(x_cnn)\nx_cnn = BatchNormalization(name='bn2')(x_cnn)\nx_cnn = Dropout(drop_rate, name='drop2')(x_cnn)\n\n# Final convolution to output 3 coordinates per residue (x, y, z)\noutput_coords_cnn = Conv1D(filters=3, kernel_size=1, padding='same', activation='linear', name='predicted_coords')(x_cnn)\n\ncnn_model = Model(inputs=input_seq_cnn, outputs=output_coords_cnn)\ncnn_model.compile(optimizer='adam', loss='mse')\n\ncnn_model.summary()","metadata":{"_uuid":"b5b77c10-dad8-4527-8f28-174c04afefe2","_cell_guid":"eb0e2b5f-347a-45b9-a475-a2a4108dd9f1","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:33:04.879561Z","iopub.execute_input":"2025-02-27T19:33:04.879805Z","iopub.status.idle":"2025-02-27T19:33:04.964425Z","shell.execute_reply.started":"2025-02-27T19:33:04.879784Z","shell.execute_reply":"2025-02-27T19:33:04.963776Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Model Training\n\nWe train the CNN model using early stopping to monitor the validation loss.\nWith the NaN issues addressed in the data, training should proceed without nan losses.","metadata":{"_uuid":"93c354e3-05d7-4632-89cb-b9c843c37977","_cell_guid":"7744df17-746a-438d-82eb-3fda8874d03c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"early_stop_cnn = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)\n\nhistory_cnn = cnn_model.fit(X_train_pad, y_train_pad,\n                            validation_data=(X_valid_pad, y_valid_pad),\n                            epochs=50,\n                            batch_size=16,\n                            callbacks=[early_stop_cnn],\n                            verbose=1)\n\n# Plot training and validation loss\nplt.figure(figsize=(8, 5))\nplt.plot(history_cnn.history['loss'], label='Train Loss (CNN)')\nplt.plot(history_cnn.history['val_loss'], label='Val Loss (CNN)')\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"MSE Loss\")\nplt.title(\"CNN Model Training vs. Validation Loss\")\nplt.legend()\nplt.show()","metadata":{"_uuid":"8b6e7d19-9670-4f06-9995-61f156f5588e","_cell_guid":"fbd23461-6579-49ed-adba-4c414b97a74b","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:33:04.965199Z","iopub.execute_input":"2025-02-27T19:33:04.965491Z","iopub.status.idle":"2025-02-27T19:33:13.720018Z","shell.execute_reply.started":"2025-02-27T19:33:04.965461Z","shell.execute_reply":"2025-02-27T19:33:13.719183Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 6. Generating Predictions and Submission File\n\nFor each test sequence, we predict the 3D coordinates using our trained CNN model.\n\nThe submission requires 5 sets of coordinates per target. In this baseline, we replicate the same predicted structure 5 times.","metadata":{"_uuid":"fe88f530-9cd2-4dd5-94f2-41e2021c50f9","_cell_guid":"fe7b78f7-a78f-4cda-b597-5cbf25d98f28","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Prepare test data: pad sequences to same length as training\nX_test = test_sequences['encoded'].tolist()\nX_test_pad = pad_sequences(X_test, maxlen=max_len, padding='post', value=0)\n\n# Predict coordinates using the trained CNN model\npredictions = cnn_model.predict(X_test_pad)\n\n# Build submission rows. Each row corresponds to a residue from a test target.\nsubmission_rows = []\nfor idx, row in test_sequences.iterrows():\n    target_id = row['target_id']\n    # Get predicted coordinates (shape: [max_len, 3])\n    pred_coords = predictions[idx]\n    # Determine actual sequence length\n    seq_length = len(row['encoded'])\n    pred_coords = pred_coords[:seq_length, :]  # only actual residues\n    \n    # For each residue, create a row in the submission file\n    for i in range(seq_length):\n        coords = pred_coords[i, :]\n        # Replicate the same prediction 5 times for submission format\n        submission_rows.append({\n            'ID': f\"{target_id}_{i+1}\",\n            'resname': row['sequence'][i],\n            'resid': i+1,\n            **{f\"x_{j+1}\": coords[0] for j in range(5)},\n            **{f\"y_{j+1}\": coords[1] for j in range(5)},\n            **{f\"z_{j+1}\": coords[2] for j in range(5)}\n        })\n\nsubmission_df = pd.DataFrame(submission_rows)\nprint(\"Submission DataFrame shape:\", submission_df.shape)\nprint(submission_df.head(10))","metadata":{"_uuid":"d1e938fc-98b7-499c-b05b-b6edd621c9d0","_cell_guid":"fbab4072-6a39-49e3-b6f7-57e64556e9d7","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:33:13.720911Z","iopub.execute_input":"2025-02-27T19:33:13.721133Z","iopub.status.idle":"2025-02-27T19:33:14.018040Z","shell.execute_reply.started":"2025-02-27T19:33:13.721114Z","shell.execute_reply":"2025-02-27T19:33:14.017273Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 7. Saving the Submission File\n\nFinally, we save the submission file as `submission.csv`.","metadata":{"_uuid":"7d9bbfd9-5f22-46bb-9a74-86c64c32623e","_cell_guid":"e122805e-51a2-411f-81f8-072ff85e4f5a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"submission_df.to_csv(\"submission.csv\", index=False)\nprint(\"Submission file saved as submission.csv\")","metadata":{"_uuid":"2632f190-329c-4373-9632-b03b99d89e90","_cell_guid":"f1b1e66f-6ed2-4fed-b7bc-b5b9d2a306c2","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-02-27T19:33:14.020146Z","iopub.execute_input":"2025-02-27T19:33:14.020381Z","iopub.status.idle":"2025-02-27T19:33:14.061056Z","shell.execute_reply.started":"2025-02-27T19:33:14.020362Z","shell.execute_reply":"2025-02-27T19:33:14.060274Z"}},"outputs":[],"execution_count":null}]}