Task04 數據完全存于內存的數據集類+節點預測與邊預測任務實踐

參考鏈接:https://github.com/datawhalechina/team-learning-nlp/blob/master/GNN/Markdown%E7%89%88%E6%9C%AC/6-1-%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%95%B4%E5%AD%98%E4%BA%8E%E5%86%85%E5%AD%98%E7%9A%84%E6%95%B0%E6%8D%AE%E9%9B%86%E7%B1%BB.md
https://github.com/datawhalechina/team-learning-nlp/blob/master/GNN/Markdown%E7%89%88%E6%9C%AC/6-2-%E8%8A%82%E7%82%B9%E9%A2%84%E6%B5%8B%E4%B8%8E%E8%BE%B9%E9%A2%84%E6%B5%8B%E4%BB%BB%E5%8A%A1%E5%AE%9E%E8%B7%B5.md

第一節、數據完全存于內存的數據集類

一、在PyG中自定義數據完全存于內存的數據集類

PyG定義使用數據的一般過程:
1.從網上下載原始數據;
2.對原始數據預處理,為每一個圖樣本生成一個Data對象;
3.對每一個Data對象執行數據處理,使其轉換成新的Data對象;
4.過濾Data對象;
5.保存Data對象到文件;
6.獲取Data對象,在每一次獲取Data對象時,都先對Data對象做數據變換(于是獲取到的是數據變換后的Data對象)。
實際中并非需要嚴格執行每一個步驟,以上步驟在特定的條件下可以被跳過。

二、InMemoryDataset基類簡介

在PyG中,通過繼承InMemoryDataset 類來自定義一個數據可全部存儲到內存的數據集類。

class InMemoryDataset(root: Optional[str] = None,transform: Optional[Callable] = None, pre_transform:Optional[Callable] = None, pre_filter:Optional[Callable] = None)

InMemoryDataset類初始化方法參數說明:

  • root:字符串類型,存儲數據集的文件夾的路徑。該文件夾下有兩個文件夾:
    一個文件夾記錄在raw_dir,它用于存儲未處理的文件;
    另一個文件夾記錄在processed_dir,它用于存儲處理后的數據,以后從此文件夾下加載文件即可獲得Data對象。
    注:raw_dir和processed_dir都是屬性方法,可以自定義要使用的文件夾。
  • transform 函數:接受Data 對象為參數,對其轉換后返回。此函數在每一次數據訪問時被調用,主要用于數據增廣(Data Augmentation)。
  • pre_transform 函數:接受 Data 對象為參數,對其轉換后返回。此函數在樣本 Data 對象保存到文件前調用,主要用于只需要做一次的大量預計算。
  • pre_filter 函數:可以在保存前手動過濾掉數據對象。例如:過濾樣本類別。

創建一個InMemoryDataset,要實現以下四個基本方法:
1.raw_file_names() —— 這是一個屬性方法,返回一個文件名列表,文件在raw_dir 文件夾中,否則調用process() 函數下載文件到raw_dir 文件夾。
2.processed_file_names()——這是一個屬性方法,返回一個文件名列表,文件在processed_dir 文件夾中,否則調用process() 函數對樣本做預處理,然后保存到processed_dir 文件夾。
3.download() ——將原始數據文件下載到raw_dir 文件夾。
4.process() ——對樣本做預處理,然后保存到processed_dir 文件夾。

三、InMemoryDataset數據集類實例

以公開數據集PubMed 為例。PubMed 數據集存儲的是文章引用網絡,文章對應圖的結點,如果兩篇文章存在引用關系(無論引用與被引),則這兩篇文章對應的結點之間存在邊。
該數據集類的使用:
在生成一個PlanetoidPubMed 類的對象時,程序運行流程如下:

  • 首先檢查數據原始文件是否已下載:
    檢查self.raw_dir 目錄下是否存在raw_file_names() 屬性方法返回的每個文件,如有文件不存在,則調用download() 方法執行原始文件下載。
  • 其次檢查數據是否經過處理:
    1.檢查之前對數據做變換的方法:檢查self.processed_dir 目錄下是否存在pre_transform.pt 文件。
    2.檢查之前樣本過濾的方法:檢查self.processed_dir 目錄下是否存在pre_filter.pt 文件。
    3.檢查是否存在處理好的數據:檢查self.processed_dir 目錄下是否存在self.processed_paths 方法返回的所有文件。
  • 最后查看這個數據集。

第二節、節點預測與邊預測任務實踐

一、目標

能夠掌握應對實際中節點預測問題或邊預測問題的能力。

二、節點預測任務實踐

首先定義GAT(注意力機制層)神經網絡模型:

class GAT(torch.nn.Module):
    def __init__(self, num_features,
        hidden_channels_list, num_classes):
        super(GAT, self).__init__()
        torch.manual_seed(12345)
        hns = [num_features] + hidden_channels_list
        conv_list = []
        for idx in range(len(hidden_channels_list)):
            conv_list.append((GATConv(hns[idx],
            hns[idx+1]), 'x, edge_index -> x'))
            conv_list.append(ReLU(inplace=True),)
        self.convseq = Sequential('x, edge_index',conv_list)
        self.linear = Linear(hidden_channels_list[-1],num_classes)
    def forward(self, x, edge_index):
        x = self.convseq(x, edge_index)
        x = F.dropout(x, p=0.5, training=self.training)
        x = self.linear(x)
        return x
  • 神經網絡由多個GATConv 順序相連而構成,因此此模型使用了torch_geometric.nn.Sequential 容器。
  • 通過hidden_channels_list參數來設置每一層GATConv的outchannel,所以hidden_channels_list長度即為GATConv的層數。通過修改hidden_channels_list,就可構造出不同的圖神經網絡。

完整的代碼可見于codes/node_classification.py。

三、邊預測任務實踐

邊預測任務,目標是預測兩個節點之間是否存在邊。一個圖數據集會有節點屬性x和邊端點edge_index。而edge_index存儲的是正樣本,沒有負樣本。為了構建邊預測任務,需要生成一些負樣本,即采樣一些不存在邊的節點對作為負樣本邊,正負樣本數量要平衡。要將樣本分為訓練集、驗證集和測試集,進行模型訓練,驗證和預測。可以通過train_test_split_edges函數隨機切分實現。

3.1獲取數據集并進行分析

import os.path as osp
from torch_geometric.utils import negative_sampling
from torch_geometric.datasets import Planetoid
import torch_geometric.transforms as T
from torch_geometric.utils import train_test_split_edges

注意:
為什么訓練集要包含邊的正向與反向,而驗證集與測試集都只包含了邊的一個方向?
因為,訓練集用于訓練,訓練時一條邊的兩個端點要互傳信息,只考慮一個方向的話,只能由一個端點傳信息給另一個端點,而驗證集與測試集的邊用于衡量檢驗邊預測的準確性,只需考慮一個方向的邊即可。

3.2構造邊預測圖神經網絡

import torch
from torch_geometric.nn import GCNConv

class Net(torch.nn.Module):
    def __init__(self, in_channels, out_channels):
        super(Net, self).__init__()
        self.conv1 = GCNConv(in_channels, 128)
        self.conv2 = GCNConv(128, out_channels)

    def encode(self, x, edge_index):
        x = self.conv1(x, edge_index)
        x = x.relu()
        return self.conv2(x, edge_index)

    def decode(self, z, pos_edge_index, neg_edge_index):
        edge_index = torch.cat([pos_edge_index, neg_edge_index], dim=-1)
        return (z[edge_index[0]] * z[edge_index[1]]).sum(dim=-1)

    def decode_all(self, z):
        prob_adj = z @ z.t()
        return (prob_adj > 0).nonzero(as_tuple=False).t()

邊預測的神經網絡主要由兩部分組成:

  • 編碼(encode):與節點表征生成一樣;
  • 解碼(decode):根據邊兩端節點的表征生成邊為真的幾率(odds)。

3.3訓練邊預測圖神經網絡

步驟如下:
Step1:定義單個epoch的訓練過程;
Step2:定義單個epoch驗證與測試過程;
Step3:運行完整的訓練、驗證與測試。

四、總結

通過上面所學的知識,我學會了:
1.更好的理解InMemoryDataset基類、簡化的InMemory數據集類和數據集的使用及代碼的實現,雖然很多模型是已經構建好的,但是很多代碼還是需要多花點時間吸收和實踐的。
2.更加深刻的理解節點預測與邊預測的運用,以及如何合理的運用數據建立模型。
3.非常感謝DataWhale提供這么好的學習機會以及DW成員們的辛勤付出。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容