【Graph Embedding】node2vec:算法原理,实现和应用
前面介紹過基于DFS鄰域的DeepWalk和基于BFS鄰域的LINE。
DeepWalk:算法原理,實現和應用
LINE:算法原理,實現和應用
node2vec是一種綜合考慮DFS鄰域和BFS鄰域的graph embedding方法。簡單來說,可以看作是eepwalk的一種擴展,可以看作是結合了DFS和BFS隨機游走的deepwalk。
nodo2vec 算法原理
優化目標
設f(u)f(u)f(u)是將頂點uuu映射為embedding向量的映射函數,對于圖中每個頂點uuu,定義NS(u)N_S(u)NS?(u)為通過采樣策略SSS采樣出的頂點uuu的近鄰頂點集合。
node2vec優化的目標是給定每個頂點條件下,令其近鄰頂點出現的概率最大。
maxf∑u∈Vlog?Pr(NS(U)∣f(u))max_f {\sum_{u\in V}\log{Pr(N_S(U)|f(u))}}maxf?∑u∈V?logPr(NS?(U)∣f(u))
為了將上述最優化問題可解,文章提出兩個假設:
- 條件獨立性假設
假設給定源頂點下,其近鄰頂點出現的概率與近鄰集合中其余頂點無關。
Pr(Ns(u)∣f(u))=∏ni∈Ns(u)Pr(ni∣f(u))Pr(N_s(u)|f(u))=\prod_{n_i\in N_s(u)} Pr(n_i|f(u))Pr(Ns?(u)∣f(u))=∏ni?∈Ns?(u)?Pr(ni?∣f(u)) - 特征空間對稱性假設
這里是說一個頂點作為源頂點和作為近鄰頂點的時候共享同一套embedding向量。(對比LINE中的2階相似度,一個頂點作為源點和近鄰點的時候是擁有不同的embedding向量的)
在這個假設下,上述條件概率公式可表示為Pr(ni∣f(u))=exp?f(ni)?f(u)∑v∈Vexp?f(v)?f(u)Pr(n_i|f(u))=\frac{\exp{f(n_i)\cdot f(u)}}{\sum_{v\in V}{\exp{f(v)\cdot f(u)}}}Pr(ni?∣f(u))=∑v∈V?expf(v)?f(u)expf(ni?)?f(u)?
根據以上兩個假設條件,最終的目標函數表示為
maxf∑u∈V[?log?Zu+∑ni∈Ns(u)f(ni)?f(u)]max_f{\sum_{u\in V}[-\log{Z_u}+\sum_{n_i\in N_s(u)}{f(n_i)\cdot f(u)}]}maxf?∑u∈V?[?logZu?+∑ni?∈Ns?(u)?f(ni?)?f(u)]
由于歸一化因子Zu=∑ni∈Ns(u)exp?(f(ni)?f(u))Z_u=\sum_{n_i\in N_s(u)}{\exp(f(n_i)\cdot f(u))}Zu?=∑ni?∈Ns?(u)?exp(f(ni?)?f(u))的計算代價高,所以采用負采樣技術優化。
采樣策略
node2vec依然采用隨機游走的方式獲取頂點的近鄰序列,不同的是node2vec采用的是一種有偏的隨機游走。
給定當前頂點vvv,訪問下一個頂點xxx的概率為
P(ci=x∣ci?1=v)={πvxZif?(v,x)∈E0otherwiseP(c_i=x|c_{i-1}=v)=\left\{ \begin{aligned} \frac{\pi_ {vx}}{Z} & & \text{if }(v,x)\in E \\ 0 & & \text{otherwise} \\ \end{aligned} \right. P(ci?=x∣ci?1?=v)=????Zπvx??0??if?(v,x)∈Eotherwise?
πvx\pi_{vx}πvx?是頂點vvv和頂點xxx之間的未歸一化轉移概率,ZZZ是歸一化常數。
node2vec引入兩個超參數ppp和qqq來控制隨機游走的策略,假設當前隨機游走經過邊(t,v)(t,v)(t,v)到達頂點vvv
設πvx=αpq(t,x)?wvx\pi_{vx}=\alpha_{pq}(t,x)\cdot w_{vx}πvx?=αpq?(t,x)?wvx?,wvxw_{vx}wvx?是頂點vvv和xxx之間的邊權,
αpq(t,x)={1p=if?dtx=01=if?dtx=11q=if?dtx=2\alpha_{pq}(t,x)=\left\{ \begin{aligned} \frac{1}{p} & = & \text{if }d_{tx}=0\\ 1 & = & \text{if }d_{tx}=1\\ \frac{1}{q} & = & \text{if }d_{tx}=2\\ \end{aligned} \right. αpq?(t,x)=??????????????p1?1q1??===?if?dtx?=0if?dtx?=1if?dtx?=2?
dtxd_{tx}dtx?為頂點ttt和頂點xxx之間的最短路徑距離。
下面討論超參數ppp和qqq對游走策略的影響
- Return parameter,p
參數ppp控制重復訪問剛剛訪問過的頂點的概率。
注意到ppp僅作用于dtx=0d_{tx}=0dtx?=0的情況,而dtx=0d_{tx}=0dtx?=0表示頂點xxx就是訪問當前頂點vvv之前剛剛訪問過的頂點。
那么若ppp較高,則訪問剛剛訪問過的頂點的概率會變低,反之變高。 - In-out papameter,q
qqq控制著游走是向外還是向內,若q>1,隨機游走傾向于訪問和ttt接近的頂點(偏向BFS)。若q<1q<1q<1,傾向于訪問遠離ttt的頂點(偏向DFS)。
下面的圖描述的是當從t訪問到v時,決定下一個訪問頂點時每個頂點對應的α\alphaα。
學習算法
采樣完頂點序列后,剩下的步驟就和deepwalk一樣了,用word2vec去學習頂點的embedding向量。
值得注意的是node2vecWalk中不再是隨機抽取鄰接點,而是按概率抽取,node2vec采用了Alias算法進行頂點采樣。
Alias Method:時間復雜度O(1)的離散采樣方法
node2vec核心代碼
node2vecWalk
通過上面的偽代碼可以看到,node2vec和deepwalk非常類似,主要區別在于頂點序列的采樣策略不同,所以這里我們主要關注node2vecWalk的實現。
由于采樣時需要考慮前面2步訪問過的頂點,所以當訪問序列中只有1個頂點時,直接使用當前頂點和鄰居頂點之間的邊權作為采樣依據。
當序列多余2個頂點時,使用文章提到的有偏采樣。
構造采樣表
preprocess_transition_probs分別生成alias_nodes和alias_edges,alias_nodes存儲著在每個頂點時決定下一次訪問其鄰接點時需要的alias表(不考慮當前頂點之前訪問的頂點)。alias_edges存儲著在前一個訪問頂點為ttt,當前頂點為vvv時決定下一次訪問哪個鄰接點時需要的alias表。
get_alias_edge方法返回的是在上一次訪問頂點ttt,當前訪問頂點為vvv時到下一個頂點xxx的未歸一化轉移概率πvx=αpq(t,x)?wvx\pi_{vx}=\alpha_{pq}(t,x)\cdot w_{vx}πvx?=αpq?(t,x)?wvx?
def get_alias_edge(self, t, v):G = self.G p = self.p q = self.qunnormalized_probs = [] for x in G.neighbors(v): weight = G[v][x].get('weight', 1.0)# w_vx if x == t:# d_tx == 0 unnormalized_probs.append(weight/p) elif G.has_edge(x, t):# d_tx == 1 unnormalized_probs.append(weight) else:# d_tx == 2 unnormalized_probs.append(weight/q) norm_const = sum(unnormalized_probs) normalized_probs = [float(u_prob)/norm_const for u_prob in unnormalized_probs]return create_alias_table(normalized_probs)def preprocess_transition_probs(self):G = self.Galias_nodes = {} for node in G.nodes(): unnormalized_probs = [G[node][nbr].get('weight', 1.0) for nbr in G.neighbors(node)] norm_const = sum(unnormalized_probs) normalized_probs = [float(u_prob)/norm_const for u_prob in unnormalized_probs] alias_nodes[node] = create_alias_table(normalized_probs)alias_edges = {}for edge in G.edges(): alias_edges[edge] = self.get_alias_edge(edge[0], edge[1])self.alias_nodes = alias_nodes self.alias_edges = alias_edgesreturnnode2vec應用
使用node2vec在wiki數據集上進行節點分類任務和可視化任務。 wiki數據集包含 2,405 個網頁和17,981條網頁之間的鏈接關系,以及每個網頁的所屬類別。 通過簡單的超參搜索,這里使用p=0.25,q=4的設置。
本例中的訓練,評測和可視化的完整代碼在下面的git倉庫中,
https://github.com/shenweichen/GraphEmbedding
G = nx.read_edgelist('../data/wiki/Wiki_edgelist.txt',create_using=nx.DiGraph(),nodetype=None,data=[('weight',int)])model = Node2Vec(G,walk_length=10,num_walks=80,p=0.25,q=4,workers=1) model.train(window_size=5,iter=3) embeddings = model.get_embeddings()evaluate_embeddings(embeddings) plot_embeddings(embeddings)分類任務
micro-F1: 0.6757
macro-F1: 0.5917
這個結果相比于DeepWalk和LINE是有提升的。
可視化
這個結果相比于DeepWalk和LINE可以看到不同類別的分布更加分散了。
參考資料
- Grover A, Leskovec J. node2vec: Scalable Feature Learning for Networks[C]// Acm Sigkdd International Conference on Knowledge Discovery & Data Mining. 2016.
圖算法干貨匯總
我把近年來主流的圖表示學習方法的paper和對應的代碼實現進行了匯總整理,掃碼關注公眾號【淺夢的學習筆記】,后臺回復【圖算法】即可打包下載。
歡迎加入星球群,一個由1300+小伙伴共建的交流平臺,專注于前沿graph embedding算法技術與實踐經驗的分享學習。
總結
以上是生活随笔為你收集整理的【Graph Embedding】node2vec:算法原理,实现和应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux svnadmin,linux
- 下一篇: Debian10: 安装iF.SVNAd