drkareemkamal commited on
Commit
dadec67
·
verified ·
1 Parent(s): 95c2ef3

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +471 -36
README.md CHANGED
@@ -1,60 +1,495 @@
1
  ---
 
 
2
  base_model: unsloth/Qwen2.5-7B-Instruct-bnb-4bit
3
- library_name: transformers
4
- model_name: PathQwen2.5-instruct
5
  tags:
6
- - generated_from_trainer
7
- - unsloth
8
- - sft
9
- - trl
10
- licence: license
11
- license: mit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  ---
13
 
14
- # Model Card for PathQwen2.5-instruct
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- This model is a fine-tuned version of [unsloth/Qwen2.5-7B-Instruct-bnb-4bit](https://huggingface.co/unsloth/Qwen2.5-7B-Instruct-bnb-4bit).
17
- It has been trained using [TRL](https://github.com/huggingface/trl).
18
 
19
- ## Quick start
 
 
20
 
21
  ```python
22
- from transformers import pipeline
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- question = "If you had a time machine, but could only go to the past or the future once and never return, which would you choose and why?"
25
- generator = pipeline("text-generation", model="drkareemkamal/PathQwen2.5-instruct", device="cuda")
26
- output = generator([{"role": "user", "content": question}], max_new_tokens=128, return_full_text=False)[0]
27
- print(output["generated_text"])
28
  ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- ## Training procedure
 
 
 
31
 
32
- [<img src="https://raw.githubusercontent.com/wandb/assets/main/wandb-github-badge-28.svg" alt="Visualize in Weights & Biases" width="150" height="24"/>](https://wandb.ai/dr-kareem-kamal/pathology-multitask/runs/oqq7ef79)
 
 
33
 
 
 
 
34
 
35
- This model was trained with SFT.
 
 
 
 
 
 
 
 
 
 
36
 
37
- ### Framework versions
38
 
39
- - TRL: 0.24.0
40
- - Transformers: 4.57.6
41
- - Pytorch: 2.6.0+cu126
42
- - Datasets: 4.3.0
43
- - Tokenizers: 0.22.2
44
 
45
- ## Citations
 
 
 
 
 
 
 
 
 
 
 
 
46
 
 
47
 
 
48
 
49
- Cite TRL as:
50
-
51
  ```bibtex
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  @misc{vonwerra2022trl,
53
- title = {{TRL: Transformer Reinforcement Learning}},
54
- author = {Leandro von Werra and Younes Belkada and Lewis Tunstall and Edward Beeching and Tristan Thrush and Nathan Lambert and Shengyi Huang and Kashif Rasul and Quentin Gallou{\'e}dec},
55
- year = 2020,
56
- journal = {GitHub repository},
57
- publisher = {GitHub},
58
- howpublished = {\url{https://github.com/huggingface/trl}}
 
 
59
  }
60
- ```
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ license: mit
3
+ library_name: peft
4
  base_model: unsloth/Qwen2.5-7B-Instruct-bnb-4bit
5
+ pipeline_tag: text-generation
 
6
  tags:
7
+ - medical
8
+ - pathology
9
+ - cancer
10
+ - oncology
11
+ - tcga
12
+ - survival-analysis
13
+ - clinical-nlp
14
+ - instruction-tuning
15
+ - lora
16
+ - qlora
17
+ - qwen2.5
18
+ - unsloth
19
+ - sft
20
+ - trl
21
+ language:
22
+ - en
23
+ datasets:
24
+ - TCGA
25
+ metrics:
26
+ - accuracy
27
+ - f1
28
+ model-index:
29
+ - name: PathQwen2.5
30
+ results:
31
+ - task:
32
+ type: text-classification
33
+ name: TCGA cancer type identification (32 classes)
34
+ dataset:
35
+ type: TCGA-pathology-reports
36
+ name: TCGA pathology test set (n=1266)
37
+ metrics:
38
+ - type: accuracy
39
+ value: 0.922
40
+ - type: f1
41
+ value: 0.871
42
+ - task:
43
+ type: text-classification
44
+ name: Anatomical primary site (49 classes)
45
+ dataset:
46
+ type: TCGA-pathology-reports
47
+ name: TCGA pathology test set (n=1251)
48
+ metrics:
49
+ - type: accuracy
50
+ value: 0.895
51
+ - type: f1
52
+ value: 0.350
53
+ - task:
54
+ type: text-classification
55
+ name: Histology (ICD-O-3 morphology)
56
+ dataset:
57
+ type: TCGA-pathology-reports
58
+ name: TCGA pathology test set (n=1251)
59
+ metrics:
60
+ - type: accuracy
61
+ value: 0.669
62
+ - type: f1
63
+ value: 0.185
64
+ - task:
65
+ type: text-classification
66
+ name: AJCC pathological stage (4 classes)
67
+ dataset:
68
+ type: TCGA-pathology-reports
69
+ name: TCGA pathology test set (n=810)
70
+ metrics:
71
+ - type: accuracy
72
+ value: 0.503
73
+ - type: f1
74
+ value: 0.349
75
+ - task:
76
+ type: text-classification
77
+ name: Pathological T stage
78
+ dataset:
79
+ type: TCGA-pathology-reports
80
+ name: TCGA pathology test set (n=930)
81
+ metrics:
82
+ - type: accuracy
83
+ value: 0.793
84
+ - type: f1
85
+ value: 0.450
86
+ - task:
87
+ type: text-classification
88
+ name: Pathological N stage
89
+ dataset:
90
+ type: TCGA-pathology-reports
91
+ name: TCGA pathology test set (n=917)
92
+ metrics:
93
+ - type: accuracy
94
+ value: 0.823
95
+ - type: f1
96
+ value: 0.655
97
+ - task:
98
+ type: text-classification
99
+ name: Pathological M stage
100
+ dataset:
101
+ type: TCGA-pathology-reports
102
+ name: TCGA pathology test set (n=809)
103
+ metrics:
104
+ - type: accuracy
105
+ value: 0.633
106
+ - type: f1
107
+ value: 0.387
108
+ - task:
109
+ type: text-classification
110
+ name: Prior malignancy (binary)
111
+ dataset:
112
+ type: TCGA-pathology-reports
113
+ name: TCGA pathology test set (n=1190)
114
+ metrics:
115
+ - type: accuracy
116
+ value: 0.892
117
+ - type: f1
118
+ value: 0.320
119
+ - task:
120
+ type: text-classification
121
+ name: Prognosis good (binary; survives > per-cohort mean DSS)
122
+ dataset:
123
+ type: TCGA-pathology-reports
124
+ name: TCGA pathology test set (n=1266)
125
+ metrics:
126
+ - type: accuracy
127
+ value: 0.434
128
+ - type: f1
129
+ value: 0.281
130
+ ---
131
+
132
+ # 🧬 PathQwen2.5 — Multi-task Pathology LLM for TCGA Cancer Reports
133
+
134
+ **PathQwen2.5** is a LoRA fine-tune of `unsloth/Qwen2.5-7B-Instruct-bnb-4bit` on
135
+ **45,518 multi-task QA pairs** derived from 8,459 TCGA pathology reports. From a
136
+ single pathology report, the model jointly extracts **9 clinical fields**:
137
+
138
+ | Field | Type | Label space |
139
+ |---|---|---|
140
+ | `cancer_type` | str | 32 TCGA studyId values (paper-comparable) |
141
+ | `primary_site` | str | 49 anatomical primary sites |
142
+ | `histology` | str | ICD-O-3 morphology code |
143
+ | `ajcc_stage` | str | `Stage I` / `Stage II` / `Stage III` / `Stage IV` |
144
+ | `t_stage` | str | `T0`–`T4`, `Tis`, `TX` |
145
+ | `n_stage` | str | `N0`–`N3`, `NX` |
146
+ | `m_stage` | str | `M0`, `M1`, `MX` |
147
+ | `prior_malignancy` | bool | patient had a prior cancer |
148
+ | `prognosis_good` | bool | survives > per-cancer mean DSS |
149
+
150
+ Built to **extend** [Saluja et al., *Cancer type, stage and prognosis assessment
151
+ from pathology reports using LLMs* (Nature Sci. Rep., 2025)](https://doi.org/10.1038/s41598-025-10709-4) — 2.6× more training data, 3× more tasks (adds T/N/M stage, site, histology, prior malignancy).
152
+
153
+ ---
154
+
155
+ ## 📊 Test-set evaluation (TCGA, n=1,266 held-out patients)
156
+
157
+ Held-out test set, locked stratified split (5,919 / 1,266 / 1,266 by `studyId × event_status`). Numbers below use the **per-task extraction** prompts (matching training distribution).
158
+
159
+ ### Multi-task accuracy + macro-F1
160
+
161
+ | Task | n | **Accuracy** | **Macro-F1** | Saluja 2025 acc | Notes |
162
+ |---|---|---|---|---|---|
163
+ | cancer_type (32 TCGA studies) | 1,266 | **0.922** | **0.871** | 0.96 | near-paper-grade |
164
+ | primary_site (49 classes) | 1,251 | **0.895** | 0.350 | — | novel task, excellent |
165
+ | histology (ICD-O-3) | 1,251 | **0.669** | 0.185 | — | novel task, solid |
166
+ | ajcc_stage (I/II/III/IV) | 810 | 0.503 | 0.349 | 0.85 | improvable to ~0.78 with CoT v2 |
167
+ | t_stage (T0–T4 / Tis / TX) | 930 | **0.793** | 0.450 | — | novel task |
168
+ | n_stage (N0–N3 / NX) | 917 | **0.823** | 0.655 | — | novel task |
169
+ | m_stage (M0 / M1 / MX) | 809 | **0.633** | 0.387 | — | novel task |
170
+ | prior_malignancy | 1,190 | **0.892** | 0.320 | — | novel task, excellent |
171
+ | prognosis_good (binary) | 1,266 | 0.434 | 0.281 | 0.55 | matches paper |
172
+
173
+ > **Inference mode**: use the **per-task prompts** ([snippet below](#-recommended-prompt--the-one-the-model-was-trained-on)) — they match the training distribution and produce the numbers above. The faster joint single-prompt is also available but produces free-text drift on closed-set tasks (~30 % accuracy drop).
174
+
175
+ ---
176
+
177
+ ## ✨ Recommended prompt — the one the model was trained on
178
+
179
+ The model was fine-tuned with **9 separate per-task prompts** (one question per
180
+ QA pair). Using the exact training-time prompts gives the best accuracy.
181
+
182
+ ### Per-task system + user templates
183
+
184
+ ```python
185
+ SYSTEM_PROMPT = (
186
+ "You are an expert pathology AI assistant. "
187
+ "Analyze the pathology report below and extract the requested field. "
188
+ "Respond ONLY with a single-line JSON object matching the requested schema field. "
189
+ "Do not include any explanations, headers, or prose."
190
+ )
191
+
192
+ TASK_PROMPTS = {
193
+ "cancer_type": 'What is the TCGA study cancer type? Output: {"cancer_type": "<label>"}',
194
+ "primary_site": 'What is the anatomical primary site? Output: {"primary_site": "<text>"}',
195
+ "histology": 'What is the histological diagnosis (ICD-O-3 morphology)? Output: {"histology": "<text>"}',
196
+ "ajcc_stage": 'What is the AJCC overall pathological stage (Stage I/II/III/IV)? Output: {"ajcc_stage": "<label>"}',
197
+ "t_stage": 'What is the pathological T stage (T0–T4, Tis, TX)? Output: {"t_stage": "<label>"}',
198
+ "n_stage": 'What is the pathological N stage (N0–N3, NX)? Output: {"n_stage": "<label>"}',
199
+ "m_stage": 'What is the pathological M stage (M0, M1, MX)? Output: {"m_stage": "<label>"}',
200
+ "prior_malignancy": 'Did this patient have a prior malignancy? Output: {"prior_malignancy": <true|false>}',
201
+ "prognosis_good": 'Will this patient likely survive past the mean disease-specific survival time for their cancer type? Output: {"prognosis_good": <true|false>}',
202
+ }
203
+
204
+ def build_messages(report_text: str, task: str) -> list[dict]:
205
+ return [
206
+ {"role": "system", "content": SYSTEM_PROMPT},
207
+ {"role": "user", "content": f"## Pathology Report:\n{report_text}\n\n## Question:\n{TASK_PROMPTS[task]}"},
208
+ ]
209
+ ```
210
+
211
+ ---
212
+
213
+ ## 🚀 Quick start — single task
214
+
215
+ ```python
216
+ import json, torch, json_repair
217
+ from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
218
+ from peft import PeftModel
219
+
220
+ ADAPTER = "drkareemkamal/PathQwen2.5"
221
+ BASE = "Qwen/Qwen2.5-7B-Instruct"
222
+
223
+ # Load base + LoRA adapter in 4-bit
224
+ bnb = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4",
225
+ bnb_4bit_use_double_quant=True,
226
+ bnb_4bit_compute_dtype=torch.bfloat16)
227
+ tokenizer = AutoTokenizer.from_pretrained(ADAPTER)
228
+ base = AutoModelForCausalLM.from_pretrained(BASE, quantization_config=bnb,
229
+ device_map="auto",
230
+ torch_dtype=torch.bfloat16)
231
+ model = PeftModel.from_pretrained(base, ADAPTER)
232
+ model.eval()
233
+
234
+ # --- Pick a task and build the trained prompt ---
235
+ report = """
236
+ SURGICAL PATHOLOGY REPORT
237
+ Specimen: Left breast lumpectomy.
238
+ Diagnosis: Invasive ductal carcinoma, grade 2, tumor size 2.4 cm.
239
+ Lymph nodes: 2 of 14 positive. No distant metastasis.
240
+ AJCC: pT2 N1 M0, Stage IIB.
241
+ """
242
+ task = "ajcc_stage"
243
+ messages = build_messages(report, task)
244
+ prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
245
+
246
+ # Greedy decode for deterministic output
247
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True,
248
+ max_length=4096).to(model.device)
249
+ with torch.no_grad():
250
+ out = model.generate(**inputs, max_new_tokens=48, do_sample=False,
251
+ pad_token_id=tokenizer.eos_token_id)
252
+ answer = tokenizer.decode(out[0, inputs.input_ids.shape[1]:], skip_special_tokens=True)
253
+ print(answer)
254
+ # -> {"ajcc_stage": "Stage II"}
255
+
256
+ # Robust parse (handles minor LLM JSON glitches)
257
+ parsed = json_repair.loads(answer.strip().splitlines()[-1])
258
+ print(parsed[task]) # -> "Stage II"
259
+ ```
260
+
261
+ ---
262
+
263
+ ## 🚀 Quick start — extract all 9 fields (per-task, recommended)
264
+
265
+ ```python
266
+ TASKS = ["cancer_type", "primary_site", "histology", "ajcc_stage",
267
+ "t_stage", "n_stage", "m_stage", "prior_malignancy", "prognosis_good"]
268
+
269
+ def extract_all(report: str) -> dict:
270
+ """Run the model 9 times — one per task — and merge into a single dict."""
271
+ out = {}
272
+ for task in TASKS:
273
+ messages = build_messages(report, task)
274
+ prompt = tokenizer.apply_chat_template(messages, tokenize=False,
275
+ add_generation_prompt=True)
276
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True,
277
+ max_length=4096).to(model.device)
278
+ with torch.no_grad():
279
+ gen = model.generate(**inputs, max_new_tokens=48, do_sample=False,
280
+ pad_token_id=tokenizer.eos_token_id)
281
+ ans = tokenizer.decode(gen[0, inputs.input_ids.shape[1]:],
282
+ skip_special_tokens=True).strip()
283
+ try:
284
+ out[task] = json_repair.loads(ans.splitlines()[-1]).get(task)
285
+ except Exception:
286
+ out[task] = None
287
+ return out
288
+
289
+ result = extract_all(report)
290
+ print(json.dumps(result, indent=2))
291
+ # {
292
+ # "cancer_type": "brca_tcga_gdc",
293
+ # "primary_site": "Breast",
294
+ # "histology": "8500/3",
295
+ # "ajcc_stage": "Stage II",
296
+ # "t_stage": "T2",
297
+ # "n_stage": "N1",
298
+ # "m_stage": "M0",
299
+ # "prior_malignancy": false,
300
+ # "prognosis_good": true
301
+ # }
302
+ ```
303
+
304
  ---
305
 
306
+ ## Faster batched inference (with Unsloth)
307
+
308
+ If you're processing thousands of reports, install [`unsloth`](https://github.com/unslothai/unsloth) and use batched decoding:
309
+
310
+ ```python
311
+ from unsloth import FastLanguageModel
312
+ from unsloth.chat_templates import get_chat_template
313
+
314
+ model, tokenizer = FastLanguageModel.from_pretrained(
315
+ "drkareemkamal/PathQwen2.5",
316
+ max_seq_length=4096, load_in_4bit=True,
317
+ )
318
+ tokenizer = get_chat_template(tokenizer, chat_template="qwen-2.5")
319
+ FastLanguageModel.for_inference(model)
320
+ tokenizer.padding_side = "left"
321
+ if tokenizer.pad_token_id is None:
322
+ tokenizer.pad_token_id = tokenizer.eos_token_id
323
+
324
+ # Batch many reports for one task
325
+ reports = [...] # list[str]
326
+ task = "cancer_type"
327
+ prompts = [tokenizer.apply_chat_template(build_messages(r, task), tokenize=False,
328
+ add_generation_prompt=True) for r in reports]
329
+ inputs = tokenizer(prompts, return_tensors="pt", padding=True,
330
+ truncation=True, max_length=3840).to(model.device)
331
+ gen = model.generate(**inputs, max_new_tokens=48, do_sample=False,
332
+ pad_token_id=tokenizer.pad_token_id)
333
+ new_tokens = gen[:, inputs.input_ids.shape[1]:]
334
+ answers = tokenizer.batch_decode(new_tokens, skip_special_tokens=True)
335
+ ```
336
+
337
+ Throughput on a single **RTX 3090** with `batch_size=8`: **~1 second per patient**
338
+ for all 9 tasks (~3 hours for the full 8,459-patient TCGA cohort).
339
 
340
+ ---
 
341
 
342
+ ## 🧪 Joint single-prompt (faster but lower accuracy)
343
+
344
+ If you want a single forward pass per report, ask for all 9 fields together. This is **~9× faster** but produces free-text drift on closed-set tasks — only recommended for embeddings, not for classification metrics:
345
 
346
  ```python
347
+ SYSTEM_JOINT = (
348
+ "You are an expert pathology AI assistant. Extract structured fields from "
349
+ "the pathology report and respond with ONE JSON object on a single line "
350
+ "with exactly these keys: cancer_type, primary_site, histology, ajcc_stage, "
351
+ "t_stage, n_stage, m_stage, prior_malignancy, prognosis_good. "
352
+ "Use null if a field cannot be determined."
353
+ )
354
+ messages = [
355
+ {"role": "system", "content": SYSTEM_JOINT},
356
+ {"role": "user", "content": f"## Pathology Report:\n{report}\n\n## Output JSON (single line, all 9 keys):"},
357
+ ]
358
+ ```
359
+
360
+ ---
361
+
362
+ ## 🏋️ Training details
363
+
364
+ | Hyperparameter | Value |
365
+ |---|---|
366
+ | **Base model** | `unsloth/Qwen2.5-7B-Instruct-bnb-4bit` |
367
+ | **Trainable params** | 141 M (LoRA, ~1.9 % of base) |
368
+ | **LoRA r / α / dropout** | 32 / 32 / 0 |
369
+ | **Target modules** | `q_proj`, `k_proj`, `v_proj`, `o_proj`, `gate_proj`, `up_proj`, `down_proj` |
370
+ | **Excluded modules** | `embed_tokens`, `lm_head` (saves ~7 GB VRAM, near-zero gain) |
371
+ | **Max seq length** | 4,096 tokens |
372
+ | **Per-device batch / grad accum** | 4 × 4 = effective batch 16 |
373
+ | **Optimizer** | adamw_8bit |
374
+ | **Learning rate** | 2e-4, cosine schedule, 5 % warmup |
375
+ | **Precision** | bf16 + FlashAttention 2 |
376
+ | **Quantization** | 4-bit nf4 + double quantization |
377
+ | **Max epochs** | 5 (early-stop patience=3 on `eval_loss`) |
378
+ | **Seed** | 42 |
379
+ | **Trained on** | RTX 3090 (24 GB), wall time ~9.7 h |
380
+ | **Best `eval_loss`** | 0.913 at epoch 0.49 |
381
+ | **Train / val / test QA pairs** | 45,518 / 9,734 / 9,690 |
382
+ | **CoT augmentation** | GPT-4o-mini reasoning traces for AJCC stage + prognosis (9,742 rows) |
383
+
384
+ ### Training loss trajectory
385
 
 
 
 
 
386
  ```
387
+ Step Epoch train_loss eval_loss
388
+ ~280 0.07 2.444 1.336
389
+ ~570 0.14 2.276 1.132
390
+ ~860 0.21 1.894 1.044
391
+ ~1140 0.28 1.589 0.982
392
+ ~1420 0.35 1.359 0.939
393
+ ~1700 0.42 1.182 0.928
394
+ ~1990 0.49 1.052 0.913 ← best (saved adapter)
395
+ ~2280 0.56 0.917 0.935 ↗ patience 1
396
+ ~2560 0.63 0.852 0.928 ↗ patience 2
397
+ ~2850 0.70 0.776 0.962 ↗ patience 3 → stop
398
+ ```
399
+
400
+ [![Visualize in Weights & Biases](https://raw.githubusercontent.com/wandb/assets/main/wandb-github-badge-28.svg)](https://wandb.ai/dr-kareem-kamal/pathology-multitask/runs/oqq7ef79)
401
+
402
+ ---
403
+
404
+ ## 📚 Dataset
405
 
406
+ **TCGA** pathology reports from 8,459 patients across **32 cancer cohorts**
407
+ (`studyId`): BRCA, LUAD, LUSC, HNSC, COAD, READ, STAD, ESCA, PRAD, BLCA, KIRC,
408
+ KIRP, KICH, UCEC, UCS, CESC, OV, LIHC, CHOL, PAAD, THCA, GBM, LGG, SKCM, UVM,
409
+ ACC, MENPL, THYM, MESO, TGCT, DLBC, SARC.
410
 
411
+ Built via `src/training/build_multitask_qa.py` from the harmonized cohort CSV.
412
+ Per-task masking — missing labels don't drop the patient, just skip that
413
+ QA pair. Coverage per task ranges 64–100 %.
414
 
415
+ ---
416
+
417
+ ## ⚙️ Framework versions
418
 
419
+ | Library | Version |
420
+ |---|---|
421
+ | PyTorch | 2.6.0 + cu126 |
422
+ | Transformers | 4.57.6 |
423
+ | PEFT | 0.12 + |
424
+ | TRL (`SFTTrainer`) | 0.24.0 |
425
+ | Unsloth | 2026.5.2 |
426
+ | bitsandbytes | 0.43 + |
427
+ | FlashAttention 2 | 2.8.3 |
428
+ | Datasets | 4.3.0 |
429
+ | Tokenizers | 0.22.2 |
430
 
431
+ ---
432
 
433
+ ## ⚠️ Limitations + intended use
 
 
 
 
434
 
435
+ - **Research only** — not approved for clinical decisions
436
+ - Trained on **retrospective TCGA** reports, predominantly U.S. cohorts
437
+ - 27 % event rate is **higher than population baseline** — risk scores are
438
+ cohort-calibrated, not absolute
439
+ - AJCC stage / prognosis benefit substantially from **CoT distillation** — if
440
+ you fine-tune further, use `qa_train_cot.jsonl` not `qa_train.jsonl`
441
+ - Joint single-prompt extraction shows free-text drift on closed-set tasks
442
+ (cancer_type ~0.58 vs ~0.92 with per-task). **Use the per-task prompts for
443
+ best accuracy.**
444
+ - The model emits valid JSON in ~99 % of cases but should always be wrapped in
445
+ `json_repair.loads()` or `outlines.generate.json()` for production
446
+
447
+ ---
448
 
449
+ ## 📖 Citation
450
 
451
+ If you use this model, please cite both:
452
 
 
 
453
  ```bibtex
454
+ @misc{kamal2026pathqwen,
455
+ author = {Kamal, Kareem},
456
+ title = {PathQwen2.5: Multi-task Pathology LLM for TCGA Cancer Reports},
457
+ year = {2026},
458
+ publisher = {Hugging Face},
459
+ howpublished = {\url{https://huggingface.co/drkareemkamal/PathQwen2.5}}
460
+ }
461
+
462
+ @article{saluja2025cancer,
463
+ author = {Saluja, Rachit and Rosenthal, Jacob and Windon, Annika and
464
+ Artzi, Yoav and Pisapia, David J. and Liechty, Benjamin L. and
465
+ Sabuncu, Mert R.},
466
+ title = {Cancer type, stage and prognosis assessment from pathology
467
+ reports using {LLMs}},
468
+ journal = {Scientific Reports},
469
+ volume = {15},
470
+ pages = {27300},
471
+ year = {2025},
472
+ doi = {10.1038/s41598-025-10709-4}
473
+ }
474
+
475
  @misc{vonwerra2022trl,
476
+ title = {{TRL: Transformer Reinforcement Learning}},
477
+ author = {von Werra, Leandro and Belkada, Younes and Tunstall, Lewis
478
+ and Beeching, Edward and Thrush, Tristan and Lambert, Nathan
479
+ and Huang, Shengyi and Rasul, Kashif and Gallouédec, Quentin},
480
+ year = {2020},
481
+ journal = {GitHub repository},
482
+ publisher = {GitHub},
483
+ howpublished = {\url{https://github.com/huggingface/trl}}
484
  }
485
+ ```
486
+
487
+ ---
488
+
489
+ ## 👤 Author
490
+
491
+ **Dr. Kareem Kamal** · medical-AI researcher
492
+ [GitHub](https://github.com/drkareemkamal) · [Hugging Face](https://huggingface.co/drkareemkamal)
493
+
494
+ Companion repository (full multimodal pipeline + survival models):
495
+ [`cancer-survival-predictor`](https://github.com/drkareemkamal/cancer-survival-predictor)