Skip to main content

MedSimplify: fine-tuning Gemma 4 for medical Easy Read

Share:XLinkedInHN
Cover for MedSimplify: fine-tuning Gemma 4 for medical Easy Read

The paragraph that started it

Here is the top of a real discharge summary, lightly redacted:

The patient presented with acute exacerbation of chronic obstructive pulmonary disease secondary to community-acquired pneumonia. Empirical broad-spectrum antibiotic therapy was initiated pending sputum culture sensitivities. Nebulised bronchodilators and systemic corticosteroids were administered. Follow-up spirometry is indicated at six weeks post-discharge to reassess baseline pulmonary function.

And here is what MedSimplify does to it:

You came to hospital because your lung problem got much worse. This was because of a chest infection. We gave you strong medicine to fight the infection. We also gave you medicine to open up your airways and reduce swelling. In six weeks, you need a breathing test. This will show how well your lungs are working now.

Same clinical content. About one third of the words. No jargon that a first-time reader has to Google before they can act on the advice. That gap between the two paragraphs is the whole reason MedSimplify exists, and it is the gap I spent the Gemma 4 Good Hackathon trying to close.

The code lives at github.com/kaushiksaravanan/medsimplify (private for now) and the running Space is at huggingface.co/spaces/kaushikss/medsimplify.

Why Easy Read for medical text

Easy Read is a specific writing style that the UK NHS, the Australian government, and several EU disability agencies use for people with learning disabilities, low literacy, English as a second language, or cognitive impairment from a recent hospital stay. It has a set of rules that are more concrete than "plain English": short sentences, one idea per sentence, active voice, common words, and images that repeat the message.

Medical discharge summaries fail every single one of these rules. They are written for the next clinician who will see the patient, not for the patient. The tragedy is that the patient is the one who has to act on them: take the pills at the right time, come back to clinic in six weeks, watch for the symptoms that mean "call an ambulance now." A summary written at university reading level, handed to a 74-year-old with post-hospital delirium and no relatives in the room, is a compliance failure dressed up as a document.

Two categories at the Gemma 4 Good Hackathon fit this squarely. Digital Equity and Inclusivity was the obvious one. The Unsloth Special Technology Prize was the other, because the entire fine-tune had to run on a Kaggle free tier or it was not a real accessibility tool for people who might want to reproduce it.

The pipeline has two Gemma calls. The first one is Vision. The second one is language.

Vision OCR with Gemma 4 Vision

Discharge summaries in the UK come as scanned PDFs about as often as they come as digital text. The ones that arrive as PDF are usually rasterised print output from a hospital PAS system, so even the "digital" ones are pictures of characters. If MedSimplify only worked on clean text I would have skipped a large part of the population it was for.

I used Gemma 4 Vision for the OCR step. This was the first time I put a Gemma vision model in front of medical scans, and it changed what I thought the OCR problem looked like. Traditional OCR (Tesseract, EasyOCR, PaddleOCR) reads characters. Gemma 4 Vision reads the document. When a table cell says "Amoxicillin 500 mg TDS 7/7" and the row above says "Regular medications," a character-only OCR gives you back the tokens and hopes you can reassemble the meaning. Gemma 4 Vision gives you back a JSON-shaped structure that says this is a prescription, the drug is Amoxicillin, the dose is 500 mg, the frequency is three times per day, and the duration is seven days.

I did not have to prompt-engineer this heavily. A short instruction to return structured medication entries and section markers was enough to get 95%-plus faithful extraction on the twenty discharge summaries I hand-checked. The failure modes were the ones I expected: handwritten annotations in the margins, and photocopied-of-photocopied pages where even a human reader has to guess. For the second failure mode I fell back to asking the user to retype the paragraph, because guessing at a medication dose from a smudged page is not a feature, it is a lawsuit.

The Vision output is not the final Easy Read. It is the input to the language step, which is where the actual simplification happens.

The fine-tune

The language model is Gemma 4 9B, fine-tuned with Unsloth in 4-bit QLoRA with LoRA adapters on top. The whole thing fits on a Kaggle T4, which was one of the two constraints I refused to relax. If reproducing the training needs an A100 that is not really an accessibility project, that is a well-funded research project with an accessibility label.

The training data was pairs of complex-to-simple document rewrites drawn from Simple Wikipedia, Easy Read documents published by UK Gov, and MedlinePlus patient-facing pages, plus a small batch of hand-written synthetic examples covering medical, government, and legal templates. The synthetic batch was the smallest but it was the one that taught the model the specific bits I cared about, which was the discharge-instruction shape and the "call 999 if" pattern.

Training config was almost defaults: 2e-4 learning rate, LoRA r=16 with alpha 16 on both the attention and MLP projections (q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj), 3 epochs, per-device batch size 2 with gradient accumulation of 4 for an effective batch of 8, and a linear schedule with a 10-step warmup. Nothing exotic. The interesting part was not the hyperparameters, it was that the earlier Gemma 2 2B run I had done for a proof of concept was not good enough on the recall side. Gemma 2 2B would produce fluent Easy Read but it would drop clinically important content, and dropping "take this antibiotic for the full seven days" is exactly the failure mode Easy Read is supposed to prevent. Moving to Gemma 4 E4B, and then to the 9B for the final training run, closed most of that gap. Bigger base model, same training data, better preservation of the medication and follow-up fields.

The two numbers I ended on were a Flesch-Kincaid grade level of 3.8 (down from 14.2 on the source text) and 94% information preservation on the eval set. I will unpack what that 94% means at the end, because it is the number that most needs unpacking.

The P100 vs T4 story

Halfway through fine-tuning, Kaggle assigned me a P100 instead of a T4. I did not notice at first because both GPUs have 16 GB of memory and both are Pascal-or-newer, and the Unsloth training script started up fine. It got about two hundred steps in before it died with a compute capability error.

The P100 is compute capability 6.0. Unsloth needs 7.0 or higher, because it uses Triton kernels that assume Volta-class instructions (Tensor Cores, in practice). The T4 is compute capability 7.5, which is why every Unsloth tutorial that says "runs on a free Kaggle GPU" implicitly means the T4. When Kaggle hands you a P100 the tutorial does not work, and the error message points at the kernel, not at the GPU choice, so you spend an hour thinking your training script is broken before you check nvidia-smi and realise Kaggle rotated you off the T4.

There is no clean fix. Unsloth is not going to backport to Pascal, and it should not have to. The fix was to swap Unsloth out for standard PEFT with bitsandbytes 4-bit, which is slower but runs on the P100 because it does not need Volta kernels. I kept both training paths in the repo: an Unsloth path for T4 sessions, which is what the Hugging Face Space uses when it retrains, and a plain-PEFT path for P100 fallback.

P100 vs T4 is now the first thing I check on any Kaggle-based training project. nvidia-smi before anything else, and if it says Tesla P100 I either restart the session to try for a T4 reroll or I take the PEFT path. Kaggle does not let you pick the GPU, so this is a coin flip you have to plan for.

What 94% information preservation actually means

The eval was structured. I built a checklist per discharge summary of the clinically important facts that had to survive the rewrite: every medication, every dose, every follow-up appointment, every red-flag symptom, every self-care instruction. About twelve to twenty items per summary on average. A rewrite got a point per surviving item, and 94% is the average across a held-out set of 50 real discharge summaries.

Six percent got dropped. That is not a small number when a dropped item is "take this antibiotic for the full seven days" or "if you have chest pain again, call 999." I went back through the misses and about half of them were the model deciding a follow-up phone call was less important than a follow-up clinic visit and dropping the phone call. The other half were medication frequencies where the model kept the drug name but lost the "twice a day" qualifier, which is a specific failure mode that a rules-based post-processor can catch and flag.

So 94% is not a green light. It is a number that says the model is good enough that a nurse or a family member reviewing the Easy Read output has a small, bounded set of things to check, instead of having to re-verify the whole document. That is the honest bar for a tool that sits between a hospital and a patient at home. The next thing on the list is a structured extractor that pulls medications, doses, and follow-up dates out of the source separately, and cross-checks that every item appears in the Easy Read output. Not a replacement for a human reader, but a second pair of eyes that catches the missed frequency qualifier before the discharge summary lands on the kitchen table.

See also

Cite as: Saravanan, K. (2026). MedSimplify: fine-tuning Gemma 4 for medical Easy Read. Kaushik Saravanan. https://www.kaushik.cv/blog/medsimplify-gemma4-easy-read