基於DGL庫圖神經網絡教程(1)——基本的建圖操作

最近接觸了圖神經網絡,感覺這個玩意可以提供多粒度視角。在對研究對象進行抽象後,既可以對節點進行分類,也可以對邊進行分類,還可以對圖整體進行分類。 圖神經網絡這種結構就提供一種多粒度描述特定對象的能力。想想還是蠻不錯的啊。

所以就搞搞圖神經網絡。
目前來看圖神經網絡的理論學習路線有:

  1. 圖論基本知識。這個很熟,不用學。
  2. 線性代數,尤其是傅里葉變換的矩陣形式。這個只要回去看看書就行,我看了圖卷積網絡的理論基礎,其實就是傅里葉變換矩陣形式的應用,這個在矩陣分析課已經很常見了,不算什麼新知識點。但是要接觸圖神經網絡的話,這一部分需要仔細掌握!
  3. 神經網絡相關知識。

大概過了一遍《深入淺出圖神經網絡》,感覺差不多,還是實操爲主。於是寫寫實踐方面的東西。

圖神經網絡庫

本人用的庫是DGL :https://docs.dgl.ai/index.html
github地址:https://github.com/dmlc/dgl
這個庫好像是紐約大學寫的。它裏面實現了目前常見的圖神經網絡模型,直接搭積木一樣構造網絡,然後構造好數據就可以了。
在這裏插入圖片描述

安裝DGL庫

無cuda安裝:

pip3 install dgl -i https://mirrors.aliyun.com/pypi/simple/

cuda10安裝:

python3 -m pip install dgl-cu100 -i https://mirrors.aliyun.com/pypi/simple/

DGL默認使用pytorch作爲底層神經網絡庫

教程

加節點、加邊、及可視化圖

DGL裏面的邊都是有向邊,對於無向邊,可以同時建立兩邊反向邊。

__author__ = 'dk'
#構建圖,添加節點和邊
import networkx as nx
import  dgl
import matplotlib.pyplot as plt
#構建星型圖
u=[0,0,0,0,0]
v=[1,2,3,4,5]
#第一種方式,u和v的數組,他們是相同的長度
star1 = dgl.DGLGraph((u,v))
nx.draw(star1.to_networkx(),with_labels=True)#可視化圖
plt.show()

star2 = dgl.DGLGraph((0,v))
#對於星型,是可以廣播的
nx.draw(star2.to_networkx(),with_labels=True)
plt.show()

star3= dgl.DGLGraph([(0,1),(0,2),(0,3),(0,4),(0,5)])
#直接枚舉
nx.draw(star3.to_networkx(),with_labels=True)
plt.show()

也可以後續加邊,而不是在構造函數裏面加邊:

#也可以邊構圖,邊加邊,而不是在構造函數裏面加邊
g = dgl.DGLGraph()#這是一張空白圖
g.add_nodes(9)#添加節點,注意一定要先加節點,後加邊
for i in range(1,8):
    g.add_edge(0,i)
nx.draw(g.to_networkx(),with_labels=True)
plt.show()

注意! 添加邊(u,v)的時候,u和v不能超過網絡已經擁有的最大節點ID(節點數減1),對於多餘的沒有出入度的節點,DGL認爲他們是孤立的。
比如上圖:8號節點是沒有變跟它連接的。
在這裏插入圖片描述

節點特徵的賦值、提取

建立好圖後,就可以給節點加特徵了。
在DGL裏面,節點的特徵是被當做字典來處理的。用戶可以給節點的特徵取一個有表徵意義的key_name,同時用戶也可以給一個node同時定義多個特徵。
比如上面的星型模型:

import numpy as np
features = np.random.normal(0,1,(9,5)) #隨機生成一個9x5的正態分佈矩陣
print(features)
g.ndata['features'] = features

輸出:

[[-0.73241917  0.78738566  1.21160063 -0.83944648 -0.15739715]
 [-0.05520377  0.83418124 -0.68477259 -1.29054549 -1.2375015 ]
 [-0.23807425 -0.40030208  1.74318389 -0.70699831 -0.61449034]
 [-0.48597221  0.65284435 -0.27101916 -0.69242791 -0.83134013]
 [-0.00580359  1.29773141  1.28545031 -0.41703836  0.97254182]
 [-1.19173936  1.18409306 -0.24284504 -1.93560515 -1.1080128 ]
 [-0.4854841   0.06257814 -1.3432515  -0.53297016 -0.01270537]
 [-0.16906863  0.17349874  1.0057332   1.85554737  0.13355367]
 [-1.45619866  0.77784642  1.52454762 -0.86608947  0.28595569]]

ndata屬性就是node-data的縮寫。它是一個dict。
注意features的行數需要和圖目前擁有的節點數一致。
否則報錯:

dgl._ffi.base.DGLError: Expect number of features to match number of nodes (len(u)). Got 7 and 9 instead.

訪問節點特徵:
直接g.ndata[特徵名][節點ID] 就可以:
例如:訪問節點3的特徵

print(g.ndata['features'][3])

輸出:就是features矩陣的第三行

tensor([-0.4860,  0.6528, -0.2710, -0.6924, -0.8313], dtype=torch.float64)

當然可以修改某個節點的特徵:

g.ndata['features'][3]=th.zeros(1,5)
print(g.ndata['features'][3])

輸出:

tensor([0., 0., 0., 0., 0.], dtype=torch.float64)

可以看出,DGL內部用一個張量來存儲節點特徵。節點特徵的修改、訪問最後落實到對這個矩陣的修改、訪問。

print(g.ndata)

輸出:

{'features': tensor([[-0.4771,  1.7900, -1.1160,  0.2916, -0.7986],
        [-1.6190, -0.5006, -0.0437,  1.6412, -1.6979],
        [ 1.8872,  0.5236,  0.5123, -0.7658, -0.5050],
        [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
        [-0.3382, -0.4218,  0.8622,  1.1720,  0.3460],
        [-0.1710,  0.2713, -0.1639, -1.1159, -0.3623],
        [-0.9241,  1.2943,  0.1137,  1.5348,  0.1791],
        [-1.0372,  1.4145, -2.0653, -0.1469, -0.6041],
        [ 0.1035, -1.4215,  0.3288, -0.5195, -1.4120]], dtype=torch.float64)}

邊特徵的賦值和訪問、修改

與節點特徵一樣,邊也可以指定特徵以及訪問、修改特徵
賦值:

g.edata['w']=th.randn(len(g.edges),2)

注意,賦值矩陣的行函數需要等於邊的數目
訪問某條邊的特徵,最終需要落實到訪問邊矩陣裏面的某一行。而到底是那一行,則是邊的ID。
因此需要確定,我們訪問的邊的ID是多少。
比如:獲取(0,7)這條邊的ID:

g.edge_id(0,7)

訪問這條邊的特徵:

g.edata(g.edge_id(0,7))

然後也可以刪除特徵:這其實就是字典的操作

g.ndata.pop('features')
g.edata.pop('w')
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章