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 環境需求

項目最低需求建議配置
GPU1x NVIDIA GPU (24GB)4x A100 (40/80GB)
CUDA11.7+12.1+
Python3.8+3.10
RAM32GB64GB+
磁碟~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無 LoRAShallow 不加 LoRA
--llm_use_lora FalseLLM 凍結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 的關係
R2GenGPTFrozen LLM + Visual Alignment本專案:最低參數、最快訓練
LLM-RG4GPT-4V/LLaVA 做 RRG用更大 LLM,品質更高但成本也更高
RadFact放射報告事實性 NLI 評估可作為 R2GenGPT 的下游品質驗證器
FORTEFaithfulness-Oriented RRG強調事實一致性,可借鑑其 loss 設計

5.2 Synthetic Data Generation (SDG; 合成資料生成) 角度

R2GenGPT 可直接用於 大規模合成放射報告生成

  1. 報告增量 (Report Augmentation):對既有影像生成多版本報告,擴增訓練資料
  2. 風格遷移 (Style Transfer):用不同 prompt 生成同一影像的不同寫法
  3. 缺失報告補齊:對有影像但缺報告的案例自動補齊
  4. 教學資料生成:為放射科住院醫師生成練習用報告範本

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 EncoderSwin-BaseBiomedCLIP, RadImageNet ViT, CheXNet需調整 num_features 維度
LLMLLaMA-2-7B-ChatMistral-7B, Qwen-7B, BioMedLM需換 Tokenizer + 調 hidden_size
ProjectionSingle LinearMLP (2-layer), Q-Former需修改 llama_proj
LoRAPEFT r=16QLoRA (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.pyclean_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 微調。