R2GenGPT 完整教學
Repository: https://github.com/wang-zhanyu/R2GenGPT Stars: 125 | Tags: radiology, report-generation, LLM License: BSD 3-Clause | Language: Python | Last Updated: 2025-05-18
2. 核心架構
2.1 整體管線
flowchart TB
subgraph Input["輸入層"]
CXR["Chest X-Ray Image
(胸部 X 光影像)"]
end
subgraph VE["Vision Encoder (視覺編碼器)"]
SWIN["Swin-Base Transformer
patch4-window7-224"]
LORA_V["LoRA Adapter
(Delta 模式啟用)"]
SWIN --> LORA_V
end
subgraph AL["Alignment Layer (對齊層)"]
PROJ["Linear Projection
Swin hidden → LLaMA hidden"]
LN["LayerNorm
(層正規化)"]
PROJ --> LN
end
subgraph PW["Prompt Wrapping (提示包裝)"]
PROMPT["'Human: <Img>{img_embeds}</Img>
Generate a comprehensive...
Assistant:'"]
end
subgraph LLM["Frozen LLM (凍結語言模型)"]
EMBED["Embedding Layer
(嵌入層)"]
LLAMA["LLaMA-2-7B-Chat"]
LORA_L["LoRA Adapter
(Deep 模式啟用)"]
EMBED --> LLAMA
LLAMA --> LORA_L
end
subgraph Output["輸出層"]
REPORT["Radiology Report
(放射報告文字)"]
end
CXR --> SWIN
LORA_V --> PROJ
LN --> PROMPT
PROMPT --> EMBED
LORA_L --> REPORT
style Input fill:#e1f5fe,stroke:#0288d1
style VE fill:#f3e5f5,stroke:#7b1fa2
style AL fill:#fff3e0,stroke:#ef6c00
style PW fill:#e8f5e9,stroke:#2e7d32
style LLM fill:#fce4ec,stroke:#c62828
style Output fill:#e0f2f1,stroke:#00695c
2.2 資料流詳解
sequenceDiagram
participant IMG as X-Ray Image
participant SWIN as Swin Encoder
participant PROJ as Linear Projection
participant LN as LayerNorm
participant WRAP as Prompt Wrapper
participant EMB as LLaMA Embedding
participant LLM as LLaMA-2-7B
IMG->>SWIN: pixel_values [B, 3, 224, 224]
SWIN->>SWIN: Hierarchical feature extraction
SWIN-->>PROJ: last_hidden_state [B, 49, 1024]
Note over SWIN,PROJ: 或 pooler_output [B, 1, 1024]
(global_only=True)
PROJ->>LN: projected [B, 49, 4096]
LN->>WRAP: normalized img_embeds
WRAP->>WRAP: Concatenate: [BOS] + p_before + img_embeds + p_after
WRAP->>EMB: text tokens → embeddings
EMB->>LLM: [bos_embeds, img_embeds, text_embeds]
LLM->>LLM: Autoregressive generation
LLM-->>IMG: Generated report text
2.3 專案檔案結構
1R2GenGPT/
2├── train.py # 訓練/驗證/測試主入口
3├── configs/
4│ └── config.py # Argparse 超參數定義 (~60 個參數)
5├── models/
6│ └── R2GenGPT.py # 核心模型 (PyTorch Lightning Module)
7├── dataset/
8│ ├── data_module.py # Lightning DataModule
9│ └── data_helper.py # 資料解析 + 報告清洗
10├── evalcap/ # 評估指標 (BLEU/ROUGE/METEOR/CIDEr)
11│ ├── bleu/
12│ ├── cider/
13│ ├── meteor/
14│ ├── rouge/
15│ └── tokenizer/
16├── lightning_tools/
17│ ├── callbacks.py # ModelCheckpoint + TensorBoard Logger
18│ └── optim.py # 最佳化設定
19├── scripts/
20│ ├── 1-*.sh ~ 3-*.sh # IU X-Ray 訓練/測試腳本
21│ └── 4-*.sh ~ 6-*.sh # MIMIC-CXR 訓練/測試腳本
22├── data/ # (需自行下載)
23│ ├── iu_xray/
24│ └── mimic_cxr/
25└── requirements.txt
3. 安裝與設定
3.1 環境需求
| 項目 | 最低需求 | 建議配置 |
|---|---|---|
| GPU | 1x NVIDIA GPU (24GB) | 4x A100 (40/80GB) |
| CUDA | 11.7+ | 12.1+ |
| Python | 3.8+ | 3.10 |
| RAM | 32GB | 64GB+ |
| 磁碟 | ~50GB (模型+資料) | ~100GB |
3.2 安裝步驟
1# 1. Clone 專案
2git clone https://github.com/wang-zhanyu/R2GenGPT.git
3cd R2GenGPT
4
5# 2. 建立虛擬環境 (推薦使用 uv)
6uv venv .venv --python 3.10
7source .venv/bin/activate
8
9# 3. 安裝依賴
10uv pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
11uv pip install -r requirements.txt
12
13# requirements.txt 包含:
14# torch, peft, tensorboardX, transformers==4.30.2,
15# lightning==2.0.5, Pillow, numpy, gradio
3.3 資料集準備
IU X-Ray
1# 從 Google Drive 下載 annotation + images
2# https://drive.google.com/file/d/1c0BXEuDy8Cmm2jfN0YYGkQxFZd2ZIoLg/view
3mkdir -p data/iu_xray
4# 解壓後放入 data/iu_xray/
MIMIC-CXR (需 PhysioNet 帳號)
1# 1. 下載預處理 annotation (作者提供)
2# https://drive.google.com/file/d/14689ztodTtrQJYs--ihB_hgsPMMNHX-H/view
3mkdir -p data/mimic_cxr
4
5# 2. 下載影像 (需先申請 PhysioNet credentialed access)
6# https://physionet.org/content/mimic-cxr-jpg/2.0.0/
7# 解壓後影像放入 data/mimic_cxr/images/
Annotation JSON (註解 JSON) 格式:
1{
2 "train": [
3 {
4 "id": "s50414267",
5 "report": "The lungs are clear. No pleural effusion or pneumothorax...",
6 "image_path": ["p10/p10000032/s50414267/02aa804e-bde0afdd-112c0b34-7bc16630-4e384014.jpg"]
7 }
8 ],
9 "val": [...],
10 "test": [...]
11}
3.4 模型權重
1# LLaMA-2-7B-Chat (需 Meta 授權)
2# 方法 A:Hugging Face Hub
3huggingface-cli download meta-llama/Llama-2-7b-chat-hf --local-dir ./models/llama2-7b-chat
4
5# 方法 B:設定 HF_HOME 快取路徑,讓 transformers 自動下載
6export HF_HOME=~/.cache/huggingface
7
8# Swin-Base (自動從 HuggingFace 下載)
9# model name: microsoft/swin-base-patch4-window7-224
10
11# 預訓練 Delta Checkpoint (作者提供)
12# https://drive.google.com/drive/folders/1ywEITWfYIAAYy0VY1IZ24Ec_GoNmkqIY
13mkdir -p save/mimic_cxr/pretrained
14# 下載後放入 save/mimic_cxr/pretrained/
4. 使用方式與程式碼範例
4.1 範例一:Shallow Alignment 訓練 (最少資源)
Shallow Alignment (淺層對齊) 只訓練 Linear Projection + LayerNorm,Vision Encoder 與 LLM 全部凍結。適合快速驗證管線是否正確運作。
1#!/bin/bash
2# === Shallow Alignment on MIMIC-CXR ===
3
4dataset="mimic_cxr"
5annotation="./data/mimic_cxr/annotation.json"
6base_dir="./data/mimic_cxr/images"
7savepath="./save/$dataset/v1_shallow"
8
9mkdir -p "$savepath"
10
11python -u train.py \
12 --dataset ${dataset} \
13 --annotation ${annotation} \
14 --base_dir ${base_dir} \
15 --batch_size 8 \
16 --val_batch_size 12 \
17 --freeze_vm True \
18 --vis_use_lora False \
19 --llm_use_lora False \
20 --savedmodel_path ${savepath} \
21 --learning_rate 1e-4 \
22 --gradient_clip_val 1 \
23 --max_length 100 \
24 --min_new_tokens 80 \
25 --max_new_tokens 120 \
26 --repetition_penalty 2.0 \
27 --length_penalty 2.0 \
28 --num_workers 8 \
29 --devices 1 \
30 --max_epochs 5 \
31 --limit_val_batches 0.5 \
32 --val_check_interval 0.5 \
33 --num_sanity_val_steps 2 \
34 2>&1 | tee -a ${savepath}/log.txt
關鍵參數說明:
| 參數 | 數值 | 說明 |
|---|---|---|
--freeze_vm True | 凍結 Swin | 不更新視覺編碼器 |
--vis_use_lora False | 無 LoRA | Shallow 不加 LoRA |
--llm_use_lora False | LLM 凍結 | LLaMA 參數不動 |
--devices 1 | 單卡 | 可用 1 張 GPU 即可跑 |
--repetition_penalty 2.0 | 抑制重複 | 避免生成重複句子 |
4.2 範例二:Delta Alignment 訓練 (推薦平衡方案)
Delta Alignment (差量對齊) 在 Vision Encoder 加入 LoRA (Low-Rank Adaptation; 低秩適配器),讓視覺表示微調以適應醫學影像領域,同時保持 LLM 凍結。
1#!/bin/bash
2# === Delta Alignment on MIMIC-CXR (推薦) ===
3
4dataset="mimic_cxr"
5annotation="data/mimic_cxr/my_mimic_anno.json"
6base_dir="./data/mimic_cxr/images"
7savepath="./save/$dataset/v1_delta"
8
9mkdir -p "$savepath"
10
11python -u train.py \
12 --dataset ${dataset} \
13 --annotation ${annotation} \
14 --base_dir ${base_dir} \
15 --batch_size 8 \
16 --val_batch_size 16 \
17 --freeze_vm True \
18 --vis_use_lora True \
19 --vis_r 16 \
20 --vis_alpha 16 \
21 --llm_use_lora False \
22 --savedmodel_path ${savepath} \
23 --max_length 100 \
24 --min_new_tokens 80 \
25 --max_new_tokens 120 \
26 --repetition_penalty 2.0 \
27 --length_penalty 2.0 \
28 --num_workers 16 \
29 --devices 4 \
30 --max_epochs 5 \
31 --limit_val_batches 0.5 \
32 --val_check_interval 0.5 \
33 --num_sanity_val_steps 2 \
34 2>&1 | tee -a ${savepath}/log.txt
Delta 模式下可訓練的元件:
1# 模型內部結構 (摘自 models/R2GenGPT.py)
2
3# 1. Vision LoRA — 在 Swin 的 query/value 注入低秩矩陣
4peft_config_visual = LoraConfig(
5 r=16, # LoRA rank (低秩維度)
6 lora_alpha=16, # Scaling factor (縮放因子)
7 target_modules=["query", "value"], # 注入目標模組
8 lora_dropout=0.1,
9 bias="none",
10 modules_to_save=["classifier"],
11)
12self.visual_encoder = get_peft_model(self.visual_encoder, peft_config_visual)
13
14# 2. Projection Layer — Swin hidden_dim → LLaMA hidden_dim
15self.llama_proj = nn.Linear(
16 self.visual_encoder.num_features, # 1024 (Swin-Base)
17 self.llama_model.config.hidden_size # 4096 (LLaMA-7B)
18)
19
20# 3. Layer Normalization
21self.layer_norm = nn.LayerNorm(self.llama_model.config.hidden_size)
4.3 範例三:使用預訓練 Checkpoint 測試
1#!/bin/bash
2# === 用作者提供的 Delta Checkpoint 在 MIMIC-CXR 測試集跑推論 ===
3
4dataset="mimic_cxr"
5annotation="data/mimic_cxr/my_mimic_anno.json"
6base_dir="./data/mimic_cxr/images"
7savepath="./save/$dataset/pretrained_delta_test"
8
9mkdir -p "$savepath"
10
11python -u train.py \
12 --test \
13 --dataset ${dataset} \
14 --annotation ${annotation} \
15 --base_dir ${base_dir} \
16 --test_batch_size 16 \
17 --freeze_vm True \
18 --vis_use_lora True \
19 --vis_r 16 \
20 --vis_alpha 16 \
21 --delta_file ./save/mimic_cxr/pretrained/delta_checkpoint.pth \
22 --savedmodel_path ${savepath} \
23 --max_length 100 \
24 --min_new_tokens 80 \
25 --max_new_tokens 120 \
26 --beam_size 3 \
27 --repetition_penalty 2.0 \
28 --length_penalty 2.0 \
29 --num_workers 8 \
30 --devices 1 \
31 2>&1 | tee -a ${savepath}/test_log.txt
32
33# 測試結果輸出到:
34# ./save/mimic_cxr/pretrained_delta_test/result/test_result.json (生成報告)
35# ./save/mimic_cxr/pretrained_delta_test/result/test_refs.json (參考報告)
報告清洗邏輯 (Report Cleaning):
1# dataset/data_helper.py 的 clean_report 方法
2# MIMIC-CXR 報告清洗流程:
3# 1. 移除換行、多餘空白、多餘句點
4# 2. 移除編號列表標記 ("1. ", ". 2. ", etc.)
5# 3. 冒號前加空格
6# 4. 移除特殊標點 (保留字母數字空格句點冒號)
7# 5. 全部轉小寫
8# 6. 以 " . " 重新 join 句子
解碼輸出 (Decoding):
1# 模型推論時的生成控制參數
2outputs = self.llama_model.generate(
3 inputs_embeds=inputs_embeds,
4 num_beams=3, # Beam Search 寬度
5 do_sample=False, # 不做隨機取樣 (deterministic)
6 min_new_tokens=80, # 最少生成 80 tokens
7 max_new_tokens=120, # 最多生成 120 tokens
8 repetition_penalty=2.0, # 重複懲罰 (抑制冗餘)
9 length_penalty=2.0, # 長度懲罰 (鼓勵較長輸出)
10 temperature=0, # 溫度 0 = greedy
11)
5. 在醫療 LLM / 文本 SDG 生態系中的定位
5.1 Domain 6-C:Radiology Report Generation
R2GenGPT 屬於 Bio-SDG 生態系 Domain 6 的 Sub-domain C (放射報告生成),與以下專案形成互補:
| 專案 | 核心方法 | 與 R2GenGPT 的關係 |
|---|---|---|
| R2GenGPT | Frozen LLM + Visual Alignment | 本專案:最低參數、最快訓練 |
| LLM-RG4 | GPT-4V/LLaVA 做 RRG | 用更大 LLM,品質更高但成本也更高 |
| RadFact | 放射報告事實性 NLI 評估 | 可作為 R2GenGPT 的下游品質驗證器 |
| FORTE | Faithfulness-Oriented RRG | 強調事實一致性,可借鑑其 loss 設計 |
5.2 Synthetic Data Generation (SDG; 合成資料生成) 角度
R2GenGPT 可直接用於 大規模合成放射報告生成:
- 報告增量 (Report Augmentation):對既有影像生成多版本報告,擴增訓練資料
- 風格遷移 (Style Transfer):用不同 prompt 生成同一影像的不同寫法
- 缺失報告補齊:對有影像但缺報告的案例自動補齊
- 教學資料生成:為放射科住院醫師生成練習用報告範本
5.3 在 AIKT Pipeline (AI Knowledge Template 管線) 中的角色
1影像管線 (Imaging Pipeline)
2 ├── 前處理:CXR 標準化 / DICOM → JPEG
3 ├── 分析:分類 / 偵測 / 分割
4 ├── 報告生成:★ R2GenGPT ★
5 └── 品質驗證:RadFact NLI 驗證
6
7文本 SDG 管線 (Text SDG Pipeline)
8 ├── 原始報告 → 增量/風格變換 → 合成報告庫
9 ├── 合成報告 → Fine-tune Domain LLM (MedicalGPT / BioGPT)
10 └── Domain LLM → 臨床筆記 / EHR 敘述 / 法規文件 SDG
5.4 Blue Ocean (藍海機會)
| 機會領域 | 說明 | 可行性 |
|---|---|---|
| 病理報告生成 | 替換 Swin 為病理 ViT,適配 WSI (Whole Slide Image) | 高 — 架構可直接遷移 |
| 多語言報告 | 替換 LLaMA-2 為多語言 LLM (如 Qwen-7B) | 中高 — 只需換 LLM + 重新對齊 |
| 結構化輸出 | 生成 FHIR-compatible JSON 而非自由文字 | 中 — 需 prompt engineering |
| 藥物安全報告 | CXR → 不良反應描述,用於 pre-IND 安全報告 | 中 — 需 domain annotation |
6. 與其他工具的整合
6.1 與 RadFact 串接:報告品質驗證
1# 假設已有 R2GenGPT 生成的報告和 ground truth
2import json
3
4# 1. 載入 R2GenGPT 輸出
5with open("save/mimic_cxr/v1_delta/result/test_result.json") as f:
6 generated = json.load(f)
7
8with open("save/mimic_cxr/v1_delta/result/test_refs.json") as f:
9 references = json.load(f)
10
11# 2. 轉換為 RadFact 輸入格式
12radfact_input = []
13for sample_id in generated:
14 radfact_input.append({
15 "id": sample_id,
16 "hypothesis": generated[sample_id][0], # 生成報告
17 "reference": references[sample_id][0], # 參考報告
18 })
19
20# 3. 用 RadFact 做 NLI-based factual consistency 評估
21# 參見 RadFact 專案:microsoft/RadFact
6.2 與 MedicalGPT 串接:報告做為 SFT 資料
1# R2GenGPT 生成的報告可作為 MedicalGPT SFT 階段的訓練資料
2
3# 1. 從 R2GenGPT 批量生成報告
4# 2. 人工 / RadFact 過濾低品質報告
5# 3. 轉換為 instruction-following 格式
6
7sft_sample = {
8 "instruction": "Based on the following chest X-ray findings, "
9 "write a complete radiology report.",
10 "input": "Bilateral clear lung fields, normal cardiac silhouette, "
11 "no pleural effusion.",
12 "output": generated_report # R2GenGPT 生成
13}
14
15# 4. 匯入 MedicalGPT 的 SFT 訓練管線
6.3 與 AIKT Paper-Search / Paper-QA-Lite 整合
1# 在 AIKT 管線中搜尋相關文獻
2paper: "radiology report generation frozen LLM" year=2023-2025 n=10
3
4# 用 paper-qa-lite 對 R2GenGPT 論文 + 相關論文做 RAG 問答
5pq: "What is the BLEU-4 improvement of delta alignment over shallow alignment?"
6.4 替換元件的可能性
| 元件 | 原始 | 可替換為 | 注意事項 |
|---|---|---|---|
| Vision Encoder | Swin-Base | BiomedCLIP, RadImageNet ViT, CheXNet | 需調整 num_features 維度 |
| LLM | LLaMA-2-7B-Chat | Mistral-7B, Qwen-7B, BioMedLM | 需換 Tokenizer + 調 hidden_size |
| Projection | Single Linear | MLP (2-layer), Q-Former | 需修改 llama_proj |
| LoRA | PEFT r=16 | QLoRA (4-bit), IA3, Prefix-Tuning | 需修改 peft_config |
7. 優缺點分析
7.1 優點
| 面向 | 說明 |
|---|---|
| 極低訓練成本 | Shallow Alignment 只需訓練 ~4M 參數,單卡 24GB 可跑 |
| 模組化架構 | Vision Encoder / LLM / Alignment Layer 三層各自獨立,任一可替換 |
| PyTorch Lightning 整合 | 分散式訓練 (DDP)、混合精度 (bf16-mixed)、自動 Checkpoint 全內建 |
| 完整評估管線 | 內建 BLEU/ROUGE/METEOR/CIDEr 四大指標 |
| LoRA 支援 | Vision 和 LLM 端都支援 LoRA,漸進式提升不同層的可訓練程度 |
| 乾淨的 Prompt 設計 | MiniGPT-4 風格的 <Img>...</Img> prompt template,易於擴展 |
7.2 缺點
| 面向 | 說明 | 建議緩解方式 |
|---|---|---|
| 資料集受限 | 只支援 IU X-Ray 和 MIMIC-CXR 兩個資料集 | 自行擴展 data_helper.py 的 clean_report |
| 單一 Prompt | 固定 prompt 無法客製化報告風格 | 修改 self.prompt 或加入 prompt pool |
| Transformers 版本鎖定 | 鎖定 transformers==4.30.2,較舊 | 升級需測試 LLaMA tokenizer 相容性 |
| 無 Gradio Demo 腳本 | requirements.txt 含 Gradio 但無 demo 入口 | 需自行撰寫推論 + UI 腳本 |
| 評估偏重 NLG 指標 | 缺乏臨床事實正確性評估 (Clinical Efficacy) | 串接 RadFact 或 CheXbert labeler |
| 單一影像輸入 | 雖支援多影像 path,但 encode_img 做 mean pooling 可能丟失資訊 | 可改用 cross-attention 或 concatenation |
| 無 Inference-only 腳本 | 缺少單張影像推論的簡潔入口 | 需包裝 validation_step 邏輯 |
7.3 適用場景總結
| 場景 | 適用度 | 原因 |
|---|---|---|
| 胸部 X 光報告自動生成 | 高 | 核心設計目標 |
| 合成放射報告做為 SDG 資料來源 | 高 | 批量推論 + 多樣化 prompt |
| 其他模態醫學影像報告 (MRI/CT/病理) | 中 | 需替換 Vision Encoder + 資料集 |
| 即時臨床輔助 (Real-time Clinical) | 低 | LLaMA-7B 推論延遲較高 |
| 多語言報告 (中文/日文) | 中 | 需替換為多語言 LLM |
總結:R2GenGPT 以「凍結兩端、只學中間」的極簡策略,在 Radiology Report Generation 領域提供了一條低成本、高品質的 baseline 路線。其模組化架構使其天然適合作為醫療文本 SDG 管線的影像-到-報告轉換器,產出的合成報告可進一步餵入 MedicalGPT / BioGPT 做 domain-specific LLM 微調。
Comments