{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpuV5e8","dataSources":[{"sourceType":"competition","sourceId":116062,"databundleVersionId":14875579},{"sourceType":"datasetVersion","sourceId":5499219,"datasetId":3167603,"databundleVersionId":5573606},{"sourceType":"modelInstanceVersion","sourceId":517386,"databundleVersionId":13356330,"modelInstanceId":407978,"modelId":425816},{"sourceType":"modelInstanceVersion","sourceId":592536,"databundleVersionId":13890899,"modelInstanceId":443355,"modelId":459869},{"sourceType":"kernelVersion","sourceId":130671737}],"dockerImageVersionId":31261,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-21T23:20:41.976491Z","iopub.execute_input":"2026-04-21T23:20:41.977037Z","iopub.status.idle":"2026-04-21T23:20:42.366170Z","shell.execute_reply.started":"2026-04-21T23:20:41.977010Z","shell.execute_reply":"2026-04-21T23:20:42.365107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import DataLoader, TensorDataset\nfrom sklearn.model_selection import KFold\nimport os\n\n# --- SETTINGS ---\nNUM_LABELS = 1500\nBATCH_SIZE = 256\nEPOCHS = 12\nN_FOLDS = 5\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# --- LOAD T5 EMBEDDINGS ONLY ---\nprint(\"📦 Loading T5 embeddings...\")\nt5_path = '/kaggle/input/t5embeds'\nX_all = np.load(os.path.join(t5_path, 'train_embeds.npy'))\nX_test = np.load(os.path.join(t5_path, 'test_embeds.npy'))\ntrain_ids = np.load(os.path.join(t5_path, 'train_ids.npy'))\ntest_ids = np.load(os.path.join(t5_path, 'test_ids.npy'))\n\n# Labels\ntrain_terms = pd.read_csv(\"/kaggle/input/cafa-6-protein-function-prediction/Train/train_terms.tsv\", sep=\"\\t\")\nall_go_terms = train_terms['term'].value_counts().head(NUM_LABELS).index.tolist()\n\npivot = train_terms[train_terms['term'].isin(all_go_terms)].pivot(index='EntryID', columns='term', values='term').notna().astype(np.int8)\npivot = pivot.reindex(index=train_ids, columns=all_go_terms, fill_value=0)\ny_all = pivot.values.astype(np.float32)\n\nINPUT_DIM = X_all.shape[1]\n\n# --- MODEL ARCHITECTURE ---\nclass ResidualBlock(nn.Module):\n    def __init__(self, dim):\n        super().__init__()\n        self.block = nn.Sequential(\n            nn.Linear(dim, dim),\n            nn.BatchNorm1d(dim),\n            nn.ReLU(),\n            nn.Dropout(0.2)\n        )\n    def forward(self, x): return x + self.block(x)\n\nclass Top5Model(nn.Module):\n    def __init__(self, input_dim, num_labels):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear(input_dim, 1024),\n            nn.BatchNorm1d(1024),\n            nn.ReLU(),\n            ResidualBlock(1024),\n            nn.Linear(1024, 2048),\n            nn.BatchNorm1d(2048),\n            nn.ReLU(),\n            ResidualBlock(2048),\n            nn.Linear(2048, num_labels),\n            nn.Sigmoid()\n        )\n    def forward(self, x): return self.net(x)\n\n# --- 5-FOLD CROSS-VALIDATION ---\nkfold = KFold(n_splits=N_FOLDS, shuffle=True, random_state=42)\nall_test_preds = []\n\nfor fold, (train_idx, val_idx) in enumerate(kfold.split(X_all)):\n    print(f\"\\n🌀 Training Fold {fold+1}/{N_FOLDS}...\")\n    \n    X_train, y_train = X_all[train_idx], y_all[train_idx]\n    train_loader = DataLoader(TensorDataset(torch.from_numpy(X_train).float(), torch.from_numpy(y_train).float()),\n                              batch_size=BATCH_SIZE, shuffle=True)\n    \n    model = Top5Model(INPUT_DIM, NUM_LABELS).to(device)\n    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)\n    criterion = nn.BCELoss()\n    \n    for epoch in range(EPOCHS):\n        model.train()\n        epoch_loss = 0\n        for batch_x, batch_y in train_loader:\n            batch_x, batch_y = batch_x.to(device), batch_y.to(device)\n            optimizer.zero_grad()\n            loss = criterion(model(batch_x), batch_y)\n            loss.backward()\n            optimizer.step()\n            epoch_loss += loss.item()\n        if (epoch+1) % 4 == 0:\n            print(f\"  Epoch {epoch+1} Loss: {epoch_loss/len(train_loader):.4f}\")\n    \n    # Fold inference\n    model.eval()\n    fold_preds = []\n    with torch.no_grad():\n        test_tensor = torch.from_numpy(X_test).float().to(device)\n        for i in range(0, len(test_tensor), BATCH_SIZE):\n            batch = test_tensor[i:i+BATCH_SIZE]\n            fold_preds.append(model(batch).cpu().numpy())\n    \n    all_test_preds.append(np.concatenate(fold_preds))\n    print(f\"✅ Fold {fold+1} Complete.\")\n\n# --- ENSEMBLE & WEIGHTED SUBMISSION ---\nprint(\"\\n🤖 Averaging predictions across folds...\")\nfinal_preds = np.mean(all_test_preds, axis=0)\n\ngo_freq = train_terms['term'].value_counts(normalize=True).to_dict()\ntop_go_terms = list(pd.Series(go_freq).sort_values(ascending=False).head(100).index)\nboost_factor = 1.05\n\ntop_k_main = 30\ntop_k_extra = 50\nmain_threshold = 0.05\nextra_threshold = 0.1\nsubmission_file = \"submission.tsv\"\n\nwith open(submission_file, \"w\") as f:\n    for i in range(len(final_preds)):\n        prot_id = test_ids[i]\n        top_indices = np.argsort(final_preds[i])[::-1][:top_k_extra]\n        for rank, idx in enumerate(top_indices):\n            score = final_preds[i][idx]\n            term = all_go_terms[idx]\n            # Weighted boost\n            if term in top_go_terms:\n                score *= boost_factor\n                score = min(score, 1.0)\n            # Threshold logic\n            if rank < top_k_main and score > main_threshold:\n                f.write(f\"{prot_id}\\t{term}\\t{score:.3f}\\n\")\n            elif rank >= top_k_main and score > extra_threshold:\n                f.write(f\"{prot_id}\\t{term}\\t{score:.3f}\\n\")\n\nprint(f\"🚀 Submission ready: '{submission_file}' (Top-5 target, T5-only)\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T23:20:42.366773Z","iopub.execute_input":"2026-04-21T23:20:42.366996Z","iopub.status.idle":"2026-04-22T00:03:27.180603Z","shell.execute_reply.started":"2026-04-21T23:20:42.366979Z","shell.execute_reply":"2026-04-22T00:03:27.179264Z"}},"outputs":[],"execution_count":null}]}