{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":118765,"databundleVersionId":15231210,"sourceType":"competition"},{"sourceId":290985927,"sourceType":"kernelVersion"}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\"\"\"\nStanford RNA 3D Folding - 2: Solution-Aware Inference Engine (v9.1)\nOptimized for: rna_gnn_production_v9.pth\nHardware: T4 x2\nFeature: TTA (5 Preds) + Physical Sanitization\n\"\"\"\n\nimport pandas as pd\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom tqdm import tqdm\nimport os\n\nclass InferenceConfig:\n    TEST_CSV = \"/kaggle/input/stanford-rna-3d-folding-2/test_sequences.csv\"\n    WEIGHT_PATH = \"/kaggle/input/notebook7a0f3361b2/rna_gnn_production_v9.pth\"\n    OUTPUT_PATH = \"submission.csv\"\n    DEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    NUM_PREDS = 5 \n\nclass RNAGNN_v9(nn.Module):\n    def __init__(self, embed_dim=128, hidden_dim=256):\n        super(RNAGNN_v9, self).__init__()\n        self.res_emb = nn.Embedding(5, embed_dim)\n        self.encoder = nn.LSTM(embed_dim, hidden_dim, num_layers=4, bidirectional=True, batch_first=True, dropout=0.2)\n        self.coord_proj = nn.Sequential(nn.Linear(hidden_dim * 2, hidden_dim), nn.ReLU(), nn.Dropout(0.2), nn.Linear(hidden_dim, 3))\n    def forward(self, seq):\n        x = self.res_emb(seq)\n        self.encoder.flatten_parameters()\n        f, _ = self.encoder(x.contiguous())\n        out = self.coord_proj(f)\n        return torch.clamp(out, -800.0, 800.0) # 與訓練一致的安全截斷\n\ndef run_final_inference():\n    print(f\"🚀 啟動最終推論引擎 v9.1 | 載入權重: {InferenceConfig.WEIGHT_PATH}\")\n    model = RNAGNN_v9().to(InferenceConfig.DEVICE)\n    \n    # 載入 v9 權重\n    if os.path.exists(InferenceConfig.WEIGHT_PATH):\n        sd = torch.load(InferenceConfig.WEIGHT_PATH, map_location=InferenceConfig.DEVICE)\n        model.load_state_dict(sd, strict=False)\n        print(\"✅ 權重載入成功！\")\n    else:\n        print(\"❌ 未找到權重，請確認檔名！\")\n        return\n\n    model.train() # 開啟 Dropout 進行測試時增強 (TTA)\n    \n    test_df = pd.read_csv(InferenceConfig.TEST_CSV)\n    res_map = {'A': 0, 'C': 1, 'G': 2, 'U': 3, 'X': 4}\n    results = []\n    \n    print(f\"⚙️ 正在處理 {len(test_df)} 條測試序列...\")\n    \n    with torch.no_grad():\n        for _, row in tqdm(test_df.iterrows(), total=len(test_df)):\n            tid, seq_str = row['target_id'], row['sequence']\n            seq_idx = torch.tensor([res_map.get(r, 4) for r in seq_str], dtype=torch.long).unsqueeze(0).to(InferenceConfig.DEVICE)\n            \n            # 5 次預測\n            preds_all = []\n            for _ in range(InferenceConfig.NUM_PREDS):\n                # 輸出經過縮放還原 (* 10)\n                out = model(seq_idx).cpu().numpy()[0] * 10.0\n                preds_all.append(out)\n            \n            # 格式化\n            for i, res_char in enumerate(seq_str):\n                row_data = {'ID': f\"{tid}_{i+1}\", 'resname': res_char, 'resid': i+1}\n                for k in range(InferenceConfig.NUM_PREDS):\n                    row_data[f'x_{k+1}'], row_data[f'y_{k+1}'], row_data[f'z_{k+1}'] = preds_all[k][i]\n                results.append(row_data)\n\n    # 儲存\n    final_df = pd.DataFrame(results)\n    final_df.to_csv(InferenceConfig.OUTPUT_PATH, index=False)\n    print(f\"🎉 提交檔案已生成: {InferenceConfig.OUTPUT_PATH} (共 {len(final_df)} 行)\")\n\nif __name__ == \"__main__\":\n    run_final_inference()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}