일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 부스트캠프 AI테크
- Operation function
- Numpy data I/O
- 가능도
- pivot table
- groupby
- linalg
- Python
- 정규분포 MLE
- Numpy
- 딥러닝
- Array operations
- Python 유래
- ndarray
- namedtuple
- Comparisons
- scatter
- BOXPLOT
- python 문법
- VSCode
- 카테고리분포 MLE
- type hints
- boolean & fancy index
- seaborn
- 표집분포
- subplot
- 최대가능도 추정법
- Python 특징
- unstack
- dtype
- Today
- Total
또르르's 개발 Story
[25-1] GraphSAGE 모델 구현 using DGL Library 본문
1️⃣ 설정
DGL 모듈을 설치합니다.
!pip install dgl
필요한 모듈을 import 합니다.
import numpy as np
import time
import torch
import torch.nn as nn # building block들을 제공
import torch.nn.functional as F # 다양한 함수 제공
import dgl # Depp Graph Libaray
from dgl.data import CoraGraphDataset
from sklearn.metrics import f1_score
하이퍼파라미터를 초기화합니다.
dropoutProb = 0.5
learningRate = 1e-2
numEpochs = 50
numHiddenDim = 128
numLayers = 2
weightDecay = 5e-4
aggregatorType = "gcn"
2️⃣ Cora 데이터셋 사용하기
Cora 데이터셋은 2708개의 논문(노드), 10556개의 인용관계(엣지)로 이루어졌습니다.
NumFeat은 각 노드를 나타내는 특성을 말합니다.
Cora 데이터셋은 각 노드가 1433개의 특성을 가지고, 개개의 특성은 '1'혹은 '0'으로 나타내어지며 특정 단어의 논문 등장 여부를 나타냅니다.
즉, 2708개의 논문에서 특정 단어 1433개를 뽑아서, 1433개의 단어의 등장 여부를 통해 각 노드를 표현합니다.
노드의 라벨은 총 7개가 존재하고, 각 라벨은 논문의 주제를 나타냅니다
[Case_Based, Genetic_Algorithms, Neural_Networks, Probabilistic_Methods, Reinforcement_Learning, Rule_Learning, Theory]
2708개의 노드 중, 학습에는 140개의 노드를 사용하고 모델을 테스트하는 데에는 1000개를 사용합니다.
(모든 정점이 집계함수를 공유하기 때문에 소수의 정점을 가지고 집계함수를 훈련시킨 다음 그 집계함수를 다른 정점에도 적용할 수 있음 )
요약하자면, 앞서 학습시킬 모델은 Cora 데이터셋의 [논문 내 등장 단어들, 논문들 사이의 인용관계]를 활용하여 논문의 주제를 예측하는 모델입니다.
Cora Graph Dataset 불러옵니다.
G = CoraGraphDataset()
numClasses = G.num_classes # class 개수 받아오기
노드들의 feauture & feature의 차원을 가져옵니다.
G = G[0]
features = G.ndata['feat'] # 2708 (논문) x 1433 (속성)
inputFeatureDim = features.shape[1] # 1433 (속성 수)
각 노드들의 실제 라벨은 'label'에 저장되어 있습니다.
labels = G.ndata['label']
학습/테스트에 사용할 노드들에 대한 표시 (마스크가 씌어진 data로 test값들은 가린 train data)합니다.
trainMask = G.ndata['train_mask']
testMask = G.ndata['test_mask']
3️⃣ 모델 구현
모델은 GraphSAGE입니다.
1)에서는 기존에 구현되어 있는 SAGEConv 모듈을 불러와서, SAGEConv로 이루어진 GraphSAGE 모델을 구축합니다.
2)에서는 SAGEConv를 직접 구현 GraphSAGE 모델을 학습시킵니다.
1) 기존 SAGEConv 모듈을 사용한 GraphSAGE 모델
parameter들은 다음과 같이 정의됩니다.
- graph : 학습할 그래프
- inFeatDim : 데이터의 feature의 차원
- numHiddenDim : 모델의 hidden 차원
- numClasses : 예측할 라벨의 경우의 수
- numLayers : 인풋, 아웃풋 레이어를 제외하고 중간 레이어의 갯수
- activationFunction : 활성화 함수의 종류
- dropoutProb : 드롭아웃할 확률
- aggregatorType : [mean, gcn, pool (for max), lstm]
from dgl.nn.pytorch.conv import SAGEConv
class GraphSAGE(nn.Module):
def __init__(self,graph, inFeatDim, numHiddenDim, numClasses, numLayers, activationFunction, dropoutProb, aggregatorType):
super(GraphSAGE, self).__init__()
self.layers = nn.ModuleList() # 층들이 어떻게 구성되는지
self.graph = graph
# 인풋 레이어
# SAGEConv 사용
# inFeatDim : input dimension, numHiddenDim : 0번째 레이어 dimension, aggregatorType : AGG (평균, lstm, element-wise-max )
# dropoutProb : 드랍아웃 확률값, activationFunction : sigmoid, ReLU
self.layers.append(SAGEConv(inFeatDim, numHiddenDim, aggregatorType, dropoutProb, activationFunction))
# 히든 레이어 (나머지 층)
for i in range(numLayers):
self.layers.append(SAGEConv(numHiddenDim, numHiddenDim, aggregatorType, dropoutProb, activationFunction))
# 출력 레이어
# 출력 레이어는 activation 사용 X, numClasess를 output dimension으로 넣어줌
self.layers.append(SAGEConv(numHiddenDim, numClasses, aggregatorType, dropoutProb, activation=None))
def forward(self, features):
x = features
for layer in self.layers:
x = layer(self.graph, x)
return x
2) SAGEConv을 직접 구현한 GraphSAGE 모델
SAGEConv는 다음과 같이 구현됩니다.

여기서 AGG는 평균으로 정의합니다.
∑u∈N(v)hk−1u|N(v)|
SAGECOnv의 parameter들은 다음과 같습니다.
- in_feats: 인풋 feature의 사이즈
- out_feats: 아웃풋 feature의 사이즈
- activation: None이 아니라면, 노드 피쳐의 업데이트를 위해서 해당 activation function을 적용.
- ref:
https://arxiv.org/pdf/1706.02216.pdf
https://docs.dgl.ai/en/0.4.x/_modules/dgl/nn/pytorch/conv/sageconv.html
class SAGEConv(nn.Module):
def __init__(self, in_feats, out_feats, activation):
super(SAGEConv, self).__init__()
self._in_feats = in_feats
self._out_feats = out_feats
self.activation = activation
# 입력에 in_feats+in_feats를 넣어주는 이유는 두 개의 vector를 concat하는 부분이 있기 때문에
self.W = nn.Linear(in_feats+in_feats, out_feats, bias=True)
def forward(self, graph, feature):
graph.ndata['h'] = feature # 이전 layer의 embedding을 가지고와서 'h'라는 이름으로 저장
# h를 복사해서 m에 저장, m을 sum해서 neigh으로 저장
# Graph.update_all(message_func : edge를 따라 메시지를 생성하는 function, reduce_func: 메시지를 집계하는 function)
# message function은 '필드'에 메세지를 저장해주는 역할이고, reduce function은 해당 메세지를 모아주는 역할을 합니다.
# in_degrees의 수를 세는 것
# 이웃들의 합이므로 sum (h^{k-1}_u)가 됨
graph.update_all(fn.copy_src('h', 'm'), fn.sum('m', 'neigh'))
# Aggregate & Noramlization
degs = graph.in_degrees().to(feature) # degree 계산(연결성 수) : N(v)
# graph.ndata['neigh'] : h^k-1_u
hkNeigh = graph.ndata['neigh']/degs.unsqueeze(-1) # sum한 값을 / degree로 나눠줘서 평균 계산
# 이웃들 평균이랑 (hkNeigh) 이전 layer의 embedding (graph.ndata['h'])을 concat해서 신경망 통과
hk = self.W(torch.cat((graph.ndata['h'], hkNeigh), dim=-1))
if self.activation != None:
hk = self.activation(hk)
return hk
따라서 GraphSage 함수를 작성하면 다음과 같습니다.
class GraphSAGE(nn.Module):
def __init__(self, graph, inFeatDim, numHiddenDim, numClasses, numLayers, activationFunction):
super(GraphSAGE, self).__init__()
self.layers = nn.ModuleList()
self.graph = graph
# 인풋 레이어
self.layers.append(SAGEConv(inFeatDim, numHiddenDim, activationFunction))
# 히든 레이어
for i in range(numLayers):
self.layers.append(SAGEConv(numHiddenDim, numHiddenDim, activationFunction))
# 출력 레이어 (Activation 사용하지 않음)
self.layers.append(SAGEConv(numHiddenDim, numClasses, activation=None))
def forward(self, features):
x = features
for layer in self.layers:
x = layer(self.graph, x)
return x
4️⃣ 평가 함수
평가함수는 Train과 Test 두가지로 나눠집니다.
1) Train 평가함수
def evaluateTrain(model, features, labels, mask):
model.eval()
with torch.no_grad():
logits = model(features)
logits = logits[mask]
labels = labels[mask]
_, indices = torch.max(logits, dim=1)
correct = torch.sum(indices == labels)
return correct.item() * 1.0 / len(labels)
2) Test 평가함수
def evaluateTest(model, features, labels, mask):
model.eval()
with torch.no_grad(): # no update gradients
logits = model(features)
logits = logits[mask]
labels = labels[mask]
_, indices = torch.max(logits, dim=1)
macro_f1 = f1_score(labels, indices, average = 'macro') # f1 score 계산
correct = torch.sum(indices == labels) # 정답을 잘 분류한 경우에만 개수+1
return correct.item() * 1.0 / len(labels), macro_f1 # 테스트 수로 나눠서 accuracy를 측정
5️⃣ Train & Test
Train 함수와 Test 함수를 정의합니다.
def train(model, lossFunction, features, labels, trainMask, optimizer, numEpochs):
executionTime=[]
for epoch in range(numEpochs):
model.train()
startTime = time.time()
logits = model(features) # 포워딩
loss = lossFunction(logits[trainMask], labels[trainMask]) # 모델의 예측값과 실제 라벨을 비교하여 loss 값 계산
optimizer.zero_grad()
loss.backward()
optimizer.step()
executionTime.append(time.time() - startTime)
acc = evaluateTrain(model, features, labels, trainMask)
print("Epoch {:05d} | Time(s) {:.4f} | Loss {:.4f} | Accuracy {:.4f}".format(epoch, np.mean(executionTime), loss.item(), acc))
def test(model, feautures, labels, testMask):
acc, macro_f1 = evaluateTest(model, features, labels, testMask)
print("Test Accuracy {:.4f}".format(acc))
print("Test macro-f1 {:.4f}".format(macro_f1))
모델을 생성하고 Optimizer를 초기화시킵니다.
# 모델 생성
model = GraphSAGE(G, inputFeatureDim, numHiddenDim, numClasses, numLayers, F.relu, dropoutProb, aggregatorType)
lossFunction = torch.nn.CrossEntropyLoss()
# 옵티마이저 초기화
optimizer = torch.optim.Adam(model.parameters(), lr=learningRate, weight_decay=weightDecay)
>>> print(model)
GraphSAGE(
(layers): ModuleList(
(0): SAGEConv(
(feat_drop): Dropout(p=0.5, inplace=False)
(fc_neigh): Linear(in_features=1433, out_features=128, bias=True)
)
(1): SAGEConv(
(feat_drop): Dropout(p=0.5, inplace=False)
(fc_neigh): Linear(in_features=128, out_features=128, bias=True)
)
(2): SAGEConv(
(feat_drop): Dropout(p=0.5, inplace=False)
(fc_neigh): Linear(in_features=128, out_features=128, bias=True)
)
(3): SAGEConv(
(feat_drop): Dropout(p=0.5, inplace=False)
(fc_neigh): Linear(in_features=128, out_features=7, bias=True)
)
)
)
모델을 Train 시킵니다.
>>> train(model, lossFunction, features, labels, trainMask, optimizer, numEpochs)
Epoch 00000 | Time(s) 0.3029 | Loss 1.9706 | Accuracy 0.1500
Epoch 00001 | Time(s) 0.2044 | Loss 1.9943 | Accuracy 0.6143
Epoch 00002 | Time(s) 0.1721 | Loss 1.8316 | Accuracy 0.7000
Epoch 00003 | Time(s) 0.1560 | Loss 1.6697 | Accuracy 0.4929
Epoch 00004 | Time(s) 0.1472 | Loss 1.4903 | Accuracy 0.8143
Epoch 00005 | Time(s) 0.1420 | Loss 1.2114 | Accuracy 0.9071
Epoch 00006 | Time(s) 0.1380 | Loss 0.9237 | Accuracy 0.9286
Epoch 00007 | Time(s) 0.1342 | Loss 0.6185 | Accuracy 0.9357
Epoch 00008 | Time(s) 0.1313 | Loss 0.4386 | Accuracy 0.9643
Epoch 00009 | Time(s) 0.1287 | Loss 0.2836 | Accuracy 0.9643
Epoch 00010 | Time(s) 0.1269 | Loss 0.2154 | Accuracy 0.9643
Epoch 00011 | Time(s) 0.1251 | Loss 0.1319 | Accuracy 0.9786
Epoch 00012 | Time(s) 0.1237 | Loss 0.1230 | Accuracy 0.9857
Epoch 00013 | Time(s) 0.1223 | Loss 0.0734 | Accuracy 0.9857
Epoch 00014 | Time(s) 0.1214 | Loss 0.0901 | Accuracy 0.9857
Epoch 00015 | Time(s) 0.1204 | Loss 0.0459 | Accuracy 0.9929
Epoch 00016 | Time(s) 0.1197 | Loss 0.0521 | Accuracy 0.9929
Epoch 00017 | Time(s) 0.1191 | Loss 0.0243 | Accuracy 0.9929
Epoch 00018 | Time(s) 0.1188 | Loss 0.0360 | Accuracy 0.9929
Epoch 00019 | Time(s) 0.1184 | Loss 0.0179 | Accuracy 0.9929
Epoch 00020 | Time(s) 0.1179 | Loss 0.0416 | Accuracy 0.9857
Epoch 00021 | Time(s) 0.1174 | Loss 0.0333 | Accuracy 1.0000
Epoch 00022 | Time(s) 0.1169 | Loss 0.0298 | Accuracy 0.9857
Epoch 00023 | Time(s) 0.1164 | Loss 0.0428 | Accuracy 1.0000
Epoch 00024 | Time(s) 0.1161 | Loss 0.0231 | Accuracy 1.0000
Epoch 00025 | Time(s) 0.1157 | Loss 0.0126 | Accuracy 0.9857
Epoch 00026 | Time(s) 0.1154 | Loss 0.0488 | Accuracy 0.9929
Epoch 00027 | Time(s) 0.1150 | Loss 0.1046 | Accuracy 1.0000
Epoch 00028 | Time(s) 0.1146 | Loss 0.0209 | Accuracy 0.9857
Epoch 00029 | Time(s) 0.1144 | Loss 0.0119 | Accuracy 0.9786
Epoch 00030 | Time(s) 0.1142 | Loss 0.1598 | Accuracy 1.0000
Epoch 00031 | Time(s) 0.1139 | Loss 0.0210 | Accuracy 1.0000
Epoch 00032 | Time(s) 0.1140 | Loss 0.0305 | Accuracy 0.9929
Epoch 00033 | Time(s) 0.1137 | Loss 0.1382 | Accuracy 0.9857
Epoch 00034 | Time(s) 0.1137 | Loss 0.1129 | Accuracy 0.9857
Epoch 00035 | Time(s) 0.1134 | Loss 0.0555 | Accuracy 0.9714
Epoch 00036 | Time(s) 0.1132 | Loss 0.0649 | Accuracy 0.9857
Epoch 00037 | Time(s) 0.1129 | Loss 0.0926 | Accuracy 1.0000
Epoch 00038 | Time(s) 0.1128 | Loss 0.0531 | Accuracy 0.9929
Epoch 00039 | Time(s) 0.1126 | Loss 0.0174 | Accuracy 1.0000
Epoch 00040 | Time(s) 0.1125 | Loss 0.0871 | Accuracy 0.9929
Epoch 00041 | Time(s) 0.1123 | Loss 0.0732 | Accuracy 1.0000
Epoch 00042 | Time(s) 0.1122 | Loss 0.0499 | Accuracy 1.0000
Epoch 00043 | Time(s) 0.1122 | Loss 0.0428 | Accuracy 1.0000
Epoch 00044 | Time(s) 0.1121 | Loss 0.0458 | Accuracy 1.0000
Epoch 00045 | Time(s) 0.1119 | Loss 0.0150 | Accuracy 1.0000
Epoch 00046 | Time(s) 0.1118 | Loss 0.0363 | Accuracy 0.9929
Epoch 00047 | Time(s) 0.1118 | Loss 0.0516 | Accuracy 1.0000
Epoch 00048 | Time(s) 0.1117 | Loss 0.0171 | Accuracy 1.0000
Epoch 00049 | Time(s) 0.1117 | Loss 0.0197 | Accuracy 1.0000
Train 된 모델을 test 할 때 Test Accuracy가 77% 정도 나오는 것을 알 수 있습니다.
>>> test(model, features, labels, testMask)
Test Accuracy 0.7750
Test macro-f1 0.7652
'부스트캠프 AI 테크 U stage > 실습' 카테고리의 다른 글
[31-3] Scratch Training VS Fine Tuning (VGG-11) (0) | 2021.03.08 |
---|---|
[31-2] VGG-11 구현 using PyTorch (0) | 2021.03.08 |
[24-3] 추천 시스템 using surprise library (0) | 2021.02.26 |
[24-2] Node2vec Library (0) | 2021.02.26 |
[23-3] Collaborative Filtering 구현 (0) | 2021.02.25 |