{"notes":[{"id":"graph-learning/graphsage","category":"graph-learning","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"GraphSAGE","subtitle":"抽樣點名","author":"Z. Yuan","paperDate":"2017-06-07","publishedOn":"2023-10-21","world":"external/graphsage","paperTitle":"Inductive Representation Learning on Large Graphs","paperUrl":"https://arxiv.org/abs/1706.02216","bibliography":{"title":"Inductive Representation Learning on Large Graphs","authors":["William L. Hamilton","Rex Ying","Jure Leskovec"],"url":"https://arxiv.org/abs/1706.02216","pdfUrl":"https://arxiv.org/pdf/1706.02216v4","codeUrl":"https://github.com/williamleif/GraphSAGE","preprint":{"id":"1706.02216","submittedOn":"2017-06-07","url":"https://arxiv.org/abs/1706.02216"},"publication":{"venue":"NeurIPS","year":2017,"kind":"conference","url":"https://papers.nips.cc/paper_files/paper/2017/hash/5dd9db5e033da9c6fb5ba83c7a7ebea9-Abstract.html"},"citation":"@misc{graphlearninggraphsage2017,\n  title = {{Inductive Representation Learning on Large Graphs}},\n  author = {William L. Hamilton and Rex Ying and Jure Leskovec},\n  year = {2017},\n  eprint = {1706.02216},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1706.02216}\n}\n","citationPath":"/content/papers/citations/graph-learning/graphsage.1a0b3fd6e8b8.bib"},"thumbnail":{"src":"/content/papers/assets/88f6d9397f20155603c7ee0daef374fb944aac5fb73ed6573a573560b5b9c6aa.png","width":1090,"height":384},"locale":"zh-hant","path":"/content/papers/zh-hant/graph-learning/graphsage.fecc6d2df60f.md","headings":[{"id":"定義問題","text":"定義問題","depth":2},{"id":"transductive-與-inductive","text":"transductive 與 inductive","depth":3},{"id":"鄰居太多","text":"鄰居太多","depth":3},{"id":"解決問題","text":"解決問題","depth":2},{"id":"演算法","text":"演算法","depth":3},{"id":"鄰居用抽的","text":"鄰居用抽的","depth":3},{"id":"三種聚合器","text":"三種聚合器","depth":3},{"id":"沒有標籤也能學","text":"沒有標籤也能學","depth":3},{"id":"和-wl-測試的關係","text":"和 WL 測試的關係","depth":3},{"id":"討論","text":"討論","depth":2},{"id":"三個資料集","text":"三個資料集","depth":3},{"id":"主要結果","text":"主要結果","depth":3},{"id":"速度與取樣的取捨","text":"速度與取樣的取捨","depth":3},{"id":"後來的事","text":"後來的事","depth":3},{"id":"結論","text":"結論","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"graph-learning/gat","relation":"citing","topic":""}]},{"id":"graph-learning/gat","category":"graph-learning","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"GAT","subtitle":"鄰居有輕重","author":"Z. Yuan","paperDate":"2017-10-30","publishedOn":"2023-11-10","world":"external/gat","paperTitle":"Graph Attention Networks","paperUrl":"https://arxiv.org/abs/1710.10903","bibliography":{"title":"Graph Attention Networks","authors":["Petar Veličković","Guillem Cucurull","Arantxa Casanova","Adriana Romero","Pietro Liò","Yoshua Bengio"],"url":"https://arxiv.org/abs/1710.10903","pdfUrl":"https://arxiv.org/pdf/1710.10903v3","codeUrl":"https://github.com/PetarV-/GAT","preprint":{"id":"1710.10903","submittedOn":"2017-10-30","url":"https://arxiv.org/abs/1710.10903"},"publication":{"venue":"ICLR","year":2018,"kind":"conference","url":"https://arxiv.org/abs/1710.10903"},"citation":"@misc{graphlearninggat2017,\n  title = {{Graph Attention Networks}},\n  author = {Petar Veličković and Guillem Cucurull and Arantxa Casanova and Adriana Romero and Pietro Liò and Yoshua Bengio},\n  year = {2017},\n  eprint = {1710.10903},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1710.10903}\n}\n","citationPath":"/content/papers/citations/graph-learning/gat.402fa57986a5.bib"},"thumbnail":{"src":"/content/papers/assets/1a68144470e25ef01f2cd6ccf405483bc7960ca1cc9f7ebf5991060fb3a92010.png","width":1124,"height":540},"locale":"zh-hant","path":"/content/papers/zh-hant/graph-learning/gat.99de7ab34688.md","headings":[{"id":"定義問題","text":"定義問題","depth":2},{"id":"解決問題","text":"解決問題","depth":2},{"id":"一個注意力頭","text":"一個注意力頭","depth":3},{"id":"多頭","text":"多頭","depth":3},{"id":"成本與特性","text":"成本與特性","depth":3},{"id":"討論","text":"討論","depth":2},{"id":"實驗設定","text":"實驗設定","depth":3},{"id":"主要結果","text":"主要結果","depth":3},{"id":"學到了什麼","text":"學到了什麼","depth":3},{"id":"作者自己列的限制","text":"作者自己列的限制","depth":3},{"id":"靜態的注意力","text":"靜態的注意力","depth":3},{"id":"結論","text":"結論","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"graph-learning/gcn","relation":"referenced","topic":""},{"id":"graph-learning/graphsage","relation":"referenced","topic":""}]}],"text":"Reddit 每分鐘都有新貼文，YouTube 每天都有新影片。推薦系統要替這些新節點算出表示，最好當下就能算，不必把整張圖重新訓練一次。\n\n2017 年以前，主流的節點嵌入做不到這件事。DeepWalk、node2vec 這類方法替每個節點各學一個向量，像查表一樣，新節點根本不在表裡。GCN 雖然吃進節點特徵，但論文的設定是整張圖一起訓練，每一步都要把整個鄰接矩陣放進記憶體。\n\nStanford 的 William Hamilton、Rex Ying 與 Jure Leskovec 換了一個學習目標。不學每個節點的向量，改學「怎麼從鄰居算出一個向量」的函數。學會之後，新節點只要有特徵、有鄰居，就能直接套用。\n\n## 定義問題\n\n### transductive 與 inductive\n\n論文把問題分成兩類。\n\n**transductive**（直推式）是訓練時整張圖都看得到，只是部分節點沒有標籤，GCN 論文的半監督分類就是這一類。**inductive**（歸納式）是訓練完之後，還會冒出沒見過的節點，甚至整張新的圖。\n\n作者認為 inductive 更難。模型不能靠「記住」每個節點的位置，得學會從節點的局部結構與特徵，辨認它在圖上扮演的角色；而且要能把新節點放到和舊節點一致的空間裡，訓練時也沒有機會看到它們。\n\n已有的嵌入方法大多是 transductive，硬要支援新節點，就得在測試時再跑幾輪最佳化，很慢。\n\n### 鄰居太多\n\n第二個問題是規模。論文用的 Reddit 資料集有 232,965 篇貼文，同一個使用者留言過的兩篇貼文就連一條邊，平均每篇有 492 條邊。\n\n如果每個節點都要看完整的兩步鄰居，一個節點就可能牽動幾十萬個節點，批次的記憶體和時間都無法預測。\n\n## 解決問題\n\n### 演算法\n\nGraphSAGE 的名字來自 SAmple and aggreGatE，取樣與聚合。前向傳遞跑 $K$ 輪，每一輪對每個節點 $v$ 做三件事：\n\n$$\nh^k_{\\mathcal N(v)}=\\mathrm{AGGREGATE}_k\\left(\\left\\{h^{k-1}_u,\\ \\forall u\\in\\mathcal N(v)\\right\\}\\right)\n$$\n\n$$\nh^k_v=\\sigma\\left(W^k\\cdot\\mathrm{CONCAT}\\left(h^{k-1}_v,\\ h^k_{\\mathcal N(v)}\\right)\\right)\n$$\n\n$$\nh^k_v\\leftarrow h^k_v/\\left\\|h^k_v\\right\\|_2\n$$\n\n先把鄰居的表示聚合成一個向量，再和自己上一輪的表示**串接**，乘上權重矩陣、過非線性，最後做 L2 正規化。$h^0_v$ 是輸入特徵，第 $K$ 輪的輸出就是節點的嵌入。\n\n和 GCN 最大的差別在串接。GCN 把自己當成鄰居之一，一起加權平均，所有人共用一個 $W$；GraphSAGE 把自己和鄰居分開，等於自己和鄰居各用一組權重。作者說這可以看成不同深度之間的一種 skip connection，在實驗裡帶來明顯的進步。\n\n[![原論文 Figure 1：三個步驟由左到右。第一步在目標節點周圍取樣一步與兩步的鄰居；第二步把鄰居的特徵經由聚合器傳回目標節點；第三步用聚合後的表示預測圖的脈絡與標籤。](/content/papers/assets/88f6d9397f20155603c7ee0daef374fb944aac5fb73ed6573a573560b5b9c6aa.png)](/content/papers/assets/88f6d9397f20155603c7ee0daef374fb944aac5fb73ed6573a573560b5b9c6aa.png)\n\n*原論文 Figure 1（Hamilton 等人），取自 [**PDF 第 2 頁**](https://arxiv.org/pdf/1706.02216v4#page=2)。圖說未收錄。*\n\n### 鄰居用抽的\n\n$\\mathcal N(v)$ 不是完整的鄰居集合，而是**均勻抽出的固定數量**鄰居，每一輪重抽一次。第 $i$ 輪抽 $S_i$ 個，一個批次的成本就固定在 $O\\left(\\prod_{i=1}^KS_i\\right)$。作者發現 $K=2$、$S_1\\cdot S_2\\le500$ 就能有很好的表現，實驗用的是 $S_1=25$、$S_2=10$。\n\n拿 Reddit 的平均度數算一下差多少：\n\n```python\nS1, S2 = 25, 10                 # 第一層抽 25 個、第二層每個再抽 10 個\navg_deg = 492                   # Reddit 的平均度數\nprint(\"取樣後，一個目標節點最多牽動:\", 1 + S1 + S1 * S2, \"個節點\")\nprint(\"不取樣、兩步以內的上限:     \", 1 + avg_deg + avg_deg ** 2, \"個節點\")\n```\n\n```text\n取樣後，一個目標節點最多牽動: 276 個節點\n不取樣、兩步以內的上限:      242557 個節點\n```\n\n第二個數字只是粗略的上限，沒有扣掉重複的鄰居，但它已經比整個資料集的 232,965 個節點還多。不取樣的話，算一個節點的兩層表示，幾乎等於把整張圖讀一遍。\n\n小批次訓練時，作者先從目標節點往外取樣出所需的鄰居集合，再由外往內一層一層算回來，只算遞迴真正用到的節點。\n\n接著用一張六個節點的小圖跑一層，每個節點抽 2 個鄰居，聚合器用平均：\n\n```python\nimport numpy as np\nnp.set_printoptions(precision=2, suppress=True)\nrng = np.random.default_rng(0)\n\nneighbors = [[1, 2], [0, 2, 3], [0, 1, 3, 4, 5], [1, 2], [2], [2]]\nh = rng.random((6, 3))                       # 6 個節點、每個 3 維特徵\nW = rng.normal(size=(4, 6))                  # 串接後 6 維 → 輸出 4 維\n\ndef sage_layer(h, neighbors, W, S):\n    out = []\n    for v, nbrs in enumerate(neighbors):\n        picked = rng.choice(nbrs, size=S, replace=len(nbrs) < S)   # 鄰居不夠就重複抽\n        h_nb = h[picked].mean(0)                                    # mean 聚合\n        z = np.maximum(W @ np.concatenate([h[v], h_nb]), 0)          # 自己和鄰居串接後乘 W\n        out.append(z / (np.linalg.norm(z) + 1e-12))                  # L2 正規化\n    return np.array(out)\n\nh1 = sage_layer(h, neighbors, W, S=2)\nprint(h1.shape)\nprint(h1)\n```\n\n```text\n(6, 4)\n[[0.96 0.12 0.   0.24]\n [0.74 0.   0.19 0.64]\n [0.99 0.   0.   0.16]\n [0.95 0.3  0.   0.  ]\n [0.71 0.   0.   0.7 ]\n [0.81 0.   0.13 0.57]]\n```\n\n2 號節點有五個鄰居，每次只抽兩個；4 號、5 號只有一個鄰居，就重複抽。官方的 TensorFlow 實作也是先替每個節點建一張固定長度的鄰居表，鄰居不夠就重複抽到滿，每個批次再從表裡隨機取前 $S$ 個。\n\n權重 $W$ 和節點無關，所以新節點進來時，只要有特徵、有鄰居，同一個 `sage_layer` 就能直接算。\n\n### 三種聚合器\n\n鄰居沒有順序，聚合器最好對輸入的排列保持不變，同時又要可訓練、夠有表達力。作者試了三種：\n\n- **Mean**：逐元素取平均。作者也做了一個 GCN 式的變體，不串接自己，直接對 $\\{v\\}\\cup\\mathcal N(v)$ 取平均再乘權重，稱為 convolutional 聚合器。\n- **LSTM**：表達力最強，但 LSTM 本身會看順序。作者的折衷是每次把鄰居隨機打亂再餵進去。\n- **Pooling**：每個鄰居先各自過一層全連接，再逐元素取最大值，$\\max\\left(\\left\\{\\sigma\\left(W_{\\text{pool}}h_{u}+b\\right)\\right\\}\\right)$。作者說把 max 換成 mean，差異不大。\n\n### 沒有標籤也能學\n\n如果沒有任務標籤，GraphSAGE 用一個以圖為基礎的損失：\n\n$$\nJ_{\\mathcal G}(z_u)=-\\log\\sigma\\left(z_u^\\top z_v\\right)-Q\\cdot\\mathbb{E}_{v_n\\sim P_n(v)}\\log\\sigma\\left(-z_u^\\top z_{v_n}\\right)\n$$\n\n$v$ 是在固定長度的隨機遊走上，出現在 $u$ 附近的節點；$v_n$ 是負樣本，$Q$ 是負樣本數。這和 DeepWalk 的目標很像，差別在 $z_u$ 是由鄰居特徵算出來的，不是查表查來的。有標籤時，把這個損失換成或加上交叉熵就好。\n\n### 和 WL 測試的關係\n\n作者也指出，把 $K$ 設成節點數、權重設成單位矩陣、聚合器換成雜湊函數，演算法 1 就是 Weisfeiler-Lehman 圖同構測試。GraphSAGE 可以看成 WL 的連續近似，只是目的是學出有用的節點表示，不是判斷同構。\n\n## 討論\n\n### 三個資料集\n\n實驗都是 inductive 的設定：\n\n- **引用網路**：Web of Science 中六個生物相關領域、2000 到 2005 年的論文，共 302,424 個節點，平均度數 9.15。用 2000 到 2004 年訓練，預測 2005 年論文的領域。\n- **Reddit**：2014 年 9 月、50 個大型子版的貼文。用前 20 天訓練，預測之後貼文屬於哪個子版。\n- **PPI**：蛋白質交互作用圖，一張圖對應一種人體組織。20 張圖訓練、2 張驗證、2 張測試，預測蛋白質的 121 種功能標籤。測試的圖在訓練時完全沒見過。\n\n### 主要結果\n\n| 方法 | 引用網路 | Reddit | PPI |\n| --- | ---: | ---: | ---: |\n| 原始特徵 | 0.575 | 0.585 | 0.422 |\n| DeepWalk + 特徵 | 0.701 | 0.691 | — |\n| GraphSAGE-GCN | 0.772 | 0.930 | 0.500 |\n| GraphSAGE-mean | 0.820 | 0.950 | 0.598 |\n| GraphSAGE-LSTM | 0.832 | **0.954** | **0.612** |\n| GraphSAGE-pool | **0.839** | 0.948 | 0.600 |\n\n表中是監督式版本的 micro-F1。GCN 式的聚合器在三個資料集都墊底，PPI 上和其他三種差了將近 0.1；它和 mean 版本最大的差別，就是少了自己與鄰居的串接。\n\n作者用 Wilcoxon 檢定比較六組設定（三個資料集各有監督與非監督兩種），LSTM、pool、mean 都顯著勝過 GCN 式的聚合器；LSTM 和 pool 之間沒有顯著差異。作者也承認只有六組設定，檢定力有限。\n\nLSTM 本來是給序列用的，在無序的鄰居集合上也表現很好，作者自己也覺得有點意外。\n\n### 速度與取樣的取捨\n\n[![原論文 Figure 2：A 圖是 Reddit 上的訓練與測試時間，DeepWalk 的測試時間遠高於各種 GraphSAGE；B 圖是引用網路上，每層取樣的鄰居數從個位數增加到 60 多個時，micro-F1 從約 0.78 升到約 0.83，執行時間則一路上升。](/content/papers/assets/ff62a5b1aa8349edd927fc6014fd713adf4d76672507efe1a7527ff6ecdc45ca.png)](/content/papers/assets/ff62a5b1aa8349edd927fc6014fd713adf4d76672507efe1a7527ff6ecdc45ca.png)\n\n*原論文 Figure 2（Hamilton 等人），取自 [**PDF 第 7 頁**](https://arxiv.org/pdf/1706.02216v4#page=7)。圖說未收錄。*\n\n左圖裡，各方法的訓練時間差不多，GraphSAGE-LSTM 最慢；但到了測試，DeepWalk 得替新節點重新跑隨機遊走和 SGD，比 GraphSAGE 慢了 100 到 500 倍。\n\n右圖是取樣數量的影響。鄰居抽得越多，F1 越高，但報酬遞減得很快，時間卻持續增加。作者也發現，$K=2$ 比 $K=1$ 平均好 10% 到 15%；再往上加到 $K>2$，只多 0% 到 5%，執行時間卻多了 10 到 100 倍。\n\n### 後來的事\n\n論文最後提到，想要更好的 PPI 成績，可以參考 Chen 與 Zhu 的後續工作，他們專門替 PPI 調了 GraphSAGE 的超參數，又加上 dropout、layer normalization 和新的取樣方法。\n\n隔年，Rex Ying、Hamilton、Leskovec 和 Pinterest 合作發表了 PinSage，在一張 30 億個節點的圖上，用 75 億筆樣本訓練，部署成 Pinterest 的推薦引擎。[PinSage 摘要](https://arxiv.org/abs/1806.01973v1)\n\n## 結論\n\nGraphSAGE 讓圖神經網路學的是一個函數，不是一張查表。鄰居用抽的，批次的成本固定下來，大圖也能用小批次訓練；自己和鄰居分開乘權重，就比 GCN 式的平均好上一截。\n\n要在自己的大圖上用，先從 $K=2$、$S_1=25$、$S_2=10$ 開始，mean 聚合器就夠當基準。PyTorch Geometric 有 `SAGEConv` 和配套的 `NeighborLoader`，取樣和小批次都不用自己寫。\n","imageSizes":{"/content/papers/assets/88f6d9397f20155603c7ee0daef374fb944aac5fb73ed6573a573560b5b9c6aa.png":{"width":1090,"height":384},"/content/papers/assets/ff62a5b1aa8349edd927fc6014fd713adf4d76672507efe1a7527ff6ecdc45ca.png":{"width":1092,"height":336}}}
