RadFact 完整教學
Repository: https://github.com/microsoft/RadFact Stars: 98 | Fork: 12 | License: MIT Tags: radiology, evaluation, LLM, Microsoft 語言: Python | 最後更新: 2026-06-02
2. 核心架構
2.1 整體流程架構
flowchart TB
subgraph INPUT["輸入層"]
NAR["敘述性報告
(Narrative Text)"]
GND["定位報告
(Grounded Phrases + Boxes)"]
end
subgraph PREPROCESS["前處理層"]
R2P["Report-to-Phrases
報告拆句
(LLM: GPT-4)"]
NEG["Negative Filtering
負面發現過濾
(僅 CT)"]
end
subgraph CORE["核心評估層"]
direction TB
NLI_FWD["正向 NLI 驗證
Prediction → Ground Truth
(計算 Precision)"]
NLI_BWD["反向 NLI 驗證
Ground Truth → Prediction
(計算 Recall)"]
BOX["Bounding Box
空間蘊含計算
(Pixel Precision ≥ 0.5)"]
end
subgraph ENGINE["平行處理引擎"]
LLM_ENG["LLMEngine
多端點平行處理"]
CACHE["中間結果快取
(Batch Outputs)"]
SHARD["資料分片
(Speed Factor)"]
end
subgraph OUTPUT["輸出層"]
METRICS["RadFactScore
6 項指標"]
BOOT["Bootstrap CI
信賴區間"]
JSON["outputs.json
完整結果"]
end
NAR --> R2P
R2P --> NEG
NEG --> NLI_FWD
GND --> NLI_FWD
NLI_FWD --> BOX
NLI_BWD --> BOX
R2P --> NLI_BWD
GND --> NLI_BWD
NLI_FWD -.->|"透過"| LLM_ENG
NLI_BWD -.->|"透過"| LLM_ENG
LLM_ENG --> CACHE
LLM_ENG --> SHARD
BOX --> METRICS
METRICS --> BOOT
BOOT --> JSON
2.2 NLI 蘊含驗證流程
RadFact 的核心是雙向 NLI (Bidirectional NLI)。對每一對 (prediction, ground_truth),執行兩次蘊含驗證:
- 正向 (Forward):以 ground truth 為前提 (Premise),逐句判斷 prediction 中的每句是否被蘊含 → 得到 Precision 指標
- 反向 (Backward):以 prediction 為前提,逐句判斷 ground truth 中的每句是否被蘊含 → 得到 Recall 指標
每次蘊含判斷的輸出包含:
- entailment status:是否蘊含 (entailed / not entailed)
- evidence:來自前提的支持證據句
2.3 模組結構
1src/radfact/
2├── cli/ # 命令列介面
3│ ├── run_radfact.py # 主要執行入口
4│ ├── run_radfact_test_examples.py # 測試範例驗證
5│ └── run_report_to_phrases.py # 報告拆句工具
6├── data_utils/
7│ └── grounded_phrase_list.py # 定位短語資料結構
8├── llm_utils/
9│ ├── endpoint.py # LLM 端點管理
10│ ├── text_utils.py # 文字處理工具
11│ ├── prompt_tasks.py # Prompt 任務定義
12│ ├── engine/ # 平行處理引擎
13│ │ ├── engine.py # LLMEngine 核心
14│ │ ├── arguments.py # 引擎參數
15│ │ ├── data_subset.py # 資料分片
16│ │ ├── endpoint_utils.py # 端點工具
17│ │ └── redis_cache.py # Redis 快取
18│ ├── nli/ # NLI 蘊含驗證
19│ │ ├── processor.py # NLI 處理器
20│ │ ├── schema.py # NLI 資料結構
21│ │ └── prompts/ # Few-shot 範例
22│ │ ├── cxr/ # 胸部 X 光
23│ │ └── ct/ # 電腦斷層
24│ ├── report_to_phrases/ # 報告拆句
25│ │ ├── processor.py # 拆句處理器
26│ │ ├── schema.py # 拆句資料結構
27│ │ └── prompts/ # 拆句 Few-shot
28│ │ ├── cxr/
29│ │ └── ct/
30│ ├── negative_filtering/ # 負面發現過濾
31│ │ ├── processor.py
32│ │ └── prompts/ct/
33│ └── processor/ # 處理器抽象層
34│ ├── base_processor.py
35│ └── structured_processor.py
36├── metric/ # 指標計算
37│ ├── radfact.py # RadFactMetric 主類別
38│ ├── schema.py # RadFactScore 資料結構
39│ ├── box_metrics.py # 框體指標計算
40│ ├── bootstrapping.py # Bootstrap 信賴區間
41│ └── print_utils.py # 輸出格式化
42├── azure_utils/ # Azure 認證工具
43│ ├── auth.py
44│ └── bearer_token_provider.py
45└── paths.py # 路徑常數
2.4 設定管理
RadFact 使用 Hydra 進行設定管理,設定檔位於 configs/ 目錄:
| 設定檔 | 用途 | MAIRA-2 使用的 LLM |
|---|---|---|
radfact.yaml | NLI 蘊含驗證端點 | Llama-3-70B-Instruct |
report_to_phrases.yaml | 報告拆句端點 | GPT-4 |
negative_filtering.yaml | 負面發現過濾端點 | (自訂) |
endpoints/*.yaml | 端點類型模板 | AzureChatOpenAI / ChatOpenAI |
3. 安裝與設定
3.1 基本安裝
1# 方法一:pip 直接安裝
2git clone https://github.com/microsoft/RadFact.git
3cd RadFact
4pip install .
5
6# 方法二:使用 Makefile + conda(推薦用於開發)
7make miniconda
8make mamba
9make env
10conda activate radfact
建議:若使用 uv 管理 Python 環境:
1git clone https://github.com/microsoft/RadFact.git 2cd RadFact 3uv venv --python 3.10 4source .venv/bin/activate 5uv pip install .
3.2 LLM 端點設定
RadFact 需要存取 LLM API。支援兩種端點類型:
方式 A:ChatOpenAI(適用 Llama-3 等自建模型)
建立 configs/endpoints/my_endpoint.yaml:
1MY_LLAMA3_ENDPOINT:
2 type: "CHAT_OPENAI"
3 url: "https://your-model-endpoint.example.com/v1"
4 deployment_name: "llama3-70b"
5 api_key_env_var_name: "RADFACT_API_KEY"
6 speed_factor: 1.0
7 num_parallel_processes: 10
方式 B:AzureChatOpenAI(適用 Azure 上的 GPT 模型)
1MY_GPT4_ENDPOINT:
2 type: "AZURE_CHAT_OPENAI"
3 url: "https://your-azure-endpoint.openai.azure.com/"
4 deployment_name: "gpt-4"
5 api_key_env_var_name: "AZURE_OPENAI_KEY"
6 speed_factor: 1.0
7 num_parallel_processes: 5
3.3 認證方式
RadFact 支援三種認證方式(優先順序由高到低):
- 環境變數 API Key:
export RADFACT_API_KEY="your-key-here" - Azure Key Vault:透過
config.json(含subscription_id/resource_group/workspace_name)+ 端點設定中的keyvault_secret_name - Azure Token Provider:自動產生 Azure AD Token(僅限 AzureChatOpenAI 類型)
3.4 驗證安裝
1# 設定 API Key
2export API_KEY="your-api-key"
3
4# 執行測試範例,確認 NLI 蘊含驗證正常
5python src/radfact/cli/run_radfact_test_examples.py
此測試使用預設的 few-shot 範例驗證蘊含判斷行為。預期結果基於 Llama-3-70B-Instruct;如果使用不同的 LLM,結果可能有差異。
4. 使用方式與程式碼範例
4.1 範例一:評估非定位敘述報告 (Narrative Text)
這是最基礎的使用情境——比較 AI 生成的放射報告與標準報告,不涉及空間定位。
準備輸入 CSV(格式與 examples/findings_generation_examples.csv 相同):
1example_id,prediction,target
20,"The lungs are well expanded. Large nodular density in the right midlung field measuring 3.5 x 3.5 cm.","Ovoid density superimposed lung fields in the mid and lower lung fields measuring half to 3 cm."
31,"Patient is status post median sternotomy and CABG. Heart size and pulmonary vasculature are normal.","Patient is status post median sternotomy and CABG. The lungs are well aerated without focal consolidation."
執行評估:
1# 基本執行(含 Bootstrap 信賴區間,預設 500 次取樣)
2run_radfact \
3 --input_path examples/findings_generation_examples.csv \
4 --is_narrative_text \
5 --report_type cxr
6
7# 快速測試(關閉 Bootstrap)
8run_radfact \
9 --input_path examples/findings_generation_examples.csv \
10 --is_narrative_text \
11 --bootstrap_samples 0
12
13# CT 報告 + 過濾負面發現
14run_radfact \
15 --input_path my_ct_reports.csv \
16 --is_narrative_text \
17 --report_type ct \
18 --filter_negatives
輸出結構:
1outputs/radfact/run_20240814_075225/
2├── batch_outputs/ # 每批次中間結果
3│ ├── outputs_0_100.json
4│ └── outputs_100_200.json
5├── progress/ # 各端點處理進度
6├── skipped/ # 失敗跳過的樣本
7├── outputs.json # 最終完整結果
8├── progress.csv
9└── skipped.csv
4.2 範例二:評估定位報告 (Grounded Report)
若 AI 模型的輸出不僅包含文字描述,還帶有 Bounding Box 空間定位(如 MAIRA-2),可以使用定位模式進行更全面的評估。
準備輸入 JSON(格式與 examples/grounded_reporting_examples.json 相同):
1[
2 {
3 "prediction": [
4 {
5 "text": "Cardiac silhouette remains normal in size.",
6 "boxes": null
7 },
8 {
9 "text": "Moderate-sized area of airspace opacity in the left base has improved.",
10 "boxes": [
11 {"x_min": 0.535, "y_min": 0.355, "x_max": 0.865, "y_max": 0.725}
12 ]
13 }
14 ],
15 "target": [
16 {
17 "text": "Cardiac silhouette is normal in size.",
18 "boxes": null
19 },
20 {
21 "text": "Left basilar airspace opacity has improved since prior exam.",
22 "boxes": [
23 {"x_min": 0.520, "y_min": 0.340, "x_max": 0.880, "y_max": 0.740}
24 ]
25 }
26 ]
27 }
28]
執行評估:
1# 定位報告不需要 --is_narrative_text 旗標
2run_radfact \
3 --input_path examples/grounded_reporting_examples.json \
4 --report_type cxr \
5 --bootstrap_samples 500 \
6 --output_dir my_results/
此模式會計算完整的六項指標,包含 Grounding Precision/Recall 和 Spatial Precision/Recall。空間蘊含的判定標準為:候選框的像素精確度 (Pixel Precision) 相對於證據框聯集需 ≥ 0.5。
4.3 範例三:Python API 程式化使用
若要將 RadFact 整合到自己的評估管線 (Evaluation Pipeline) 中,可直接使用 Python API:
1from radfact.metric.radfact import RadFactMetric
2
3# 初始化 RadFactMetric
4metric = RadFactMetric(
5 is_narrative_text=True, # 輸入為敘述文字
6 report_type="cxr", # 報告類型: cxr 或 ct
7 filter_negatives=False, # 是否過濾負面發現
8)
9
10# 準備資料:字典格式 {study_id: report_text}
11generations = {
12 "study_001": "The lungs are clear. No pleural effusion. Heart size is normal.",
13 "study_002": "Right lower lobe consolidation. Small right pleural effusion.",
14}
15
16ground_truths = {
17 "study_001": "Clear lungs bilaterally. Normal cardiac silhouette. No effusion.",
18 "study_002": "Right basilar consolidation with associated pleural effusion.",
19}
20
21# 計算 RadFact 指標
22results = metric.compute(
23 generations=generations,
24 ground_truths=ground_truths,
25)
26
27# 取得彙總分數
28aggregate_score = results["aggregate_scores"]
29print(f"Logical Precision: {aggregate_score.logical_precision:.3f}")
30print(f"Logical Recall: {aggregate_score.logical_recall:.3f}")
31print(f"Logical F1: {aggregate_score.logical_f1:.3f}")
32
33# 取得每個樣本的詳細結果
34for sample_result in results["per_sample_results"]:
35 print(f"\nStudy: {sample_result.study_id}")
36 if sample_result.scores:
37 print(f" Precision: {sample_result.scores.logical_precision:.3f}")
38 print(f" Recall: {sample_result.scores.logical_recall:.3f}")
39 # 檢視候選句的蘊含細節
40 for phrase in sample_result.candidate_phrases:
41 print(f" [{phrase.status}] {phrase.text}")
42 for ev in phrase.evidence:
43 print(f" evidence: {ev.text}")
RadFactScore 資料結構 提供的屬性:
1from radfact.metric.schema import RadFactScore
2
3# RadFactScore 是 frozen dataclass
4score: RadFactScore
5score.logical_precision # float: 邏輯精確度
6score.logical_recall # float: 邏輯召回率
7score.logical_f1 # property: 邏輯 F1
8score.spatial_precision # float: 空間精確度
9score.spatial_recall # float: 空間召回率
10score.spatial_f1 # property: 空間 F1
11score.grounding_precision # float: 定位精確度
12score.grounding_recall # float: 定位召回率
13score.grounding_f1 # property: 定位 F1
14score.num_candidate_phrases # int: 候選句數量
15score.num_reference_phrases # int: 參考句數量
16score.num_candidate_phrases_with_boxes # int: 帶框候選句數量
17score.num_reference_phrases_with_boxes # int: 帶框參考句數量
5. 在醫療 LLM / 文本 SDG 生態系中的定位
5.1 Domain 6 生態系定位
在 Bio-SDG Domain 6「醫療 LLM 與文本 SDG」的生態系中,RadFact 佔據 評估層 (Evaluation Layer) 的關鍵位置:
| 子領域 | 角色 | 代表工具 |
|---|---|---|
| A. 醫療 LLM 訓練管線 | 訓練模型 | MedicalGPT, BioGPT, BioMedLM |
| B. LLM 驅動的 SDG 框架 | 產生合成資料 | distilabel, sdg_hub |
| C. 放射報告生成 | 生成 + 評估 | R2GenGPT (生成), RadFact (評估), LLM-RG4, FORTE |
| D. 醫療對話生成 | 產生對話資料 | GEML-MDG |
| E. 資源與教學 | 參考工具 | NeMo DataDesigner |
RadFact 在子領域 C 中扮演的是 品質守門員 (Quality Gatekeeper) 角色。當 R2GenGPT、LLM-RG4、FORTE 等工具產生合成放射報告時,RadFact 提供了超越傳統 NLP 指標的臨床語義評估能力。
5.2 為什麼需要 RadFact
傳統指標的侷限性:
| 指標 | 問題 |
|---|---|
| BLEU | 只看 n-gram 重疊,同義不同詞即低分 |
| ROUGE | 同上,且對長報告偏頗 |
| BERTScore | 捕捉部分語義,但無法處理放射學特有的邏輯關係 |
| CheXbert | 僅限 14 個預定義標籤,無法評估自由文本細節 |
RadFact 的優勢:
- 臨床語義等價判斷:「心臟大小正常」與「心臟輪廓未增大」被正確視為蘊含
- 雙向評估:同時捕捉「說錯了」(precision) 與「漏掉了」(recall)
- 定位評估:不只評文字,還能評估空間標註的正確性
- 可解釋性:每句都有蘊含狀態與證據鏈,便於錯誤分析
5.3 合成資料品質評估的藍海
RadFact 目前聚焦於 CXR 與 CT 報告,但其核心方法——LLM-based 雙向蘊含驗證——可以延伸到更廣泛的醫療文本 SDG 品質評估:
- 合成臨床筆記 (Synthetic Clinical Notes):判斷合成病歷與真實範本的臨床資訊一致性
- 合成 EHR 敘述 (Synthetic EHR Narratives):評估合成電子病歷的完整性與正確性
- 法規文件 (Regulatory Documents):在 pre-IND 流程中,評估 AI 輔助生成的 CMC (Chemistry, Manufacturing, and Controls) 或非臨床摘要文件的事實一致性
- 病理報告 (Pathology Reports):將 few-shot 範例替換為病理學領域的蘊含範例
6. 與其他工具的整合
6.1 與放射報告生成工具的整合
R2GenGPT + RadFact 評估迴圈:
1# 1. 用 R2GenGPT 生成報告
2python generate_reports.py \
3 --model r2gengpt \
4 --images test_images/ \
5 --output generated_reports.csv
6
7# 2. 用 RadFact 評估生成品質
8run_radfact \
9 --input_path generated_reports.csv \
10 --is_narrative_text \
11 --report_type cxr \
12 --bootstrap_samples 500
LLM-RG4 / FORTE + RadFact:相同模式——將生成工具的輸出轉換為 RadFact 的 CSV 或 JSON 輸入格式即可。
6.2 與 AIKT Pipeline 的整合
在 AI-Knowledge Template 管線中,RadFact 可以作為品質檢查節點:
1paper-search → 收集放射報告生成論文
2 ↓
3paper-qa-lite → 萃取方法論
4 ↓
5RadFact → 評估複製實驗的報告品質
6 ↓
7quarkdown → 產出品質報告 HTML
6.3 與 distilabel / SDG 框架的整合
當使用 distilabel 等 SDG 框架產生合成放射報告時,RadFact 可以作為過濾器 (Filter) 或品質指標 (Quality Signal):
1# 概念範例:將 RadFact 整合到 SDG 品質過濾
2from radfact.metric.radfact import RadFactMetric
3
4metric = RadFactMetric(is_narrative_text=True, report_type="cxr")
5
6def filter_high_quality_synthetic(
7 synthetic_reports: dict,
8 reference_reports: dict,
9 threshold: float = 0.7
10) -> dict:
11 """只保留 RadFact 邏輯精確度超過門檻的合成報告"""
12 results = metric.compute(
13 generations=synthetic_reports,
14 ground_truths=reference_reports,
15 )
16 filtered = {}
17 for sample in results["per_sample_results"]:
18 if sample.scores and sample.scores.logical_precision >= threshold:
19 filtered[sample.study_id] = synthetic_reports[sample.study_id]
20 return filtered
6.4 前置處理:獨立使用報告拆句工具
RadFact 的報告拆句功能可以獨立使用,適合將長篇敘述報告拆解為結構化句子:
1# 將 CXR 報告拆成個別句子
2run_report_to_phrases dataset.csv_path=my_reports.csv
這在訓練資料前處理、句子級標註、或 NER (Named Entity Recognition; 命名實體辨識) 管線中都很有用。
7. 優缺點分析
7.1 優點
| 面向 | 優點 |
|---|---|
| 臨床語義理解 | 透過 LLM NLI 捕捉臨床等價表述,遠超文字匹配指標 |
| 可解釋性 | 每句都有蘊含狀態 + 證據句,便於錯誤分析與模型除錯 |
| Grounding 支援 | 業界少見同時評估文字與空間定位的放射報告指標 |
| 工程品質 | Microsoft 標準:Hydra 設定管理、平行處理、中間快取、Bootstrap CI |
| 開放授權 | MIT License,可自由商用、修改、整合 |
| 多報告類型 | 支援 CXR 與 CT,且架構允許擴展到其他影像模態 |
| 容錯機制 | LLM 呼叫失敗時預設標記為 not-entailed,並記錄到 skipped 目錄 |
7.2 缺點與限制
| 面向 | 限制 |
|---|---|
| LLM 依賴 | 每次評估都需呼叫 LLM API,有成本且不可完全離線運行 |
| 一致性問題 | 不同 LLM 後端會產生不同結果,cross-study 比較需統一模型 |
| 報告類型有限 | 目前僅支援 CXR 與 CT,MRI / PET / Ultrasound 等需自行擴展 few-shot |
| 負面過濾限 CT | Negative Filtering 功能目前僅支援 CT 報告 |
| Few-shot 品質瓶頸 | NLI few-shot 範例來自私有資料集 (USMix),無法檢視或客製化原始標註邏輯 |
| 延遲與成本 | 大量樣本評估需要長時間 LLM API 呼叫,適合離線批次評估而非即時評分 |
| 無中文支援 | 目前 prompts 與 few-shot 均為英文,直接用於中文放射報告需自行改造 |
7.3 適用場景建議
| 場景 | 建議 |
|---|---|
| 放射報告生成模型開發 | 強烈推薦作為主要評估指標 |
| 合成放射報告品質過濾 | 推薦作為品質守門員 |
| 快速迭代的模型訓練 | 考慮搭配 CheXbert 做粗篩,RadFact 做精評 |
| 即時線上評估 | 不建議(API 延遲與成本) |
| 非放射領域報告 | 需自行替換 few-shot 範例與 system prompt |
| pre-IND 法規文件品質 | 概念可行但需大量客製化 |
7.4 與同類工具比較
| 指標工具 | 是否需 LLM | 語義理解 | Grounding | 可解釋性 | 成本 |
|---|---|---|---|---|---|
| RadFact | 是 | 強 | 支援 | 高 | 高 |
| CheXbert | 否 | 中 | 不支援 | 中 | 低 |
| BERTScore | 否 | 中 | 不支援 | 低 | 低 |
| BLEU/ROUGE | 否 | 弱 | 不支援 | 低 | 零 |
| GREEN (Liu et al.) | 是 | 強 | 不支援 | 中 | 高 |
一句話總結:RadFact 是目前最全面的 LLM-based 放射報告評估框架,透過雙向 NLI 蘊含驗證提供六項涵蓋邏輯 / 空間 / 定位的精確度與召回率指標,適合作為放射報告生成與合成資料品質控制的標準評估工具。
Comments