圖神經網絡-圖遊走算法核心代碼DeepWalk實現

2021-03-02 從零起步學習人工智慧

本文主要涉及圖遊走算法DeepWalk的代碼實現。

1. DeepWalk採樣算法

對於給定的節點,DeepWalk會等概率的選取下一個相鄰節點加入路徑,直至達到最大路徑長度,或者沒有下一個節點可選。Graph類的實現可參考 https://github.com/PaddlePaddle/PGL/blob/main/pgl/graph.py,DeepWalk的代碼詳見 ./deepwalk.py

安裝依賴

構建一張圖,其中包含了10個節點以及14條邊

請實現Graph類的random_walk函數

%%writefile userdef_graph.pyfrom pgl.graph import Graph
import numpy as npfrom pgl.utils.logger import logclass UserDefGraph(Graph): def random_walk(self, nodes, walk_len): """ 輸入:nodes - 當前節點id list (batch_size,) walk_len - 最大路徑長度 int 輸出:以當前節點為起點得到的路徑 list (batch_size, walk_len)
用到的函數 1. self.successor(nodes) 描述:獲取當前節點的下一個相鄰節點id列表 輸入:nodes - list (batch_size,) 輸出:succ_nodes - list of list ((num_successors_i,) for i in range(batch_size)) 2. self.outdegree(nodes) 描述:獲取當前節點的出度 輸入:nodes - list (batch_size,) 輸出:out_degrees - list (batch_size,) """ walks = [[node] for node in nodes] walks_ids = np.arange(0, len(nodes)) cur_nodes = np.array(nodes) for l in range(walk_len): """選取有下一個節點的路徑繼續採樣,否則結束""" outdegree = self.outdegree(cur_nodes) walk_mask = (outdegree != 0) if not np.any(walk_mask): break cur_nodes = cur_nodes[walk_mask] walks_ids = walks_ids[walk_mask] outdegree = outdegree[walk_mask]
succ_nodes = self.successor(cur_nodes) sample_index = np.floor( np.random.rand(outdegree.shape[0]) * outdegree).astype("int64")
next_nodes = [] for s, ind, walk_id in zip(succ_nodes, sample_index, walks_ids): walks[walk_id].append(s[ind]) next_nodes.append(s[ind]) cur_nodes = np.array(next_nodes) log.info(walks ) return walks

PGL官網的random_walk原始碼:

    def random_walk(self, nodes, max_depth):        """Implement of random walk.        This function get random walks path for given nodes and depth.        Args:            nodes: Walk starting from nodes            max_depth: Max walking depth        Return:            A list of walks.        """        walk = []        for node in nodes:            walk.append([node])
cur_walk_ids = np.arange(0, len(nodes)) cur_nodes = np.array(nodes) for l in range(max_depth): outdegree = self.outdegree(cur_nodes) mask = (outdegree != 0) if np.any(mask): cur_walk_ids = cur_walk_ids[mask] cur_nodes = cur_nodes[mask] outdegree = outdegree[mask] else: break succ = self.successor(cur_nodes) sample_index = np.floor( np.random.rand(outdegree.shape[0]) * outdegree).astype("int64")
nxt_cur_nodes = [] for s, ind, walk_id in zip(succ, sample_index, cur_walk_ids): walk[walk_id].append(s[ind]) nxt_cur_nodes.append(s[ind]) cur_nodes = np.array(nxt_cur_nodes) return walk

運行腳本:

!python my_deepwalk.py --use_my_random_walk --epoch 5 

運行結果:

[INFO] 2021-02-13 17:27:03,835 [my_deepwalk.py:  302]:  Namespace(batch_size=512, epoch=5, hidden_size=128, neg_num=20, processes=2, save_path='./tmp/deepwalk', use_my_random_walk=True, walk_len=5, win_size=5)[INFO] 2021-02-13 17:27:04,651 [my_deepwalk.py:  219]:  Start random walk on disk...[INFO] 2021-02-13 17:27:04,675 [userdef_graph.py:   51]:  [[9, 7, 2, 0], [8, 0], [6, 5, 0], [0], [2, 1], [3, 1], [1], [5, 0], [7, 3, 1], [4, 0]][INFO] 2021-02-13 17:27:04,675 [userdef_graph.py:   51]:  [[4, 0], [0], [2, 1], [7, 1], [5, 0], [8, 0], [1], [6, 5, 0], [9, 7, 3, 1], [3, 1]][INFO] 2021-02-13 17:27:04,677 [userdef_graph.py:   51]:  [[6, 0], [7, 3, 1], [5, 0], [4, 0], [3, 1], [8, 0], [2, 0], [1], [0], [9, 7, 0]][INFO] 2021-02-13 17:27:04,677 [userdef_graph.py:   51]:  [[9, 7, 1], [5, 0], [3, 1], [7, 2, 1], [2, 1], [0], [8, 0], [4, 0], [1], [6, 5, 0]][INFO] 2021-02-13 17:27:04,678 [userdef_graph.py:   51]:  [[9, 7, 0], [8, 0], [0], [4, 0], [1], [2, 1], [5, 0], [3, 1], [7, 3, 1], [6, 4, 0]][INFO] 2021-02-13 17:27:04,679 [my_deepwalk.py:  230]:  Random walk on disk Done.2021-02-13 17:27:04,680-WARNING: paddle.fluid.layers.py_reader() may be deprecated in the near future. Please use paddle.fluid.io.DataLoader.from_generator() instead.[INFO] 2021-02-13 17:27:04,730 [my_deepwalk.py:  278]:  Step 1 DeepWalk Loss: 0.718020  0.020003 s/step.

附錄:my_deepwalk.py

"""DeepWalk代碼文件"""
import argparseimport timeimport osimport ioimport mathfrom multiprocessing import Poolimport glob
import numpy as np
from pgl import data_loaderfrom pgl.utils.logger import logimport paddle.fluid as fluidimport paddle.fluid.layers as l

def deepwalk_model(graph, hidden_size=16, neg_num=5): """ 該函數為Skip Gram模型部分,即課堂所講的 Skip Gram + 負採樣 函數參數含義: graph: 圖 hidden_size: 節點維度 neg_num: 負採樣數目 """
pyreader = l.py_reader( capacity=70, shapes=[[-1, 1, 1], [-1, 1, 1], [-1, neg_num, 1]], dtypes=['int64', 'int64', 'int64'], lod_levels=[0, 0, 0], name='train', use_double_buffer=True)
embed_init = fluid.initializer.UniformInitializer(low=-1.0, high=1.0) weight_init = fluid.initializer.TruncatedNormal(scale=1.0 / math.sqrt(hidden_size))
src, pos, negs = l.read_file(pyreader)
embed_src = l.embedding( input=src, size=[graph.num_nodes, hidden_size], param_attr=fluid.ParamAttr( name='content', initializer=embed_init))
weight_pos = l.embedding( input=pos, size=[graph.num_nodes, hidden_size], param_attr=fluid.ParamAttr( name='weight', initializer=weight_init)) weight_negs = l.embedding( input=negs, size=[graph.num_nodes, hidden_size], param_attr=fluid.ParamAttr( name='weight', initializer=weight_init))
pos_logits = l.matmul( embed_src, weight_pos, transpose_y=True) neg_logits = l.matmul( embed_src, weight_negs, transpose_y=True)
ones_label = pos_logits * 0. + 1. ones_label.stop_gradient = True pos_loss = l.sigmoid_cross_entropy_with_logits(pos_logits, ones_label)
zeros_label = neg_logits * 0. zeros_label.stop_gradient = True neg_loss = l.sigmoid_cross_entropy_with_logits(neg_logits, zeros_label)
loss = (l.reduce_mean(pos_loss) + l.reduce_mean(neg_loss)) / 2
return pyreader, loss

def gen_pair(walks, left_win_size=2, right_win_size=2): """ 該函數用於生成正樣本對 函數參數含義: walks: 多條節點遊走序列 left_win_size: 左窗口值大小 right_win_size: 右窗口值大小 """ src = [] pos = [] for walk in walks: for left_offset in range(1, left_win_size + 1): src.extend(walk[left_offset:]) pos.extend(walk[:-left_offset]) for right_offset in range(1, right_win_size + 1): src.extend(walk[:-right_offset]) pos.extend(walk[right_offset:]) src, pos = np.array(src, dtype=np.int64), np.array(pos, dtype=np.int64) src, pos = np.expand_dims(src, -1), np.expand_dims(pos, -1) src, pos = np.expand_dims(src, -1), np.expand_dims(pos, -1) return src, pos

def deepwalk_generator(graph, batch_size=512, walk_len=5, win_size=2, neg_num=5, epoch=200, filelist=None): """ 此函數用於生成訓練所需要的(中心節點、正樣本、負樣本) """ def walks_generator(): if filelist is not None: bucket = [] for filename in filelist: with io.open(filename) as inf: for line in inf: walk = [int(x) for x in line.strip('\n').split(' ')] bucket.append(walk) if len(bucket) == batch_size: yield bucket bucket = [] if len(bucket): yield bucket else: for _ in range(epoch): for nodes in graph.node_batch_iter(batch_size): walks = graph.random_walk(nodes, walk_len) yield walks
def wrapper(): for walks in walks_generator(): src, pos = gen_pair(walks, win_size, win_size) if src.shape[0] == 0: continue negs = graph.sample_nodes([len(src), neg_num, 1]).astype(np.int64) yield [src, pos, negs]
return wrapper

def process(args): idx, graph, save_path, epoch, batch_size, walk_len, seed = args with open('%s/%s' % (save_path, idx), 'w') as outf: for _ in range(epoch): np.random.seed(seed) for nodes in graph.node_batch_iter(batch_size): walks = graph.random_walk(nodes, walk_len) for walk in walks: outf.write(' '.join([str(token) for token in walk]) + '\n')

def main(args): """ 主函數 """ hidden_size = args.hidden_size neg_num = args.neg_num epoch = args.epoch save_path = args.save_path batch_size = args.batch_size walk_len = args.walk_len win_size = args.win_size
if not os.path.isdir(save_path): os.makedirs(save_path) dataset = data_loader.ArXivDataset()
num_nodes = 10
edge_list = [(2, 0), (2, 1), (3, 1),(4, 0), (5, 0), (6, 0), (6, 4), (6, 5), (7, 0), (7, 1), (7, 2), (7, 3), (8, 0), (9, 7)]
d = 16 feature = np.random.randn(num_nodes, d).astype("float32")
edge_feature = np.random.randn(len(edge_list), 1).astype("float32")


if args.use_my_random_walk: from userdef_graph import UserDefGraph ''' pgl_graph = dataset.graph dataset.graph = UserDefGraph(num_nodes=pgl_graph.num_nodes, edges=pgl_graph.edges, node_feat=pgl_graph.node_feat, edge_feat=pgl_graph.edge_feat) ''' dataset.graph = UserDefGraph(num_nodes = num_nodes, edges = edge_list, node_feat = {'feature':feature}, edge_feat ={'edge_feature': edge_feature})


log.info("Start random walk on disk...") walk_save_path = os.path.join(save_path, "walks") if not os.path.isdir(walk_save_path): os.makedirs(walk_save_path) pool = Pool(args.processes) args_list = [(x, dataset.graph, walk_save_path, 1, batch_size, walk_len, np.random.randint(2**32)) for x in range(epoch)] pool.map(process, args_list) filelist = glob.glob(os.path.join(walk_save_path, "*")) log.info("Random walk on disk Done.")
train_steps = int(dataset.graph.num_nodes / batch_size) * epoch
place = fluid.CPUPlace() deepwalk_prog = fluid.Program() startup_prog = fluid.Program()
with fluid.program_guard(deepwalk_prog, startup_prog): with fluid.unique_name.guard(): deepwalk_pyreader, deepwalk_loss = deepwalk_model( dataset.graph, hidden_size=hidden_size, neg_num=neg_num) lr = 0.0001 adam = fluid.optimizer.Adam(lr) adam.minimize(deepwalk_loss)
deepwalk_pyreader.decorate_tensor_provider( deepwalk_generator( dataset.graph, batch_size=batch_size, walk_len=walk_len, win_size=win_size, epoch=epoch, neg_num=neg_num, filelist=filelist))
deepwalk_pyreader.start()
exe = fluid.Executor(place) exe.run(startup_prog)
prev_time = time.time() step = 0
while 1: try: deepwalk_loss_val = exe.run(deepwalk_prog, fetch_list=[deepwalk_loss], return_numpy=True)[0] cur_time = time.time() use_time = cur_time - prev_time prev_time = cur_time step += 1 if step == 1 or step % 10 == 0: log.info("Step %d " % step + "DeepWalk Loss: %f " % deepwalk_loss_val + " %f s/step." % use_time) except fluid.core.EOFException: deepwalk_pyreader.reset() break
fluid.io.save_persistables(exe, os.path.join(save_path, "paddle_model"), deepwalk_prog)

if __name__ == '__main__': parser = argparse.ArgumentParser(description='deepwalk') parser.add_argument("--use_my_random_walk", action='store_true', help="use_my_random_walk") parser.add_argument("--hidden_size", type=int, default=128) parser.add_argument("--neg_num", type=int, default=20) parser.add_argument("--epoch", type=int, default=1) parser.add_argument("--batch_size", type=int, default=512) parser.add_argument("--walk_len", type=int, default=5) parser.add_argument("--win_size", type=int, default=5) parser.add_argument("--save_path", type=str, default="./tmp/deepwalk") parser.add_argument("--processes", type=int, default=2) args = parser.parse_args() log.info(args) main(args)

相關焦點

  • 【Graph Embedding】DeepWalk算法原理,實現和應用
    本文首先從整體介紹一下圖表示學習,然後分別從原理,核心代碼,應用三個部分介紹 DeepWalk 。
  • DeepWalk:圖網絡與NLP的巧妙融合
    :https://github.com/phanein/deepwalkenjoy~TL;DRDeepWalk是首次將深度學習技術(無監督學習)引入到網絡分析(network analysis)中的工作,它的輸入是一個圖,最終目標就是獲得網絡圖中每個結點的向量表示
  • 網絡表達學習系列(一):深度遊走(Deepwalk)
    什麼是網絡表達學習?網絡表達學習嗎,顧名思義,是指學習出一個網絡的低維度隱含表達。網絡表達學習的目標是將一個複雜網絡中的結點和邊的信息都通過向量來表達,我們通過算法可以學習出這些向量,因此這些向量中包含著網絡的結構信息。學習得到的向量可以作為圖的特徵用在基於圖的各種任務上,比如連接預測,節點分類,社區發現以及可視化等問題上。
  • 神經網絡圖的簡介(基本概念,DeepWalk以及GraphSage算法)
    GNN在對圖節點之間依賴關係進行建模的強大功能使得與圖分析相關的研究領域取得了突破。 本文旨在介紹圖形神經網絡的基礎知識兩種較高級的算法,DeepWalk和GraphSage。 圖 在我們學習GAN之前,大家先了解一下什麼圖。
  • 從圖網絡表示到圖神經網絡
    首先, 如何表示一張圖, 圖包含節點和連接的信息, 我們用兩個矩陣, A和X來表達相鄰連接和節點的信息。然後,我們來看用機器學習研究網絡問題的核心方法, 也就是如何把網絡核心的信息壓縮到機器學習模型裡。而你除了A和X不知道網絡的任何信息。你可以怎麼做呢?首先這個網絡核心信息可以是什麼呢?
  • 圖神經網絡-圖採樣Graphsage代碼實現
    二 Graphsage 採樣代碼實踐GraphSage的PGL完整代碼實現位於https://github.com/PaddlePaddle/PGL/tree/main/examples/graphsage,本文實現一個簡單的graphsage 採樣代碼 。
  • 使用DeepWalk從圖中提取特徵
    通常是諸如社交網絡,網際網路,已連接的IoT設備,鐵路網絡或電信網絡之類的事物。在圖論中,這些網絡稱為圖。網絡是互連節點的集合。節點表示實體,它們之間的連接是某種關係。這些向量能夠捕獲有關周圍節點的信息(上下文信息)用於學習節點嵌入的兩個重要的現代算法是DeepWalk和Node2Vec。在本文中,我們將介紹並實現DeepWalk算法。
  • 一文帶你入門圖神經網絡基礎、DeepWalk及GraphSage
    最近,圖神經網絡(GNN)在各種領域越來越流行,包括社交網絡,知識圖,推薦系統,甚至生命科學。
  • 萬物皆可Embedding之Deepwalk算法解讀
    算法思路DeepWalk將一個圖作為輸入,並產生一個潛在表示(將圖中的每個節點表示為一個向量)作為輸出,如下圖示例所示:對於圖結構來說,算法設計需要滿足以下幾個要求:1、適應性:社交網絡是不斷變化的,當網絡發生變化不能對整個網絡重新進行計算。
  • 強化學習、聯邦學習、圖神經網絡,飛槳全新工具組件詳解
    更多內容可參考:https://github.com/PaddlePaddle/PALMPGL 圖神經網絡近幾年來,深度神經網絡的成功推動了人工智慧的發展,然而,在實際場景中,有大量的數據是在非歐式空間的,這限制了深度神經網絡的應用。
  • 圖神經網絡概述第三彈:來自IEEE Fellow的GNN綜述
    許多網絡嵌入算法都是無監督算法,它們大致可分為三組 [32],即矩陣分解 [38], [39]、隨機遊走 [40] 和深度學習方法。用於網絡嵌入的深度學習方法同時還屬於圖神經網絡,包括基於圖自編碼器的算法(如 DNGR [41] 和 SDNE [42])和具有無監督訓練的圖卷積神經網絡(如 GraphSage [24])。圖 2 描述了本文中網絡嵌入和圖神經網絡的區別。
  • AI從入門到放棄:BP神經網絡算法推導及代碼實現筆記
    數學上,處處可導為後面降到的後向傳播算法(BP算法)提供了核心條件輸出範圍有限:一般是限定在[0,1],有限的輸出範圍使得神經元對於一些比較大的輸入也會比較穩定。非飽和性:飽和就是指,當輸入比較大的時候,輸出幾乎沒變化了,那麼會導致梯度消失!什麼是梯度消失:就是你天天給女生送花,一開始妹紙還驚喜,到後來直接麻木沒反應了。
  • 從數據結構到算法:圖網絡方法初探
    其實早在很多年前,圖神經網絡就以圖嵌入、圖表示學習、網絡嵌入等別名呈現出來,其實所有的這些方法本質上都是作用在圖上的機器學習。本文將根據近兩年的綜述對圖網絡方法做一個總結,為初入圖世界的讀者提供一個總體的概覽。本文作者朱梓豪為中科院信工所在讀碩士,主要研究方向為圖神經網絡、視覺問答、視覺對話等。
  • 一文讀懂圖神經網絡
    我們可以通過上述的更新步驟來套用目前的圖神經網絡算法,當然這裡的步驟的順序並不是嚴格固定的,比如可以先更新全局信息,再更新節點信息以及邊的信息。比較常見的圖神經網絡算法主要有Graph Convolutional Network(GCN)和Graph Attention Network(GAT)等網絡及其變種,在本文第三部分會給出基於圖神經網絡框架DGL的GCN以及GAT的代碼及註解。
  • 深入淺出圖神經網絡實現方式,讓圖神經網絡不再難!
    文章《A Comprehensive Survey on Graph Neural Networks》[1]提供了一個全面的圖神經網絡(GNNs) 概述,並且將最新的圖神經網絡分為四類,即遞歸圖神經網絡(RecGNNs)、卷積圖神經網絡(ConvGNNs)、圖自動編碼器(GAEs)和時空圖神經網絡(STGNNs)。
  • 圖神經網絡的「前世今生」
    GNN的前世今生 Humility簡介GNN的分類GCNGATGAEGGNGSTN附參考文獻代碼實現GNN的前世今生簡介而現實中很多數據之間都有著相當複雜的關係, 一般表現為非歐空間之上的圖結構.為處理圖數據之上的任務, 圖神經網絡就應運而生了.
  • 表徵圖數據絕不止圖神經網絡一種方法
    近年來,圖神經網絡掀起了將深度學習方法應用於圖數據分析的浪潮。不過其作為一門古老的認識世界的方法論,人們對於圖表徵技術的研究從很早以前就開始了。雖然現在深度神經網絡在物體識別、圖像分類和自然語言處理領域都取得了巨大的成功。然而,「設計出最優的神經網絡,學習並輸出任意的圖」仍然是一個熱門的研究課題。
  • 2021年的第一盆冷水:有人說別太把圖神經網絡當回事兒
    機器之心編輯部圖神經網絡(GNN)是目前熱門的研究方向,但我們是否應把注意力過多地放在這上面?數據科學家 Matt Ranger 從模型的本質、性能基準測試、實踐應用等方面陳述了自己的觀點。圖神經網絡(GNN)是機器學習中最熱門的領域之一,在過去短短數月內就有多篇優秀的綜述論文。但數據科學家 Matt Ranger 對 GNN 卻並不感冒。
  • 逆勢而上的技術:圖神經網絡學習來了!
    作為百度圖神經網絡研究的中堅力量,百度PGL團隊戰績累累,刷新圖神經網絡權威榜單OGB三項榜單SOTA以及獲得今年COLING協辦比賽TextGraph冠軍!圖神經網絡技術已被應用在百度內數十個實際項目中,大幅度提升公司效益。為了幫助廣大同學入門圖學習,百度飛槳PGL團隊開發了「圖神經網絡7日打卡營」。
  • 表徵圖數據,絕不止圖神經網絡一種方法
    主要有兩類圖聚類方法:圖內聚類和圖間聚類方法。顧名思義,圖內聚類方法將一個圖內部的頂點劃分到不同的簇中,而圖間聚類方法則將一系列圖劃分到不同的聚類中。在生物學領域,圖聚類技術被應用在基因調控網絡、代謝網絡、神經網絡和血液網絡中。在社交網絡中,聚類算法被用於社區發現任務。其它用例:諸如網頁或社交網絡圖等典型的大規模圖包含超過十億條邊並且會迅速增長。