如何來構建神經網路?看看這篇乾貨
導讀 | 在這篇文章中,您將學習圖神經網路如何工作的基礎知識,以及如何使用Pytorch Geometric(PyG)庫和Open Graph Benchmark(OGB)庫並透過Python程式設計實現這樣一個圖神經網路。 |
深度學習為對非結構化資料進行預測開闢了一個全新的可能性世界。如今,人們常用卷積神經網路(CNN)處理影像資料,而採用遞迴神經網路(RNN)來處理文字資料,等等。
在過去幾年中,又出現了一類新的令人興奮的神經網路:圖神經網路(Graph Neural Networks,簡稱“GNN”)。顧名思義,這個網路型別專注於處理圖資料。
在這篇文章中,您將學習圖神經網路如何工作的基礎知識,以及如何使用Pytorch Geometric(PyG)庫和Open Graph Benchmark(OGB)庫並透過Python程式設計實現這樣一個圖神經網路。
注意,您可以在我的Github和Kaggle網站上找到本文提供的示例工程原始碼。
隨著圖卷積網路(GCN)[見參考文獻1]的引入,GNN開始流行起來,該網路將CNN中的一些概念借用到了圖世界。這種網路的主要思想,也稱為訊息傳遞框架(Message-Passing Framework),多年來成為該領域的黃金標準。我們將在本文中探討這一概念。
訊息傳遞框架指出,對於圖中的每個節點,我們將做兩件事:
聚合來自其鄰節點的資訊
使用來自其上一層及其鄰節點聚合的資訊更新當前節點資訊
訊息傳遞框架示意圖。來源:維基百科
上圖中顯示了訊息傳遞框架的工作原理。在GCN之後開發的許多架構側重於定義聚合和更新資料的最佳方式。
PyG是Pytorch庫的擴充套件,它允許我們使用研究中已經建立的層快速實現新的圖神經網路架構。
OGB[見參考文獻2]是作為提高該領域研究質量的一種方式開發的,因為它提供了可使用的策劃圖,也是評估給定架構結果的標準方式,從而使提案之間的比較更加公平。
於是,我們可以將這兩個庫一起使用,一方面可以更容易地提出一個架構,另一方面也不必擔心資料獲取和評估機制的問題。
首先,讓我們安裝示例工程必需的庫。請注意,您必須首先安裝PyTorch:
pip install ogb pip install torch_geometric
現在,讓我們匯入所需的方法和庫:
import os import torch import torch.nn.functional as Ffrom tqdm import tqdm from torch_geometric.loader import NeighborLoader from torch.optim.lr_scheduler import ReduceLROnPlateau from torch_geometric.nn import MessagePassing, SAGEConv from ogb.nodeproppred import Evaluator, PygNodePropPredDataset
接下來,第一步是從OGB下載資料集。我們將使用ogbn-arxiv網路,其中每個節點都是arxiv網站上的電腦科學論文,每個有向邊表示一篇論文引用了另一篇論文。我們的任務是:將每個節點分類為一個論文類別。
下載過程非常簡單:
target_dataset = 'ogbn-arxiv'#我們將把ogbn-arxiv下載到當前示例工程的'networks'資料夾下 dataset = PygNodePropPredDataset(name=target_dataset, root='networks')
其中,dataset變數是一個名為PygNodePropPredDataset的類的例項,該類特定於OGB庫。要將該資料集作為可在Pyrotch Geometric上使用的資料類進行訪問,我們只需執行以下操作:
data = dataset[0]
如果我們透過除錯跟蹤看一下這個變數,我們會看到如下結果:
Data(num_nodes=169343, edge_index=[2, 1166243], x=[169343, 128], node_year=[169343, 1], y=[169343, 1])
至此,我們已經準備好了節點數目、鄰接列表、網路的特徵向量、每個節點的年份資訊,並確定下目標標籤。
另外,ogbn-arxiv網路已經配備好了分別用於訓練、驗證和測試的分割資料子集。這是OGB提供的一種提高該網路研究再現性和質量的好方法。我們可以透過以下方式提取:
split_idx = dataset.get_idx_split() train_idx = split_idx['train'] valid_idx = split_idx['valid'] test_idx = split_idx['test']
現在,我們將定義兩個在訓練期間使用的資料載入器。第一個將僅載入訓練集中的節點,第二個將載入網路上的所有節點。
我們將使用Pytorch Geometric庫中的鄰節點載入函式NeighborLoader。該資料載入器為每個節點取樣給定數量的鄰節點。這是一種避免具有數千個節點的節點的RAM和計算時間癱瘓的方法。在本教程中,我們將在訓練載入程式上每個節點使用30個鄰節點。
train_loader = NeighborLoader(data, input_nodes=train_idx, shuffle=True, num_workers=os.cpu_count() - 2, batch_size=1024, num_neighbors=[30] * 2)total_loader = NeighborLoader(data, input_nodes=None, num_neighbors=[-1], batch_size=4096, shuffle=False, num_workers=os.cpu_count() - 2)
注意,我們把訓練資料載入器中的資料以隨機方式打亂次序,但沒有打亂總載入器中資料的次序。此外,訓練載入程式的鄰節點數定義為網路每層的數量。因為我們將在這裡使用兩層網路,所以我們將其設定為兩個值為30的列表。
現在是時候建立我們的GNN架構了。對於任何熟悉Pytorch的人來說,這應該都是平常的事情。
我們將使用SAGE圖層。這些層是在一篇很好的論文[見參考文獻3]中定義的,該論文非常細緻地介紹了鄰節點取樣的思想。幸運的是,Pytorch Geometric 庫已經為我們實現了這一層。
因此,與每個PyTorch架構一樣,我們必須定義一個包含我們將要使用的層的類:
class SAGE(torch.nn.Module): def __init__(self, in_channels, hidden_channels, out_channels, n_layers=2): super(SAGE, self).__init__() self.n_layers = n_layers self.layers = torch.nn.ModuleList() self.layers_bn = torch.nn.ModuleList() if n_layers == 1: self.layers.append(SAGEConv(in_channels, out_channels, normalize=False)) elif n_layers == 2: self.layers.append(SAGEConv(in_channels, hidden_channels, normalize=False)) self.layers_bn.append(torch.nn.BatchNorm1d(hidden_channels)) self.layers.append(SAGEConv(hidden_channels, out_channels, normalize=False)) else: self.layers.append(SAGEConv(in_channels, hidden_channels, normalize=False)) self.layers_bn.append(torch.nn.BatchNorm1d(hidden_channels)) for _ in range(n_layers - 2): self.layers.append(SAGEConv(hidden_channels, hidden_channels, normalize=False)) self.layers_bn.append(torch.nn.BatchNorm1d(hidden_channels)) self.layers.append(SAGEConv(hidden_channels, out_channels, normalize=False)) for layer in self.layers: layer.reset_parameters() def forward(self, x, edge_index): if len(self.layers) > 1: looper = self.layers[:-1] else: looper = self.layers for i, layer in enumerate(looper): x = layer(x, edge_index) try: x = self.layers_bn[i](x) except Exception as e: abs(1) finally: x = F.relu(x) x = F.dropout(x, p=0.5, training=self.training) if len(self.layers) > 1: x = self.layers[-1](x, edge_index) return F.log_softmax(x, dim=-1), torch.var(x) def inference(self, total_loader, device): xs = [] var_ = [] for batch in total_loader: out, var = self.forward(batch.x.to(device), batch.edge_index.to(device)) out = out[:batch.batch_size] xs.append(out.cpu()) var_.append(var.item()) out_all = torch.cat(xs, dim=0) return out_all, var_
讓我們一步一步地將上述程式碼分開解釋:
我們必須定義網路的in_channels數量,這個值代表資料集中的特徵數。out_channels代表我們試圖預測的類別的總數。隱藏通道引數idden_channels是一個我們可以定義的值,表示隱藏單元的數量。
我們可以設定網路的層數。對於每個隱藏層,我們新增一個批次歸一化層,然後重置每個層的引數。
forward方法執行正向過程的單個迭代。期間,獲得特徵向量和鄰接列表,並將其傳遞給SAGE層,然後將結果傳遞給批次歸一化層。此外,我們還應用ReLU非線性和衰減層進行正則化。
最後,推理方法(inference)將為資料集中的每個節點生成預測。我們將使用它進行驗證。
現在,讓我們定義模型的一些引數:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')model = SAGE(data.x.shape[1], 256, dataset.num_classes, n_layers=2) model.to(device) epochs = 100 optimizer = torch.optim.Adam(model.parameters(), lr=0.03) scheduler = ReduceLROnPlateau(optimizer, 'max', patience=7)
現在,我們可以開始測試了,以驗證我們的所有預測:
def test(model, device): evaluator = Evaluator(name=target_dataset) model.eval() out, var = model.inference(total_loader, device) y_true = data.y.cpu() y_pred = out.argmax(dim=-1, keepdim=True) train_acc = evaluator.eval({ 'y_true': y_true[split_idx['train']], 'y_pred': y_pred[split_idx['train']], })['acc'] val_acc = evaluator.eval({ 'y_true': y_true[split_idx['valid']], 'y_pred': y_pred[split_idx['valid']], })['acc'] test_acc = evaluator.eval({ 'y_true': y_true[split_idx['test']], 'y_pred': y_pred[split_idx['test']], })['acc']return train_acc, val_acc, test_acc, torch.mean(torch.Tensor(var))
在這個函式中,我們從OGB庫中例項化一個驗證器類Validator。這個類將負責驗證我們之前檢索到的每個分割的模型。這樣,我們將看到每個世代上的訓練、驗證和測試集的得分值。
最後,讓我們建立我們的訓練迴圈:
for epoch in range(1, epochs): model.train() pbar = tqdm(total=train_idx.size(0)) pbar.set_description(f'Epoch {epoch:02d}') total_loss = total_correct = 0 for batch in train_loader: batch_size = batch.batch_size optimizer.zero_grad() out, _ = model(batch.x.to(device), batch.edge_index.to(device)) out = out[:batch_size] batch_y = batch.y[:batch_size].to(device) batch_y = torch.reshape(batch_y, (-1,)) loss = F.nll_loss(out, batch_y) loss.backward() optimizer.step() total_loss += float(loss) total_correct += int(out.argmax(dim=-1).eq(batch_y).sum()) pbar.update(batch.batch_size) pbar.close() loss = total_loss / len(train_loader) approx_acc = total_correct / train_idx.size(0) train_acc, val_acc, test_acc, var = test(model, device) print(f'Train: {train_acc:.4f}, Val: {val_acc:.4f}, Test: {test_acc:.4f}, Var: {var:.4f}')
這個迴圈將訓練我們的GNN的100個世代,如果我們的驗證得分連續7個世代沒有增長的話,它將提前停止訓練。
總之,GNN是一類有趣的神經網路。今天,人們已經開發出了一些現成的工具來幫助我們開發這種解決方案。正如您在本文中所見到的,藉助Pytorch Geometric和OGB這兩個庫就可以輕鬆實現某些型別的圖的GNN設計。
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2914184/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- keras構建神經網路Keras神經網路
- 乾貨!這裡有一份神經網路入門指導,請收下!神經網路
- Pytorch | Tutorial-04 構建神經網路模型PyTorch神經網路模型
- 機器學習:神經網路構建(上)機器學習神經網路
- 機器學習:神經網路構建(下)機器學習神經網路
- 【神經網路篇】--RNN遞迴神經網路初始與詳解神經網路RNN遞迴
- 【深度學習篇】--神經網路中的卷積神經網路深度學習神經網路卷積
- 神經網路篇——從程式碼出發理解BP神經網路神經網路
- [譯] Keras 速查表:使用 Python 構建神經網路KerasPython神經網路
- 構建兩層以上BP神經網路(python程式碼)神經網路Python
- [譯] 如何用 Python 從零開始構建你自己的神經網路Python神經網路
- Tensorflow系列專題(四):神經網路篇之前饋神經網路綜述神經網路
- 從零開始用 Python 構建迴圈神經網路Python神經網路
- 神經網路架構參考:2-2 卷積篇神經網路架構卷積
- 神經網路最佳化篇:如何理解 dropout(Understanding Dropout)神經網路
- 使用PyTorch從零開始構建Elman迴圈神經網路PyTorch神經網路
- 迴圈神經網路之embedding,padding,模型構建與訓練神經網路padding模型
- 神經網路之卷積篇:詳解經典網路(Classic networks)神經網路卷積
- 神經網路:numpy實現神經網路框架神經網路框架
- [乾貨]如何使用webpack構建多頁應用Web
- 神經網路是如何工作的?神經網路
- 如何優化深度神經網路?優化神經網路
- 如何入手卷積神經網路卷積神經網路
- 看了這7篇論文,你會完全掌握卷積神經網路!卷積神經網路
- 神經網路神經網路
- 1.4 神經網路入門-資料處理與模型圖構建神經網路模型
- 用Keras框架構建一個簡單的卷積神經網路!Keras框架架構卷積神經網路
- TensorFlow 下構建高效能神經網路模型的最佳實踐神經網路模型
- 文字生成神經網路架構發展神經網路架構
- 常見迴圈神經網路結構神經網路
- 如何除錯神經網路引數除錯神經網路
- 構建深度神經網路,我有20條「不成熟」的小建議神經網路
- Jina:在雲上構建神經網路搜尋的更簡單方法神經網路
- 神經網路基礎篇神經網路
- 3.4 神經網路為什麼這麼強神經網路
- LSTM神經網路神經網路
- 8、神經網路神經網路
- 2019年上半年收集到的人工智慧圖神經網路乾貨文章人工智慧神經網路