JoyDaJun commited on
Commit
15f2326
·
verified ·
1 Parent(s): 586171f

Create README.md

Browse files
Files changed (1) hide show
  1. README.md +290 -0
README.md ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ ---
4
+ # MedRAGChecker Claim Extractor · LoRA Adapter
5
+
6
+ Biomedical claim-triple extractor fine-tuned from a medical LLM using GPT-4.1 teacher labels.
7
+ This adapter is part of the **MedRAGChecker** pipeline for claim-level verification in biomedical RAG.
8
+
9
+ > **Task:** given a medical question and its answer, extract factual triples of the form
10
+ > `[subject, relation, object]` as a pure JSON array.
11
+
12
+ ---
13
+
14
+ ## Model summary
15
+
16
+ - **Base model:** `<BASE_MODEL_ID>` (for example: `med42-llama3-8b`, `Meditron3-8B`, `PMC_LLaMA_13B`, or `qwen2-med-7b`)
17
+ - **Adapter type:** LoRA (rank = 16, alpha = 32, dropout = 0.0) via PEFT
18
+ - **Architecture:** same as base causal LM (LLaMA-style or Qwen-style)
19
+ - **Task:** biomedical claim triple extraction
20
+ - **Input:** question text + model answer (plain text)
21
+ - **Output:** JSON array of triples, e.g.
22
+
23
+ ```json
24
+ [
25
+ ["Psoriasis", "is", "chronic inflammatory skin disease"],
26
+ ["Psoriasis", "is associated with", "systemic comorbidities"]
27
+ ]
28
+ ```
29
+
30
+ You can either:
31
+ - keep one Hugging Face repo per adapter (recommended), or
32
+ - store several adapters in one repo and refer to specific subfolders.
33
+
34
+ Replace `<BASE_MODEL_ID>` and any placeholder names below with your actual base model and repo id (for example: `JoyDaJun/MedRAGChecker-Extractor-Meditron3-8B`).
35
+
36
+ ---
37
+
38
+ ## Intended use
39
+
40
+ - Post-hoc analysis of biomedical QA systems at *claim level*.
41
+ - Use inside a RAG or QA evaluation pipeline to:
42
+ - extract atomic factual statements from a generated answer;
43
+ - feed those triples to a checker model (e.g. MedRAGChecker NLI+KG).
44
+
45
+ This adapter is **not** a general-purpose chat model and **must not** be used as a standalone medical assistant.
46
+
47
+ ---
48
+
49
+ ## How to use
50
+
51
+ ### 1. LLaMA-style base models (Meditron, Med42, PMC-LLaMA, etc.)
52
+
53
+ ```python
54
+ from transformers import AutoTokenizer, AutoModelForCausalLM
55
+ from peft import PeftModel
56
+ import torch, json
57
+
58
+ base_model_id = "<BASE_MODEL_ID>" # e.g. "med42-llama3-8b"
59
+ adapter_id = "<ADAPTER_REPO_ID>" # e.g. "JoyDaJun/MedRAGChecker-Extractor-Med42-8B"
60
+
61
+ tokenizer = AutoTokenizer.from_pretrained(base_model_id)
62
+ model = AutoModelForCausalLM.from_pretrained(
63
+ base_model_id,
64
+ torch_dtype=torch.bfloat16,
65
+ device_map="auto",
66
+ )
67
+ model = PeftModel.from_pretrained(model, adapter_id)
68
+
69
+ def build_prompt(question: str, answer: str) -> str:
70
+ system_part = (
71
+ "You are an information extraction assistant. "
72
+ "Given a medical question and its answer, extract all factual triples "
73
+ "as [subject, relation, object]. "
74
+ "Return a pure JSON array of triples, with no explanations, no extra text, "
75
+ "no comments. If there are no clear factual triples, return an empty JSON array []."
76
+ )
77
+ qa_part = f"Question: {question}\nAnswer: {answer}"
78
+ return (
79
+ system_part
80
+ + "\n\n"
81
+ + qa_part
82
+ + '\n\nTriples (JSON only, e.g. [["subj", "rel", "obj"], ...]):\n'
83
+ )
84
+
85
+ question = "Does hypercholesterolemia increase leukotriene B4 in neutrophils?"
86
+ answer = "Hypercholesterolemia increases 5-LO activity in neutrophils..."
87
+
88
+ prompt = build_prompt(question, answer)
89
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
90
+
91
+ with torch.no_grad():
92
+ gen_ids = model.generate(
93
+ **inputs,
94
+ max_new_tokens=256,
95
+ do_sample=False,
96
+ )
97
+
98
+ text = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
99
+
100
+ # Optional: keep only the JSON array
101
+ start = text.find("[")
102
+ end = text.rfind("]") + 1
103
+ json_str = text[start:end] if start != -1 and end != -1 else "[]"
104
+ triples = json.loads(json_str)
105
+ print(triples)
106
+ ```
107
+
108
+ ### 2. Chat-style base models (Qwen2-med, etc.)
109
+
110
+ For chat-style models, wrap the same prompt inside the chat template.
111
+
112
+ ```python
113
+ from transformers import AutoTokenizer, AutoModelForCausalLM
114
+ from peft import PeftModel
115
+ import torch, json
116
+
117
+ base_model_id = "<QWEN_BASE_MODEL_ID>" # e.g. "qwen2-med-7b"
118
+ adapter_id = "<ADAPTER_REPO_ID_QWEN>" # e.g. "JoyDaJun/MedRAGChecker-Extractor-Qwen2-med-7B"
119
+
120
+ tokenizer = AutoTokenizer.from_pretrained(base_model_id)
121
+ model = AutoModelForCausalLM.from_pretrained(
122
+ base_model_id,
123
+ torch_dtype=torch.bfloat16,
124
+ device_map="auto",
125
+ )
126
+ model = PeftModel.from_pretrained(model, adapter_id)
127
+
128
+ def build_prompt(question: str, answer: str) -> str:
129
+ system_part = (
130
+ "Given a medical question and its answer, extract all factual triples "
131
+ "as [subject, relation, object]. "
132
+ "Return only a JSON array of triples."
133
+ )
134
+ qa_part = f"Question: {question}\nAnswer: {answer}"
135
+ return system_part + "\n\n" + qa_part + '\n\nTriples (JSON only, e.g. [["subj", "rel", "obj"], ...]):\n'
136
+
137
+ question = "Does hypercholesterolemia increase leukotriene B4 in neutrophils?"
138
+ answer = "Hypercholesterolemia increases 5-LO activity in neutrophils..."
139
+
140
+ messages = [
141
+ {"role": "system", "content": "You are an information extraction assistant."},
142
+ {"role": "user", "content": build_prompt(question, answer)},
143
+ ]
144
+ prompt = tokenizer.apply_chat_template(
145
+ messages,
146
+ tokenize=False,
147
+ add_generation_prompt=True,
148
+ )
149
+
150
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
151
+
152
+ with torch.no_grad():
153
+ gen_ids = model.generate(
154
+ **inputs,
155
+ max_new_tokens=256,
156
+ do_sample=False,
157
+ )
158
+
159
+ text = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
160
+ start = text.find("[")
161
+ end = text.rfind("]") + 1
162
+ json_str = text[start:end] if start != -1 and end != -1 else "[]"
163
+ triples = json.loads(json_str)
164
+ print(triples)
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Training details
170
+
171
+ This adapter was trained with the `DistillExtractor/train_extractor_sft.py` script in the MedRAGChecker codebase.
172
+
173
+ - **Teacher model:** GPT-4.1 as claim-triple annotator.
174
+ - **Training data:**
175
+ - JSONL file `extractor_sft.jsonl` with fields:
176
+ - `instruction`: system prompt + `Question:` + `Answer:` (from biomedical QA datasets and RAG outputs).
177
+ - `output`: pure JSON array of `[subject, relation, object]` triples labeled by GPT-4.1.
178
+ - Sources include consumer and research-style biomedical QA (e.g., MedQuAD, PubMedQA, LiveQA Medical, CSIRO MedRedQA, and AskDocs-style Reddit threads).
179
+ - **Preprocessing:**
180
+ - Parse `Question:` and `Answer:` from the `instruction` field using regex.
181
+ - Rebuild a canonical prompt with an explicit
182
+ `Triples (JSON only, e.g. [["subj", "rel", "obj"], ...]):`
183
+ header.
184
+ - **Fine-tuning setup (example):**
185
+ - Epochs: `10`
186
+ - Batch size: `1` with gradient accumulation `32` (effective batch size 32).
187
+ - Max input length: `2048`.
188
+ - Optimizer: AdamW, learning rate `1e-4`.
189
+ - LoRA config: `r = 16`, `alpha = 32`, `dropout = 0.0`.
190
+ - Precision: `bfloat16` on GPUs with `device_map="auto"`.
191
+
192
+ Example training command:
193
+
194
+ ```bash
195
+ export WANDB_PROJECT=MedRAGChecker
196
+ export WANDB_NAME=extractor_<BASE_NAME>
197
+
198
+ BASE=/path/to/<BASE_MODEL_ID>
199
+ CUDA_VISIBLE_DEVICES=0,1,2,3 \
200
+ python DistillExtractor/train_extractor_sft.py \
201
+ --model_name "$BASE" \
202
+ --train_path ./data/extractor_sft.jsonl \
203
+ --output_dir ./runs/extractor_sft_<BASE_NAME> \
204
+ --epochs 10 \
205
+ --batch_size 1 \
206
+ --grad_accum 32 \
207
+ --lr 1e-4 \
208
+ --bf16
209
+ ```
210
+
211
+ Replace `<BASE_MODEL_ID>` and `<BASE_NAME>` with your actual base model.
212
+
213
+ ---
214
+
215
+ ## Evaluation
216
+
217
+ We evaluate on a held-out split of the same GPT-4.1-annotated dataset using two families of metrics:
218
+
219
+ 1. **Strict triple match**
220
+
221
+ - Normalize to lowercase and strip whitespace.
222
+ - Treat each triple as a set element `(subject, relation, object)`.
223
+ - Compute precision/recall/F1 on exact triple matches.
224
+ - Also report exact match rate (all triples in an example match exactly).
225
+
226
+ 2. **Soft triple match**
227
+
228
+ - Tokenize subject, relation, and object.
229
+ - Compute token-level F1 for each field between predicted and gold triples.
230
+ - Aggregate into a per-triple similarity score.
231
+ - Run greedy matching between predicted and gold triples by similarity.
232
+ - Compute soft precision/recall/F1 from matched pairs.
233
+
234
+ Example metrics on a random subsample of `N = 200` examples for a Meditron3-8B-based extractor:
235
+
236
+ | Metric | Value |
237
+ |------------------|--------|
238
+ | strict_precision | 0.0890 |
239
+ | strict_recall | 0.0930 |
240
+ | strict_f1 | 0.0900 |
241
+ | exact_match | 0.0500 |
242
+ | soft_precision | 0.2052 |
243
+ | soft_recall | 0.2598 |
244
+ | soft_f1 | 0.2148 |
245
+
246
+ These numbers illustrate that:
247
+ - the model is far from perfect at exact triple reconstruction;
248
+ - soft matching shows it still captures many approximate facts, which is often sufficient for downstream diagnostics in MedRAGChecker.
249
+
250
+ You can reproduce these metrics (and compute new ones for other checkpoints) with the evaluation script:
251
+
252
+ ```bash
253
+ python DistillExtractor/run_extractor_eval_soft.py \
254
+ --base_model <BASE_MODEL_ID> \
255
+ --adapter_path <ADAPTER_REPO_OR_LOCAL_PATH> \
256
+ --data_path ./data/extractor_sft.jsonl \
257
+ --output_path ./results/extractor_soft_<BASE_NAME>.json \
258
+ --num_examples 200
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Limitations and risks
264
+
265
+ - The adapter inherits all limitations and biases of the base model and GPT-4.1 teacher.
266
+ - Extracted triples may still be incomplete, redundant, or slightly rephrased.
267
+ - The model is optimized for **English biomedical text**; performance on other domains or languages is likely poor.
268
+ - Do **not** use this model (or its extracted triples) directly for patient-facing decisions or clinical care without expert validation.
269
+
270
+ ---
271
+
272
+ ## Citation
273
+
274
+ If you use this adapter or MedRAGChecker in your work, please consider citing our paper (details to be updated):
275
+
276
+ ```bibtex
277
+ @inproceedings{ji2025medragchecker,
278
+ title = {MedRAGChecker: Claim-level Verification for Biomedical Retrieval-Augmented Generation},
279
+ author = {Ji, Yuelyu and collaborators},
280
+ booktitle = {Proceedings of a future venue},
281
+ year = {2025}
282
+ }
283
+ ```
284
+
285
+ ---
286
+
287
+ ## License
288
+
289
+ - This adapter is released under the same license terms as the corresponding base model `<BASE_MODEL_ID>`.
290
+ - You must accept and comply with the license of the base model before using this LoRA.