Repository: https://github.com/snap-stanford/KGReasoning Stars: 312 | Forks: 63 | Language: Python | License: MIT Topics: knowledge-graph, knowledge-base, embedding, reasoning


1. 專案概覽 (Project Overview)

1.1 專案背景與研究團隊

KGReasoning 是史丹佛大學 SNAP (Stanford Network Analysis Platform; 史丹佛網路分析平台) 實驗室釋出的官方程式碼庫,核心作者為 Hongyu Ren 與 Jure Leskovec 教授。這個 repo 是論文《Beta Embeddings for Multi-Hop Logical Reasoning in Knowledge Graphs (BetaE; Beta 嵌入式多跳邏輯推理)》(NeurIPS 2020) 的官方 PyTorch 實作,同時也整合了同系列另外兩個經典模型 —— Query2box (2020) 與 GQE (Graph Query Embedding; 圖查詢嵌入, 2018) —— 讓使用者能在同一套框架下比較三種 knowledge graph (KG; 知識圖譜) 多跳推理方法。

SNAP 實驗室長期專注於大規模圖形資料的表示學習 (representation learning; 表徵學習),KGReasoning 屬於其「query embedding (查詢嵌入)」研究脈絡的核心產出,與同實驗室的 OGB (Open Graph Benchmark; 開放圖形基準)、GraphSAGE、PinSAGE 等專案共享研究哲學:用簡潔、可重現的程式碼實作發表在頂級會議 (NeurIPS/ICLR) 的演算法。

1.2 解決什麼問題、為什麼重要

傳統的 knowledge graph completion (KGC; 知識圖譜補全) 只回答「單跳」問題,例如:「誰是《駭客任務》的導演?」對應圖上的一條邊 (駭客任務, 導演, ?)。但真實世界的問題往往是多跳且帶邏輯運算子的複雜查詢 (complex logical query; 複雜邏輯查詢),例如:

「找出曾與『克里斯多福·諾蘭』合作過,但『沒有』得過奧斯卡獎的演員,這些演員還演過哪些『科幻片』?」

這種查詢同時包含:

  • 投影 (projection; p):沿著關係邊走一步(誰演過諾蘭的電影?)
  • 交集 (intersection; i):多條件同時成立(既演過諾蘭電影、又演過科幻片)
  • 否定 (negation; n):排除某條件(沒得過奧斯卡)
  • 聯集 (union; u):多選一成立即可

若要在真實 KG(通常有數百萬節點、且不完整——存在遺漏的邊)上直接用符號式圖遍歷 (graph traversal; 圖遍歷) 回答這類查詢,會面臨兩大困境:

  1. 組合爆炸:多跳查詢的搜尋空間隨跳數指數成長。
  2. 不完整性:KG 本身缺邊,符號式方法答不出「隱含但未直接記錄」的答案。

KGReasoning 的核心貢献是把「邏輯查詢」轉換成「嵌入空間中的幾何運算」——每個實體是空間中的一個點/區域,每個邏輯運算子(投影/交集/聯集/否定)對應一個可學習的神經網路運算子。這樣一來,回答查詢就變成「在嵌入空間做幾何運算 + 最近鄰搜尋」,既能處理不完整的 KG(透過泛化能力補全隱含答案),複雜度也不再隨跳數爆炸(每一跳只是一次向量運算)。

1.3 在 snap-stanford 生態系中的定位

KGReasoning 是 SNAP query-embedding 系列研究的「集大成」版本:


flowchart LR
    A["GQE (2018)
幾何:向量
邏輯:僅交集"] --> B["Query2box (2020)
幾何:超矩形盒子
邏輯:交集"] B --> C["BetaE (2020)
幾何:Beta 分布
邏輯:交集+聯集+否定"] C --> D["KGReasoning repo
統一框架整合三者"] D --> E["下游生態
SMORE / ULTRA 等
後續 SNAP 查詢嵌入研究"]

此 repo 本身不含後續模型(如 SMORE 大規模訓練框架、或更晚期的 pre-training 式 KG 推理模型),但作為「baseline 官方實作」被大量後續論文引用與比較,是進入這個研究子領域的標準入口。

1.4 相關論文引用

1@inproceedings{ren2020beta,
2 title={Beta Embeddings for Multi-Hop Logical Reasoning in Knowledge Graphs},
3 author={Hongyu Ren and Jure Leskovec},
4 booktitle={Neural Information Processing Systems},
5 year={2020}
6}

延伸論文(repo 中實作但另外發表):

  • Query2box: Query2box: Reasoning over Knowledge Graphs in Vector Space Using Box Embeddings (arXiv:2002.05969)
  • GQE: Embedding Logical Queries on Knowledge Graphs (arXiv:1806.01445)

2. 核心架構 (Core Architecture)

2.1 系統架構圖

KGReasoning 只有 6 個核心 Python 檔案,架構極度精簡但職責分明:


flowchart TB
    subgraph sg1["資料準備層"]
        A["create_queries.py
從 KG 邊產生訓練/驗證/測試查詢"] end subgraph sg2["資料載入層"] B["dataloader.py
TrainDataset / TestDataset
負採樣 + collate_fn"] end subgraph sg3["模型層 models.py"] C["KGReasoning (nn.Module)
三種幾何運算子切換"] C1["GQE:CenterIntersection
(向量 + 注意力交集)"] C2["Query2box:CenterIntersection
+ BoxOffsetIntersection
(盒子中心+偏移)"] C3["BetaE:BetaIntersection
+ BetaProjection
(Beta 分布參數)"] C --> C1 C --> C2 C --> C3 end subgraph sg4["工具層"] D["util.py
query_structure 序列化
list2tuple/flatten"] end subgraph sg5["訓練/評估層"] E["main.py
argparse 設定
訓練迴圈 + 評估迴圈
TensorBoard 記錄"] end A --> B D --> B B --> E C --> E D --> C

2.2 關鍵模組、類別與資料流說明

檔案關鍵類別/函式角色
create_queries.pyindex_dataset, query sampling functionstrain.txt/valid.txt/test.txt 三元組出發,透過圖上隨機遊走 (random walk) 產生 14 種查詢結構的訓練/驗證/測試樣本
dataloader.pyTrainDataset, TestDataset, SingledirectionalOneShotIterator將查詢 + 答案包裝成 PyTorch DatasetTrainDataset 做負採樣 (negative sampling),TestDataset 對每個查詢枚舉全部實體算分
util.pylist2tuple, tuple2list, flatten, eval_tuple, parse_time, set_global_seed查詢結構本質是巢狀 tuple,如 (e, (r,)) 代表 1-hop 查詢;這些函式負責在 list/tuple 間轉換以支援 pickle 序列化與 batch 攤平
models.pyKGReasoning, CenterIntersection, BoxOffsetIntersection, BetaIntersection, BetaProjection, Regularizer核心模型;依 --geo 參數 (vec/box/beta) 切換三種前向傳播路徑 forward_vec/forward_box/forward_beta
main.pyparse_args, evaluate, main()CLI 入口,組裝 dataloader + model + optimizer,執行訓練迴圈與 MRR/Hit@K 評估

資料流(以 BetaE 訓練一步為例)


flowchart LR
    Q["查詢 tuple
((e,(r,)),(e,(r,)))
= 2i 查詢"] --> EMB1["實體嵌入
Beta(alpha, beta)"] EMB1 --> PROJ["BetaProjection
沿關係 r 投影"] PROJ --> INTER["BetaIntersection
注意力加權合併
兩條路徑的 alpha/beta"] INTER --> DIST["KL 散度
查詢分布 vs 候選答案分布"] DIST --> LOSS["margin loss
正樣本距離小
負樣本距離大"]

2.3 設計哲學與技術選擇分析

為何用 query_structure 當 dictionary key? 14 種查詢類型(1p/2p/3p/2i/3i/ip/pi/2in/3in/inp/pin/pni/2u/up)在程式碼中被編碼成巢狀 tuple(如 main.py 中的 query_name_dict),因為 tuple 是 Python 中唯一「可雜湊 (hashable)」的巢狀容器,能直接當 defaultdict 的 key,把「同結構的查詢」自動分組批次處理——這是一個很聰明但也犧牲可讀性的設計取捨(原始碼註解自嘲 “in case we run out of names”)。

為何三種模型共用一個 KGReasoning class? 作者選擇用單一 nn.Module、內部依 self.geo 分支呼叫不同 forward_* 方法,而非三個獨立 class。優點是共用資料載入、訓練迴圈、評估邏輯,程式碼量小;代價是 models.py 內有不少 if self.geo == 'box' / 'vec' / 'beta' 的條件分支,違反單一職責原則,但對一個研究比較型 repo 而言,這種「犧牲一點物件導向純度換取實驗切換方便性」是合理取捨。

幾何表示法的演進邏輯

  • GQE 用純向量 + 注意力交集,最簡單但無法表達「不確定性」與「聯集/否定」。
  • Query2box 用超矩形盒子(中心 + 偏移量),盒子的體積天然表達「答案集合的不確定範圍」,交集運算變成盒子的幾何相交。
  • BetaE 用 Beta 分布(用 alpha/beta 兩個參數描述機率密度),比盒子更能表達「模糊/機率性」的答案集合,且天生支援聯集與否定(分布的補集運算),是三者中表達力最強的版本。

3. 安裝與設定 (Installation & Setup)

3.1 環境需求

  • Python 3.6+(原始碼使用 from __future__ import 相容寫法,判斷是舊版 PyTorch 生態)
  • PyTorch(GPU 版本,建議支援 CUDA)
  • 套件:numpy, tqdm, tensorboardX, clickcreate_queries.pyclick 做 CLI)
  • GPU:官方範例全部用 CUDA_VISIBLE_DEVICES=0,在 CPU 上訓練 450,001 steps 幾乎不可行;建議至少一張 8GB+ 顯存的 GPU(hidden_dim=800 時嵌入矩陣會佔用可觀顯存)
  • 磁碟空間:KG 資料集(FB15k / FB15k-237 / NELL995)解壓後約數 GB

3.2 完整安裝步驟

依照本機規範,Python 環境一律用 uv 建立隔離虛擬環境,避免污染系統 Python:

 1# 1. clone 專案
 2git clone https://github.com/snap-stanford/KGReasoning.git
 3cd KGReasoning
 4
 5# 2. 用 uv 建立虛擬環境並安裝依賴
 6uv venv --python 3.10
 7source .venv/bin/activate
 8
 9# 3. 安裝 PyTorch(依你的 CUDA 版本調整,以下示範 CUDA 12.1)
10uv pip install torch --index-url https://download.pytorch.org/whl/cu121
11
12# 4. 安裝其餘依賴
13uv pip install numpy tqdm tensorboardX click

注意:原始 repo 沒有附 requirements.txt,上述套件清單是根據 import 陳述式反推的最小集合。若要長期維護,建議自行補一份 requirements.txtpyproject.toml

3.3 資料集下載與準備

官方提供已經預處理好查詢的資料集打包檔:

1# 下載 BetaE/Query2box 論文使用的三個 KG(FB15k、FB15k-237、NELL995)
2wget http://snap.stanford.edu/betae/KG_data.zip
3unzip KG_data.zip -d data/

資料夾內每個 KG 包含:

檔案內容
train.txt / valid.txt / test.txtKG 的原始三元組邊 (head, relation, tail)
id2rel.pkl / rel2id.pkl / ent2id.pkl / id2ent.pkl實體與關係的 ID 對照表
train-queries.pkl / valid-queries.pkl / test-queries.pkldefaultdict(set),key 為查詢結構,value 為該結構下的具體查詢實例
train-answers.pkl訓練圖(僅 train.txt)下每個查詢的答案集合
valid-easy-answers.pkl / test-easy-answers.pkl在訓練圖中就能找到的「簡單」答案
valid-hard-answers.pkl / test-hard-answers.pkl只有加入 valid/test 邊後才能發現的「困難」答案(真正考驗泛化能力的部分)

若想從零自建查詢(例如換成自己的 KG),使用 create_queries.py

1python create_queries.py --dataset FB15k-237 --gen_train --gen_valid --gen_test \
2  --max_ans_num 100 --index_only

--index_only 會先幫實體/關係編號存成 ent2id.pkl 等對照表,後續再不加此旗標執行以產生實際查詢 pickle。

3.4 常見問題排解

問題原因解法
CUDA out of memoryhidden_dim-d)設太大或 batch_size-b)太大官方範例 FB15k-237 用 -d 800 -b 512;顯存不足先降 -b 到 128 或 256
訓練極慢--cpu_num 設太低導致 DataLoader 成瓶頸依 CPU 核心數調高 -cpu(官方範例用 1,但那是配合大 batch GPU 吃滿;一般機器可調到 4-8)
pickle 版本不相容Python 2/3 或 numpy 版本差異導致 pickle 讀取失敗確認用官方打包的 .pkl 搭配對應 Python 版本讀取,或自行用 create_queries.py 重新產生
--geo box 訓練不收斂-boxmcenter_reg 參數(如 0.02)需要調整依論文附錄的超參數表逐一嘗試,不同資料集最佳值不同
TensorBoard 沒有圖SummaryWriter 預設寫入 save_path執行 tensorboard --logdir <save_path> 並確認路徑正確

4. 核心概念詳解 (Key Concepts)

4.1 查詢結構 (Query Structure) 是什麼

想像知識圖譜是一張巨大的人際關係圖。「查詢結構」描述的是「你要沿著這張圖做什麼形狀的搜尋」,而非「具體搜誰」。例如:

  • 1p(1-hop projection):(e, (r,)) —— 「從實體 e 出發,走一條關係 r 的邊」,如「誰是 e 的導演?」
  • 2i(2-way intersection):((e1,(r1,)), (e2,(r2,))) —— 「同時滿足兩個 1-hop 條件的交集」,如「既是 e1 的導演、又是 e2 的演員」
  • 2in:帶否定的交集,如「是 e1 的導演,但不是 e2 的演員」

這就像是 SQL 查詢的「查詢計畫(query plan)」——你不寫死「搜哪個人」,而是描述搜尋的形狀(幾個 JOIN、幾個 WHERE NOT),模型再依這個形狀去執行對應的幾何運算。

4.1.1 完整 14 種查詢結構對照表

main.pyquery_name_dict 定義了全部支援的查詢形狀。理解這張表是讀懂整個 repo 的關鍵,因為訓練資料、評估指標、模型的 forward_* 分支全都圍繞這 14 種結構展開:

縮寫tuple 結構中文語意對應邏輯運算類比自然語言查詢
1p(e, (r,))1 跳投影單一關係投影「e 的導演是誰?」
2p(e, (r, r))2 跳投影兩次連續投影「e 導演的其他電影的演員有誰?」
3p(e, (r, r, r))3 跳投影三次連續投影「…的…的…是誰?」(三層鏈式關係)
2i((e,(r,)), (e,(r,)))2 路交集兩個 1p 的交集「同時是 e1 的導演、e2 的演員的人」
3i((e,(r,)), (e,(r,)), (e,(r,)))3 路交集三個 1p 的交集三個條件同時成立
ip(((e,(r,)),(e,(r,))), (r,))交集後投影先交集、再投影一次「(A 且 B) 的合作對象是誰?」
pi((e,(r,r)), (e,(r,)))投影後交集一個 2p 與一個 1p 交集「(A 的合作對象) 且 (B 的條件)」
2in((e,(r,)), (e,(r,n)))2 路交集帶否定一個正條件、一個否定條件「是 e1 的導演,但不是 e2 的演員」
3in((e,(r,)),(e,(r,)),(e,(r,n)))3 路交集帶否定兩正一負「同時滿足 A、B,但不滿足 C」
inp(((e,(r,)),(e,(r,n))), (r,))否定交集後投影2in 再投影「(A 且非 B) 的合作對象」
pin((e,(r,r)), (e,(r,n)))投影後否定交集2p 與否定 1p 交集「(A 的合作對象) 且非 (B 的條件)」
pni((e,(r,r,n)), (e,(r,)))否定投影後交集投影鏈末端帶否定,再交集「非 (A 的合作對象),但滿足 B」
2u-DNF((e,(r,)),(e,(r,)),(u,))2 路聯集(析取範式展開)A 或 B「是 e1 的導演,或是 e2 的演員」
up-DNF(((e,(r,)),(e,(r,)),(u,)), (r,))聯集後投影(DNF)2u 再投影一次「(A 或 B) 的合作對象」
2u-DM(((e,(r,n)),(e,(r,n))), (n,))2 路聯集(De Morgan 展開)用雙重否定實作聯集2u-DNF,但走 BetaE 的機率補集路徑
up-DM(((e,(r,n)),(e,(r,n))), (n,r))聯集後投影(De Morgan)2u-DM 再投影up-DNF,走 De Morgan 路徑

DNF vs De Morgan 的差異(對應 main.py--evaluate_union 參數,可選 DNFDM):

  • DNF (Disjunctive Normal Form; 析取範式):直接把聯集查詢拆成多個交集子查詢分別算距離,再取最小值(最像答案的那個子查詢決定分數)。GQE/Query2box 用此法,因為它們的幾何表示沒有原生的「否定」運算,無法走 De Morgan 路徑。
  • De Morgan 轉換:利用邏輯恆等式 A ∪ B = ¬(¬A ∩ ¬B),把聯集運算轉換成「否定 + 交集 + 否定」的組合。只有 BetaE 能用此法,因為 Beta 分布天生支援否定(補集)運算,這也是論文能同時處理聯集與否定的關鍵設計。

4.2 為何用嵌入空間做邏輯運算(類比:地圖上的形狀運算)

把每個實體想成地圖上的一個點,每個查詢的「候選答案集合」想成地圖上的一塊區域:


flowchart LR
    subgraph sg2["Query2box 直覺"]
        B1["實體 e1
(一個點)"] -->|"沿關係 r1
投影"| B2["候選答案區域
(一個盒子)"] B3["實體 e2
(一個點)"] -->|"沿關係 r2
投影"| B4["候選答案區域
(另一個盒子)"] B2 --> B5["盒子相交
= 交集運算"] B4 --> B5 B5 --> B6["落在交集盒子內的
實體 = 查詢答案"] end
  • 投影 (projection):從一個點出發,「走一步關係」相當於把這個點移動/擴散成一個新區域(因為一個 head 可能對應多個 tail,答案不再是單點而是一群候選)。
  • 交集 (intersection):兩個區域的共同部分,就是同時滿足兩個條件的答案。
  • 聯集 (union):多個區域的合併,滿足任一條件即可。
  • 否定 (negation):BetaE 用 Beta 分布的特性可以直接定義「補集」的分布形狀,這是盒子模型做不到的(盒子沒有天然的「否定」幾何操作,Query2box 論文因此不支援否定查詢)。

4.3 三種幾何表示法的直覺比較

模型幾何表示直覺類比能否表達否定/聯集
GQE單一向量點 + 注意力加權平均用「一個座標」代表答案的重心否(只有交集)
Query2box中心點 + 偏移量 = 一個超矩形盒子用「一個範圍框」代表所有可能答案,框越大表示越不確定支援聯集(用 DNF/析取範式展開),不支援否定
BetaE每個維度一個 Beta(α, β) 分布用「機率密度」代表答案的信心分布,分布越平代表越不確定支援聯集 + 否定(機率補集運算天然存在)

一個直觀的類比:GQE 像用一個「點」猜答案在哪;Query2box 像用一個「畫框」圈出答案可能在的範圍;BetaE 像用一張「熱力圖」表達每個位置是答案的機率高低——熱力圖天生就能表達「非(NOT)」(把熱力圖反轉)與「或(OR)」(把兩張熱力圖疊加取最大值),這是點和框做不到的。

4.4 訓練目標:Margin Loss 與距離度量

三種模型都用「距離越近越像答案」的對比學習框架:


flowchart TB
    A["查詢嵌入 q"] --> C{"距離函式
(依 geo 不同)"} B1["正樣本答案嵌入 t+"] --> C B2["負採樣答案嵌入 t-"] --> C C --> D["GQE/Query2box:
L2 距離或盒子距離"] C --> E["BetaE:
KL 散度"] D --> F["margin loss
拉近正樣本、推遠負樣本"] E --> F
  • GQE/Query2box 用類似 TransE 的距離度量(歐氏距離),配合 gamma(margin,邊界值)做 hinge loss。
  • BetaE 用 KL 散度 (Kullback-Leibler divergence) 衡量「查詢分布」與「答案分布」的差異,這是機率分布特有的距離度量,向量/盒子模型沒有對應概念。

4.5 評估指標:MRR 與 Hit@K(類比:搜尋引擎排名品質)

main.pyevaluate() 函式對每個查詢計算兩類指標,概念上等同於搜尋引擎排名評估:

  • MRR (Mean Reciprocal Rank; 平均倒數排名):對每個查詢,把所有實體依「與查詢嵌入的距離」排序,找出正確答案排在第幾名,取倒數(第 1 名 = 1.0,第 10 名 = 0.1),再對所有查詢取平均。這個指標對「答案排名極前面」特別敏感——就像使用者只會看搜尋結果的第一頁,答案若排第 1 名比排第 5 名的加分幅度遠大於排第 100 名比排第 105 名。
  • Hit@K(K 通常取 1/3/10):正確答案是否落在前 K 名之內的比例,比 MRR 更直觀但區分度較低。

Easy vs Hard 答案的評估邏輯(對應 valid-easy-answers.pklvalid-hard-answers.pkl 的區分):評估時模型必須先把 Easy 答案(訓練圖中就查得到的答案)從候選排名中「過濾」掉,只針對 Hard 答案(只有加入驗證/測試邊後才浮現的答案)計算排名——這確保評估的是模型「推理出圖中隱含新知識」的能力,而不是死記訓練資料就能拿到高分。這個設計呼應第 1.2 節提到的「不完整性」問題核心。


flowchart LR
    A["查詢 q"] --> B["對全部實體計算距離分數"]
    B --> C["依分數排序全部實體"]
    C --> D["過濾掉 Easy Answers
(訓練圖已知答案)"] D --> E["找出 Hard Answer 的排名"] E --> F["MRR = 1/排名
Hit@K = 排名<=K ? 1 : 0"]

5. 使用方式與程式碼範例 (Usage & Code Examples)

5.1 基礎用法:訓練 BetaE 模型

以下完整指令對應 example.sh 中 FB15k-237 資料集上的 BetaE 訓練設定:

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4CUDA_VISIBLE_DEVICES=0 python main.py --cuda --do_train --do_valid --do_test \
 5  --data_path data/FB15k-237-betae \
 6  -n 128 \
 7  -b 512 \
 8  -d 400 \
 9  -g 60 \
10  -lr 0.0001 \
11  --max_steps 450001 \
12  --cpu_num 1 \
13  --geo beta \
14  --valid_steps 15000 \
15  -betam "(1600,2)"

參數說明:

  • -n 128:每個正樣本配 128 個負採樣實體
  • -b 512:batch size,每步處理 512 個查詢
  • -d 400:嵌入維度(entity_dim = relation_dim = 400)
  • -g 60:margin(gamma),BetaE 用 KL 散度所以 margin 值通常比向量模型(如 GQE 用 24)設得更大
  • -betam "(1600,2)":BetaProjection 的 (hidden_dim, num_layers),即關係投影 MLP 用隱藏層 1600 維、2 層網路

5.2 進階用法:比較三種模型的訓練指令

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4DATA=data/FB15k-237-betae
 5TASKS="1p.2p.3p.2i.3i.ip.pi.2u.up"
 6
 7# GQE (向量模型):只支援交集,不支援否定查詢
 8CUDA_VISIBLE_DEVICES=0 python main.py --cuda --do_train --do_valid --do_test \
 9  --data_path "$DATA" -n 128 -b 512 -d 800 -g 24 \
10  -lr 0.0001 --max_steps 450001 --cpu_num 1 --geo vec \
11  --valid_steps 15000 --tasks "$TASKS"
12
13# Query2box (盒子模型):支援聯集,用 DNF 展開處理
14CUDA_VISIBLE_DEVICES=0 python main.py --cuda --do_train --do_valid --do_test \
15  --data_path "$DATA" -n 128 -b 512 -d 400 -g 24 \
16  -lr 0.0001 --max_steps 450001 --cpu_num 1 --geo box \
17  -boxm "(none,0.02)" --valid_steps 15000 --tasks "$TASKS"
18
19# BetaE (Beta 分布模型):完整支援否定查詢(額外測試 2in/3in/inp/pin/pni)
20CUDA_VISIBLE_DEVICES=0 python main.py --cuda --do_train --do_valid --do_test \
21  --data_path "$DATA" -n 128 -b 512 -d 400 -g 60 \
22  -lr 0.0001 --max_steps 450001 --cpu_num 1 --geo beta \
23  -betam "(1600,2)" --valid_steps 15000 \
24  --tasks "1p.2p.3p.2i.3i.ip.pi.2in.3in.inp.pin.pni.2u.up"

5.3 從 checkpoint 續訓或只做評估

1# 只做測試(載入既有 checkpoint,不重新訓練)
2CUDA_VISIBLE_DEVICES=0 python main.py --cuda --do_test \
3  --data_path data/FB15k-237-betae \
4  --geo beta -d 400 -betam "(1600,2)" \
5  --checkpoint_path logs/FB15k-237-betae/beta/xxxx-xx-xx

5.4 程式化讀取查詢資料的範例

若要在自己的分析腳本中檢視查詢資料的結構(例如做資料探索或除錯),可以這樣讀取:

 1"""
 2檢視 KGReasoning 查詢資料集的結構範例
 3"""
 4import pickle
 5from collections import defaultdict
 6
 7DATA_PATH = "data/FB15k-237-betae"
 8
 9# 讀取查詢(key=查詢結構 tuple, value=set of 查詢實例)
10with open(f"{DATA_PATH}/test-queries.pkl", "rb") as f:
11    test_queries = pickle.load(f)
12
13# 讀取答案
14with open(f"{DATA_PATH}/test-hard-answers.pkl", "rb") as f:
15    test_hard_answers = pickle.load(f)
16
17query_name_dict = {
18    ('e', ('r',)): '1p',
19    (('e', ('r',)), ('e', ('r',))): '2i',
20    (('e', ('r',)), ('e', ('r', 'n'))): '2in',
21}
22
23for query_structure, queries in test_queries.items():
24    name = query_name_dict.get(query_structure, str(query_structure))
25    queries_list = list(queries)
26    print(f"查詢類型 {name}: 共 {len(queries_list)} 筆")
27    if queries_list:
28        sample_query = queries_list[0]
29        sample_answers = test_hard_answers[sample_query]
30        print(f"  範例查詢: {sample_query}")
31        print(f"  對應 hard answers 數量: {len(sample_answers)}")

執行後可以看到每種查詢類型(1p/2i/2in…)的樣本數與答案分佈,這對於評估「模型在哪種邏輯結構上表現較差」非常有用——通常帶否定 (n) 的查詢類型是所有模型的弱點。

5.5 解析 TensorBoard 訓練日誌並繪製各查詢類型表現

訓練結束後,main.py 會把每種查詢結構的 MRR/Hit@K 分別寫入 TensorBoard(writer.add_scalar 呼叫,tag 格式為 "{mode}_{query_name}_{metric}")。以下範例示範如何在不啟動 TensorBoard 網頁的情況下,直接用程式解析事件檔案,比較不同查詢結構的表現差異:

 1"""
 2從 TensorBoard event 檔案批次擷取各查詢結構的 MRR,
 3用於離線比較 GQE / Query2box / BetaE 在同一資料集上的
 4「哪種邏輯結構最容易/最難」分析。
 5"""
 6from tensorboard.backend.event_processing import event_accumulator
 7
 8def load_final_mrr(log_dir: str) -> dict[str, float]:
 9    """讀取一個訓練 log 目錄,回傳 {查詢類型: 最終 test MRR}"""
10    ea = event_accumulator.EventAccumulator(log_dir)
11    ea.Reload()
12
13    results = {}
14    for tag in ea.Tags().get("scalars", []):
15        if tag.startswith("Test_") and tag.endswith("_MRR"):
16            # tag 範例:"Test_2in_MRR"
17            query_name = tag.replace("Test_", "").replace("_MRR", "")
18            events = ea.Scalars(tag)
19            results[query_name] = events[-1].value  # 取最後一次記錄的值
20    return results
21
22
23if __name__ == "__main__":
24    beta_results = load_final_mrr("logs/FB15k-237-betae/beta/2026.01.01-00:00:00")
25    box_results = load_final_mrr("logs/FB15k-237-betae/box/2026.01.01-00:00:00")
26
27    print(f"{'查詢類型':<10}{'BetaE MRR':>12}{'Query2box MRR':>16}")
28    for query_name in sorted(beta_results.keys() | box_results.keys()):
29        beta_mrr = beta_results.get(query_name, float("nan"))
30        box_mrr = box_results.get(query_name, float("nan"))
31        print(f"{query_name:<10}{beta_mrr:>12.3f}{box_mrr:>16.3f}")

典型觀察(依論文報告的趨勢):帶否定 (2in/3in/inp/pin/pni) 的查詢類型只有 BetaE 支援,Query2box 完全無法評估;即使在兩者都支援的類型(如 1p/2i)中,BetaE 通常也略優於 Query2box,因為機率分布比幾何盒子有更強的表達彈性。


6. 進階應用場景 (Advanced Use Cases)

6.1 真實世界應用案例

  • 生物醫學知識圖譜推理:把 KGReasoning 的 BetaE 用在 Hetionet 或藥物-基因-疾病三元組圖上,回答「哪些藥物同時作用於基因 X 的通路、但不會引發副作用 Y」這類多跳帶否定查詢——這正是藥物重定位 (drug repurposing; 藥物再利用) 研究常見的問題形式。
  • 推薦系統的複雜條件查詢:電商知識圖譜可用 2i/3i 查詢回答「同時符合品牌 A 偏好且屬於類別 B、但排除已購買過的商品」。
  • 企業知識圖譜的合規查詢:金融領域可用否定查詢排查「與受制裁實體有關聯、但未被列入現有黑名單」的間接風險節點。

6.2 與其他 snap-stanford 工具的整合

  • OGB (Open Graph Benchmark):可將 OGB 提供的大規模 KG 資料集(如 ogbl-biokg)轉換成 KGReasoning 需要的 train.txt/valid.txt/test.txt 格式,再用 create_queries.py 產生查詢,把 KGReasoning 的評估方法應用在 OGB 標準資料集上做比較研究。
  • PyTorch Geometric (PyG):雖然 KGReasoning 未直接依賴 PyG,但社群常見做法是用 PyG 的 Data/HeteroData 物件先做圖預處理與取樣,再轉換成 KGReasoning 期待的三元組格式,結合兩者的圖操作生態。

6.3 案例研究:資料集規模與訓練成本的實務估算

以官方三個資料集為例,實務規劃訓練資源時可參考下表(依 BetaE 論文與 repo 的預設超參數推估):

資料集實體數 (nentity)關係數 (nrelation)訓練查詢量級建議 -d/-b相對訓練時間(單張 GPU)
FB15k~15,000~1,345較大-d 800 -b 512基準(電影/人物領域,關係稠密)
FB15k-237~15,000~237中等-d 400 -b 512略快於 FB15k(關係數少但更難泛化,因為移除了逆向關係造成的資料洩漏)
NELL995~63,000~200較大-d 800 -b 512實體數最多,單步計算量最大

FB15k vs FB15k-237 的重要差異(容易誤解之處):FB15k-237 是 FB15k 移除「逆關係洩漏」(例如同時存在 (A, 導演, B)(B, 被導演, A) 這種可以直接反推的簡單逆關係)後的版本,因此 FB15k-237 上的分數天然會比 FB15k 低,兩者分數不可直接比較優劣,只能在各自資料集內比較不同模型。這是使用此 repo 做研究時最常見的誤讀之一。

6.4 效能調校與最佳實踐

  1. 負採樣數量 (-n) 與訓練穩定性的權衡:負採樣數越多,梯度估計越穩定但每步計算量越大;官方預設 128,資源有限時可降到 64,但需相應調整學習率。
  2. test_batch_size 保持 1 的原因TestDataset 對每個查詢會枚舉「全部實體」計算排序分數(self.batch_entity_range),若 batch 太大會迅速吃滿顯存,官方預設 test_batch_size=1 是刻意為之的保守設定。
  3. --tasks 分階段訓練策略:可先用純 1p 任務訓練(讓模型先學好基礎的單跳投影嵌入),穩定後再擴充到完整 14 種任務繼續訓練,比一開始就丟全部任務更容易收斂(源自論文附錄的訓練技巧)。
  4. center_reg-boxm 第二參數)調參:Query2box 的 center_reg 平衡「盒子內距離」與「盒子外距離」的權重,是影響 Query2box 收斂品質的關鍵超參數,建議在 0.01~0.05 範圍網格搜尋。

7. 與 AIKT 的關聯分析 (AIKT Integration Analysis)

7.1 此專案可作為 AIKT 的資料來源或分析工具嗎?

可以,且有兩種互補的角度

  1. 作為「分析工具」:KGReasoning 本身就是 AIKT L4(graphify)的一種進階延伸想像——AIKT 目前的 graphify 建的是「程式碼結構圖」的靜態索引(誰引用誰),而 KGReasoning 展示的是「如何在圖上做複雜邏輯查詢並容忍圖不完整」的技術。若 AIKT 未來想讓 graphify 支援「多跳帶條件的程式碼查詢」(例如「找出呼叫了函式 A、但沒有寫測試、且屬於已棄用模組的類別」),KGReasoning 的查詢結構設計(1p/2i/2in…)是現成的形式化參考框架。

  2. 作為「資料來源」:這份 repo 本身(README + 6 個 .py 檔)就是 L2(ai-gh-save)與 L12(gh-tutorial-qd)處理的標準輸入樣本——本教學本身即是此流程的產出實例。

7.2 AIKT 的哪些 Layer 可以包裝或編排此專案?


flowchart TB
    subgraph sg1["L1-L3 知識擷取"]
        A1["L2 ai-gh-save
已擷取 README+程式碼結構"] end subgraph sg2["L4-L6 知識索引"] A2["L4 graphify
可對 6 個 .py 檔建立
函式/類別依賴圖
(models.py 內 3 種 forward_* 路徑)"] A3["L6 gitnexus
程式碼符號圖
查詢「KGReasoning class
依賴哪些 nn.Module」"] end subgraph sg3["L9-L10 研究工具"] A4["L9 paper-search
搜尋 BetaE/Query2box/GQE
後續引用論文,追蹤
此研究脈絡最新進展"] A5["L10 paper-qa-lite
針對已下載的 BetaE 論文
PDF 做本地 RAG 問答"] end subgraph sg4["L12-L15 教學輸出"] A6["L12 gh-tutorial-qd
本教學文件的產出管道"] A7["L15 paper-tutorial
結合論文+程式碼
做精讀教學(本專案
兼具論文與程式碼,
是絕佳候選)"] end subgraph sg5["L18-L19 研究延伸"] A8["L18 research-pipeline-v2
若要做「KG 推理方法
比較研究」的多輪迭代分析"] end A1 --> A2 A1 --> A4 A2 --> A3 A4 --> A5 A1 --> A6 A4 --> A7 A5 --> A7 A7 --> A8

7.3 潛在的整合場景與價值

  • 場景一:程式碼結構教學自動化。用 L4 graphify 對 models.py 建索引,自動產出「KGReasoning.forward()geo 參數分派到 forward_vec/forward_box/forward_beta 三條路徑」的依賴圖,再餵給 L12 gh-tutorial-qd 輔助撰寫本教學的第 2 節架構圖——這正是本教學撰寫時人工完成的工作,未來可半自動化。

  • 場景二:論文+程式碼雙軌精讀。KGReasoning 是少數「論文與程式碼一一對應清晰」的 repo(query_name_dict 的 14 種結構直接對應論文 Table),適合用 L15 paper-tutorial 做「先讀論文 Section 3 的查詢結構定義,再對照 main.pyquery_name_dict」的雙軌教學,比純程式碼導讀或純論文導讀都更完整。

  • 場景三:研究脈絡追蹤。用 L9 paper-search 定期搜尋引用 BetaE 的最新論文(如後續的 pre-training 式 KG 推理模型),搭配 L18 research-pipeline-v2 做「三輪迭代」文獻回顧,追蹤這個子領域從 2018 GQE 到現今的技術演進,產出結構化的技術脈絡報告。

  • 場景四:治理層的分流參考。此 repo 只有 6 個檔案、無 CI/測試、無 requirements.txt——這種「輕量研究型 repo」的特徵,可作為 AIKT L28 aikt-governance 判斷「這個 repo 適合直接讀原始碼教學,不需要啟動完整 docling 深度解析」的參考案例(副檔名分流優先序中,純 .py + README.md 走 gh-tutorial-qd 即已足夠,不需 L8 docling)。

7.4 機密邊界檢查


8. 優缺點與生態系定位 (Strengths, Limitations & Ecosystem Position)

8.1 核心優勢與創新點

  • 統一框架比較三代模型:GQE / Query2box / BetaE 共用同一套資料載入、訓練迴圈與評估流程,讓研究者能在完全一致的實驗條件下比較三種幾何表示法的優劣,這是此 repo 相較於三篇論文各自獨立程式碼庫的最大加值。
  • 查詢結構的形式化設計優雅:用巢狀 tuple 編碼 14 種邏輯查詢結構,雖然可讀性不是最佳,但兼顧了「可雜湊當 dict key 做批次分組」與「可任意巢狀擴展」兩個工程需求,是研究程式碼中少見的精巧設計。
  • 官方 checkpoint 與資料集齊全:README 提供現成的資料下載連結,降低了重現論文結果的門檻,這對學術可重現性 (reproducibility) 貢獻很大。
  • BetaE 的否定查詢支援是理論突破:在此之前的向量/盒子模型都無法優雅處理邏輯否定,BetaE 用機率分布的補集運算解決了這個長期痛點。

8.2 目前限制與改進空間

  • 程式碼工程性不足:無單元測試、無 CI、無 requirements.txt/pyproject.toml、無型別註記 (type hints)——這是典型「研究可重現優先、工程規範其次」的學術程式碼,若要在生產環境使用需自行補強。
  • 可擴展性受限:僅支援固定的 14 種查詢結構,若要處理更複雜的邏輯(如巢狀否定、任意深度的混合運算),需要修改 query_name_dict 與對應的 forward_* 邏輯,缺乏一般化的查詢解析器 (query parser)。
  • 記憶體效率TestDataset 在評估階段對每個查詢枚舉全部實體算分(self.batch_entity_range),在百萬節點規模的 KG 上會遇到嚴重的記憶體與速度瓶頸,未使用近似最近鄰搜尋 (ANN; approximate nearest neighbor) 等加速技術。
  • 停止維護狀態:從 repo 更新時間可推斷此 repo 主要是論文發表時的一次性交付,之後僅有零星維護,若需要跟上最新研究(如結合 pretrained language model 的 KG 推理),需另尋後續論文的程式碼庫。

8.3 與同領域工具的比較

工具定位與 KGReasoning 的關係
OpenKE傳統 KGC 嵌入方法大集合(TransE/DistMult/ComplEx 等)只做單跳三元組評分,不支援多跳邏輯查詢;KGReasoning 是其查詢複雜度的延伸
PyKEENKG 嵌入的通用 Python 函式庫,工程化程度高工程完整度遠高於 KGReasoning(有測試、CI、文件),但同樣缺乏原生的多跳邏輯查詢支援
SMORE (SNAP 後續專案)大規模分散式查詢嵌入訓練框架可視為 KGReasoning 的「工業級擴展版」,解決其百萬節點規模下的效率瓶頸
NBFNet基於路徑的歸納式 KG 推理走不同技術路線(路徑聚合 vs 幾何嵌入),適合對比研究不同推理範式的優劣

8.4 適用場景建議

  • 適合:學術研究複現、教學展示邏輯查詢嵌入的核心概念、中小規模 KG(十萬節點以下)的原型驗證、比較三種幾何表示法的實驗設計基礎。
  • 不適合直接用於生產:若需要處理百萬級節點規模、或需要嚴謹的工程化保證(測試覆蓋率、型別安全),建議改用 PyKEEN 等工程化程度更高的函式庫,或參考 SMORE 的分散式訓練架構。
  • 最佳切入時機:當你需要「快速理解多跳邏輯查詢嵌入」這個概念、或需要一份三種代表性方法(GQE/Query2box/BetaE)的對照實作時,KGReasoning 是目前最精簡、最權威的起點。

附錄:完整檔案清單

1KGReasoning/
2├── LICENSE                # MIT License
3├── README.md               # 專案說明、資料集連結、引用格式
4├── create_queries.py       # 從 KG 邊產生 14 種結構的查詢 pickle
5├── dataloader.py           # TrainDataset / TestDataset / SingledirectionalOneShotIterator
6├── example.sh               # FB15k / FB15k-237 / NELL 三個資料集 × 三種模型的完整訓練指令範例
7├── main.py                  # CLI 入口:argparse 參數、訓練/評估迴圈、TensorBoard 記錄
8├── models.py                # KGReasoning nn.Module + 各種 Intersection/Projection/Regularizer
9└── util.py                  # tuple/list 轉換、隨機種子設定、時間格式化等工具函式