CoolFace
Modelpublic

junma/MedJev-Qwen3.5-0.8B

sourceHugging Faceapache-2.0updated 3d agoView on Hugging Face
10likes37downloads
Model Card

MedJev-Qwen3.5-0.8B

Ultra-fast extraction of predefined clinical variables from free-text clinical notes.

MedJev reads a clinical note once and answers every predefined variable about it in a single forward pass — 11 variables in ~62 ms on one GPU. It is a LoRA adapter plus a small pointer head on Qwen3.5-0.8B-Base, with no text generation: each question's options are scored directly, so the output is always a proper probability distribution over exactly the allowed answers. There is nothing to parse, no format drift, and no possibility of an answer outside the schema.

testdevelopment
Micro accuracy (26,286 / 27,197 questions)0.8780.874
Macro accuracy over the 11 variables0.8830.878
Brier score0.1790.182
Majority-class floor0.6020.602

It beats a hand-written regex baseline, the zero-shot base model, the instruction-tuned model, and the hosted Jev API on every one of the 11 variables.

Task

Each variable is posed as one of three question types:

TypeAnswerExample
noulyes/no, as a probabilityhospital_admission — was the patient admitted?
choiceone of 2–255 named optionsprimary_diagnostic_modality — imaging, histopathology, laboratory, …
scorean ordered levelsymptom_severity — mild / moderate / severe

The 11 variables were selected so they are not solvable by regular expressions: each candidate was screened against a hand-written regex baseline and rejected if the regex nearly solved it. Sex and age, for example, were dropped because a regex matches the label 98.9% of the time.

Results

Micro accuracy on the held-out test split (2,895 notes, 26,286 labelled questions):

systemmicro accuracyp50 latency / note
MedJev-0.8B0.87862 ms (bf16)
Regex / counting rules0.6673.6 ms (CPU)
Hosted Jev (jev-1.13.0)0.602284 ms
Majority class per question0.602—
Qwen3.5-0.8B-Instruct, chat prompt0.517—
Qwen3.5-0.8B-Base, zero-shot letter logits0.500—

By question type on test: noul 0.941, choice 0.793, score 0.810.

Per variable (test split):

variabletypenmajorityMedJevmacro-F1Brier
surgical_managementnoul2,8950.7090.9540.9440.073
follow_up_plannednoul2,8950.8890.9510.8700.074
drug_therapynoul2,8950.6350.9410.9370.086
prior_comorbiditynoul2,8950.7920.9400.9060.090
hospital_admissionnoul2,8950.8010.9180.8690.117
smoking_statuschoice7260.5250.9750.9440.039
principal_medical_therapychoice2,8950.2080.7870.8080.312
primary_diagnostic_modalitychoice2,8950.3430.7520.6860.355
symptom_severityscore9900.5540.9110.8950.130
treatment_responsescore1,4100.4650.7990.7440.294
diagnostic_workup_intensityscore2,8950.5410.7800.7650.317

Accuracy is fp32; latency is bf16, which costs no measurable accuracy and is ~3× faster. Model selection was done on development; the test split was read once, after selection.

Usage

MedJev needs the medjev package (the adapter alone is not enough — the pointer head lives in head.pt and the input format is specific):

bash
git clone https://github.com/<your-org>/MedJev && cd MedJev
pip install torch --index-url https://download.pytorch.org/whl/cu130
pip install -e '.[cuda]'
python
import torch
from huggingface_hub import snapshot_download
from medjev.checkpoint import load
from medjev.model import MAX_BRANCH, MAX_STATE
from medjev.records import materialize

path = snapshot_download("junma/MedJev-Qwen3.5-0.8B")
tok, model = load(path, "cuda", dtype=torch.bfloat16)
model.lm.config.use_cache = True

request = {
    "state": "A 36-year-old woman was admitted with severe left hip pain. MRI showed a lesion; "
             "biopsy confirmed osteosarcoma. She underwent resection and received chemotherapy, "
             "with complete resolution at 6 months.",
    "questions": {
        # `label` and `src` are required by materialize(); the label is ignored at inference
        "hospital_admission": {
            "type": "noul",
            "instructions": "Was this patient admitted to a hospital or other care centre?",
            "criteria": {"true": "Admitted as an inpatient", "false": "Outpatient visit only"},
            "label": True, "src": "demo",
        },
        "symptom_severity": {
            "type": "score",
            "instructions": "How severe is the patient's presentation overall?",
            "criteria": ["Mild", "Moderate", "Severe"],
            "label": 0, "src": "demo",
        },
    },
}

enc = model.encode(tok, materialize(request), max_state=MAX_STATE, max_branch=MAX_BRANCH)
with torch.no_grad():
    probs, _ = model.probs_and_prefix(enc)   # state encoded once; branches read its cache

for p, (qid, q) in zip(probs, request["questions"].items()):
    print(qid, [round(float(x), 3) for x in p])

# hospital_admission [0.0, 1.0]        noul:  [P(false), P(true)]
# symptom_severity   [0.0, 0.0, 1.0]   score: one entry per level

The schema is free-form — reuse the 11 specs in medjev.labels.QUESTIONS or pass your own instructions and criteria in the same shape. Option order is shuffled during training, so choice answers are order-robust, but the model is fine-tuned on this corpus's vocabulary; new option sets work best after a short further fine-tune.

How it works

The backbone is hybrid — 12 Gated DeltaNet + 12 attention layers. Because the DeltaNet layers are recurrent and ignore attention masks, each question runs as its own causal row with the state repeated, which makes question isolation exact by construction: questions cannot read each other.

At serving time the state is encoded once and every question branch reads its cache, so answering all 11 variables costs about as much as answering one. That is where the speed comes from, and why the reported latency is per note, not per question.

The pointer head scores each option's boundary token against a <decide> position; a softmax over those scores is the answer. Training is cross-entropy, plus a ranked probability score term for the three genuinely ordered score variables.

Training

BaseQwen3.5-0.8B-Base (frozen)
AdapterLoRA r=16, α=32, dropout 0.05, on attention + MLP + DeltaNet projections
HeadPointer head, 256-dim, trained from scratch
Trainable11.3 M parameters (adapter ≈ 43 MB)
Data23,719 notes / 215,425 labelled questions
Schedule2 epochs, all ~9 questions per record, 431,124 question rows, 5,272 steps
OptimizerAdamW, OneCycle, lr 1e-4, weight decay 0.01, effective batch 9
State budget2,048 tokens (truncates 33 of 2,895 test notes)
Precisionbf16 autocast, fp32 master weights, gradient checkpointing
Hardware3 × RTX 6000 Ada, 5.39 h, 9.4 GB peak per GPU

Training data and label provenance

Derived from Augmented Clinical Notes (Bonnet & Boulenger, EPFL; MIT), whose notes come from PMC-Patients — open-access PubMed Central case reports.

Labels are silver, not gold. They are normalised from that corpus's structured patient summaries, which were themselves generated by GPT-4 against a medical template. MedJev is therefore trained to reproduce a GPT-4 extraction, not clinician adjudication. Accuracy figures here measure agreement with those silver labels.

Input is full_note only; the corpus's note, conversation and summary fields are never model input. Splits are 80/10/10 by sha1(idx), so they are stable across rebuilds, with no note overlap.

Limitations and intended use

Not for clinical use. This is a research artifact. It has not been validated against clinician adjudication, has no regulatory clearance, and is not a medical device. Do not use it to inform care.

  • —Silver labels. Ceiling accuracy is agreement with a GPT-4 extraction, which carries its own errors, especially on the harder aggregation variables.
  • —Domain. Published, English-language, single-patient case reports — typically short, curated and unusually complete. Real EHR notes are longer, messier, more abbreviated and more templated; expect degradation.
  • —Population. Case reports over-represent rare and severe presentations. Base rates here are not clinical base rates.
  • —Long notes. The state budget is 2,048 tokens, truncated from the beginning of the note. ~1% of notes in this corpus exceed it.
  • —Calibration. Brier scores are good in-domain; they are not validated out of domain. Test any probability threshold on your own data.
  • —Schema drift. The model is tuned to these 11 variables' wording. Substantially different questions warrant a short further fine-tune.

Citation

The architecture (LoRA + pointer head, question isolation, the System One request format) is from kev by Jared Palmer, Apache-2.0. MedJev vendors and adapts it; the training data and all weights here are MedJev's own.

bibtex
@software{medjev2026,
  title  = {MedJev: ultra-fast clinical variable extraction with a small decision model},
  author = {Ma, Jun},
  year   = {2026},
  url    = {https://huggingface.co/junma/MedJev-Qwen3.5-0.8B}
}

License

Apache-2.0, matching kev and the Qwen3.5 base model. The corpus is MIT; the base model carries its own licence. Review both before redistributing anything derived from this.

Framework versions

  • —PEFT 0.21.0
  • —transformers 5.17.0
  • —torch 2.14.0+cu130