λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
1️⃣ AI•DS/πŸ“˜ GNN

Pytorch Geometric Basic code

by isdawell 2022. 9. 30.
728x90

πŸ“Œ μ•„λž˜ λΈ”λ‘œκ·Έμ˜ μ½”λ“œμ™€ Pytorch Geometric 라이브러리 μ„€λͺ…을 μ°Έκ³ ν•΄ κ³΅λΆ€ν–ˆμŠ΅λ‹ˆλ‹€. 

 

https://baeseongsu.github.io/posts/pytorch-geometric-introduction/

 

예제λ₯Ό 톡해 μ•Œμ•„λ³΄λŠ” PyTorch Geometric 5 Basic Concepts

λ‹€μŒ 글은 PyTorch Geometric 라이브러리 μ„€λͺ…μ„œμ— μžˆλŠ” Introduction by Example λ₯Ό μ°Έκ³ ν•˜μ—¬ μž‘μ„±ν–ˆμŠ΅λ‹ˆλ‹€.

baeseongsu.github.io

 

https://pytorch-geometric.readthedocs.io/en/latest/notes/introduction.html#

 

Introduction by Example — pytorch_geometric documentation

We shortly introduce the fundamental concepts of PyG through self-contained examples. At its core, PyG provides the following main features: PyG contains a large number of common benchmark datasets, e.g., all Planetoid datasets (Cora, Citeseer, Pubmed), al

pytorch-geometric.readthedocs.io

 

 

 

1️⃣  Data Handling of Graphs 


 

πŸ”Ή κ·Έλž˜ν”„

 

•  Node 와 이λ₯Ό μ—°κ²°ν•˜λŠ” Edge λ₯Ό ν•˜λ‚˜λ‘œ λͺ¨μ€ 자료ꡬ쑰 

•  G = (V,E) 

 

 

 

πŸ”Ή Pytorch Geometric 

 

•  ν•˜λ‚˜μ˜ κ·Έλž˜ν”„ → torch_geometric.data.Data λΌλŠ” 클래슀둜 ν‘œν˜„ 

 

•  μ†μ„±

 

data.x •  λ…Έλ“œ νŠΉμ§• ν–‰λ ¬ 
•  [num_nodes, num_node_features] 
data.edge_index •  κ·Έλž˜ν”„μ˜ μ—°κ²°μ„± 
•  [2, num_edges] 
data.edge_attr •  엣지 νŠΉμ§• ν–‰λ ¬ 
•  [num_deges, num_edge_features] 
data.y •  ν•™μŠ΅ν•˜κ³  싢은 λŒ€μƒ (target) 
•  κ·Έλž˜ν”„ 레벨 → [num_nodes, *] 
•  λ…Έλ“œ 레벨 → [1, *] 
data.pos •  λ…Έλ“œ μœ„μΉ˜ ν–‰λ ¬ 
•  [num_nodes, num_dimensions] 

 

→ μœ„μ˜ 속성듀은 μ˜΅μ…˜μœΌλ‘œ κ΅¬μ„±ν•˜κ³  싢은 속성을 선택해 λ‹€μ–‘ν•˜κ²Œ λͺ¨λΈλ§μ΄ κ°€λŠ₯ν•˜λ‹€. 

 

 

 

β‘  κ·Έλž˜ν”„ 데이터 생성 μ½”λ“œ μ˜ˆμ‹œ 

 

import torch 
from torch_geometric.data import Data 

edge_index = torch.tensor([[0,1,1,2],
                           [1,0,2,1]], dtype = torch.long) # (2,4) 크기의 ν–‰λ ¬ : 4개의 엣지

x = torch.tensor([[-1],[0],[1]], dtype = torch.float) # (3,1) 크기의 ν–‰λ ¬ : 3개의 λ…Έλ“œ 

data = Data(x=x, edge_index = edge_index)

 

 

∘ edge_index : (2,4) 크기의 ν–‰λ ¬ → 4개의 엣지듀 (μ–‘λ°©ν–₯ κ·Έλž˜ν”„) 

∘ x : (3,1) 크기의 ν–‰λ ¬  → 3개의 λ…Έλ“œμ™€ 각 λ…Έλ“œλŠ” 단일값을 가짐 

 

μ‹€μ œ κ·Έλž˜ν”„ λͺ¨μ–‘

 

 

 

β‘‘ κ·Έλž˜ν”„ 데이터 생성 μ½”λ“œ μ˜ˆμ‹œ : 엣지λ₯Ό λ…Έλ“œμ˜ μˆœμ„œμŒμœΌλ‘œ λ‚˜νƒ€λ‚Έ 경우 

 

  • (v1, v2) 와 같은 μžλ£Œν˜• ꡬ쑰 ν˜•νƒœλ‘œ μž…λ ₯ν•  경우 contiguous() λ₯Ό μ‚¬μš©ν•΄ ν‘œν˜„ν•œλ‹€. 

 

# 엣지λ₯Ό λ…Έλ“œμ˜ μˆœμ„œμŒμœΌλ‘œ ν‘œν˜„ν•œ 경우 

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.keys •  ν•΄λ‹Ή 속성 이름 
data.num_nodes •  λ…Έλ“œμ˜ 총 개수 
data.num_edges •  μ—£μ§€μ˜ 총 개수 
data.contains_isolated_nodes() •  κ³ λ¦½ λ…Έλ“œ μ—¬λΆ€ 확인
data.contains_self_loops() •  μ…€ν”„ 루프 포함 μ—¬λΆ€ 확인 
data.is_directed() •  κ·Έλž˜ν”„μ˜ λ°©ν–₯μ„± μ—¬λΆ€ 확인 

 

# ν•¨μˆ˜ 

print(data.keys) # ν•΄λ‹Ή 속성 이름 

πŸ‘‰ ['edge_index', 'x']



print(data['x']) # λ…Έλ“œ κ°’ 

πŸ‘‰ tensor([[-1.],
        [ 0.],
        [ 1.]])
        


for key, item in data : 
  print(f'{key} found in data')
  print(f'{item} found in data')
  print()
  

πŸ‘‰ κ²°κ³Ό 

x found in data
tensor([[-1.],
        [ 0.],
        [ 1.]]) found in data

edge_index found in data
tensor([[0, 1, 1, 2],
        [1, 0, 2, 1]]) found in data



'edge_attr' in data  # 엣지 νŠΉμ§• 행렬이 μžˆλƒ - μ—†λ‹€. 

πŸ‘‰ False


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





# Transfer data object to GPU.
device = torch.device('cuda')
data = data.to(device) # GPU μ‚¬μš©μœΌλ‘œ λ³€κ²½

 

 

 

 

2️⃣  Common Benchmark Datasets 


 

πŸ”Ή 데이터셋 

 

•  PyTorch Geometric 은 λ‹€μ–‘ν•œ 곡톡 벀치마크 데이터셋을 ν¬ν•¨ν•œλ‹€. 

•  각 λ°μ΄ν„°μ…‹λ§ˆλ‹€ κ·Έλž˜ν”„ 데이터 속성이 달라, μ‚¬μš©λ˜λŠ” ν•¨μˆ˜κ°€ λ‹€λ₯Ό 수 μžˆλ‹€. 

 

 

https://pytorch-geometric.readthedocs.io/en/latest/modules/datasets.html

 

torch_geometric.datasets — pytorch_geometric documentation

category (string) – The category of the images (one of "Aeroplane", "Bicycle", "Bird", "Boat", "Bottle", "Bus", "Car", "Cat", "Chair", "Diningtable", "Dog", "Horse", "Motorbike", "Person", "Pottedplant", "Sheep", "Sofa", "Train", "TVMonitor")

pytorch-geometric.readthedocs.io

 

→ 데이터 λͺ©λ‘ 

 

 

πŸ”Ή ENZYMES 데이터셋 예제 

 

• νš¨μ†Œ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 얻은 600개의 λ‹¨λ°±μ§ˆ 3μ°¨ ꡬ쑰 데이터셋 : νš¨μ†Œμ˜ λͺ…λͺ…법과 κ΄€λ ¨λœ 정보가 μ €μž₯λ˜μ–΄ μžˆλ‹€. 

6개의 νš¨μ†Œμ— κ΄€λ ¨λœ 데이터가 ν¬ν•¨λ˜μ–΄ μžˆλ‹€. 

 

 

from torch_geometric.datasets import TUDataset 

dataset = TUDataset(root = '/tmp/ENZYMES', name = 'ENZYMES')  
dataset

 

 

print(len(dataset)) # κ·Έλž˜ν”„ 개수 
print(dataset.num_classes) # κ·Έλž˜ν”„ 클래슀 수 
print(dataset.num_node_features) # λ…Έλ“œμ˜ νŠΉμ§• 수 

600
6
3

 

→ 6μ’…λ₯˜μ˜ 클래슀λ₯Ό 가진 600개의 κ·Έλž˜ν”„ 

 

 

 

•  인덱슀 μŠ¬λΌμ΄μ‹±μ„ 톡해 데이터 확인

 

data = dataset[0] # 인덱슀 μŠ¬λΌμ΄μ‹±μ„ 톡해 데이터 ν™•μΈν•˜κΈ° 
print(data) 

πŸ‘‰ κ²°κ³Ό

Data(edge_index=[2, 168], x=[37, 3], y=[1])


# edge_index=[2, 168] : 84 개의 엣지 (2 : μ–‘λ°©ν–₯, 엣지 개수 : 84개) 
#  x=[37, 3]  : 37개의 λ…Έλ“œμ™€ 3개의 λ…Έλ“œ νŠΉμ„± (ν•˜λ‚˜μ˜ λ…Έλ“œκ°€ 3개의 값을 가짐)
#  y=[1]   : κ·Έλž˜ν”„ 레벨 νƒ€κ²Ÿ

 

∘  edge_index=[2, 168] : 84 개의 엣지 (2 : μ–‘λ°©ν–₯, 엣지 개수 : 84개) 

∘  x=[37, 3]  : 37개의 λ…Έλ“œμ™€ 3개의 λ…Έλ“œ νŠΉμ„± (ν•˜λ‚˜μ˜ λ…Έλ“œκ°€ 3개의 κ°’을 κ°€μ§)

∘  y=[1]   : κ·Έλž˜ν”„ λ ˆλ²¨ νƒ€κ²Ÿ 

 

 

data.is_undirected() # μ–‘λ°©ν–₯ κ·Έλž˜ν”„μΈκ°€μš” - λ„€

True

 

 

train_dataset = dataset[:540] 
test_dataset = dataset[540:] 

print(train_dataset)
print(test_dataset) 


πŸ‘‰ κ²°κ³Ό

ENZYMES(540)
ENZYMES(60)

 

 

 

dataset = dataset.shuffle() # 데이터셋 μ…”ν”Œ 
print(dataset)

πŸ‘‰ κ²°κ³Ό

ENZYMES(600)

 

 

 

πŸ”Ή Cora λ°μ΄ν„°μ…‹ 예제

 

• 2708 개의 κ³Όν•™ λ…Όλ¬Έλ“€λ‘œ κ΅¬μ„±λœ 데이터셋 

• 논문은 λ‹€λ₯Έ 논문듀을 μΈμš©ν•˜κΈ°λ„ ν•˜λŠ”λ°, 이 연결ꡬ쑰λ₯Ό ν‘œν˜„ν•œ 것이 λ°”λ‘œ Citation Network 이닀.

 

Co-citation network : https://www.researchgate.net/figure/Co-citation-network-graph-largest-connected-component_fig2_337127523

 

 

from torch_geometric.datasets import Planetoid

dataset = Planetoid(root = 'tmp/Cora', name = 'Cora')

 

print(len(dataset)) # 데이터셋 전체가 ν•˜λ‚˜μ˜ κ·Έλž˜ν”„μž„ 

1

print(dataset.num_classes)  # 7개의 클래슀 

7

print(dataset.num_node_features) # 1433개의 λ…Έλ“œ νŠΉμ„± (ν•˜λ‚˜μ˜ λ…Έλ“œμ— 1433개의 값이 쑴재)

1433

 

∘  데이터셋 전체가 ν•˜λ‚˜μ˜ κ·Έλž˜ν”„μ΄κ³ , 7개의 λ…Έλ“œ ν΄λž˜μŠ€κ°€ μ‘΄μž¬ν•˜λ©°, ν•˜λ‚˜μ˜ λ…Έλ“œμ— 1433개의 값이 μ €μž₯λ˜μ–΄ μžˆλŠ” ꡬ쑰

 

 

data = dataset[0] 

data

πŸ‘‰ κ²°κ³Ό

Data(x=[2708, 1433], edge_index=[2, 10556], y=[2708], train_mask=[2708], val_mask=[2708], test_mask=[2708])

 

∘  edge_index=[2, 10556] : 5,278 개의 엣지 (2 : μ–‘λ°©ν–₯, 엣지 개수 : 5,278개) 

∘  x=[2708, 1433]  : 2708개의 λ…Έλ“œμ™€ 1433개의 λ…Έλ“œ νŠΉμ„± (ν•˜λ‚˜μ˜ λ…Έλ“œκ°€ 1433개의 값을 가짐)

∘  y=[2708]   : κ·Έλž˜ν”„ 레벨 νƒ€κ²Ÿ 

 

 

print(data.is_undirected())  # μ–‘λ°©ν–₯ κ·Έλž˜ν”„ 

True

 

data.train_mask.sum().item() # ν•™μŠ΅ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” λ…Έλ“œ

140

data.val_mask.sum().item() # 검증 μ‹œ μ‚¬μš©ν•˜λŠ” λ…Έλ“œ

500

data.test_mask.sum().item()  # ν…ŒμŠ€νŠΈ μ‹œ μ‚¬μš©ν•˜λŠ” λ…Έλ“œ

1000

 

 

 

 

 

3️⃣ Mini-batches


 

πŸ”Ή λ°°μΉ˜ λ‹¨μœ„μ˜ ν•™μŠ΅ 

 

•  PyTorch Geometric 은 sparse block diagonal adjacency matrices λ₯Ό 톡해 λ―Έλ‹ˆλ°°μΉ˜ ν˜•νƒœλ‘œ λ§Œλ“€κ³ , λ³‘λ ¬ν™”μ²˜λ¦¬λ₯Ό μˆ˜ν–‰ν•œλ‹€. 

•  feature ν–‰λ ¬κ³Ό target 행렬도 λ…Έλ“œ κΈ°μ€€μœΌλ‘œ λ™μΌν•œ ν˜•νƒœλ‘œ ꡬ성해야 ν•œλ‹€. 

 

ν•˜λ‚˜μ˜ λ°°μΉ˜κ°€ λ˜μ–΄ λ™μž‘

 

 

•  torch_geometric.data.DataLoader λ₯Ό 톡해 배치 λ‹¨μœ„λ‘œ 데이터λ₯Ό 처리 

 

 

from torch_geometric.datasets import TUDataset 
from torch_geometric.data 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 : 

  print(batch)

  print(batch.num_graphs)

 

...

 

 

 

 

 

batch λŠ” 열벑터 (각 λ…Έλ“œλ“€μ„ 각각의 λ°°μΉ˜μ™€ 맀핑)

 

•  batch = [984] : 984 개의 λ…Έλ“œμ— λŒ€ν•΄ 32개의 배치λ₯Ό λΆ€μ—¬ 

 

 

 

4️⃣  Data Transforms 


 

πŸ”Ή 데이터 λ³€ν™˜ 

 

•  torch_geometric.transforms 둜 데이터 λ³€ν™˜μ„ μ†μ‰½κ²Œ ν•  수 μžˆλ‹€. 

•  torch_geometric.transforms.Compose λ₯Ό 톡해 λ‹€μ–‘ν•œ λ³€ν™˜ν•¨μˆ˜λ“€μ„ μ†μ‰½κ²Œ ꡬ성할 수 μžˆλ‹€. 

 

 

 

πŸ”Ή ShapeNet dataset 에 데이터 λ³€ν™˜μ„ μ μš©ν•œ 예제 

 

•  17,000 건의 3D ν˜•νƒœμ˜ 점 ꡬ름 (point clouds) 데이터λ₯Ό ν¬ν•¨ν•˜κ³  있으며 총 16개의 μΉ΄ν…Œκ³ λ¦¬λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆλ‹€. 

 

 

 

 

from torch_geometric.datasets import ShapeNet 

dataset = ShapeNet(root = '/tmp/ShapeNet', categories = ['Airplane']) 

dataset[0]

πŸ‘‰ κ²°κ³Ό 

Data(x=[2518, 3], y=[2518], pos=[2518, 3], category=[1])

 

∘  pos = [2518, 3] : 2518 개의 점 데이터와 3차원 

∘  edge_index κ°€ μ—†μŒ → 연결관계가 μ—†λŠ” 데이터 

 

 

 

import torch_geometric.transforms as T 
from torch_geometric.datasets import ShapeNet 

# κ·Έλž˜ν”„ λ³€ν™˜ 

dataset = ShapeNet(root = '/tmp/ShapeNet', categories = ['Airplane'], 
                   pre_transform = T.KNNGraph(k=6), 
                   transform = T.RandomTranslate(0.01))

 

∘  pre_transform = T.KNNGraph(k=6) : KNN 으둜 데이터λ₯Ό κ·Έλž˜ν”„ ν˜•νƒœλ‘œ λ³€ν™˜ν•œλ‹€. μ•„λž˜μ˜ 좜λ ₯ κ²°κ³Όλ₯Ό 보면 edge_index κ°€ μΆ”κ°€λœ 것을 λ³Ό 수 μžˆλ‹€. (μ—°κ²°μƒνƒœκ°€ 생성됨) 

∘  transform = T.RandomTranslate(0.01) : 각 λ…Έλ“œμ˜ μœ„μΉ˜λ₯Ό 쑰금 μ΄λ™μ‹œν‚¨λ‹€. 

 

dataset[0]

πŸ‘‰ κ²°κ³Ό

Data(x=[2518, 3], y=[2518], pos=[2518, 3], category=[1])

 

 

 

 

 

5️⃣  Learning Methods on Graphs 


 

πŸ”Ή κ·Έλž˜ν”„λ‘œ ν•™μŠ΅ν•˜κΈ° 

 

•   κ·Έλž˜ν”„ 데이터 핸듀링, dataloader 생성, transforms λ₯Ό 톡해 데이터 λ³€ν™˜ πŸ‘‰ 이제 κ·Έλž˜ν”„λ₯Ό ν•™μŠ΅μ‹œμΌœλ³΄μž

 

 

 

πŸ”Ή GCN layer λ₯Ό κ΅¬μ„±ν•˜μ—¬ Cora 데이터셋에 μ μš©ν•˜λŠ” 예제 

 

•   Task : Graph node classification (λ…Έλ“œ λΆ„λ₯˜ 문제) : λ…Όλ¬Έ λ‚΄ λ“±μž₯ν•œ 단어듀과 인용 κ΄€κ³„λ§Œμ„ ν†΅ν•˜μ—¬ μ–΄λ–€ μ’…λ₯˜μ˜ 논문인지 λ§žνžˆλŠ” 문제 

 

 

(1) 데이터 λ‹€μš΄λ‘œλ“œ 

 

from torch_geometric.datasets import Planetoid

dataset = Planetoid(root='/tmp/Cora', name='Cora')

 

•   Citation Network πŸ‘‰ Node = λ…Όλ¬Έ ,  Edge = μΈμš©κ΄€κ³„ 

•   λ…Όλ¬Έμ—μ„œ λ“±μž₯ν•˜λŠ” 1433개의 νŠΉμ • 단어λ₯Ό λͺ¨μ•„ 단어 μ‚¬μ „μœΌλ‘œ λ§Œλ“€κ³ , λ…Όλ¬Έλ§ˆλ‹€ λ‹¨μ–΄λ“€μ˜ λ“±μž₯ μ—¬λΆ€λ₯Ό feature vector 둜 λ§Œλ“€μ–΄ λ…Έλ“œμ˜ νŠΉμ§•μ„ λ§Œλ“€μ–΄μ€€λ‹€. 

 

 

(2) GNN μƒμ„±ν•˜κΈ° 

 

import torch 
import torch.nn.functional as F 
from torch_geometric.nn import GCNConv 

class Net(torch.nn.Module) : 
  def __init__(self) : 
    super(Net, self).__init__() 
    self.conv1 = GCNConv(dataset.num_node_features, 16) 
    self.conv2 = GCNConv(16, dataset.num_classes)

    # 2개의 GCNConv layer

  def forward(self, data) : 
    x, edge_index = data.x, data.edge_index   

    x = self.conv1(x, edge_index) 
    x = F.relu(x) 
    x = F.dropout(x, training = self.training) 
    x = self.conv2(x, edge_index) 

    return F.log_softmax(x, dim=1)

 

 

(3)  ν•™μŠ΅ν•˜κΈ° 

 

 

# GPU μ„€μ •
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = Net().to(device)  
data = dataset[0].to(device) 

# μ˜΅ν‹°λ§ˆμ΄μ € 생성
optimizer = torch.optim.Adam(model.parameters(), lr = 0.01, weight_decay = 5e-4) 

# ν›ˆλ ¨ 

model.train() # ν•™μŠ΅ μ€€λΉ„ 

for epoch in range(200) : 
  optimizer.zero_grad() # νŒŒλΌλ―Έν„° μ΄ˆκΈ°ν™”
  out = model(data) # μ˜ˆμΈ‘κ°’
  loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask]) # μ†μ‹€ν•¨μˆ˜ 계산 
  loss.backward() # μ—­μ „νŒŒ 
  optimizer.step() # νŒŒλΌλ―Έν„° 업데이터

 

(3)  ν…ŒμŠ€νŠΈ λ°μ΄ν„°λ‘œ λͺ¨λΈ 평가 

 

# λͺ¨λΈ 평가 

model.eval() 

_, pred = model(data).max(dim=1) 

correct = float (pred[data.test_mask].eq(data.y[data.test_mask]).sum().item()) 
acc = correct / data.test_mask.sum().item() 

print('정확도 : {:.4f}'.format(acc))

 

 

 

 

 

https://colab.research.google.com/drive/1HDtOE5sZUPvA93ZT-OzXHuGgrSeU7hNk?usp=sharing 

 

1주차 볡슡과제.ipynb

Colaboratory notebook

colab.research.google.com

 

 

728x90

'1️⃣ AIβ€’DS > πŸ“˜ GNN' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

[CS224W] Graph Neural Network  (0) 2022.11.24
[CS224W] Message Passing and Node classification  (0) 2022.11.17
[CS224W] PageRank  (0) 2022.11.02
[CS224W] 1κ°• Machine Learning With Graphs  (0) 2022.10.11
[CS224W] NetworkX , pytorch geometric Tutorial  (2) 2022.10.07

λŒ“κΈ€