最近看了“Graph Convolutional Neural Networks for Web-Scale Recommender Systems”這篇文章,是Pinterest將GCN成功應用在大規模真實場景的論文,唯一可惜的是沒有公開源碼。
論文下載地址:https://arxiv.org/pdf/1806.01973
論文包含了理論創新和實際落地實現中的一些工程優化。這裏對算法理論這塊做一下簡單記錄。
這篇文章雖然說是GCN算法,但是全文看下來其實和卷積並沒有很大的關係。GCN算法大多數都是端到端的計算,需要在整個graph上訓練。這樣的話很難將算法擴展應用到實際的大規模工業應用上。
所以文章提出了一個局部卷積的概念,不在全局的graph上優化算法,而是給特定的節點形成一個包含有限領域節點的子圖,在子圖上構造局部卷積,然後不同節點共享同樣的局部卷積參數,也許正是因爲要共享參數,所以作者把這個叫做卷積吧。
局部卷積
整個算法中,局部卷積算法'CONVOLVE'應該是最核心的部分。
這個CONVOLVE是逐點優化的算法,所以輸入是當前計算的節點u的embedding,以及它所對應的領域節點的embedding。而具體的卷積操作其實就是一些全聯接構造成的映射。
分析一下上圖的後面三行僞代碼。
第一行裏面的指的是領域節點v的embedding,這裏感覺作者沒寫清楚,我剛開始也沒看明白,後來看了圖纔看明白。
一個CONVOLVE模塊(流程圖中的那三行僞代碼)就是如下圖這樣的一個模塊:
先是對節點的領域節點經過Q映射後,再利用weight-pooling函數讓輸出的維度和輸入保持一致,生成所有領域節點統一的embedding向量。
第二行的僞代碼描述的是節點embedding的更新,直接把上一層或者初始的embedding和領域節點embedding一起concate起來,再加上一層全聯接就可以生成新的節點embedding。第三行的代碼只是對輸出的節點embedding做了L2歸一化,讓訓練更穩定。
這一個CONVOLVE裏的參數,比如Q,q,W,w這些都是共享的,每個節點都一樣。
怎麼用
現在最核心的算法模塊有了,需要先構造輸入,輸入是按節點迭代,那麼每次輸入CONVOLVE的就是當前節點,和選擇出來的領域。那麼領域怎麼選?
Importance-based neighborhoods.
作者爲了統一每個節點的領域個數,已經進一步引入每個領域節點對當前節點的重要性,採用了隨機遊走的策略來生成節點的領域。並且通過計算隨機遊走對頂點的訪問次數的 𝐿1 歸一化值。來定義領域節點的重要性,按對定點的訪問次數排序後取top-T個節點作爲當前節點的領域。
在分析代碼流程圖的時候,裏面的weight-pooling函數的weight方式並沒有提到,其實就是這裏這裏隨機遊走產生的這個L1歸一化值。
其實到這裏這個算法也勉強能用了,不過作者爲了讓這個算法更像卷積,進一步將CONVOLVE模塊進行了stack。
Stacking convolutions.
思路比較簡單,就是把CONVOLVE輸出的embedding,再傳入一個CONVOLVE,類似多層全聯接一樣,連起來。代碼寫起來可能會比較麻煩了,因爲不同節點的領域不一樣,那麼堆疊到第二層的時候,輸入CONVOLVE的節點就是上一層CONVOLVE的minibatch的節點的領域的領域。有點拗口。具體流程圖如下:
具體分兩部分。
第一部分,首先把每一層裏節點的領域都計算好。(流程圖裏smpling neighborhoods of minibatch nodes下的代碼)
第二部分就是循環計算每一層的CONVOLVE,把上一層CONVOLVE的輸出作爲下一層CONVOLVE的輸入。
算法的最後是把最後一層CONVOLVE的輸出再經過G1和G2做全聯接映射後輸出最終的節點embedding。
這裏需要注意的是,前面我說過一個CONVOLVE的參數都是共享,這裏的共享指的是同一層的CONVOLVE。對應不同層之間的CONVOLVE不共享參數。
能發現,這整個網絡結構確實很像一個多層卷積網絡,輸入是節點和節點領域embedding,輸出是新的節點embedding。
這個針對不同任務已經完全能夠遷移作爲backbone。
Loss function.
graph embedding和GNN的區別其中的一點是一個是無監督的,一個是有監督。
作者定義的損失函數是hinge loss:
像這種loss,以及雙通道結構的網絡結構在工業界好像很受歡迎。
文章後面還寫了一些實際實現工程中的加速優化,這裏就不說了。大家可以直接去看原文,或者這裏看這哥們翻譯的文章。
完