# NARS 文檔 ###### tags: `autoglab`, `Travis` ## 概述 **NARS** 是 **GNN** 的一種,在很多分類任務上達到了 SOTA,開發模組旨在降低使用門檻,提高開發效率。 ## 應用場景 對異質圖中特定類型的節點 (以下簡稱**目標節點**) 進行類別預測。例如,**ACM** 資料集的目標節點為所有 **paper** 類型的節點,要預測其屬於哪個學術範疇。 ## 環境 ![](https://i.imgur.com/ZxDL0Xt.png) ![](https://i.imgur.com/0qbT9kZ.png) ![](https://i.imgur.com/KAsoWqU.png) ## NARS 原理 如下圖所示,**NARS** 流程可分為四個步驟: ![](https://i.imgur.com/Jp9Rmky.png) 1. **抽樣關連子集** (relation subsets): 這個階段會抽樣出多個關連子集,每個關連子集包含一種或多種關連。 :::info **注意**: 關連子集中至少要有一種邊連接目標節點類型。 ::: 3. **生成關連子圖** (relation subgraphs): 這個階段會根據上個階段生成的關連子集生成相對應的關連子圖,且生成的關連子圖均為無向圖。 4. **特徵平均** (feature averaging): 計算每個關連子圖中所有目標節點的 L-hops 特徵 (L 為超參數),計算邏輯如下公式: $$ H_{v, l}^{i} = \sum_{u \in N_i(v)} \frac{1}{|N_i(v)|} H_{u, l-1}^{i} $$ 其中,$N_i(v)$ 為第 $i$ 個關連子圖 $G_i$ 中 $v$ 節點的所有鄰居節點,$H_{u,l-1}^{i}$ 為節點 $u$ 在 $G_i$ 中的 $(l-1)-hop$ 特徵。因此,特徵平均後的特徵包含下列四個維度: + `hop_dim` + `rel_subset_dim` + `num_target_node` + `feat_dim` 5. **模型**: 將特徵放入 1-D convolution (`out_channels=1`)中進行特徵壓縮,過程中`rel_subset_dim` 的維度會被壓縮掉,這樣可以降低特徵維度,避免過擬合,公式如下: $$ H_{v, l}^{agg} = \sum_{i=1}^{K} a_{i,l} \cdot H_{v, l}^{i} $$ 特徵接著會放入 **SIGN** 模型 (如下圖),**SIGN** 會先將所有 hop 的特徵一一放入全連結層,再將每個 hop 的輸出結果進行合併,最後,放入全連結層輸出最後結果。 <center> <img src=https://i.imgur.com/m1bMqDk.png width=300> </center> 上述方法有個嚴重的缺點,**模型需要事先生成所有目標節點的特徵**,無法像 GraphSage 一樣可以一個個批次地處理資料,若同時又有很多關連子集,這對電腦 RAM 開銷非常大,為了解決這個問題,原文引進 **部分訓練** 模式,模型訓練若干個 epoch 就會抽樣一部分(非全部,抽樣的數量 `args.sample_size` 為超參數)的關連子集,並產生對應的 L-hop 特徵,放入模型訓練,這樣就能節省 RAM 的使用。部分訓練公式如下: $$ H_{v,l}^{(t)} = \sum_{G_i \in S^{(t)} \subseteq S} b_{i, l} \cdot H_{v, l}^{i} + \alpha H_{v, l}^{(t-1)} $$ 其中,$S^{(t)}$ 為第 $t$ 個 stage 抽樣出的關連子圖集合,$b_{i,l}, \alpha$ 為第 $t$ 個 stage 的可訓練參數。 :::info 為應對訓練大圖的需求,步驟 1~3 都會在 CPU 中進行,避免 GPU OOM,訓練模型時也要注意 CPU 的容量是否能夠負荷。 ::: ## NARS 模組參數 1. **參數設定** + `data`: 選擇訓練模型的資料集,目前有 `acm` & `mag` 兩個資料集可直接使用,若要訓練其他資料集,輸入 `other`,且需要改寫 `nars_data.py` 中的 `load_custom_dataset` 函數,詳見**第二點**。 :::info **注意**: 因 NARS 模型有特徵平均的步驟,因此,要確保所有類型的節點特徵在同一個維度上,如果不滿足這個條件,常見做法會把所有節點類型的特徵投影到同一個維度上。另外,若訓練資料未提供部分節點類型的特徵,常見做法會使用知識圖譜的方法來產生這些節點的嵌入,在 `mag` & `acm` 的資料集中,我們使用此做法,詳見[此](https://github.com/facebookresearch/NARS/tree/main/graph_embed)。 ::: + `target_node_type`: 目標節點的類型,以 `acm` 資料集為例如,`args.target_node_type='paper'`。 + `rel_path`: 儲存所有關連子集的檔案,格式如下圖所示,每一行代表一個關連子集的關連,圖中共有 8 個關連子集。 :::info **注意**: 1. 如果 `args.rel_path` 路徑下不存在檔案,程式會自動隨機生成關連子集檔案在該路徑下,過程中,會要求使用者輸入想要生成關連子集的數量。 2. 主程式還會在訓練模型前驗證每個關連子集是否符合符合規範下列: + 關連子集中的每種關連是否存在。 + 關連子集中是否至少有一種關連會連接到目標節點。 ::: <center> <img src=https://i.imgur.com/wvlCs9n.png width=500> </center> + `R`: 使用多少個 hop 當作特徵。 + `partial`: 是否進行部分訓練模式。 + `sample_size`: 若 `args.partial=True` 才會有作用,代表每個 stage 抽樣關連子集的數量。 + `resample_every`: 若 `args.partial=True` 才會有作用,代表訓練多少個 epoch 後進行關連子集的重新抽樣。 + `num_hidden`: **SIGN** 全連結層 $\Theta_0, \Theta_1, ..., \Theta_L$ 的輸出維度。 + `ff_layer`: **SIGN** 全連結層 $\Theta_0, \Theta_1, ..., \Theta_L$ 的層數。 + `dropout`: **SIGN** 全連結層 $\Theta_0, \Theta_1, ..., \Theta_L$ 的 dropout rate。 + `input_dropout`: 是否進行 dropout。 + `lr`: 訓練時的學習率。 + `weight_decay`: 訓練時的 $L_2$ 懲罰項超參數。 + `num_epochs`: 訓練多少個 epoch。 + `batch_size`: 訓練時的批次數量。 2. **客製化資料**: 使用客製化的資料來訓練模型時,必須改寫 `nars_data.py` 中的 `load_custom_dataset` 函數,且函數的回傳值需滿足一定的規範,如下範例: ```python def load_custom_dataset(): ... return data, labels, n_classes, train_nid, val_nid, test_nid ``` + **data**: `PyG` 中的 `HeteroData` 物件,包含異質圖的節點、節點特徵與邊,資料務必遵循下圖的格式進行編寫 (**ACM** 範例)。 + `data` 的鍵包含所有類型的節點與邊,且邊的格式為 `(src, edge, dst)`。 + 使用 `.feat` 來取得特定類型節點的特徵,特徵形狀為 `(num_nodes, feat_size)` 的 `torch.Tensor`。例如:範例中 paper 的特徵為 `data['paper'].feat`。 + 使用 `.edge_index` 來取得特定類型的邊,形狀為 `(2, num_edges)` 的 `torch.Tensor`。例如:範例中 pa 關連的邊為 `data['paper', 'pa', 'author'].edge_index`。 ![](https://i.imgur.com/kLWs2RJ.png) + **labels**: 所有目標節點的標籤,形狀為 `(num_target_nodes,)` 的 `torch.Tensor`。 + **n_classes**: 分類問題中的類別數量。 + **train_nid** & **val_nid** & **test_nid**: 對目標節點進行訓練、驗證、測試分割,形狀均為 `(num_target_nodes,)` 的 `numpy.ndarray`。 ## 執行程式 1. **文件配置** ``` train_nars.py nars_trainer.py nars_utils.py nars_model.py nars_data.py ``` 2. **訓練範例**: 在 `nars_trainer.py` 的路徑下輸入下列指令。 + **acm**(`args.partial=False`) ``` python train_nars.py --data "acm" --target_node_type "paper" --rel_path "./acm/acm" --R 2 --partial "False" \ --num_hidden 64 --ff_layer 2 --dropout 0.5 --input_dropout "True" \ --lr 3e-3 --weight_decay 0 --num_epochs 1000 --batch_size 50000 ``` + **acm**(`args.partial=True`) ``` python train_nars.py --data "acm" --target_node_type "paper" --rel_path "./acm/acm" --R 2 --partial "True" \ --sample_size 1 --num_hidden 64 --ff_layer 2 --dropout 0.5 --input_dropout "True" \ --lr 3e-3 --weight_decay 0 --num_epochs 1000 --batch_size 50000 --resample_every 10 ``` + **mag**(`args.partial=False`) ``` python train_nars.py --data "mag" --target_node_type "paper" --rel_path "./mag/mag" --R 5 --partial "False" \ --num_hidden 512 --ff_layer 2 --dropout 0.5 --input_dropout "True" --lr 1e-3 --weight_decay 0\ --num_epochs 300 --batch_size 50000 ``` + **mag**(`args.partial=True`) ``` python train_nars.py --data "mag" --target_node_type "paper" --rel_path "./mag/mag" --R 2 --partial "True" \ --sample_size 3 --num_hidden 512 --ff_layer 2 --dropout 0.5 --input_dropout "True" \ --lr 1e-3 --weight_decay 0 --num_epochs 300 --batch_size 50000 --resample_every 10 ``` ## Code Review 問題 1. **特徵維度是否符合要求**: + `args.partial=False` 符合要求: `gen_rel_subset_feature` 的輸出為 `[hop0_feat, hop1_feat, ..., hopL_feat]`,`hop{k}` 的形狀為 `(num_nodes, feat_size)`。 ```python def preprocess_features(g, rel_subsets, args, device): # pre-process heterogeneous graph g to generate neighbor-averaged features # for each relation subsets num_paper, feat_size = g.nodes["paper"].data["feat"].shape new_feats = [torch.zeros(num_paper, len(rel_subsets), feat_size) for _ in range(args.R + 1)] print("Start generating features for each sub-metagraph:") for subset_id, subset in enumerate(rel_subsets): print(subset) feats = gen_rel_subset_feature(g, subset, args, device) for i in range(args.R + 1): feat = feats[i] new_feats[i][:feat.shape[0], subset_id, :] = feat feats = None return new_feats ``` + `args.partial=True` 不符合要求,但實驗結果較好,暫不修改 2. **新增驗證 edge type 的 程式碼**: 已完成。 3. **mag** 資料集實驗: 使用 AWS EC2 p2.8xlarge 執行個體,訓練 300 個 epoch。 | hop | Accuracy | Time | | ---- | ---- | ---- | | 2 | 0.4369 | 1h 27min | | 3 | 0.4557 | 1h 50min | | 4 | 0.4613 | 2h 15min | | 5 | 0.4649 | 2h 41min | ## 參考資料 1. [論文](https://arxiv.org/pdf/2011.09679.pdf) 2. [github](https://github.com/facebookresearch/NARS)