cxrfescore 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ # Byte-compiled / cache
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ .eggs/
7
+ dist/
8
+ build/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .venv/
13
+ venv/
14
+ *.egg
15
+
16
+ # Editor / OS
17
+ .DS_Store
18
+ .idea/
19
+ .vscode/
20
+
21
+ # Local caches / notebooks checkpoints
22
+ .ipynb_checkpoints/
23
+ *.pkl
@@ -0,0 +1,42 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use CXRFEScore, please cite the following paper."
3
+ title: "CXRFEScore"
4
+ type: software
5
+ authors:
6
+ - family-names: Messina
7
+ given-names: Pablo
8
+ - family-names: Vidal
9
+ given-names: Rene
10
+ - family-names: Parra
11
+ given-names: Denis
12
+ - family-names: Soto
13
+ given-names: Alvaro
14
+ - family-names: Araujo
15
+ given-names: Vladimir
16
+ preferred-citation:
17
+ type: conference-paper
18
+ title: "Extracting and Encoding: Leveraging Large Language Models and Medical Knowledge to Enhance Radiological Text Representation"
19
+ authors:
20
+ - family-names: Messina
21
+ given-names: Pablo
22
+ - family-names: Vidal
23
+ given-names: Rene
24
+ - family-names: Parra
25
+ given-names: Denis
26
+ - family-names: Soto
27
+ given-names: Alvaro
28
+ - family-names: Araujo
29
+ given-names: Vladimir
30
+ collection-title: "Findings of the Association for Computational Linguistics: ACL 2024"
31
+ month: 8
32
+ year: 2024
33
+ publisher: "Association for Computational Linguistics"
34
+ address: "Bangkok, Thailand"
35
+ url: "https://aclanthology.org/2024.findings-acl.236/"
36
+ doi: "10.18653/v1/2024.findings-acl.236"
37
+ pages: "3955-3986"
38
+ identifiers:
39
+ - type: doi
40
+ value: "10.18653/v1/2024.findings-acl.236"
41
+ repository-code: "https://github.com/PabloMessina/CXRFEScore"
42
+ license: MIT
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Pablo Messina
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.4
2
+ Name: cxrfescore
3
+ Version: 0.2.2
4
+ Summary: Fact-level embedding metric for evaluating chest X-ray radiology report generation.
5
+ Project-URL: Homepage, https://github.com/PabloMessina/CXRFEScore
6
+ Project-URL: Bug Tracker, https://github.com/PabloMessina/CXRFEScore/issues
7
+ Project-URL: Paper, https://aclanthology.org/2024.findings-acl.236/
8
+ Author-email: Pablo Messina <pablo.messina.g@gmail.com>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2024 Pablo Messina
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Classifier: Intended Audience :: Science/Research
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Classifier: Programming Language :: Python :: 3
35
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
36
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
37
+ Requires-Python: >=3.10
38
+ Requires-Dist: nltk>=3.8.0
39
+ Requires-Dist: numpy>=1.24.0
40
+ Requires-Dist: platformdirs>=4.0.0
41
+ Requires-Dist: scikit-learn>=1.3.0
42
+ Requires-Dist: torch>=2.0.0
43
+ Requires-Dist: tqdm>=4.65.0
44
+ Requires-Dist: transformers>=4.40.0
45
+ Provides-Extra: dev
46
+ Requires-Dist: build>=1.0.0; extra == 'dev'
47
+ Requires-Dist: matplotlib>=3.7.0; extra == 'dev'
48
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
49
+ Requires-Dist: twine>=5.0.0; extra == 'dev'
50
+ Provides-Extra: viz
51
+ Requires-Dist: matplotlib>=3.7.0; extra == 'viz'
52
+ Description-Content-Type: text/markdown
53
+
54
+ # CXRFEScore
55
+
56
+ Fact-level embedding metric for evaluating chest X-ray radiology report generation.
57
+
58
+ CXRFEScore extracts factual statements from reports with a T5 fact extractor, embeds them with CXRFE (or compatible CXR-BERT models), and scores hypothesis/reference pairs via soft bipartite matching of fact embeddings.
59
+
60
+ Based on:
61
+
62
+ > Messina et al., *Extracting and Encoding: Leveraging Large Language Models and Medical Knowledge to Enhance Radiological Text Representation*, Findings of ACL 2024.
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ pip install cxrfescore
68
+ ```
69
+
70
+ Optional visualization support:
71
+
72
+ ```bash
73
+ pip install cxrfescore[viz]
74
+ ```
75
+
76
+ **Python:** 3.10+
77
+
78
+ ### Runtime dependencies (installed automatically)
79
+
80
+ | Package | Role |
81
+ |---|---|
82
+ | `torch` | Model inference |
83
+ | `transformers` | Load Hugging Face models |
84
+ | `numpy` | Arrays / aggregation |
85
+ | `scikit-learn` | Cosine similarity |
86
+ | `nltk` | Sentence splitting |
87
+ | `tqdm` | Progress bars |
88
+ | `platformdirs` | Default cache directory |
89
+
90
+ Optional: `matplotlib` via `cxrfescore[viz]` for `visualize_fact_similarity`.
91
+
92
+ ### Additional requirements (not pip packages)
93
+
94
+ On first use the metric downloads:
95
+
96
+ - **Encoder (default):** [`pamessina/CXRFE`](https://huggingface.co/pamessina/CXRFE)
97
+ - **Fact extractor:** [`pamessina/T5FactExtractor`](https://huggingface.co/pamessina/T5FactExtractor)
98
+ - **NLTK** `punkt` / `punkt_tab` (auto-downloaded)
99
+
100
+ Supported alternate encoders:
101
+
102
+ - `microsoft/BiomedVLP-CXR-BERT-specialized`
103
+ - `microsoft/BiomedVLP-BioViL-T`
104
+ - `StanfordAIMI/SRR-BERT-Leaves` (CLS embeddings only; label head unused)
105
+
106
+ A GPU is recommended but not required. CPU works and is slower.
107
+
108
+ Known-good stack from development: PyTorch 2.x + `transformers` 4.x / 5.x.
109
+
110
+ ## Quick start
111
+
112
+ ```python
113
+ from cxrfescore import CXRFEScore
114
+
115
+ metric = CXRFEScore(device="cuda") # or "cpu"
116
+
117
+ hyps = [
118
+ "There is a small right pleural effusion. The heart size is normal.",
119
+ ]
120
+ refs = [
121
+ "Small right pleural effusion. Normal heart size.",
122
+ ]
123
+
124
+ result = metric(hyps, refs)
125
+ print(result["mean_similarity"])
126
+ print(result["per_pair_similarity"])
127
+ ```
128
+
129
+ ### Extract facts or embeddings only
130
+
131
+ `extract_facts` treats each input string as a **full report**: sentence-split → T5 fact extraction → unique-fact aggregation (same pipeline as scoring).
132
+
133
+ ```python
134
+ facts_per_report = metric.extract_facts(hyps)
135
+ embeddings = metric.embed_facts(facts_per_report[0]) # shape (num_facts, dim)
136
+ ```
137
+
138
+ ### Caching
139
+
140
+ With `use_cache=True` (default), results are kept **in memory** during the session:
141
+
142
+ - Sentence→facts: `{cache_dir}/sent_to_facts.pkl` (shared across encoders)
143
+ - Fact→embedding: `{cache_dir}/embeddings/<encoder_name>/fact_to_embedding.pkl` (per encoder)
144
+
145
+ Disk writes are **not** automatic. Call `save_cache()` explicitly after a large batch or at the end of a job:
146
+
147
+ ```python
148
+ metric = CXRFEScore(use_cache=True, cache_dir="/path/to/cache")
149
+ # ... score / extract / embed ...
150
+ metric.save_cache()
151
+ ```
152
+
153
+ Disable with `use_cache=False`.
154
+
155
+ Inspect what is already on disk:
156
+
157
+ ```python
158
+ from cxrfescore import inspect_cache
159
+
160
+ inspect_cache() # default cache location
161
+ # or: metric.inspect_cache() # uses this instance's cache_dir
162
+ ```
163
+
164
+ ### Visualization
165
+
166
+ ```python
167
+ # requires: pip install cxrfescore[viz]
168
+ metric.visualize_fact_similarity(ref_report=refs[0], cand_report=hyps[0])
169
+ ```
170
+
171
+ ## Examples
172
+
173
+ | File | Purpose |
174
+ |---|---|
175
+ | [`examples/colab_smoke.md`](examples/colab_smoke.md) | Full Colab smoke + adversarial pairs |
176
+ | [`examples/minimal_cxrfe.md`](examples/minimal_cxrfe.md) | Default CXRFE encoder |
177
+ | [`examples/minimal_cxr_bert_specialized.md`](examples/minimal_cxr_bert_specialized.md) | CXR-BERT specialized |
178
+ | [`examples/minimal_biovil_t.md`](examples/minimal_biovil_t.md) | BioViL-T |
179
+ | [`examples/minimal_srr_bert_leaves.md`](examples/minimal_srr_bert_leaves.md) | SRR-BERT-Leaves CLS |
180
+ | [`examples/minimal_extract_embed.md`](examples/minimal_extract_embed.md) | `extract_facts` / `embed_facts` |
181
+
182
+ ## Try it on Google Colab
183
+
184
+ After publishing, install in Colab (GPU runtime recommended):
185
+
186
+ ```python
187
+ !pip install cxrfescore
188
+ ```
189
+
190
+ For a pre-release check against TestPyPI:
191
+
192
+ ```python
193
+ !pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "cxrfescore==0.2.2"
194
+ ```
195
+
196
+ ## Citation
197
+
198
+ If you use CXRFEScore, please cite:
199
+
200
+ > Pablo Messina, Rene Vidal, Denis Parra, Alvaro Soto, and Vladimir Araujo. 2024. [Extracting and Encoding: Leveraging Large Language Models and Medical Knowledge to Enhance Radiological Text Representation](https://aclanthology.org/2024.findings-acl.236/). In *Findings of the Association for Computational Linguistics: ACL 2024*, pages 3955–3986, Bangkok, Thailand. Association for Computational Linguistics.
201
+
202
+ ```bibtex
203
+ @inproceedings{messina-etal-2024-extracting,
204
+ title = "Extracting and Encoding: Leveraging Large Language Models and Medical Knowledge to Enhance Radiological Text Representation",
205
+ author = "Messina, Pablo and
206
+ Vidal, Rene and
207
+ Parra, Denis and
208
+ Soto, Alvaro and
209
+ Araujo, Vladimir",
210
+ editor = "Ku, Lun-Wei and
211
+ Martins, Andre and
212
+ Srikumar, Vivek",
213
+ booktitle = "Findings of the Association for Computational Linguistics: ACL 2024",
214
+ month = aug,
215
+ year = "2024",
216
+ address = "Bangkok, Thailand",
217
+ publisher = "Association for Computational Linguistics",
218
+ url = "https://aclanthology.org/2024.findings-acl.236/",
219
+ doi = "10.18653/v1/2024.findings-acl.236",
220
+ pages = "3955--3986"
221
+ }
222
+ ```
223
+
224
+ Paper: [ACL Anthology](https://aclanthology.org/2024.findings-acl.236/) · [PDF](https://aclanthology.org/2024.findings-acl.236.pdf) · [arXiv](https://arxiv.org/abs/2407.01948)
225
+
226
+ ## Development
227
+
228
+ ```bash
229
+ pip install -e ".[dev]"
230
+ pytest # unit + mocked tests (default)
231
+ pytest -m integration # downloads HF models; needs network / GPU optional
232
+ python -m build
233
+ ```
234
+
235
+ ## License
236
+
237
+ MIT
@@ -0,0 +1,184 @@
1
+ # CXRFEScore
2
+
3
+ Fact-level embedding metric for evaluating chest X-ray radiology report generation.
4
+
5
+ CXRFEScore extracts factual statements from reports with a T5 fact extractor, embeds them with CXRFE (or compatible CXR-BERT models), and scores hypothesis/reference pairs via soft bipartite matching of fact embeddings.
6
+
7
+ Based on:
8
+
9
+ > Messina et al., *Extracting and Encoding: Leveraging Large Language Models and Medical Knowledge to Enhance Radiological Text Representation*, Findings of ACL 2024.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install cxrfescore
15
+ ```
16
+
17
+ Optional visualization support:
18
+
19
+ ```bash
20
+ pip install cxrfescore[viz]
21
+ ```
22
+
23
+ **Python:** 3.10+
24
+
25
+ ### Runtime dependencies (installed automatically)
26
+
27
+ | Package | Role |
28
+ |---|---|
29
+ | `torch` | Model inference |
30
+ | `transformers` | Load Hugging Face models |
31
+ | `numpy` | Arrays / aggregation |
32
+ | `scikit-learn` | Cosine similarity |
33
+ | `nltk` | Sentence splitting |
34
+ | `tqdm` | Progress bars |
35
+ | `platformdirs` | Default cache directory |
36
+
37
+ Optional: `matplotlib` via `cxrfescore[viz]` for `visualize_fact_similarity`.
38
+
39
+ ### Additional requirements (not pip packages)
40
+
41
+ On first use the metric downloads:
42
+
43
+ - **Encoder (default):** [`pamessina/CXRFE`](https://huggingface.co/pamessina/CXRFE)
44
+ - **Fact extractor:** [`pamessina/T5FactExtractor`](https://huggingface.co/pamessina/T5FactExtractor)
45
+ - **NLTK** `punkt` / `punkt_tab` (auto-downloaded)
46
+
47
+ Supported alternate encoders:
48
+
49
+ - `microsoft/BiomedVLP-CXR-BERT-specialized`
50
+ - `microsoft/BiomedVLP-BioViL-T`
51
+ - `StanfordAIMI/SRR-BERT-Leaves` (CLS embeddings only; label head unused)
52
+
53
+ A GPU is recommended but not required. CPU works and is slower.
54
+
55
+ Known-good stack from development: PyTorch 2.x + `transformers` 4.x / 5.x.
56
+
57
+ ## Quick start
58
+
59
+ ```python
60
+ from cxrfescore import CXRFEScore
61
+
62
+ metric = CXRFEScore(device="cuda") # or "cpu"
63
+
64
+ hyps = [
65
+ "There is a small right pleural effusion. The heart size is normal.",
66
+ ]
67
+ refs = [
68
+ "Small right pleural effusion. Normal heart size.",
69
+ ]
70
+
71
+ result = metric(hyps, refs)
72
+ print(result["mean_similarity"])
73
+ print(result["per_pair_similarity"])
74
+ ```
75
+
76
+ ### Extract facts or embeddings only
77
+
78
+ `extract_facts` treats each input string as a **full report**: sentence-split → T5 fact extraction → unique-fact aggregation (same pipeline as scoring).
79
+
80
+ ```python
81
+ facts_per_report = metric.extract_facts(hyps)
82
+ embeddings = metric.embed_facts(facts_per_report[0]) # shape (num_facts, dim)
83
+ ```
84
+
85
+ ### Caching
86
+
87
+ With `use_cache=True` (default), results are kept **in memory** during the session:
88
+
89
+ - Sentence→facts: `{cache_dir}/sent_to_facts.pkl` (shared across encoders)
90
+ - Fact→embedding: `{cache_dir}/embeddings/<encoder_name>/fact_to_embedding.pkl` (per encoder)
91
+
92
+ Disk writes are **not** automatic. Call `save_cache()` explicitly after a large batch or at the end of a job:
93
+
94
+ ```python
95
+ metric = CXRFEScore(use_cache=True, cache_dir="/path/to/cache")
96
+ # ... score / extract / embed ...
97
+ metric.save_cache()
98
+ ```
99
+
100
+ Disable with `use_cache=False`.
101
+
102
+ Inspect what is already on disk:
103
+
104
+ ```python
105
+ from cxrfescore import inspect_cache
106
+
107
+ inspect_cache() # default cache location
108
+ # or: metric.inspect_cache() # uses this instance's cache_dir
109
+ ```
110
+
111
+ ### Visualization
112
+
113
+ ```python
114
+ # requires: pip install cxrfescore[viz]
115
+ metric.visualize_fact_similarity(ref_report=refs[0], cand_report=hyps[0])
116
+ ```
117
+
118
+ ## Examples
119
+
120
+ | File | Purpose |
121
+ |---|---|
122
+ | [`examples/colab_smoke.md`](examples/colab_smoke.md) | Full Colab smoke + adversarial pairs |
123
+ | [`examples/minimal_cxrfe.md`](examples/minimal_cxrfe.md) | Default CXRFE encoder |
124
+ | [`examples/minimal_cxr_bert_specialized.md`](examples/minimal_cxr_bert_specialized.md) | CXR-BERT specialized |
125
+ | [`examples/minimal_biovil_t.md`](examples/minimal_biovil_t.md) | BioViL-T |
126
+ | [`examples/minimal_srr_bert_leaves.md`](examples/minimal_srr_bert_leaves.md) | SRR-BERT-Leaves CLS |
127
+ | [`examples/minimal_extract_embed.md`](examples/minimal_extract_embed.md) | `extract_facts` / `embed_facts` |
128
+
129
+ ## Try it on Google Colab
130
+
131
+ After publishing, install in Colab (GPU runtime recommended):
132
+
133
+ ```python
134
+ !pip install cxrfescore
135
+ ```
136
+
137
+ For a pre-release check against TestPyPI:
138
+
139
+ ```python
140
+ !pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "cxrfescore==0.2.2"
141
+ ```
142
+
143
+ ## Citation
144
+
145
+ If you use CXRFEScore, please cite:
146
+
147
+ > Pablo Messina, Rene Vidal, Denis Parra, Alvaro Soto, and Vladimir Araujo. 2024. [Extracting and Encoding: Leveraging Large Language Models and Medical Knowledge to Enhance Radiological Text Representation](https://aclanthology.org/2024.findings-acl.236/). In *Findings of the Association for Computational Linguistics: ACL 2024*, pages 3955–3986, Bangkok, Thailand. Association for Computational Linguistics.
148
+
149
+ ```bibtex
150
+ @inproceedings{messina-etal-2024-extracting,
151
+ title = "Extracting and Encoding: Leveraging Large Language Models and Medical Knowledge to Enhance Radiological Text Representation",
152
+ author = "Messina, Pablo and
153
+ Vidal, Rene and
154
+ Parra, Denis and
155
+ Soto, Alvaro and
156
+ Araujo, Vladimir",
157
+ editor = "Ku, Lun-Wei and
158
+ Martins, Andre and
159
+ Srikumar, Vivek",
160
+ booktitle = "Findings of the Association for Computational Linguistics: ACL 2024",
161
+ month = aug,
162
+ year = "2024",
163
+ address = "Bangkok, Thailand",
164
+ publisher = "Association for Computational Linguistics",
165
+ url = "https://aclanthology.org/2024.findings-acl.236/",
166
+ doi = "10.18653/v1/2024.findings-acl.236",
167
+ pages = "3955--3986"
168
+ }
169
+ ```
170
+
171
+ Paper: [ACL Anthology](https://aclanthology.org/2024.findings-acl.236/) · [PDF](https://aclanthology.org/2024.findings-acl.236.pdf) · [arXiv](https://arxiv.org/abs/2407.01948)
172
+
173
+ ## Development
174
+
175
+ ```bash
176
+ pip install -e ".[dev]"
177
+ pytest # unit + mocked tests (default)
178
+ pytest -m integration # downloads HF models; needs network / GPU optional
179
+ python -m build
180
+ ```
181
+
182
+ ## License
183
+
184
+ MIT
@@ -0,0 +1,169 @@
1
+ # Colab smoke test (human)
2
+
3
+ Run this **after** `cxrfescore` is uploaded to TestPyPI and **before** production PyPI.
4
+
5
+ ## Setup
6
+
7
+ 1. Open [Google Colab](https://colab.research.google.com/).
8
+ 2. Runtime → Change runtime type → **GPU** (T4 is enough).
9
+ 3. Run the cells below in order.
10
+
11
+ ## Install from TestPyPI
12
+
13
+ ```python
14
+ !pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "cxrfescore==0.2.2"
15
+ ```
16
+
17
+ After the production release, use:
18
+
19
+ ```python
20
+ !pip install cxrfescore
21
+ ```
22
+
23
+ ## Smoke test
24
+
25
+ ```python
26
+ import torch
27
+ from cxrfescore import CXRFEScore
28
+
29
+ print("torch:", torch.__version__)
30
+ print("cuda available:", torch.cuda.is_available())
31
+
32
+ metric = CXRFEScore(
33
+ device="cuda" if torch.cuda.is_available() else "cpu",
34
+ batch_size=8,
35
+ num_workers=0,
36
+ verbose=True,
37
+ )
38
+
39
+ hyps = [
40
+ "There is a small right pleural effusion. The heart size is normal.",
41
+ "Large left pneumothorax with mediastinal shift.",
42
+ ]
43
+ refs = [
44
+ "Small right pleural effusion. Normal heart size.",
45
+ "No acute cardiopulmonary process. Lungs are clear.",
46
+ ]
47
+
48
+ result = metric(hyps, refs)
49
+ print("mean_similarity:", result["mean_similarity"])
50
+ print("per_pair_similarity:", result["per_pair_similarity"])
51
+
52
+ # Sanity checks
53
+ assert result["per_pair_similarity"].shape == (2,)
54
+ assert result["per_pair_similarity"][0] > result["per_pair_similarity"][1]
55
+ print("Colab smoke test PASSED")
56
+ ```
57
+
58
+ ## Optional visualization (easy pair)
59
+
60
+ ```python
61
+ !pip install "cxrfescore[viz]"
62
+ metric.visualize_fact_similarity(ref_report=refs[0], cand_report=hyps[0])
63
+ ```
64
+
65
+ ## Challenging / adversarial pairs
66
+
67
+ These cases are designed to stress clinical nuances that embedding metrics often mishandle
68
+ (or handle well). For each pair, inspect the printed score **and** the fact-similarity heatmap.
69
+ A good metric should score clinically wrong pairs lower than near-paraphrases.
70
+
71
+ ```python
72
+ adversarial_cases = [
73
+ {
74
+ "name": "Negation flip (should score LOW)",
75
+ "note": "Nearly identical wording, opposite clinical meaning.",
76
+ "ref": "There is no evidence of pneumothorax.",
77
+ "hyp": "There is evidence of pneumothorax.",
78
+ },
79
+ {
80
+ "name": "Laterality swap (should score LOW)",
81
+ "note": "Same finding, wrong side — clinically important error.",
82
+ "ref": "Moderate right pleural effusion.",
83
+ "hyp": "Moderate left pleural effusion.",
84
+ },
85
+ {
86
+ "name": "Severity / size swap (should score MID-LOW)",
87
+ "note": "Correct finding/side, wrong magnitude.",
88
+ "ref": "Small right pleural effusion.",
89
+ "hyp": "Large right pleural effusion.",
90
+ },
91
+ {
92
+ "name": "Synonym paraphrase (should score HIGH)",
93
+ "note": "Different wording, same meaning — robustness check.",
94
+ "ref": "The heart is enlarged. There is pulmonary edema.",
95
+ "hyp": "Cardiomegaly is present. Findings consistent with pulmonary oedema.",
96
+ },
97
+ {
98
+ "name": "Presence vs absence of shared anatomy (should score LOW)",
99
+ "note": "Shares anatomy tokens but asserts opposite finding.",
100
+ "ref": "The lungs are clear without focal consolidation.",
101
+ "hyp": "There is focal consolidation in the lungs.",
102
+ },
103
+ {
104
+ "name": "Device / support device mismatch (should score LOW)",
105
+ "note": "Support-device facts often confuse overlap-based metrics.",
106
+ "ref": "Endotracheal tube tip is appropriately positioned above the carina.",
107
+ "hyp": "Nasogastric tube tip is appropriately positioned in the stomach.",
108
+ },
109
+ ]
110
+ ```
111
+
112
+ ```python
113
+ # Score all adversarial pairs in one batch
114
+ adv_hyps = [c["hyp"] for c in adversarial_cases]
115
+ adv_refs = [c["ref"] for c in adversarial_cases]
116
+ adv_result = metric(adv_hyps, adv_refs)
117
+
118
+ print("=== Adversarial pair scores ===")
119
+ for case, score in zip(adversarial_cases, adv_result["per_pair_similarity"]):
120
+ print(f"\n[{case['name']}] score={float(score):.4f}")
121
+ print(f" note: {case['note']}")
122
+ print(f" REF: {case['ref']}")
123
+ print(f" HYP: {case['hyp']}")
124
+ ```
125
+
126
+ ```python
127
+ # Visualize each challenging pair (one heatmap per case)
128
+ for case in adversarial_cases:
129
+ print("\n" + "=" * 72)
130
+ print(case["name"])
131
+ print(case["note"])
132
+ print("=" * 72)
133
+ metric.visualize_fact_similarity(
134
+ ref_report=case["ref"],
135
+ cand_report=case["hyp"],
136
+ )
137
+ ```
138
+
139
+ ```python
140
+ # Quick relative sanity check (not a hard unit test — inspect failures by eye)
141
+ scores = {c["name"]: float(s) for c, s in zip(adversarial_cases, adv_result["per_pair_similarity"])}
142
+ synonym = scores["Synonym paraphrase (should score HIGH)"]
143
+ negation = scores["Negation flip (should score LOW)"]
144
+ laterality = scores["Laterality swap (should score LOW)"]
145
+
146
+ print("synonym:", synonym)
147
+ print("negation:", negation)
148
+ print("laterality:", laterality)
149
+
150
+ if synonym <= negation:
151
+ print("WARNING: synonym paraphrase did not outscore negation flip — inspect heatmaps.")
152
+ if synonym <= laterality:
153
+ print("WARNING: synonym paraphrase did not outscore laterality swap — inspect heatmaps.")
154
+ if synonym > negation and synonym > laterality:
155
+ print("Relative ordering looks reasonable (synonym > negation/laterality).")
156
+ ```
157
+
158
+ ## Checklist
159
+
160
+ - [ ] Install from TestPyPI succeeds
161
+ - [ ] Models download from Hugging Face
162
+ - [ ] CUDA used when GPU runtime is selected
163
+ - [ ] Similar pair scores higher than unrelated pair
164
+ - [ ] Adversarial pairs inspected (negation, laterality, severity, synonyms, devices)
165
+ - [ ] No import / runtime errors
166
+
167
+ ## Citation
168
+
169
+ If you use CXRFEScore, please cite [Messina et al., Findings of ACL 2024](https://aclanthology.org/2024.findings-acl.236/). Full BibTeX is in the [README](../README.md#citation).
@@ -0,0 +1,18 @@
1
+ # Minimal example — BioViL-T
2
+
3
+ ```python
4
+ !pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "cxrfescore==0.2.2"
5
+
6
+ from cxrfescore import CXRFEScore
7
+
8
+ metric = CXRFEScore(
9
+ encoder_model_name="microsoft/BiomedVLP-BioViL-T",
10
+ device="cuda",
11
+ )
12
+ result = metric(
13
+ ["There is a small right pleural effusion."],
14
+ ["Small right pleural effusion."],
15
+ )
16
+ print(result["mean_similarity"])
17
+ metric.save_cache()
18
+ ```