{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":116062,"databundleVersionId":14875579,"sourceType":"competition"},{"sourceId":13482672,"sourceType":"datasetVersion","datasetId":8559888}],"dockerImageVersionId":31193,"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,"execution":{"iopub.status.busy":"2025-12-16T01:52:45.083402Z","iopub.execute_input":"2025-12-16T01:52:45.083625Z","iopub.status.idle":"2025-12-16T01:52:46.789758Z","shell.execute_reply.started":"2025-12-16T01:52:45.083607Z","shell.execute_reply":"2025-12-16T01:52:46.788976Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"emb = np.load(\"/kaggle/input/cafa6-protein-embeddings-esm2/protein_embeddings.npy\")\nids = pd.read_csv(\"/kaggle/input/cafa6-protein-embeddings-esm2/protein_ids.csv\")[\"protein_id\"].tolist()\n\nprint(emb.shape)  # (N, 1280)\nprint(len(ids))   # N\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:52:46.791372Z","iopub.execute_input":"2025-12-16T01:52:46.791710Z","iopub.status.idle":"2025-12-16T01:52:55.477900Z","shell.execute_reply.started":"2025-12-16T01:52:46.791692Z","shell.execute_reply":"2025-12-16T01:52:55.477221Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(\n    \"/kaggle/input/cafa-6-protein-function-prediction/Train/train_terms.tsv\",\n    sep=\"\\t\"\n)\n\ntrain_ids = train_df[\"EntryID\"].unique().tolist()\nlen(train_ids)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:52:55.478603Z","iopub.execute_input":"2025-12-16T01:52:55.478825Z","iopub.status.idle":"2025-12-16T01:52:55.782990Z","shell.execute_reply.started":"2025-12-16T01:52:55.478799Z","shell.execute_reply":"2025-12-16T01:52:55.782211Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install Bio","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:52:55.783776Z","iopub.execute_input":"2025-12-16T01:52:55.784484Z","iopub.status.idle":"2025-12-16T01:53:01.810163Z","shell.execute_reply.started":"2025-12-16T01:52:55.784435Z","shell.execute_reply":"2025-12-16T01:53:01.809497Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from Bio import SeqIO\n\ntest_ids = []\nfor record in SeqIO.parse(\"/kaggle/input/cafa-6-protein-function-prediction/Test/testsuperset.fasta\", \"fasta\"):\n    pid = record.id.split()[0]\n    test_ids.append(pid)\n\nlen(test_ids)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:53:01.811217Z","iopub.execute_input":"2025-12-16T01:53:01.811499Z","iopub.status.idle":"2025-12-16T01:53:03.585336Z","shell.execute_reply.started":"2025-12-16T01:53:01.811461Z","shell.execute_reply":"2025-12-16T01:53:03.584365Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"id_to_index = {pid: i for i, pid in enumerate(ids)}\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:53:03.586852Z","iopub.execute_input":"2025-12-16T01:53:03.587122Z","iopub.status.idle":"2025-12-16T01:53:03.659950Z","shell.execute_reply.started":"2025-12-16T01:53:03.587103Z","shell.execute_reply":"2025-12-16T01:53:03.659172Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_idx = [id_to_index[i] for i in train_ids if i in id_to_index]\ntest_idx  = [id_to_index[i] for i in test_ids  if i in id_to_index]\n\nX_train = emb[train_idx]\nX_test  = emb[test_idx]\n\nprint(X_train.shape, X_test.shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:53:03.661884Z","iopub.execute_input":"2025-12-16T01:53:03.662085Z","iopub.status.idle":"2025-12-16T01:53:04.286814Z","shell.execute_reply.started":"2025-12-16T01:53:03.662069Z","shell.execute_reply":"2025-12-16T01:53:04.286225Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Train IDs:\", len(train_ids))\nprint(\"Train with embedding:\", len(train_idx))\n\nprint(\"Test IDs:\", len(test_ids))\nprint(\"Test with embedding:\", len(test_idx))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:53:04.287540Z","iopub.execute_input":"2025-12-16T01:53:04.287792Z","iopub.status.idle":"2025-12-16T01:53:04.291970Z","shell.execute_reply.started":"2025-12-16T01:53:04.287762Z","shell.execute_reply":"2025-12-16T01:53:04.291276Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.save(\"X_train.npy\", X_train)\nnp.save(\"X_test.npy\", X_test)\n\npd.DataFrame(train_ids).to_csv(\"y_train_ids.csv\", index=False)\npd.DataFrame(test_ids).to_csv(\"y_test_ids.csv\", index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:53:04.293322Z","iopub.execute_input":"2025-12-16T01:53:04.293699Z","iopub.status.idle":"2025-12-16T01:53:05.224969Z","shell.execute_reply.started":"2025-12-16T01:53:04.293683Z","shell.execute_reply":"2025-12-16T01:53:05.224408Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.preprocessing import MultiLabelBinarizer\n\n# group labels\nlabels = train_df.groupby(\"EntryID\")[\"term\"].apply(list)\n\n# this MUST match X_train order\nvalid_train_ids = [i for i in train_ids if i in labels.index]\n\nmlb = MultiLabelBinarizer()\nY_train = mlb.fit_transform(labels[valid_train_ids])\n\nprint(\"X_train:\", X_train.shape)\nprint(\"Y_train:\", Y_train.shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:53:05.225682Z","iopub.execute_input":"2025-12-16T01:53:05.225920Z","iopub.status.idle":"2025-12-16T01:53:08.247711Z","shell.execute_reply.started":"2025-12-16T01:53:05.225894Z","shell.execute_reply":"2025-12-16T01:53:08.246874Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader, random_split\nfrom torch.cuda.amp import autocast, GradScaler\nimport numpy as np\n\n# ---------------- Dataset ----------------\nclass ProteinDataset(Dataset):\n    def __init__(self, X, Y):\n        # float16 nếu dùng GPU để tiết kiệm VRAM\n        self.use_gpu = torch.cuda.is_available()\n        self.X = torch.tensor(X, dtype=torch.float16 if torch.cuda.is_available() else torch.float32)\n        self.Y = torch.tensor(Y, dtype=torch.float32)\n\n    def __len__(self):\n        return len(self.X)\n\n    def __getitem__(self, i):\n        return self.X[i], self.Y[i]\n\n# ---------------- MLP model ----------------\n#Epoch 19/30 | Train Loss: 0.000923 | Val Loss: 0.001321\n# --> Saved best model at epoch 19 864,712\nclass MLP(nn.Module):\n    def __init__(self, input_dim=1280, output_dim=26125):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear(input_dim, 1024),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n\n            nn.Linear(1024, 512),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n\n\n            nn.Linear(512, output_dim)\n)\n\n\n    def forward(self, x):\n        return self.net(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:53:08.248545Z","iopub.execute_input":"2025-12-16T01:53:08.248955Z","iopub.status.idle":"2025-12-16T01:53:11.585873Z","shell.execute_reply.started":"2025-12-16T01:53:08.248936Z","shell.execute_reply":"2025-12-16T01:53:11.585019Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ---------------- Config ----------------\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nbatch_size = 128\nepochs = 30\nlr = 1e-3\nnp.random.seed(42)\n# ---------------- Dataset ----------------\ndataset = ProteinDataset(X_train, Y_train)\nval_size = int(0.1 * len(dataset))\ntrain_size = len(dataset) - val_size\ntrain_ds, val_ds = random_split(dataset, [train_size, val_size])\n\ntrain_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True)\nval_loader = DataLoader(val_ds, batch_size=batch_size)\n\n# ---------------- Model, loss, optimizer ----------------\nmodel = MLP(input_dim=X_train.shape[1], output_dim=Y_train.shape[1]).to(device)\n\ncriterion = nn.BCEWithLogitsLoss()\n\noptimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-4)\nscheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer, mode='min', factor=0.5, patience=2, verbose=True\n)\nscaler = GradScaler()\n\n# ---------------- Training loop ----------------\nbest_val_loss = float(\"inf\")  # lưu giá trị validation loss tốt nhất\n\nfor epoch in range(epochs):\n    model.train()\n    train_loss = 0\n\n    for Xb, Yb in train_loader:\n        Xb, Yb = Xb.to(device), Yb.to(device)\n\n        optimizer.zero_grad()\n        with autocast():\n            logits = model(Xb)\n            loss = criterion(logits, Yb)\n\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n\n        train_loss += loss.item() * Xb.size(0)\n\n    train_loss /= len(train_loader.dataset)\n\n    # ---------------- Validation ----------------\n    model.eval()\n    val_loss = 0\n    with torch.no_grad():\n        for Xb, Yb in val_loader:\n            Xb, Yb = Xb.to(device), Yb.to(device)\n            with autocast():\n                logits = model(Xb)\n                loss = criterion(logits, Yb)\n            val_loss += loss.item() * Xb.size(0)\n\n    val_loss /= len(val_loader.dataset)\n    scheduler.step(val_loss)\n\n    print(f\"Epoch {epoch+1}/{epochs} | Train Loss: {train_loss:.6f} | Val Loss: {val_loss:.6f}\")\n\n    # ---------------- Lưu model tốt nhất ----------------\n    if val_loss < best_val_loss:\n        best_val_loss = val_loss\n        torch.save(model.state_dict(), \"protein_mlp_best.pt\")\n        print(f\" --> Saved best model at epoch {epoch+1}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:53:11.586655Z","iopub.execute_input":"2025-12-16T01:53:11.587089Z","iopub.status.idle":"2025-12-16T01:59:17.043864Z","shell.execute_reply.started":"2025-12-16T01:53:11.587065Z","shell.execute_reply":"2025-12-16T01:59:17.043073Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport torch\nfrom tqdm import tqdm\n\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\ntorch.cuda.empty_cache()\n\n# --- Load model ---\nmodel = MLP(input_dim=X_train.shape[1], output_dim=Y_train.shape[1]).to(device)\nmodel.load_state_dict(torch.load(\"protein_mlp_best.pt\"))\nmodel.eval()\nmodel = model.half()         # giảm VRAM\n\n# --- Load test ---\nX_test = np.load(\"/kaggle/working/X_test.npy\", mmap_mode=\"r\")\ntest_ids = pd.read_csv(\"/kaggle/working/y_test_ids.csv\")[\"0\"].tolist()\ntest_id_to_index = {pid: i for i, pid in enumerate(test_ids)}\n\n# --- Load GO terms ---\ntrain_terms = pd.read_csv(\n    \"/kaggle/input/cafa-6-protein-function-prediction/Train/train_terms.tsv\",\n    sep=\"\\t\"\n)\n\nall_go_terms = sorted(train_terms[\"term\"].unique())\n\n# --- Predict with min_prob filter ---\nbatch_size = 32 \nmin_prob = 0.02 # chỉ lấy GO có xác suất >= 0.02 \ntop_k = 50 # nếu muốn dùng top_k thì đặt số, nếu không thì để None\n\nsubmission_rows = []\n\nfor i in tqdm(range(0, len(test_ids), batch_size), desc=\"Predicting & writing\"):\n    batch_ids = test_ids[i:i+batch_size]\n    batch_idx = [test_id_to_index[pid] for pid in batch_ids]\n\n    batch = X_test[batch_idx].astype(np.float16)\n    batch = torch.tensor(batch, dtype=torch.float16).to(device)\n\n    with torch.no_grad():\n        logits = model(batch)\n        probs = torch.sigmoid(logits).cpu().numpy()\n\n    for j, pid in enumerate(batch_ids):\n        p = probs[j]\n\n        # ----- Lọc theo min_prob -----\n        idx = np.where(p >= min_prob)[0]\n\n        # ----- (Tuỳ chọn) thêm top_k nếu ít kết quả quá -----\n        if top_k is not None and len(idx) < top_k:\n            top_extra = p.argsort()[-top_k:]\n            idx = np.unique(np.concatenate([idx, top_extra]))\n\n        # Sắp xếp theo xác suất giảm dần\n        idx = idx[np.argsort(-p[idx])]\n\n        # Ghi vào submission\n        for k in idx:\n            term = all_go_terms[k]\n            score = float(p[k])\n            submission_rows.append([pid, term, score])\n\n    del batch, logits, probs\n    torch.cuda.empty_cache()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T02:01:43.804775Z","iopub.execute_input":"2025-12-16T02:01:43.805557Z","iopub.status.idle":"2025-12-16T02:08:40.759246Z","shell.execute_reply.started":"2025-12-16T02:01:43.805528Z","shell.execute_reply":"2025-12-16T02:08:40.758426Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- Lưu file submission ---\nsubmission_df = pd.DataFrame(submission_rows)\nsubmission_df.to_csv(\"submission.tsv\", sep=\"\\t\", index=False)\nprint(\"Done! Submission shape:\", submission_df.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T02:08:51.489634Z","iopub.execute_input":"2025-12-16T02:08:51.489933Z","iopub.status.idle":"2025-12-16T02:09:22.355533Z","shell.execute_reply.started":"2025-12-16T02:08:51.489912Z","shell.execute_reply":"2025-12-16T02:09:22.354850Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}