一、舉例介紹
1踱启、圖數(shù)據(jù)的處理
PyG 中的單個圖由 torch_geometric.data.Data的實例描述,默認情況下它包含以下屬性:
-
data.x
: 以[num_nodes, num_node_features]
形式描述的節(jié)點特征矩陣 -
data.edge_index
: COO 格式的圖形連接,形狀為 [2, num_edges]请垛,類型為 torch.long搪搏。
COO 格式:PyTorch 實現(xiàn)了所謂的坐標(biāo)格式或 COO 格式狭握,作為實現(xiàn)稀疏張量的存儲格式之一,在 COO 格式中疯溺,指定的元素存儲為元素索引和相應(yīng)值的元組论颅“タ眩可以通過向函數(shù) torch.sparse_coo_tensor() 提供索引和值這兩個張量以及稀疏張量的大小(當(dāng)無法從索引和值張量推斷時)來構(gòu)造稀疏 COO 張量恃疯。假設(shè)我們要定義一個稀疏張量漏设,其中條目 3 位于位置 (0, 2),條目 4 位于位置 (1, 0)今妄,條目 5 位于位置 (1, 2)郑口。 未指定的元素被假定具有相同的值,填充值盾鳞,默認為零犬性。 然后我們會寫:
>>> i = [[0, 1, 1],
[2, 0, 2]]
>>> v = [3, 4, 5]
>>> s = torch.sparse_coo_tensor(i, v, (2, 3))
>>> s
tensor(indices=tensor([[0, 1, 1],
[2, 0, 2]]),
values=tensor([3, 4, 5]),
size=(2, 3), nnz=3, layout=torch.sparse_coo)
>>> s.to_dense()
tensor([[0, 0, 3],
[4, 0, 5]])
torch.long是PyTorch中的一個數(shù)據(jù)類型,它表示64位整數(shù)類型腾仅。它在PyTorch中用于表示整數(shù)張量乒裆。
-
data.edge_attr
: 形狀為[num_edges, num_edge_features]
的邊的特征矩陣 -
data.y
: 訓(xùn)練目標(biāo)(可能具有任意形狀), e.g., node-level targets of shape[num_nodes, *]
or graph-level targets of shape[1, *]
-
data.pos
: 形狀為[num_nodes, num_dimensions]
的節(jié)點位置矩陣
注意:這些屬性都不是必需的,事實上推励,Data 對象甚至不限于這些屬性鹤耍。
以下展示了一個具有三個節(jié)點和四個邊的未加權(quán)無向圖的簡單示例,每個節(jié)點只包含一個特征:
import torch
from torch_geometric.data import Data
edge_index = torch.tensor([[0, 1, 1, 2],
[1, 0, 2, 1]], dtype=torch.long)
x = torch.tensor([[-1], [0], [1]], dtype=torch.float)
data = Data(x=x, edge_index=edge_index)
>>> Data(edge_index=[2, 4], x=[3, 1])
注意:edge_index,定義所有邊的源節(jié)點和目標(biāo)節(jié)點的張量不是索引元組的列表验辞。如果您想以這種方式編寫索引稿黄,則應(yīng)該在將它們傳遞給數(shù)據(jù)構(gòu)造函數(shù)之前轉(zhuǎn)置并調(diào)用連續(xù)的索引。
import torch
from torch_geometric.data import Data
edge_index = torch.tensor([[0, 1],
[1, 0],
[1, 2],
[2, 1]], dtype=torch.long)
x = torch.tensor([[-1], [0], [1]], dtype=torch.float)
data = Data(x=x, edge_index=edge_index.t().contiguous())
>>> Data(edge_index=[2, 4], x=[3, 1])
注意: 為了最終的數(shù)據(jù)表示盡可能緊湊跌造,edge_index 中的元素必須僅保存 { 0, ..., num_nodes - 1} 范圍內(nèi)的索引杆怕。例如,我們希望分別通過 x[0] 和 x[1] 索引第一條邊 (0, 1) 的源節(jié)點特征和目標(biāo)節(jié)點特征壳贪,可以通過運行validate()檢查最終的 Data 對象是否滿足這些要求财著。
除了擁有許多節(jié)點級、邊級或圖級屬性之外撑碴,數(shù)據(jù)還提供了許多有用的實用函數(shù),例如:
data.num_nodes
>>> 3
data.num_edges
>>> 4
data.num_node_features
>>> 1
data.has_isolated_nodes()
>>> False
data.has_self_loops()
>>> False
data.is_directed()
>>> False
……
#You can find a complete list of all methods at [`torch_geometric.data.Data`]
2撑教、通用基準數(shù)據(jù)集
PyG包含大量常見的基準數(shù)據(jù)集,例如小行星數(shù)據(jù)集、圖形分類數(shù)據(jù)集等等醉拓。初始化數(shù)據(jù)集很簡單伟姐,數(shù)據(jù)集的初始化將自動下載其原始文件并將其處理為前面描述的數(shù)據(jù)格式,例如酶數(shù)據(jù)集亿卤, (由 6 個類中的 600 個圖表組成):
from torch_geometric.datasets import TUDataset
datasets = TUDataset(root='/tmp/ENZYMES', name='ENZYMES')
datasets.num_classes
#6
datasets.num_node_features
#3
data = datasets[0]
data
>>> Data(edge_index=[2, 168], x=[37, 3], y=[1])
# 包含37 個節(jié)點,每一個含有3個特征愤兵;
# 有 168/2 = 84 條無向邊,并且該圖恰好分配給一類排吴;
# 此外秆乳,數(shù)據(jù)對象恰好持有一個圖級目標(biāo)。
可以使用切片、長張量或布爾張量來分割數(shù)據(jù)集屹堰,例如肛冶,要創(chuàng)建 90/10 的訓(xùn)練/測試分割,輸入:
train_dataset = dataset[:540]
>>> ENZYMES(540)
test_dataset = dataset[540:]
>>> ENZYMES(60)
# 如果不確定數(shù)據(jù)集在分割之前是否已經(jīng)打亂,可以使用shuffle
dataset = dataset.shuffle()
#注意這一句代碼和以下代碼的得到的結(jié)果一致
perm = torch.randperm(len(dataset))
dataset = dataset[perm]
3扯键、Mini-batches
神經(jīng)網(wǎng)絡(luò)通常以批量方式進行訓(xùn)練睦袖。PyG 通過創(chuàng)建稀疏塊對角鄰接矩陣(由 edge_index 定義)并在節(jié)點維度中連接特征和目標(biāo)矩陣,實現(xiàn)小批量的并行化荣刑。這種組合允許在一批示例中使用不同數(shù)量的節(jié)點和邊馅笙,
PyG 包含自己的 torch_geometric.loader.DataLoader,它已經(jīng)處理了這個串聯(lián)過程厉亏,以下是一個例子:
from torch_geometric.datasets import TUDataset
from torch_geometric.loader import DataLoader
dataset = TUDataset(root='/tmp/ENZYMES', name='ENZYMES', use_node_attr=True)
loader = DataLoader(dataset, batch_size=32, shuffle=True)
for batch in loader:
batch
>>> DataBatch(batch=[1082], edge_index=[2, 4066], x=[1082, 21], y=[32])
batch.num_graphs
>>> 32
torch_geometric.data.Batch繼承自torch_geometric.data.Data并包含一個名為batch的附加屬性,batch是一個是一個列向量董习,將每個節(jié)點映射到batch中各自的圖。
4爱只、數(shù)據(jù)Transform
Transform是 torchvision 中變換圖像和執(zhí)行增強的常用方法阱飘。PyG 帶有自己的Transform,它期望一個 Data 對象作為輸入并返回一個新的轉(zhuǎn)換后的 Data 對象虱颗。Transforms can be chained together using [torch_geometric.transforms.Compose
] and are applied before saving a processed dataset on disk (pre_transform
) or before accessing a graph in a dataset (transform
).
5、圖的學(xué)習(xí)方法
將使用一個簡單的 GCN 層并在 Cora 引文數(shù)據(jù)集上復(fù)制實驗
#首先需要加載 Cora 數(shù)據(jù)集
from torch_geometric.datasets import Planetoid
dataset = Planetoid(root='/tmp/Cora', name='Cora')
import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
#其中包含了2個GCNConv層(圖卷積層)和一個前向傳播方法蔗喂。
class GCN(torch.nn.Module):
def __init__(self):
super().__init__()
#它接收圖的節(jié)點特征數(shù)作為輸入維度
self.conv1 = GCNConv(dataset.num_node_features, 16)
#它的輸入維度為16(與第一層的輸出維度相匹配)忘渔,輸出維度為圖的類別數(shù)(即標(biāo)簽的數(shù)量)。
self.conv2 = GCNConv(16,dataset.num_classes)
#在前向傳播方法forward中缰儿,輸入數(shù)據(jù)data包含了節(jié)點特征x和邊的索引edge_index
def forward(self,data):
x,edge_index = data.x,data.edge_index
#首先畦粮,將節(jié)點特征x和邊的索引edge_index作為輸入傳遞給第一個GCNConv層self.conv1進行圖卷積操作
x= self.conv1(x,edge_index)
#ReLU激活函數(shù)對輸出進行非線性變換
x = F.relu(x)
#應(yīng)用dropout操作以防止過擬合
x= F.dropout(x,training=self.training)
#將結(jié)果輸入到第二個GCNConv層self.conv2進行最終的圖卷積操作
x = self.conv2(x,edge_index)
#對輸出結(jié)果應(yīng)用log_softmax函數(shù),以便進行多類別分類問題的概率計算乖阵。最終宣赔,返回概率化的輸出結(jié)果。
return F.log_softmax(x,dim=1)
###########################################################
# 20次訓(xùn)練
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
#創(chuàng)建了一個GCN模型實例model瞪浸,并將其移動到之前確定的設(shè)備上
model = GCN().to(device)
data = dataset[0].to(device)
#Data(x=[2708, 1433], edge_index=[2, 10556], y=[2708], train_mask=[2708], val_mask=[2708], test_mask=[2708])
#定義了一個Adam優(yōu)化器儒将,將模型的參數(shù)作為優(yōu)化器的參數(shù),并設(shè)置學(xué)習(xí)率和權(quán)重衰減
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
model.train()
for epoch in range(200):
#首先將優(yōu)化器的梯度緩存清零
optimizer.zero_grad()
#模型前向傳播得到輸出結(jié)果out
out = model(data)
#計算損失函數(shù)对蒲,這里使用了負對數(shù)似然損失函數(shù)(negative log likelihood loss)
loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask])
#根據(jù)損失函數(shù)的梯度進行反向傳播并更新模型的參數(shù)
loss.backward()
optimizer.step()
############################################################
#最后钩蚊,我們可以在測試節(jié)點上評估我們的模型
#將模型設(shè)置為評估模式,確保在模型中的一些特定層(如dropout)在評估時不會進行隨機丟棄操作,以保持一致的輸出蹈矮。
model.eval()
#通過模型對數(shù)據(jù)進行前向傳播砰逻,得到預(yù)測結(jié)果pred。使用argmax(dim=1)將預(yù)測結(jié)果轉(zhuǎn)換為每個樣本的類別標(biāo)簽
pred = model(data).argmax(dim=1)
#計算在測試集上的預(yù)測準確率泛鸟。首先蝠咆,通過使用索引掩碼data.test_mask從預(yù)測結(jié)果pred和標(biāo)簽數(shù)據(jù)data.y中提取出測試集的部分。然后,將預(yù)測正確的樣本數(shù)進行求和計算刚操。
correct = (pred[data.test_mask] == data.y[data.test_mask]).sum()
#將預(yù)測正確的樣本數(shù)除以測試集樣本的總數(shù)
acc = int(correct) / int(data.test_mask.sum())
print(f'Accuracy: {acc:.4f}')
>>> Accuracy: 0.8150(可能會不同)
參考網(wǎng)址:https://pytorch-geometric.readthedocs.io/en/latest/index.html
備注:以上內(nèi)容如有錯誤,請聯(lián)系作者闸翅。著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者