{
  "id": 730273,
  "title": "68th Place Solution for the CAFA 6 Protein Function Prediction Competition",
  "url": "/competitions/cafa-6-protein-function-prediction/discussion/730273",
  "author_name": "ismailcanoguz",
  "post_date": "2026-07-28T16:32:45.202000",
  "votes": 2,
  "comment_count": 0,
  "views": 0,
  "content": "<p>I am happy to share the solution that placed <strong>68th of 2,259 teams</strong> and received a <strong>silver medal</strong> in CAFA 6.</p>\n<p>My final submission was not a new end-to-end protein model. It was a prediction-stage ensemble built from two complementary public notebook outputs. My contribution was the layer that made those outputs compatible and competition-ready: score harmonization, agreement-aware stacking, Gene Ontology processing, Information Accretion-aware filtering, memory-efficient inference, and strict submission validation.</p>\n<h2>Context</h2>\n<p><strong>Business context:</strong> <a href=\"https://www.kaggle.com/competitions/cafa-6-protein-function-prediction/overview\" target=\"_blank\">Competition overview</a></p>\n<p><strong>Data context:</strong> <a href=\"https://www.kaggle.com/competitions/cafa-6-protein-function-prediction/data\" target=\"_blank\">Competition data</a></p>\n<p><strong>Reproducible notebook:</strong><br>\n<a href=\"https://www.kaggle.com/code/ismailcanoguz/68th-place-solution-for-the-cafa-6\" target=\"_blank\">Open the reproducible Kaggle notebook</a></p>\n<h2>Overview of the Approach</h2>\n<h3>Public prediction sources</h3>\n<p>The final stack used two public systems:</p>\n<ol>\n<li><p><a href=\"https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378\" target=\"_blank\"><strong>CAFA ensemble 0.378</strong></a> by <strong>antonoof</strong></p>\n<p>This notebook averages three fold-level prediction files and propagates scores through the Gene Ontology graph.</p></li>\n<li><p><a href=\"https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware\" target=\"_blank\"><strong>CAFA6 GOA + ProtT5 IC-Aware</strong></a> by <strong>maryamkhaled67</strong></p>\n<p>This notebook combines GOA transfer with ProtT5/InterPro predictions, propagates GO ancestors, and applies Information Accretion-aware filtering.</p></li>\n</ol>\n<p>These systems rely on different evidence. The first is a fold ensemble; the second combines annotation-transfer and sequence/domain signals. My objective was to preserve this complementarity without mixing incompatible score spaces or producing an excessively large union.</p>\n<h3>1. Score-space diagnosis and harmonization</h3>\n<p>The first important observation was that the two files were not numerically equivalent. One source could contain values above 1, while the other was probability-like.</p>\n<p>A direct weighted average would therefore be misleading. I inspected the source distributions before loading the complete files and used:</p>\n<pre><code>if sample_max &gt; 1.2:\n    probability = sigmoid(raw_score / temperature)\nelse:\n    probability = clip(raw_score, 0.001, 1.0)\n</code></pre>\n<p>For the unbounded source, I used a temperature of <code>8.0</code> to reduce sigmoid saturation. This is a monotonic score mapping rather than a claim of formal probabilistic calibration.</p>\n<h3>2. Memory-aware loading</h3>\n<p>The source files contained tens of millions of rows. Loading both into memory and joining them directly was unnecessary.</p>\n<p>I processed each file in chunks, retained only official test targets and valid GO terms, canonicalized alternative GO IDs, merged duplicate <code>(protein, GO term)</code> pairs by maximum score, and periodically kept only the strongest terms per protein.</p>\n<p>A final exact top-k pass ensured that every protein respected the intended loading limit.</p>\n<h3>3. Agreement-aware logit stacking</h3>\n<p>For each candidate GO term, I formed three signals:</p>\n<ul>\n<li><code>pA</code>: harmonized score from Model A;</li>\n<li><code>pB</code>: probability from Model B;</li>\n<li><code>pAgreement</code>: a product-of-experts-style score when both systems predicted the term.</li>\n</ul>\n<p>The final probability was calculated in logit space:</p>\n<pre><code>z = (\n    wA * logit(pA)\n    + wB * logit(pB)\n    + wAgreement * logit(pAgreement)\n) / blend_temperature\n\nprediction = sigmoid(z)\n</code></pre>\n<p>The recorded final parameters were:</p>\n<pre><code>Model A weight:       0.0872988403\nModel B weight:       0.7224695683\nAgreement weight:     0.1902315915\nBlend temperature:    0.8547518465\n</code></pre>\n<p>The agreement term acted as a consensus gate. Terms supported by both public systems were favored, while a source-specific term needed stronger evidence to survive.</p>\n<h3>4. Gene Ontology processing</h3>\n<p>The final stack was ontology-aware:</p>\n<ul>\n<li>alternative GO IDs were mapped to primary IDs;</li>\n<li>obsolete terms with valid replacements were handled;</li>\n<li>both <code>is_a</code> and <code>part_of</code> parent relations were parsed;</li>\n<li>each child score was propagated to all ancestors using hard maximum.</li>\n</ul>\n<p>This prevented inconsistent output and duplicate semantic labels.</p>\n<h3>5. Information Accretion-aware pruning</h3>\n<p>CAFA evaluation gives more weight to informative, less generic GO terms. I therefore used Information Accretion in two ways:</p>\n<ul>\n<li>weak low-IA terms were removed unless their confidence was high;</li>\n<li>remaining terms were ranked using:</li>\n</ul>\n<pre><code>rank_score = probability * (information_accretion + epsilon)\n</code></pre>\n<p>I retained at most 650 terms per protein, below the official limit of 1,500.</p>\n<h3>6. Output shaping and validation</h3>\n<p>The final scores received a mild rank decay to separate tied values. The submission writer then enforced:</p>\n<ul>\n<li>valid test target IDs;</li>\n<li>valid canonical GO terms;</li>\n<li>scores in <code>(0, 1]</code>;</li>\n<li>up to three significant digits;</li>\n<li>no duplicate <code>(protein, GO term)</code> pairs;</li>\n<li>no more than 1,500 terms per protein;</li>\n<li>tab-separated output without a header.</li>\n</ul>\n<h2>Validation Strategy</h2>\n<p>CAFA 6 is a prospective evaluation. The true test labels are annotations that appeared after the submission deadline, so ordinary cross-validation cannot reproduce the final leaderboard.</p>\n<p>I used currently known annotations for target proteins that also appeared in <code>train_terms.tsv</code> as a proxy calibration set. The annotations were expanded with GO ancestors and split deterministically into calibration and holdout proteins.</p>\n<p>The proxy metric used IA-weighted precision, recall, and F-max independently for Biological Process, Cellular Component, and Molecular Function.</p>\n<p>This proxy was useful for checking whether the blend behaved sensibly, but it was not a reliable estimate of the final score. In particular, the GOA-derived source can be favored when current annotations are reused.</p>\n<h2>Details of the Submission</h2>\n<h3>What was most important</h3>\n<p><strong>Score compatibility before ensembling</strong><br>\nThe most important engineering decision was not to treat every public score as a probability. Diagnosing and harmonizing the score spaces made the later weighting meaningful.</p>\n<p><strong>Consensus rather than a simple union</strong><br>\nCombining every term from both sources increased coverage but also introduced weak predictions. The explicit agreement signal provided a controlled way to reward shared evidence.</p>\n<p><strong>Metric-aware ontology processing</strong><br>\nGO propagation and IA-aware ranking were not cosmetic post-processing. They aligned the output with the hierarchical label space and the competition metric.</p>\n<p><strong>Practicality on Kaggle</strong><br>\nChunked reading and per-protein pruning allowed the complete stack to run without loading both large submissions into a single dataframe.</p>\n<h3>What did not work well</h3>\n<ul>\n<li>A direct arithmetic average was rejected because the input score ranges differed.</li>\n<li>A pure union retained too many low-confidence terms.</li>\n<li>The original random search was not fully reproducible because NumPy was not seeded.</li>\n<li>The current-annotation proxy was optimistic for GOA-derived predictions.</li>\n<li>One global set of weights was used across BP, CC, and MF; ontology-specific calibration may be stronger.</li>\n</ul>\n<h3>Limitations and possible improvements</h3>\n<p>This is a stacking solution and depends on two public upstream outputs. It does not train a new protein language model.</p>\n<p>Possible improvements include:</p>\n<ul>\n<li>time-aware annotation splits;</li>\n<li>separate temperatures and weights for BP, CC, and MF;</li>\n<li>taxonomy-aware source reliability;</li>\n<li>a learned missing-evidence policy for source-specific terms;</li>\n<li>direct evaluation with the official CAFA evaluator;</li>\n<li>systematic experiment logging for every submitted variant.</li>\n</ul>\n<h2>Reproduction</h2>\n<p>Attach the following inputs to the published notebook:</p>\n<ul>\n<li><code>cafa-6-protein-function-prediction</code></li>\n<li>output of <code>antonoof/cafa-ensemble-0-378</code></li>\n<li>output of <code>maryamkhaled67/cafa6-goa-prott5-ic-aware</code></li>\n</ul>\n<p>The notebook automatically checks that the two public sources resolve to different files. It then reports score diagnostics, source overlap, proxy results, output statistics, and final validation.</p>\n<h2>Sources</h2>\n<ul>\n<li><p>CAFA 6 competition<br>\n<a href=\"https://www.kaggle.com/competitions/cafa-6-protein-function-prediction\" target=\"_blank\">https://www.kaggle.com/competitions/cafa-6-protein-function-prediction</a></p></li>\n<li><p>CAFA ensemble 0.378 — antonoof<br>\n<a href=\"https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378\" target=\"_blank\">https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378</a></p></li>\n<li><p>CAFA6 GOA + ProtT5 IC-Aware — maryamkhaled67<br>\n<a href=\"https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware\" target=\"_blank\">https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware</a></p></li>\n<li><p>Original competition notebook<br>\n<a href=\"https://www.kaggle.com/code/ismailcanoguz/mybestscore-372?scriptVersionId=295439875\" target=\"_blank\">https://www.kaggle.com/code/ismailcanoguz/mybestscore-372?scriptVersionId=295439875</a></p></li>\n<li><p>CAFA-evaluator<br>\n<a href=\"https://github.com/BioComputingUP/CAFA-evaluator\" target=\"_blank\">https://github.com/BioComputingUP/CAFA-evaluator</a></p></li>\n</ul>\n<p>Thank you to the public notebook authors, the competition organizers, and the CAFA community.</p>",
  "messages": [
    {
      "id": 3504726,
      "postDate": "2026-07-28T16:32:45.203Z",
      "content": "<p>I am happy to share the solution that placed <strong>68th of 2,259 teams</strong> and received a <strong>silver medal</strong> in CAFA 6.</p>\n<p>My final submission was not a new end-to-end protein model. It was a prediction-stage ensemble built from two complementary public notebook outputs. My contribution was the layer that made those outputs compatible and competition-ready: score harmonization, agreement-aware stacking, Gene Ontology processing, Information Accretion-aware filtering, memory-efficient inference, and strict submission validation.</p>\n<h2>Context</h2>\n<p><strong>Business context:</strong> <a href=\"https://www.kaggle.com/competitions/cafa-6-protein-function-prediction/overview\" target=\"_blank\">Competition overview</a></p>\n<p><strong>Data context:</strong> <a href=\"https://www.kaggle.com/competitions/cafa-6-protein-function-prediction/data\" target=\"_blank\">Competition data</a></p>\n<p><strong>Reproducible notebook:</strong><br>\n<a href=\"https://www.kaggle.com/code/ismailcanoguz/68th-place-solution-for-the-cafa-6\" target=\"_blank\">Open the reproducible Kaggle notebook</a></p>\n<h2>Overview of the Approach</h2>\n<h3>Public prediction sources</h3>\n<p>The final stack used two public systems:</p>\n<ol>\n<li><p><a href=\"https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378\" target=\"_blank\"><strong>CAFA ensemble 0.378</strong></a> by <strong>antonoof</strong></p>\n<p>This notebook averages three fold-level prediction files and propagates scores through the Gene Ontology graph.</p></li>\n<li><p><a href=\"https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware\" target=\"_blank\"><strong>CAFA6 GOA + ProtT5 IC-Aware</strong></a> by <strong>maryamkhaled67</strong></p>\n<p>This notebook combines GOA transfer with ProtT5/InterPro predictions, propagates GO ancestors, and applies Information Accretion-aware filtering.</p></li>\n</ol>\n<p>These systems rely on different evidence. The first is a fold ensemble; the second combines annotation-transfer and sequence/domain signals. My objective was to preserve this complementarity without mixing incompatible score spaces or producing an excessively large union.</p>\n<h3>1. Score-space diagnosis and harmonization</h3>\n<p>The first important observation was that the two files were not numerically equivalent. One source could contain values above 1, while the other was probability-like.</p>\n<p>A direct weighted average would therefore be misleading. I inspected the source distributions before loading the complete files and used:</p>\n<pre><code>if sample_max &gt; 1.2:\n    probability = sigmoid(raw_score / temperature)\nelse:\n    probability = clip(raw_score, 0.001, 1.0)\n</code></pre>\n<p>For the unbounded source, I used a temperature of <code>8.0</code> to reduce sigmoid saturation. This is a monotonic score mapping rather than a claim of formal probabilistic calibration.</p>\n<h3>2. Memory-aware loading</h3>\n<p>The source files contained tens of millions of rows. Loading both into memory and joining them directly was unnecessary.</p>\n<p>I processed each file in chunks, retained only official test targets and valid GO terms, canonicalized alternative GO IDs, merged duplicate <code>(protein, GO term)</code> pairs by maximum score, and periodically kept only the strongest terms per protein.</p>\n<p>A final exact top-k pass ensured that every protein respected the intended loading limit.</p>\n<h3>3. Agreement-aware logit stacking</h3>\n<p>For each candidate GO term, I formed three signals:</p>\n<ul>\n<li><code>pA</code>: harmonized score from Model A;</li>\n<li><code>pB</code>: probability from Model B;</li>\n<li><code>pAgreement</code>: a product-of-experts-style score when both systems predicted the term.</li>\n</ul>\n<p>The final probability was calculated in logit space:</p>\n<pre><code>z = (\n    wA * logit(pA)\n    + wB * logit(pB)\n    + wAgreement * logit(pAgreement)\n) / blend_temperature\n\nprediction = sigmoid(z)\n</code></pre>\n<p>The recorded final parameters were:</p>\n<pre><code>Model A weight:       0.0872988403\nModel B weight:       0.7224695683\nAgreement weight:     0.1902315915\nBlend temperature:    0.8547518465\n</code></pre>\n<p>The agreement term acted as a consensus gate. Terms supported by both public systems were favored, while a source-specific term needed stronger evidence to survive.</p>\n<h3>4. Gene Ontology processing</h3>\n<p>The final stack was ontology-aware:</p>\n<ul>\n<li>alternative GO IDs were mapped to primary IDs;</li>\n<li>obsolete terms with valid replacements were handled;</li>\n<li>both <code>is_a</code> and <code>part_of</code> parent relations were parsed;</li>\n<li>each child score was propagated to all ancestors using hard maximum.</li>\n</ul>\n<p>This prevented inconsistent output and duplicate semantic labels.</p>\n<h3>5. Information Accretion-aware pruning</h3>\n<p>CAFA evaluation gives more weight to informative, less generic GO terms. I therefore used Information Accretion in two ways:</p>\n<ul>\n<li>weak low-IA terms were removed unless their confidence was high;</li>\n<li>remaining terms were ranked using:</li>\n</ul>\n<pre><code>rank_score = probability * (information_accretion + epsilon)\n</code></pre>\n<p>I retained at most 650 terms per protein, below the official limit of 1,500.</p>\n<h3>6. Output shaping and validation</h3>\n<p>The final scores received a mild rank decay to separate tied values. The submission writer then enforced:</p>\n<ul>\n<li>valid test target IDs;</li>\n<li>valid canonical GO terms;</li>\n<li>scores in <code>(0, 1]</code>;</li>\n<li>up to three significant digits;</li>\n<li>no duplicate <code>(protein, GO term)</code> pairs;</li>\n<li>no more than 1,500 terms per protein;</li>\n<li>tab-separated output without a header.</li>\n</ul>\n<h2>Validation Strategy</h2>\n<p>CAFA 6 is a prospective evaluation. The true test labels are annotations that appeared after the submission deadline, so ordinary cross-validation cannot reproduce the final leaderboard.</p>\n<p>I used currently known annotations for target proteins that also appeared in <code>train_terms.tsv</code> as a proxy calibration set. The annotations were expanded with GO ancestors and split deterministically into calibration and holdout proteins.</p>\n<p>The proxy metric used IA-weighted precision, recall, and F-max independently for Biological Process, Cellular Component, and Molecular Function.</p>\n<p>This proxy was useful for checking whether the blend behaved sensibly, but it was not a reliable estimate of the final score. In particular, the GOA-derived source can be favored when current annotations are reused.</p>\n<h2>Details of the Submission</h2>\n<h3>What was most important</h3>\n<p><strong>Score compatibility before ensembling</strong><br>\nThe most important engineering decision was not to treat every public score as a probability. Diagnosing and harmonizing the score spaces made the later weighting meaningful.</p>\n<p><strong>Consensus rather than a simple union</strong><br>\nCombining every term from both sources increased coverage but also introduced weak predictions. The explicit agreement signal provided a controlled way to reward shared evidence.</p>\n<p><strong>Metric-aware ontology processing</strong><br>\nGO propagation and IA-aware ranking were not cosmetic post-processing. They aligned the output with the hierarchical label space and the competition metric.</p>\n<p><strong>Practicality on Kaggle</strong><br>\nChunked reading and per-protein pruning allowed the complete stack to run without loading both large submissions into a single dataframe.</p>\n<h3>What did not work well</h3>\n<ul>\n<li>A direct arithmetic average was rejected because the input score ranges differed.</li>\n<li>A pure union retained too many low-confidence terms.</li>\n<li>The original random search was not fully reproducible because NumPy was not seeded.</li>\n<li>The current-annotation proxy was optimistic for GOA-derived predictions.</li>\n<li>One global set of weights was used across BP, CC, and MF; ontology-specific calibration may be stronger.</li>\n</ul>\n<h3>Limitations and possible improvements</h3>\n<p>This is a stacking solution and depends on two public upstream outputs. It does not train a new protein language model.</p>\n<p>Possible improvements include:</p>\n<ul>\n<li>time-aware annotation splits;</li>\n<li>separate temperatures and weights for BP, CC, and MF;</li>\n<li>taxonomy-aware source reliability;</li>\n<li>a learned missing-evidence policy for source-specific terms;</li>\n<li>direct evaluation with the official CAFA evaluator;</li>\n<li>systematic experiment logging for every submitted variant.</li>\n</ul>\n<h2>Reproduction</h2>\n<p>Attach the following inputs to the published notebook:</p>\n<ul>\n<li><code>cafa-6-protein-function-prediction</code></li>\n<li>output of <code>antonoof/cafa-ensemble-0-378</code></li>\n<li>output of <code>maryamkhaled67/cafa6-goa-prott5-ic-aware</code></li>\n</ul>\n<p>The notebook automatically checks that the two public sources resolve to different files. It then reports score diagnostics, source overlap, proxy results, output statistics, and final validation.</p>\n<h2>Sources</h2>\n<ul>\n<li><p>CAFA 6 competition<br>\n<a href=\"https://www.kaggle.com/competitions/cafa-6-protein-function-prediction\" target=\"_blank\">https://www.kaggle.com/competitions/cafa-6-protein-function-prediction</a></p></li>\n<li><p>CAFA ensemble 0.378 — antonoof<br>\n<a href=\"https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378\" target=\"_blank\">https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378</a></p></li>\n<li><p>CAFA6 GOA + ProtT5 IC-Aware — maryamkhaled67<br>\n<a href=\"https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware\" target=\"_blank\">https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware</a></p></li>\n<li><p>Original competition notebook<br>\n<a href=\"https://www.kaggle.com/code/ismailcanoguz/mybestscore-372?scriptVersionId=295439875\" target=\"_blank\">https://www.kaggle.com/code/ismailcanoguz/mybestscore-372?scriptVersionId=295439875</a></p></li>\n<li><p>CAFA-evaluator<br>\n<a href=\"https://github.com/BioComputingUP/CAFA-evaluator\" target=\"_blank\">https://github.com/BioComputingUP/CAFA-evaluator</a></p></li>\n</ul>\n<p>Thank you to the public notebook authors, the competition organizers, and the CAFA community.</p>",
      "rawMarkdown": "I am happy to share the solution that placed **68th of 2,259 teams** and received a **silver medal** in CAFA 6.\n\nMy final submission was not a new end-to-end protein model. It was a prediction-stage ensemble built from two complementary public notebook outputs. My contribution was the layer that made those outputs compatible and competition-ready: score harmonization, agreement-aware stacking, Gene Ontology processing, Information Accretion-aware filtering, memory-efficient inference, and strict submission validation.\n\n## Context\n\n**Business context:** [Competition overview](https://www.kaggle.com/competitions/cafa-6-protein-function-prediction/overview)\n\n**Data context:** [Competition data](https://www.kaggle.com/competitions/cafa-6-protein-function-prediction/data)\n\n**Reproducible notebook:**  \n[Open the reproducible Kaggle notebook](https://www.kaggle.com/code/ismailcanoguz/68th-place-solution-for-the-cafa-6)\n\n## Overview of the Approach\n\n### Public prediction sources\n\nThe final stack used two public systems:\n\n1. [**CAFA ensemble 0.378**](https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378) by **antonoof**\n\n   This notebook averages three fold-level prediction files and propagates scores through the Gene Ontology graph.\n\n2. [**CAFA6 GOA + ProtT5 IC-Aware**](https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware) by **maryamkhaled67**\n\n   This notebook combines GOA transfer with ProtT5/InterPro predictions, propagates GO ancestors, and applies Information Accretion-aware filtering.\n\nThese systems rely on different evidence. The first is a fold ensemble; the second combines annotation-transfer and sequence/domain signals. My objective was to preserve this complementarity without mixing incompatible score spaces or producing an excessively large union.\n\n### 1. Score-space diagnosis and harmonization\n\nThe first important observation was that the two files were not numerically equivalent. One source could contain values above 1, while the other was probability-like.\n\nA direct weighted average would therefore be misleading. I inspected the source distributions before loading the complete files and used:\n\n```python\nif sample_max > 1.2:\n    probability = sigmoid(raw_score / temperature)\nelse:\n    probability = clip(raw_score, 0.001, 1.0)\n```\n\nFor the unbounded source, I used a temperature of `8.0` to reduce sigmoid saturation. This is a monotonic score mapping rather than a claim of formal probabilistic calibration.\n\n### 2. Memory-aware loading\n\nThe source files contained tens of millions of rows. Loading both into memory and joining them directly was unnecessary.\n\nI processed each file in chunks, retained only official test targets and valid GO terms, canonicalized alternative GO IDs, merged duplicate `(protein, GO term)` pairs by maximum score, and periodically kept only the strongest terms per protein.\n\nA final exact top-k pass ensured that every protein respected the intended loading limit.\n\n### 3. Agreement-aware logit stacking\n\nFor each candidate GO term, I formed three signals:\n\n- `pA`: harmonized score from Model A;\n- `pB`: probability from Model B;\n- `pAgreement`: a product-of-experts-style score when both systems predicted the term.\n\nThe final probability was calculated in logit space:\n\n```python\nz = (\n    wA * logit(pA)\n    + wB * logit(pB)\n    + wAgreement * logit(pAgreement)\n) / blend_temperature\n\nprediction = sigmoid(z)\n```\n\nThe recorded final parameters were:\n\n```text\nModel A weight:       0.0872988403\nModel B weight:       0.7224695683\nAgreement weight:     0.1902315915\nBlend temperature:    0.8547518465\n```\n\nThe agreement term acted as a consensus gate. Terms supported by both public systems were favored, while a source-specific term needed stronger evidence to survive.\n\n### 4. Gene Ontology processing\n\nThe final stack was ontology-aware:\n\n- alternative GO IDs were mapped to primary IDs;\n- obsolete terms with valid replacements were handled;\n- both `is_a` and `part_of` parent relations were parsed;\n- each child score was propagated to all ancestors using hard maximum.\n\nThis prevented inconsistent output and duplicate semantic labels.\n\n### 5. Information Accretion-aware pruning\n\nCAFA evaluation gives more weight to informative, less generic GO terms. I therefore used Information Accretion in two ways:\n\n- weak low-IA terms were removed unless their confidence was high;\n- remaining terms were ranked using:\n\n```python\nrank_score = probability * (information_accretion + epsilon)\n```\n\nI retained at most 650 terms per protein, below the official limit of 1,500.\n\n### 6. Output shaping and validation\n\nThe final scores received a mild rank decay to separate tied values. The submission writer then enforced:\n\n- valid test target IDs;\n- valid canonical GO terms;\n- scores in `(0, 1]`;\n- up to three significant digits;\n- no duplicate `(protein, GO term)` pairs;\n- no more than 1,500 terms per protein;\n- tab-separated output without a header.\n\n## Validation Strategy\n\nCAFA 6 is a prospective evaluation. The true test labels are annotations that appeared after the submission deadline, so ordinary cross-validation cannot reproduce the final leaderboard.\n\nI used currently known annotations for target proteins that also appeared in `train_terms.tsv` as a proxy calibration set. The annotations were expanded with GO ancestors and split deterministically into calibration and holdout proteins.\n\nThe proxy metric used IA-weighted precision, recall, and F-max independently for Biological Process, Cellular Component, and Molecular Function.\n\nThis proxy was useful for checking whether the blend behaved sensibly, but it was not a reliable estimate of the final score. In particular, the GOA-derived source can be favored when current annotations are reused.\n\n## Details of the Submission\n\n### What was most important\n\n**Score compatibility before ensembling**  \nThe most important engineering decision was not to treat every public score as a probability. Diagnosing and harmonizing the score spaces made the later weighting meaningful.\n\n**Consensus rather than a simple union**  \nCombining every term from both sources increased coverage but also introduced weak predictions. The explicit agreement signal provided a controlled way to reward shared evidence.\n\n**Metric-aware ontology processing**  \nGO propagation and IA-aware ranking were not cosmetic post-processing. They aligned the output with the hierarchical label space and the competition metric.\n\n**Practicality on Kaggle**  \nChunked reading and per-protein pruning allowed the complete stack to run without loading both large submissions into a single dataframe.\n\n### What did not work well\n\n- A direct arithmetic average was rejected because the input score ranges differed.\n- A pure union retained too many low-confidence terms.\n- The original random search was not fully reproducible because NumPy was not seeded.\n- The current-annotation proxy was optimistic for GOA-derived predictions.\n- One global set of weights was used across BP, CC, and MF; ontology-specific calibration may be stronger.\n\n### Limitations and possible improvements\n\nThis is a stacking solution and depends on two public upstream outputs. It does not train a new protein language model.\n\nPossible improvements include:\n\n- time-aware annotation splits;\n- separate temperatures and weights for BP, CC, and MF;\n- taxonomy-aware source reliability;\n- a learned missing-evidence policy for source-specific terms;\n- direct evaluation with the official CAFA evaluator;\n- systematic experiment logging for every submitted variant.\n\n## Reproduction\n\nAttach the following inputs to the published notebook:\n\n- `cafa-6-protein-function-prediction`\n- output of `antonoof/cafa-ensemble-0-378`\n- output of `maryamkhaled67/cafa6-goa-prott5-ic-aware`\n\nThe notebook automatically checks that the two public sources resolve to different files. It then reports score diagnostics, source overlap, proxy results, output statistics, and final validation.\n\n## Sources\n\n- CAFA 6 competition  \n  https://www.kaggle.com/competitions/cafa-6-protein-function-prediction\n\n- CAFA ensemble 0.378 — antonoof  \n  https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378\n\n- CAFA6 GOA + ProtT5 IC-Aware — maryamkhaled67  \n  https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware\n\n- Original competition notebook  \n  https://www.kaggle.com/code/ismailcanoguz/mybestscore-372?scriptVersionId=295439875\n\n- CAFA-evaluator  \n  https://github.com/BioComputingUP/CAFA-evaluator\n\nThank you to the public notebook authors, the competition organizers, and the CAFA community.\n",
      "votes": 2
    }
  ],
  "comments": [],
  "raw_markdown_by_id": {
    "3504726": "I am happy to share the solution that placed **68th of 2,259 teams** and received a **silver medal** in CAFA 6.\n\nMy final submission was not a new end-to-end protein model. It was a prediction-stage ensemble built from two complementary public notebook outputs. My contribution was the layer that made those outputs compatible and competition-ready: score harmonization, agreement-aware stacking, Gene Ontology processing, Information Accretion-aware filtering, memory-efficient inference, and strict submission validation.\n\n## Context\n\n**Business context:** [Competition overview](https://www.kaggle.com/competitions/cafa-6-protein-function-prediction/overview)\n\n**Data context:** [Competition data](https://www.kaggle.com/competitions/cafa-6-protein-function-prediction/data)\n\n**Reproducible notebook:**  \n[Open the reproducible Kaggle notebook](https://www.kaggle.com/code/ismailcanoguz/68th-place-solution-for-the-cafa-6)\n\n## Overview of the Approach\n\n### Public prediction sources\n\nThe final stack used two public systems:\n\n1. [**CAFA ensemble 0.378**](https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378) by **antonoof**\n\n   This notebook averages three fold-level prediction files and propagates scores through the Gene Ontology graph.\n\n2. [**CAFA6 GOA + ProtT5 IC-Aware**](https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware) by **maryamkhaled67**\n\n   This notebook combines GOA transfer with ProtT5/InterPro predictions, propagates GO ancestors, and applies Information Accretion-aware filtering.\n\nThese systems rely on different evidence. The first is a fold ensemble; the second combines annotation-transfer and sequence/domain signals. My objective was to preserve this complementarity without mixing incompatible score spaces or producing an excessively large union.\n\n### 1. Score-space diagnosis and harmonization\n\nThe first important observation was that the two files were not numerically equivalent. One source could contain values above 1, while the other was probability-like.\n\nA direct weighted average would therefore be misleading. I inspected the source distributions before loading the complete files and used:\n\n```python\nif sample_max > 1.2:\n    probability = sigmoid(raw_score / temperature)\nelse:\n    probability = clip(raw_score, 0.001, 1.0)\n```\n\nFor the unbounded source, I used a temperature of `8.0` to reduce sigmoid saturation. This is a monotonic score mapping rather than a claim of formal probabilistic calibration.\n\n### 2. Memory-aware loading\n\nThe source files contained tens of millions of rows. Loading both into memory and joining them directly was unnecessary.\n\nI processed each file in chunks, retained only official test targets and valid GO terms, canonicalized alternative GO IDs, merged duplicate `(protein, GO term)` pairs by maximum score, and periodically kept only the strongest terms per protein.\n\nA final exact top-k pass ensured that every protein respected the intended loading limit.\n\n### 3. Agreement-aware logit stacking\n\nFor each candidate GO term, I formed three signals:\n\n- `pA`: harmonized score from Model A;\n- `pB`: probability from Model B;\n- `pAgreement`: a product-of-experts-style score when both systems predicted the term.\n\nThe final probability was calculated in logit space:\n\n```python\nz = (\n    wA * logit(pA)\n    + wB * logit(pB)\n    + wAgreement * logit(pAgreement)\n) / blend_temperature\n\nprediction = sigmoid(z)\n```\n\nThe recorded final parameters were:\n\n```text\nModel A weight:       0.0872988403\nModel B weight:       0.7224695683\nAgreement weight:     0.1902315915\nBlend temperature:    0.8547518465\n```\n\nThe agreement term acted as a consensus gate. Terms supported by both public systems were favored, while a source-specific term needed stronger evidence to survive.\n\n### 4. Gene Ontology processing\n\nThe final stack was ontology-aware:\n\n- alternative GO IDs were mapped to primary IDs;\n- obsolete terms with valid replacements were handled;\n- both `is_a` and `part_of` parent relations were parsed;\n- each child score was propagated to all ancestors using hard maximum.\n\nThis prevented inconsistent output and duplicate semantic labels.\n\n### 5. Information Accretion-aware pruning\n\nCAFA evaluation gives more weight to informative, less generic GO terms. I therefore used Information Accretion in two ways:\n\n- weak low-IA terms were removed unless their confidence was high;\n- remaining terms were ranked using:\n\n```python\nrank_score = probability * (information_accretion + epsilon)\n```\n\nI retained at most 650 terms per protein, below the official limit of 1,500.\n\n### 6. Output shaping and validation\n\nThe final scores received a mild rank decay to separate tied values. The submission writer then enforced:\n\n- valid test target IDs;\n- valid canonical GO terms;\n- scores in `(0, 1]`;\n- up to three significant digits;\n- no duplicate `(protein, GO term)` pairs;\n- no more than 1,500 terms per protein;\n- tab-separated output without a header.\n\n## Validation Strategy\n\nCAFA 6 is a prospective evaluation. The true test labels are annotations that appeared after the submission deadline, so ordinary cross-validation cannot reproduce the final leaderboard.\n\nI used currently known annotations for target proteins that also appeared in `train_terms.tsv` as a proxy calibration set. The annotations were expanded with GO ancestors and split deterministically into calibration and holdout proteins.\n\nThe proxy metric used IA-weighted precision, recall, and F-max independently for Biological Process, Cellular Component, and Molecular Function.\n\nThis proxy was useful for checking whether the blend behaved sensibly, but it was not a reliable estimate of the final score. In particular, the GOA-derived source can be favored when current annotations are reused.\n\n## Details of the Submission\n\n### What was most important\n\n**Score compatibility before ensembling**  \nThe most important engineering decision was not to treat every public score as a probability. Diagnosing and harmonizing the score spaces made the later weighting meaningful.\n\n**Consensus rather than a simple union**  \nCombining every term from both sources increased coverage but also introduced weak predictions. The explicit agreement signal provided a controlled way to reward shared evidence.\n\n**Metric-aware ontology processing**  \nGO propagation and IA-aware ranking were not cosmetic post-processing. They aligned the output with the hierarchical label space and the competition metric.\n\n**Practicality on Kaggle**  \nChunked reading and per-protein pruning allowed the complete stack to run without loading both large submissions into a single dataframe.\n\n### What did not work well\n\n- A direct arithmetic average was rejected because the input score ranges differed.\n- A pure union retained too many low-confidence terms.\n- The original random search was not fully reproducible because NumPy was not seeded.\n- The current-annotation proxy was optimistic for GOA-derived predictions.\n- One global set of weights was used across BP, CC, and MF; ontology-specific calibration may be stronger.\n\n### Limitations and possible improvements\n\nThis is a stacking solution and depends on two public upstream outputs. It does not train a new protein language model.\n\nPossible improvements include:\n\n- time-aware annotation splits;\n- separate temperatures and weights for BP, CC, and MF;\n- taxonomy-aware source reliability;\n- a learned missing-evidence policy for source-specific terms;\n- direct evaluation with the official CAFA evaluator;\n- systematic experiment logging for every submitted variant.\n\n## Reproduction\n\nAttach the following inputs to the published notebook:\n\n- `cafa-6-protein-function-prediction`\n- output of `antonoof/cafa-ensemble-0-378`\n- output of `maryamkhaled67/cafa6-goa-prott5-ic-aware`\n\nThe notebook automatically checks that the two public sources resolve to different files. It then reports score diagnostics, source overlap, proxy results, output statistics, and final validation.\n\n## Sources\n\n- CAFA 6 competition  \n  https://www.kaggle.com/competitions/cafa-6-protein-function-prediction\n\n- CAFA ensemble 0.378 — antonoof  \n  https://www.kaggle.com/code/antonoof/cafa-ensemble-0-378\n\n- CAFA6 GOA + ProtT5 IC-Aware — maryamkhaled67  \n  https://www.kaggle.com/code/maryamkhaled67/cafa6-goa-prott5-ic-aware\n\n- Original competition notebook  \n  https://www.kaggle.com/code/ismailcanoguz/mybestscore-372?scriptVersionId=295439875\n\n- CAFA-evaluator  \n  https://github.com/BioComputingUP/CAFA-evaluator\n\nThank you to the public notebook authors, the competition organizers, and the CAFA community.\n"
  },
  "complete": true,
  "source": "existing_discussion",
  "competition_id": 116062,
  "competition_slug": "cafa-6-protein-function-prediction",
  "team_id": 15130043,
  "solution_url": "/competitions/cafa-6-protein-function-prediction/writeups/cafa6-68th-place-silver-medal-solution"
}