The goal is to design a system that automatically extracts structured clinical information from discharge summaries and related clinical data and maps that information to fields in the MDS 3.0 assessment form.
Python
2
28 commits
updated Mar 31, 2026
SEP-775 Final Project — automatically extract and map free-text MIMIC IV discharge summaries to structured MDS 3.0 (Minimum Data Set) nursing-home assessment form fields using Large Language Models (LLMs).
The system takes hospital discharge notes (unstructured free text) alongside structured clinical tables (diagnoses, medications, procedures) and automatically fills the MDS 3.0 form fields for Section I (Active Disease Diagnoses), Section N (Medications), and Section O (Special Treatments, Procedures, Programs).
┌──────────────────────────────────────────────────────────────┐
│ INPUT DATA: Discharge notes (free text) + structured tables │
└───────────────────────────────┬──────────────────────────────┘
│
┌─────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌─────────────────┐ ┌─────────────────────┐
│ RAG BioBERT │ │ Preprocessor │ │ Seq2Seq preprocessor│
│ Retriever │ │(knowledge graph)│ │ (seq2seq input) │
└───────┬────────┘ └───────┬─────────┘ └──────────┬──────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────┐ ┌─────────────────────┐
│ LLM Extractor │ │ Seq2Seq Extractor │
│ (GPT / OpenAI) │ │ (BioBART / T5) │
└─────────────┬────────────┘ └──────────┬──────────┘
│ │
▼ ▼
┌───────────┐ ┌────────────────┐
│ MDS Mapper│ │ Seq2Seq Mapper │
└─────┬─────┘ └───────┬────────┘
│ │
└────────────────┬──────────────┘
▼
┌────────────────────────────────┐
│ MDS Assessment │
│ (JSON / CSV / Excel) │
└────────────────────────────────┘
.
├── config/
│ └── config.yaml # Runtime configuration
├── scripts/
│ ├── check_gpu.py # GPU readiness diagnostic
│ ├── extract_mds_sections_from_pdf.py # Parse MDS 3.0 PDF → item text + patterns
│ ├── preview_input_data.py # Print sample input records
│ └── train_seq2seq.py # Fine-tune BioBART / ClinicalT5 on labeled pairs
├── src/
│ ├── __init__.py
│ ├── mds_schema.py # MDS 3.0 form field definitions (shared)
│ ├── pipeline.py # End-to-end orchestration
│ ├── data_preprocessor/ # ← Load notes + preprocess text
│ │ ├── data_loader.py
│ │ ├── preprocessor.py # Full preprocessor (LLM path)
│ │ ├── rag_retriever.py # BioBERT semantic retriever (Option 2)
│ │ ├── seq2seq_preprocessor.py # Simplified preprocessor (seq2seq path)
│ │ └── README.md # Detailed docs for this module
│ ├── extractor/ # ← Extract MDS fields from context
│ │ ├── extractor.py # LLM extractor (OpenAI GPT)
│ │ ├── seq2seq_extractor.py # Encoder-decoder extractor (BioBART / T5)
│ │ └── README.md # Detailed docs for this module
│ └── mapper/ # ← Validate + map results to MDSAssessment
│ ├── mapper.py # Base mapper (LLM output)
│ ├── seq2seq_mapper.py # Extended mapper (seq2seq output)
│ └── README.md # Detailed docs for this module
└── tests/
├── test_data_loader.py
├── test_preprocessor.py
├── test_extractor.py
├── test_mapper.py
└── test_pipeline.py
The pipeline runs three sequential stages. Each stage is self-contained and documented in its own README.
src/data_preprocessor/Loads MIMIC IV discharge notes and transforms raw clinical text into a structured Patient Knowledge Graph through six steps:
CHF → congestive heart failure)CONFIRMED / NEGATED / UNCERTAIN)See src/data_preprocessor/README.md for the full public API, abbreviation table, NegEx patterns, and conflict resolution logic.
src/extractor/Two interchangeable extractors consume the preprocessed context and return raw structured results:
| Extractor | Backend | API key required | GPU |
|---|---|---|---|
LLMExtractor | OpenAI Chat Completions | Yes | No |
Seq2SeqExtractor | BioBART / ClinicalT5 (encoder-decoder) | No | Optional (CUDA) |
See src/extractor/README.md for constructor parameters, GPU device selection, preprocessing modes, and fine-tuning instructions.
src/mapper/Validates extracted values against the MDS schema and packages them into MDSAssessment objects.
| Mapper | For use with |
|---|---|
MDSMapper | LLMExtractor output |
Seq2SeqMapper | Seq2SeqExtractor output — also stores raw decoder output in metadata |
See src/mapper/README.md for validation rules, output format, and serialisation options.
| Section | Title |
|---|---|
| A | Identification Information |
| B | Hearing, Speech, and Vision |
| C | Cognitive Patterns |
| D | Mood (PHQ-9) |
| E | Behavior |
| G | Functional Status (ADLs) |
| H | Bladder and Bowel |
| I | Active Disease Diagnoses |
| J | Health Conditions (Pain, Dyspnea, Falls) |
| K | Swallowing / Nutritional Status |
| M | Skin Conditions / Pressure Ulcers |
| N | Medications |
| O | Special Treatments, Procedures, Programs |
Sections I, N, and O are the primary extraction targets.
pip install -r requirements.txt
export OPENAI_API_KEY="sk-..."
GPU acceleration is used by Seq2SeqExtractor (BioBART / ClinicalT5) and BioBERTRetriever for faster model inference and fine-tuning. The pipeline automatically detects CUDA and falls back to CPU — no code changes needed.
python scripts/check_gpu.py
GPU available:
torch_version: 2.x.x+cu118
cuda_available: True
cuda_device_count: 1
cuda_device_0: NVIDIA GeForce RTX 3080 (compute_capability=8.6)
gpu_tensor_op: PASS
GPU not available:
cuda_available: False
No CUDA runtime/device detected. Workloads will run on CPU.
Script exit codes: 0 = pass, 1 = torch import error, 2 = no CUDA, 3 = tensor op failed.
from src.pipeline import ExtractionPipeline
pipeline = ExtractionPipeline(
source="data/discharge.csv/discharge.csv",
openai_api_key="sk-...",
sections=["I", "N", "O"],
output_dir="output",
output_format="json",
sample_size=5,
)
assessments = pipeline.run()
from src.data_preprocessor.data_loader import MIMICDischargeLoader
from src.mds_schema import MDSSchema
from src.extractor.seq2seq_extractor import Seq2SeqExtractor
from src.mapper.seq2seq_mapper import Seq2SeqMapper
loader = MIMICDischargeLoader(source="data/discharge.csv/discharge.csv")
notes = loader.load()
schema = MDSSchema(section_ids=["I", "N", "O"])
extractor = Seq2SeqExtractor(schema=schema, model_name="GanjinZero/biobart-v2-base")
mapper = Seq2SeqMapper(schema=schema)
note = notes[0]
raw = extractor.extract(note.text, note_metadata=note.metadata)
assessment = mapper.map(note.note_id, note.subject_id, note.hadm_id, raw)
print(assessment.to_dict())
from src.data_preprocessor.data_loader import MIMICDischargeLoader
from src.data_preprocessor.rag_retriever import BioBERTRetriever
from src.mds_schema import MDSSchema
from src.extractor.extractor import LLMExtractor
from src.mapper.mapper import MDSMapper
loader = MIMICDischargeLoader(source="data/discharge.csv/discharge.csv")
notes = loader.load()
schema = MDSSchema(section_ids=["I", "N", "O"])
retriever = BioBERTRetriever(sections=["I", "N", "O"])
extractor = LLMExtractor(schema=schema, api_key="sk-...", preprocess_input=False)
mapper = MDSMapper(schema=schema)
note = notes[0]
context = retriever.build_rag_context(note.text, note.metadata)
raw = extractor.extract(context)
assessment = mapper.map(note.note_id, note.subject_id, note.hadm_id, raw)
print(assessment.to_dict())
pipeline = ExtractionPipeline(
source="data/discharge.csv/discharge.csv",
structured_sources={
"diagnoses": "data/diagnoses_icd.csv",
"prescriptions": "data/prescriptions.csv",
"procedures": "data/procedures_icd.csv",
},
openai_api_key="sk-...",
sections=["I", "N", "O"],
output_dir="output",
)
assessments = pipeline.run()
python src/pipeline.py --source data/discharge.csv/discharge.csv
Interactive mode prompts for a processing mode:
Select processing mode:
1. sample - run the initial sample only
2. sample-compare - run the sample and compare preprocessing methods
3. full - process the entire dataset
4. full-compare - process the entire dataset and compare preprocessing methods
Enter mode [1-4] (default 1):
Scripted / non-interactive:
# Sample with LLM
python src/pipeline.py --source data/discharge.csv/discharge.csv --mode sample
# Full dataset with preprocessing comparison
python src/pipeline.py --source data/discharge.csv/discharge.csv --mode full-compare
# Custom sample size
python src/pipeline.py --source data/discharge.csv/discharge.csv --mode sample --sample-size 20
Edit config/config.yaml to change settings without touching source code.
llm:
provider: "openai"
model: "gpt-3.5-turbo"
temperature: 0.0
max_tokens: 2048
data:
csv_path: "data/discharge.csv/discharge.csv"
preprocess:
enabled: true
output:
output_dir: "output"
format: "json"
| File | Contents |
|---|---|
output/mds_assessments.json | Full MDS assessment objects |
output/mds_ino_form_ready.json | Compact I/N/O codes + confidence scores |
output/mds_ino_preprocessing_comparison.json | Heuristic vs. LLM-evidence side-by-side |
output/mds_ino_preprocessing_diff_summary.json | Fields that differ between modes |
output/mds_ino_preprocessing_diff_summary.csv | Same, flattened for spreadsheet review |
# Recommended — uses the conda environment
.conda/python.exe -m pytest tests/ -v
# Or with system Python
pytest tests/ -v
All tests mock LLM and Hugging Face calls — no API key or GPU required.
This project is designed for use with the MIMIC IV clinical notes dataset, available from PhysioNet. Access requires a credentialled PhysioNet account and completion of CITI Program training.
The discharge table in MIMIC IV contains free-text discharge summaries. Structured tables (diagnoses_icd, prescriptions, procedures_icd) can be joined by hadm_id and passed to the pipeline via structured_sources.
The goal is to design a system that automatically extracts structured clinical information from discharge summaries and related clinical data and maps that information to fields in the MDS 3.0 assessment form.
Python
2
28 commits
updated Mar 31, 2026
SEP-775 Final Project — automatically extract and map free-text MIMIC IV discharge summaries to structured MDS 3.0 (Minimum Data Set) nursing-home assessment form fields using Large Language Models (LLMs).
The system takes hospital discharge notes (unstructured free text) alongside structured clinical tables (diagnoses, medications, procedures) and automatically fills the MDS 3.0 form fields for Section I (Active Disease Diagnoses), Section N (Medications), and Section O (Special Treatments, Procedures, Programs).
┌──────────────────────────────────────────────────────────────┐
│ INPUT DATA: Discharge notes (free text) + structured tables │
└───────────────────────────────┬──────────────────────────────┘
│
┌─────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌─────────────────┐ ┌─────────────────────┐
│ RAG BioBERT │ │ Preprocessor │ │ Seq2Seq preprocessor│
│ Retriever │ │(knowledge graph)│ │ (seq2seq input) │
└───────┬────────┘ └───────┬─────────┘ └──────────┬──────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────┐ ┌─────────────────────┐
│ LLM Extractor │ │ Seq2Seq Extractor │
│ (GPT / OpenAI) │ │ (BioBART / T5) │
└─────────────┬────────────┘ └──────────┬──────────┘
│ │
▼ ▼
┌───────────┐ ┌────────────────┐
│ MDS Mapper│ │ Seq2Seq Mapper │
└─────┬─────┘ └───────┬────────┘
│ │
└────────────────┬──────────────┘
▼
┌────────────────────────────────┐
│ MDS Assessment │
│ (JSON / CSV / Excel) │
└────────────────────────────────┘
.
├── config/
│ └── config.yaml # Runtime configuration
├── scripts/
│ ├── check_gpu.py # GPU readiness diagnostic
│ ├── extract_mds_sections_from_pdf.py # Parse MDS 3.0 PDF → item text + patterns
│ ├── preview_input_data.py # Print sample input records
│ └── train_seq2seq.py # Fine-tune BioBART / ClinicalT5 on labeled pairs
├── src/
│ ├── __init__.py
│ ├── mds_schema.py # MDS 3.0 form field definitions (shared)
│ ├── pipeline.py # End-to-end orchestration
│ ├── data_preprocessor/ # ← Load notes + preprocess text
│ │ ├── data_loader.py
│ │ ├── preprocessor.py # Full preprocessor (LLM path)
│ │ ├── rag_retriever.py # BioBERT semantic retriever (Option 2)
│ │ ├── seq2seq_preprocessor.py # Simplified preprocessor (seq2seq path)
│ │ └── README.md # Detailed docs for this module
│ ├── extractor/ # ← Extract MDS fields from context
│ │ ├── extractor.py # LLM extractor (OpenAI GPT)
│ │ ├── seq2seq_extractor.py # Encoder-decoder extractor (BioBART / T5)
│ │ └── README.md # Detailed docs for this module
│ └── mapper/ # ← Validate + map results to MDSAssessment
│ ├── mapper.py # Base mapper (LLM output)
│ ├── seq2seq_mapper.py # Extended mapper (seq2seq output)
│ └── README.md # Detailed docs for this module
└── tests/
├── test_data_loader.py
├── test_preprocessor.py
├── test_extractor.py
├── test_mapper.py
└── test_pipeline.py
The pipeline runs three sequential stages. Each stage is self-contained and documented in its own README.
src/data_preprocessor/Loads MIMIC IV discharge notes and transforms raw clinical text into a structured Patient Knowledge Graph through six steps:
CHF → congestive heart failure)CONFIRMED / NEGATED / UNCERTAIN)See src/data_preprocessor/README.md for the full public API, abbreviation table, NegEx patterns, and conflict resolution logic.
src/extractor/Two interchangeable extractors consume the preprocessed context and return raw structured results:
| Extractor | Backend | API key required | GPU |
|---|---|---|---|
LLMExtractor | OpenAI Chat Completions | Yes | No |
Seq2SeqExtractor | BioBART / ClinicalT5 (encoder-decoder) | No | Optional (CUDA) |
See src/extractor/README.md for constructor parameters, GPU device selection, preprocessing modes, and fine-tuning instructions.
src/mapper/Validates extracted values against the MDS schema and packages them into MDSAssessment objects.
| Mapper | For use with |
|---|---|
MDSMapper | LLMExtractor output |
Seq2SeqMapper | Seq2SeqExtractor output — also stores raw decoder output in metadata |
See src/mapper/README.md for validation rules, output format, and serialisation options.
| Section | Title |
|---|---|
| A | Identification Information |
| B | Hearing, Speech, and Vision |
| C | Cognitive Patterns |
| D | Mood (PHQ-9) |
| E | Behavior |
| G | Functional Status (ADLs) |
| H | Bladder and Bowel |
| I | Active Disease Diagnoses |
| J | Health Conditions (Pain, Dyspnea, Falls) |
| K | Swallowing / Nutritional Status |
| M | Skin Conditions / Pressure Ulcers |
| N | Medications |
| O | Special Treatments, Procedures, Programs |
Sections I, N, and O are the primary extraction targets.
pip install -r requirements.txt
export OPENAI_API_KEY="sk-..."
GPU acceleration is used by Seq2SeqExtractor (BioBART / ClinicalT5) and BioBERTRetriever for faster model inference and fine-tuning. The pipeline automatically detects CUDA and falls back to CPU — no code changes needed.
python scripts/check_gpu.py
GPU available:
torch_version: 2.x.x+cu118
cuda_available: True
cuda_device_count: 1
cuda_device_0: NVIDIA GeForce RTX 3080 (compute_capability=8.6)
gpu_tensor_op: PASS
GPU not available:
cuda_available: False
No CUDA runtime/device detected. Workloads will run on CPU.
Script exit codes: 0 = pass, 1 = torch import error, 2 = no CUDA, 3 = tensor op failed.
from src.pipeline import ExtractionPipeline
pipeline = ExtractionPipeline(
source="data/discharge.csv/discharge.csv",
openai_api_key="sk-...",
sections=["I", "N", "O"],
output_dir="output",
output_format="json",
sample_size=5,
)
assessments = pipeline.run()
from src.data_preprocessor.data_loader import MIMICDischargeLoader
from src.mds_schema import MDSSchema
from src.extractor.seq2seq_extractor import Seq2SeqExtractor
from src.mapper.seq2seq_mapper import Seq2SeqMapper
loader = MIMICDischargeLoader(source="data/discharge.csv/discharge.csv")
notes = loader.load()
schema = MDSSchema(section_ids=["I", "N", "O"])
extractor = Seq2SeqExtractor(schema=schema, model_name="GanjinZero/biobart-v2-base")
mapper = Seq2SeqMapper(schema=schema)
note = notes[0]
raw = extractor.extract(note.text, note_metadata=note.metadata)
assessment = mapper.map(note.note_id, note.subject_id, note.hadm_id, raw)
print(assessment.to_dict())
from src.data_preprocessor.data_loader import MIMICDischargeLoader
from src.data_preprocessor.rag_retriever import BioBERTRetriever
from src.mds_schema import MDSSchema
from src.extractor.extractor import LLMExtractor
from src.mapper.mapper import MDSMapper
loader = MIMICDischargeLoader(source="data/discharge.csv/discharge.csv")
notes = loader.load()
schema = MDSSchema(section_ids=["I", "N", "O"])
retriever = BioBERTRetriever(sections=["I", "N", "O"])
extractor = LLMExtractor(schema=schema, api_key="sk-...", preprocess_input=False)
mapper = MDSMapper(schema=schema)
note = notes[0]
context = retriever.build_rag_context(note.text, note.metadata)
raw = extractor.extract(context)
assessment = mapper.map(note.note_id, note.subject_id, note.hadm_id, raw)
print(assessment.to_dict())
pipeline = ExtractionPipeline(
source="data/discharge.csv/discharge.csv",
structured_sources={
"diagnoses": "data/diagnoses_icd.csv",
"prescriptions": "data/prescriptions.csv",
"procedures": "data/procedures_icd.csv",
},
openai_api_key="sk-...",
sections=["I", "N", "O"],
output_dir="output",
)
assessments = pipeline.run()
python src/pipeline.py --source data/discharge.csv/discharge.csv
Interactive mode prompts for a processing mode:
Select processing mode:
1. sample - run the initial sample only
2. sample-compare - run the sample and compare preprocessing methods
3. full - process the entire dataset
4. full-compare - process the entire dataset and compare preprocessing methods
Enter mode [1-4] (default 1):
Scripted / non-interactive:
# Sample with LLM
python src/pipeline.py --source data/discharge.csv/discharge.csv --mode sample
# Full dataset with preprocessing comparison
python src/pipeline.py --source data/discharge.csv/discharge.csv --mode full-compare
# Custom sample size
python src/pipeline.py --source data/discharge.csv/discharge.csv --mode sample --sample-size 20
Edit config/config.yaml to change settings without touching source code.
llm:
provider: "openai"
model: "gpt-3.5-turbo"
temperature: 0.0
max_tokens: 2048
data:
csv_path: "data/discharge.csv/discharge.csv"
preprocess:
enabled: true
output:
output_dir: "output"
format: "json"
| File | Contents |
|---|---|
output/mds_assessments.json | Full MDS assessment objects |
output/mds_ino_form_ready.json | Compact I/N/O codes + confidence scores |
output/mds_ino_preprocessing_comparison.json | Heuristic vs. LLM-evidence side-by-side |
output/mds_ino_preprocessing_diff_summary.json | Fields that differ between modes |
output/mds_ino_preprocessing_diff_summary.csv | Same, flattened for spreadsheet review |
# Recommended — uses the conda environment
.conda/python.exe -m pytest tests/ -v
# Or with system Python
pytest tests/ -v
All tests mock LLM and Hugging Face calls — no API key or GPU required.
This project is designed for use with the MIMIC IV clinical notes dataset, available from PhysioNet. Access requires a credentialled PhysioNet account and completion of CITI Program training.
The discharge table in MIMIC IV contains free-text discharge summaries. Structured tables (diagnoses_icd, prescriptions, procedures_icd) can be joined by hadm_id and passed to the pipeline via structured_sources.