File size: 6,735 Bytes
634ebe8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | # Contributing to CAFF
Thank you for your interest in contributing to CAFF. This document explains
how the project is structured and how to make changes that match the
existing style.
---
## Project structure
```
caff/ Core implementation (paper Sections 6-7)
configs/ YAML training configurations
data/ Real-data placeholder (KGs, QA splits)
examples/ Demonstration files (do NOT cite their numbers)
scripts/ Standalone utilities (KG building, BFS, annotation)
tests/ Unit tests + smoke fixtures
runs/ Training artifacts (auto-generated)
```
Top-level entry points: `train.py`, `evaluate.py`, `context_swap_diagnostic.py`.
---
## Setting up a development environment
```bash
git clone https://github.com/marwan8086/caff.git
cd caff
python -m venv .venv
source .venv/bin/activate # Linux / macOS
# or: .venv\Scripts\Activate.ps1 # Windows PowerShell
pip install -r requirements.txt
```
For the smoke test you also need to build the synthetic fixtures once:
```bash
python tests/fixtures/build_smoke_data.py
```
This produces `tests/fixtures/smoke_kg.tsv` and three QA JSON splits.
---
## Running the test suite
The full suite must pass before any commit:
```bash
python -m pytest tests/ -v
```
Expected: 52 tests passing in roughly 5-10 seconds (depending on machine).
If you add new functionality, add a corresponding unit test in `tests/`. We
prefer pytest's plain-function style over class-based tests; see existing
test files for examples.
---
## Smoke training (end-to-end check)
Before submitting changes that touch the training loop, run the CPU-friendly
smoke training to verify the pipeline still trains end-to-end:
```bash
python train.py --config configs/caff_smoke.yaml --seed 42
```
Expected output (abridged):
```
KG loaded: |V|=5,000 |E|=24,995 |R|=20
Built 20,982 triple instances
[Epoch 1/2] loss=0.04xx dev_f1=0.0xxx ...
[Epoch 2/2] loss=0.04xx dev_f1=0.0xxx ...
Training complete.
```
Loss should decrease across epochs. F1 numbers will be small because the
fixtures are random synthetic data; this is expected.
---
## Coding style
- Python 3.10+ syntax (we use `X | Y` unions, `list[...]`, etc.)
- Type-annotate function signatures
- Docstrings reference the paper's equation numbers when applicable, e.g.
`Eq. 14`, `paper Section 8.4`
- Use ASCII characters in source files. Em-dashes, smart quotes, and other
Unicode punctuation should be avoided in code, configs, and the README so
that Windows cp1252 environments do not break logging
- Source files use LF line endings (enforced by `.gitattributes`)
- Configuration values must round-trip: every field added to `CAFFConfig`
must be readable from YAML and validated in `__post_init__`
---
## Validating configurations
`CAFFConfig.__post_init__` raises `ValueError` for invalid combinations.
Prefer `raise ValueError(...)` over `assert`, because `python -O` strips
asserts in production. Example:
```python
if self.rho >= self.d:
raise ValueError(f"rho ({self.rho}) must be < d ({self.d})")
```
---
## Reporting bugs
If you find a discrepancy between the paper and the code, add it to
`PAPER_DISCREPANCIES.md` rather than silently editing the code or the
paper. Include:
1. What the paper says
2. What the code produces
3. Which one is correct (with reasoning)
4. Recommended resolution
---
## Pull requests
1. Create a topic branch off `main`
2. Make focused commits with clear messages (one logical change per commit)
3. Run `pytest tests/` and the smoke training before pushing
4. Open a pull request describing the change and its motivation
5. Reference paper sections / equations when relevant
---
## Recent milestones
The following used to be open issues and have since been resolved:
- **Phase 2 - DC mining** (May 4, 2026): Implemented in `caff/miners.py` as
`DCMiner`, wired into `caff/trainer.py::__init__` and exercised by four
new unit tests in `tests/test_miners.py`. See PAPER_DISCREPANCIES.md
section 5 for the design notes.
- **Phase 3 - Real biomedical KG** (May 4, 2026): `scripts/build_kg.py`
now loads Orphanet TSV exports produced by `convert_orphanet_xml_to_tsv.py`,
and `merge_hpo_into_kg.py` adds HPO + OMIM annotations to yield a
38,456-node / 291,335-edge KG (called `merged_kg_v2.tsv`).
- **3-seed validation** (May 6, 2026): The full pipeline has been run with
seeds 42 / 1337 / 2024 on 20K QA records, producing
F1 = 0.522 +/- 0.001 on a held-out test set. See PAPER_DISCREPANCIES.md
section 10 and the README's Implementation Reality Check.
- **Phase 5 - GPU + BioLinkBERT** (May 13, 2026): Training migrated to a
single 8 GB consumer GPU and the encoder swapped to
`michiyasunaga/BioLinkBERT-large`. Same 20K / 3-seed protocol lifts
test F1 to 0.5315 +/- 0.0003 (+1.8% over CPU baseline). See
PAPER_DISCREPANCIES.md section 11 and the README's `GPU + BioLinkBERT-Large
upgrade (Phase 5)` subsection.
---
## Known gaps (good first issues)
These items would close the remaining gap between the as-shipped
F1 = 0.522 and the paper's headline F1 = 0.79:
- **DisGeNET integration**: add a gene-disease association layer to the
KG. The current public DisGeNET tier requires registration and a
manual license agreement; an issue is open to track API access.
- **Open Targets parquet pipeline**: Open Targets ships association data
in Parquet rather than TSV. A small adapter using `pyarrow` would let
us reuse the merge logic in `merge_hpo_into_kg.py`.
- **Per-relation thresholds**: `per_hop_threshold_sweep.py` tunes one
theta per hop; a per-relation variant would help when the KG contains
many high-volume relations of different signal strength (e.g. after
the MONDO experiment in PAPER_DISCREPANCIES.md section 9).
- **MONDO follow-up**: the experimental KG v3 built by
`merge_mondo_into_kg.py` improves MAP and NDCG but hurts F1 because
the model has not learned to suppress the new candidates. Longer
training or candidate filtering should recover the lost precision.
- **Paper-spec 30-epoch training on GPU**: Phase 5 used 10 epochs to
match the CPU baseline budget. The paper specifies 30 epochs with
patience 5. Running BioLinkBERT for 30 epochs might add another
+0.02 to +0.05 F1 and is the cheapest remaining gain to chase
(~3 hours total on an 8 GB GPU).
If you want to tackle any of these, please open an issue first so we can
coordinate.
---
## Contact
For questions about the algorithm or paper, contact the corresponding author:
- Marwan Dhifallah (marwan@mail.dlut.edu.cn)
- Yu Liu (supervisor) (yuliu@dlut.edu.cn)
For implementation-only questions, please open a GitHub issue.
|