目錄手動嘗試GCN圖神經網絡現在讓我們更詳細地看一下底層圖現在讓我們更詳細地檢查edge_index的屬性嵌入KarateClubNetwork訓練KarateClubNetwork總結手...
手動嘗試GCN圖神經網絡
最近,圖上的深度學習已經成為深度學習社區中最熱門的研究領域之一。 在這里,圖神經網絡(GNN)旨在將經典的深度學習概念推廣到不規則的結構化數據(與圖像或文本形成對比),并使神經網絡能夠推理出對象及其關系。
本內容介紹一些關于通過基于PyTorch幾何(PyG)庫的圖神經網絡對圖進行深度學習的基本概念。PyTorch geometry是流行的深度學習框架PyTorch的擴展庫,由各種方法和實用程序組成,以簡化圖神經網絡的實現。
在開始之前,先介紹一下配置環境:
Pytorch: 1.8.0 Cuda: 10.2 Torch-geometric
# 導入使用的模塊包 import torch import networkx as nx import matplotlib.pyplot as plt # 定義最后可視化的函數 def visualize(h, color, epoch=None, loss=None): plt.figure(figsize=(7,7)) plt.xticks([]) plt.yticks([]) if torch.is_tensor(h): h = h.detach().cpu().numpy() plt.scatter(h[:, 0], h[:, 1], s=140, c=color, cmap="Set2") if epoch is not None and loss is not None: plt.xlabel(f'Epoch: {epoch}, Loss: {loss.item():.4f}', fontsize=16) else: nx.draw_networkx(G, pos=nx.spring_layout(G, seed=42), with_labels=False, node_color=color, cmap="Set2") plt.show()
在這里,我們使用一張KarateClub圖來進行講解,這張圖描述了一個由34名空手道俱樂部成員組成的社交網絡,并記錄了俱樂部外成員之間的聯系。在這里,我們感興趣的是檢測由成員的交互產生的社區。
KarateClub圖
from torch_geometric.datasets import KarateClub dataset = KarateClub() print(f'Dataset: {dataset}:') print('======================') print(f'Number of graphs: {len(dataset)}') # 1 print(f'Number of features: {dataset.num_features}') # 34 print(f'Number of classes: {dataset.num_classes}') # 4
這里輸出的分別是:
- (編程1)圖的數量、
- (2)特征的數量
- (3)種類
在初始化KarateClub數據集之后,我們首先可以檢查它的一些屬性。
例如,我們可以看到這個數據集只持有一個圖,并且這個數據集中的每個節點被分配一個34維的特征向量(唯一地描述空手道俱樂部的成員)。
此外,圖中正好包含4個類,它們代表每個節點所屬的團體。
現在讓我們更詳細地看一下底層圖
data = dataset[0] # Get the first graph object. print(data) print('==============================================================') # Gather some statistics about the graph. print(f'Number of nodes: {data.num_nodes}') print(f'Number of edges: {data.num_edges}') print(f'Average node degree: {data.num_edges / data.num_nodes:.2f}') print(f'Number of training nodes: {編程data.train_mask.sum()}') print(f'Training node label rate: {int(data.train_mask.sum()) / data.num_nodes:.2f}') print(f'Contains isolated nodes: {data.contains_isolated_nodes()}') print(f'Contains self-loops: {data.contains_self_loops()}') print(f'Is undirected: {data.is_undirected()}')
Data(edge_index=[2, 156], train_mask=[34], x=[34, 34], y=[34]) ============================================================== Number of nodes: 34 Number of edges: 156 Average node degree: 4.59 Number of training nodes: 4 Training node label rate: 0.12 Contains isolated nodes: False Contains self-loops: False Is undirected: True
PyTorch Geometric 中的每個圖形都由單個 Data 對象表示,該對象包含描述其圖形表示的所有信息。
我們可以隨時通過 print(data) 打印數據對象,以接收有關其屬性及其形狀的簡短摘要:
Data(edge_index=[2,156],x=[34,34],y=[34],train_mask=[34])
我們可以看到該數據對象具有4個屬性:
(1)edge_index:屬性保存有關圖連接性的信息,即每個邊緣的源節點索引和目標節點索引的元組。 PyG進一步將
(2)節點特征稱為x(為34個節點中的每個節點分配了一個34維特征向量),并且將
(3)節點標簽稱為y(每個節點被精確地分配為一個類別)。
(4)還有一個名為train_mask的附加屬性,它描述了我們已經知道其社區歸屬的節點。 總共,我們只知道4個節點的基本標簽(每個社區一個),任務是推斷其余節點的社區分配。數據對象還提供一些實用程序功能來推斷基礎圖的某些基本屬性。 例如,我們可以輕松推斷圖中是否存在孤立的節點(即,任何節點都沒有邊),圖是否包含自環(即(v,v)∈E)或圖是否為 無向的(即,對于每個邊(v,w)∈E也存在邊(w,v)∈E)。
現在讓我們更詳細地檢查edge_index的屬性
from Ipython.display import Javascript # Restrict height of output cell. display(javascript('''google.colab.output.setIframeHeight(0, true, {maxHeight: 300})''')) edge_index = data.edge_index print(edge_index.t())
tensor([[ 0, 1], [ 0, 2], [ 0, 3], [ 0, 4], [ 0, 5], [ 0, 6], [ 0, 7], [ 0, 8], ........
這個edge_index描述了34個人的相關性。通過輸出edge_index,我們可以進一步了解PyG內部是如何表示圖連通性的。
我們可以看到,對于每條邊,edge_index 包含兩個節點索引的元組,其中第一個值描述源節點的節點索引,第二個值描述邊的目標節點的節點索引。
這種表示被稱為COO格式(坐標格式),通常用于表示稀疏矩陣。
PyG使用稀疏矩陣代替以密集表示形式的鄰接矩陣A∈{0,1} | V |×| V | ,這是指僅保留A中的條目不為零的坐標/值。
我們可以通過將圖轉換為networkx庫格式來進一步可視化,這種格式除了圖形操作功能之外,還實現了用于可視化的強大工具
from torch_geometric.utils import to_networkx G = to_networkx(data, to_undirected=True) visualize(G, color=data.y)
數據庫可視化
灰色、黃色、綠色、藍色代表四類不同的俱樂部,其中每一個圓圈代表一個人,一共有34個人,每個人之間的關系就如edge_index所描述的那樣。
現在,我們要通過在torch.nn.Module類繼承中定義我們的網絡架構來創建我們的第一個圖神經網絡
import torch from torch.nn import Linear from torch_geometric.nn import GCNConv class GCN(torch.nn.Module): def __init編程__(self): super(GCN, self).__init__() torch.manual_seed(12345) self.conv1 = GCNConv(dataset.num_features, 4) self.conv2 = GCNConv(4, 4) self.conv3 = GCNConv(4, 2) self.classifier = Linear(2, dataset.num_classes) def forward(self, x, edge_index): h = self.conv1(x, edge_index) h = h.tanh() h = self.conv2(h, edge_index) h = h.tanh() h = self.conv3(h, edge_index) h = h.tanh() # Final GNN embedding space. # Apply a final (linear) classifier. out = self.classifier(h) return out, h model = GCN() print(model)
GCN( (conv1): GCNConv(34, 4) (conv2): GCNConv(4, 4) (conv3): GCNConv(4, 2) (classifier): Linear(in_features=2, out_features=4, bias=True) )
在這里,我們首先在 __init__ 中初始化我們所有的構建塊,并定義我們forward網絡的計算流程。 我們首先定義并堆疊三個圖卷積層,這對應于聚合每個節點周圍的 3 個鄰域信息(所有節點最多 3python個)。 此外,GCNConv 層將節點特征維數減少到 2 ,即 34→4→4→2 。 每個 GCNConv 層都通過 tanh 非線性增強。(可以換成RELU試一試)
之后,我們應用單個線性變換 (torch.nn.Linear) 作為分類器將我們的節點映射到 4 個類/社區中的 1 個。
我們返回最終分類器的輸出以及GNN生成的最終節點嵌入。 我們繼續通過 GCN() 初始化我們的最終模型,打印我們的模型會生成所有使用的子模塊的摘要。
嵌入 Karate Club Network
讓我們看看GNN產生的節點嵌入。這里,我們將初始節點特征x和圖連通性信息edge_index傳遞給模型,并可視化其二維嵌入。
model = GCN() _, h = model(data.x, data.edge_index) print(f'Embedding shape: {list(h.shape)}') visualize(h, color=data.y)
值得注意的是,即使在訓練我們的模型的權重之前,該模型也會產生一個與圖中的社區結構非常相似的節點嵌入。
相同顏色(社區)的節點在嵌入空間中已經緊密地聚在一起,盡管我們的模型的權值是完全隨機初始化的,而且到目前為止我們還沒有進行任何訓練!由此得出結論,gnn引入了很強的歸納偏置,導致輸入圖中彼此接近的節點產生類似的嵌入。
訓練 Karate Club Network
但我們能做得更好嗎? 讓我們看一個示例,說明如何根據圖中 4 個節點的社區分配知識(每個社區一個)來訓練我們的網絡參數:
由于我們模型中的所有內容都是可微分和參數化的,我們可以添加一些標簽、訓練模型并觀察嵌入的反應。 在這里,我們使用半監督或轉導學習程序:我們只是針對每個類的一個節點進行訓練,但允許使用完整的輸入圖數據。
這個模型訓練與任何其他PyTorch模型非常相似。除了定義我們的網絡架構之外,我們還定義了一個損失標準(這里是CrossEntropyLoss),并初始化了一個隨機梯度優化器(這里是Adam)。之后,我們執行多輪優化,每輪由前向和后向傳遞來計算我們的模型參數w.r.t.對前向傳遞的損失的梯度。
import time
from IPython.display import Javascript # Restrict height of output cell.
display(Javascript('''google.colab.output.setIframeHeight(0, true, {maxHeight: 430})'''))
model = GCN()
criterion = torch.nn.CrossEntropyLoss() # Define loss criterion.
optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # Define optimizer.
def train(data):
optimizer.zero_grad() # Clear gradients.
out, h = model(data.x, data.edge_index) # Perform a single forward pass.
loss = criteri編程on(out[data.train_mask], data.y[data.train_mask]) # Compute the loss solely based on the training nodes.
loss.backward() # Derive gradients.
optimizer.step() # Update parameters based on gradients.
return loss, h
for epoch in range(401):
loss, h = train(data)
if epoch % 10 == 0:
visualize(h, color=data.y, epoch=epoch, loss=loss)
time.sleep(0.3)
可以看到,訓練400輪后,它的聚類是比較明顯的。正如可以看到的,我們的3層GCN模型管理線性分隔社區和正確分類大多數節點。
此外,我們只用了幾行代碼就完成了這一切,這要感謝PyTorch geometry庫,它幫助我們完成了數據處理和GNN實現。
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持我們。
如果認為本文對您有所幫助請贊助本站