# NARS 文檔
###### tags: `autoglab`, `Travis`
## 概述
**NARS** 是 **GNN** 的一種,在很多分類任務上達到了 SOTA,開發模組旨在降低使用門檻,提高開發效率。
## 應用場景
對異質圖中特定類型的節點 (以下簡稱**目標節點**) 進行類別預測。例如,**ACM** 資料集的目標節點為所有 **paper** 類型的節點,要預測其屬於哪個學術範疇。
## 環境



## NARS 原理
如下圖所示,**NARS** 流程可分為四個步驟:

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`。

+ **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)