그래프 신경망이란 무엇일까? (기본)

2021. 2. 26. 15:36·AI/이론
그래프 신경망이란 무엇일까 (기본)

그래프 신경망이란 무엇일까? (기본)¶


✅ 정점 표현 학습 복습¶

  • 그래프의 정점을 어떻게 벡터로 표현할까?

📌 출력으로 임베딩 자체를 얻는 변환식 임베딩 방법은 여러 한계를 갖는다.¶

  1. 학습이 진행된 이후에 추가된 정점에 대해서는 임베딩을 얻을 수 없다.
  2. 모든 정점에 대한 임베딩을 미리 계산하여 저장해두어야 한다.
  3. 정점이 속성(Attribute) 정보를 가진 경우에 이를 활용할 수 없다.

📌 출력으로 인코더를 얻는 귀납식 임베딩 방법은 여러 장점을 갖는다.¶

  1. 학습이 진행된 이후에 추가된 정점에 대해서도 임베딩을 얻을 수 있다.
  2. 모든 정점에 대한 임베딩을 미리 계산하여 저장해둘 필요가 없다.
  3. 정점이 속성(Attribute) 정보를 가진 경우에 이를 활용할 수 있다.

image.png


✅ 그래프 신경망 기본¶

1️⃣ 그래프 신경망의 구조¶

📌 그래프 신경망은 그래프와 정점의 속성 정보를 입력으로 받는다.¶

  • 그래프의 인접 행렬을 A라고 하자.
  • 인접 행렬 A는 $|V|$X$|V|$의 이진 행렬이다.
  • 각 정점 $u$의 속성(Attrivute) 벡터를 $X_{u}$라고 하자.
  • 정점 속성 벡터 $X_{u}$는 m차원 벡터이고, m은 속성의 수를 의미한다.

정점의 속성의 예시는 다음과 같다.

  • 온라인 소셜 네트워크에서 사용자의 지역, 성별, 연령, 프로필 사진 등
  • 논문 인용 그래프에서 논문에 사용된 키워드에 대한 원-핫 벡터
  • PageRank 등의 정점 중심성, 군집 계수(Clustering Coefficient) 등


📌 그래프 신경망은 이웃 정점들의 정보를 집계하는 과정을 반복하여 임베딩을 얻는다.¶

  • 아래 예시를 보면 대상 정점의 임베딩을 얻기 위해 이웃들 그리고 이웃의 이웃들의 정보를 집계한다.

image.png


📌 각 집계 단계를 층(Layer)이라고 부르고, 각 층마다 임베딩을 얻습니다¶

  • 각 층에서는 이웃들의 이전 층 임베딩을 집계하여 새로운 임베딩을 얻는다.
  • 0번 층, 즉 입력 층의 임베딩으로는 정점의 속성 벡터를 사용한다.

image.png


📌 대상 정점 마다 집계되는 정보가 상이하다.¶

  • 대상 정점 별 집계되는 구조를 계산 그래프(Computation Graph)라고 부른다.

image.png



서로 다른 대상 정점간에도 층 별 집계 함수는 공유한다.

image.png


📌 서로 다른 구조의 계산 그래프를 처리하기 위해서는 어떤 형태의 집계 함수가 필요할까?¶

image.png


집계 함수는 (1) 이웃들 정보의 평균을 계산하고 (2) 신경망에 적용하는 단계를 거친다.

image.png

image.png


마지막 층에서의 정점 별 임베딩이 해당 정점의 출력 임베딩이다.

image.png



2️⃣ 그래프 신경망의 학습¶

📌 그래프 신경망의 학습 변수(Trainable Parameter)는 층 별 신경망의 가중치이다.¶

image.png


📌 먼저 손실함수를 결정한다. 정점간 거리를 "보존"하는 것을 목표로 할 수 있다.¶

  • 변환식 정점 임베딩에서처럼 그래프에서의 정점간 거리를 "보존"하는 것을 목표로 할 수 있다.
  • 만약, 인접성을 기반으로 유사도를 정의한다면, 손실 함수는 다음과 같다.

image.png


📌후속 과제(Downstream Task)의 손실함수를 이용한 종단종(End-to-End) 학습도 가능하다.¶

🔎 정점 분류가 최종 목표인 경우를 생각해 보자.

예를 들어,

  1. 그래프 신경망을 이용하여 정점의 임베딩을 얻고
  2. 이를 분류기(Classifier)의 입력으로 사용하여
  3. 각 정점의 유형을 분류하려고 한다.

이 경우 분류기의 손실함수, 예를 들어 교차 엔트로피(Cross Entropy)를 전체 프로세스의 손실함수로 사용하여 종단종(End-to-End) 학습을 할 수 있다.

image.png

image.png


📌 그래프 신경망과 변환적 정점 임베딩을 이용한 정점 분류 비교¶

  • 그래프 신경망의 종단종(End-to-End) 학습을 통한 분류는 변환적 정점 임베딩 이후에 별도의 분류기를 학습하는 것보다 정확도가 대체로 높다.

아래 표는 다양한 데이터에서의 정점 분류의 정확도(Accuracy)를 보여준다.

image.png


📌학습에 사용할 대상 정점을 결정하여 학습 데이터를 구성¶

  • 우리는 모든 정점을 넣고 계산할 필요는 없다.
  • 선택한 대상 정점들만을 가지고 계산 그래프를 구성 가능하다.

image.png


📌 마지막으로 오차역전파(Backpropagation)을 통해 손실 함수를 최소화 한다.¶

구체적으로, 오차역전파를 통해 신경망의 학습 변수들을 학습한다.

image.png



3️⃣ 그래프 신경망의 활용¶

  • 또한 일부 정점들을 선택하여 계산 그래프를 계산하였고 학습된 신경망을 적용하여, 학습에 사용되지 않은 정점의 임베딩을 얻을 수 있다.

image.png


📌 마찬가지로, 학습 이후에 추가된 정점의 임베딩도 얻을 수 있다.¶

  • 온라인 소셜네트워크 등 많은 실제 그래프들은 시간에 따라서 변화한다.

image.png


📌 학습된 그래프 신경망을, 새로운 그래프에 적용할 수도 있다.¶

  • 예를 들어, A종의 단백질 상호 작용 그래프에서 학습한 그래프 신경망을 B종의 단백질 상호작용 그래프에 적용할 수 있다.

image.png



✅ 그래프 신경망 변형¶

1️⃣ 그래프 합성곱 신경망¶

📌 그래프 합성곱 신경망(Graph Convolutional Network, GCN)의 집계 함수이다.¶

image.png


차이를 보기 위해 기존의 집계 함수와 비교해 보자. 작은 차이지만 큰 성능의 향상으로 이어지기도 한다.

image.png


2️⃣ GraphSAGE¶

📌 GraphSAGE의 집계 함수이다.¶

  • 이웃들의 임베딩을 AGG함수를 이용해 합친 후, 자신의 임베딩과 연결(Concatenation)하는 점이 독특하다.

image.png


📌 AGG 함수로는 평균, 풀링, LSTM 등이 사용될 수 있다.¶

image.png

  • 여기서 $\pi$는 이웃들의 임베딩을 가지고 와서 순서를 섞은 다음에 LSTM에 넣는다는 의미로 이해하면 된다.



✅ 합성곱 신견망(CNN)과의 비교¶

1️⃣ 합성곱 신경망과 그래프 신경망의 유사성¶

📌 합성곱 신경망과 그래프 신경망은 모두 이웃의 정보를 집계하는 과정을 반복한다.¶

  • 구체적으로, 합성곱 신경망은 이웃 픽셀의 정보를 집계하는 과정을 반복한다.

image.png


2️⃣ 합성곱 신경망과 그래프 신경망의 차이¶

❗❗ 합성곱 신경망에서는 이웃의 수가 균일하지만, 그래프 신경망에서는 아니다.

  • 그래프 신경망에서는 정점 별로 집계하는 이웃의 수가 다르다.

image.png


❓ 그래프의 인접 행렬에 합성곱 신경망을 적용하면 효과적일까요?¶

그래프에는 합성곱 신경망이 아닌 그래프 신경망을 적용해야 한다 ❗❗

  • 합성곱 신경망이 주로 쓰이는 이미지에서는 인접 픽셀이 유용한 정보를 담고 있을 가능성이 높다.
  • 하지만, 그래프의 인접 행렬에서의 인접 원소는 제한된 정보를 가진다.
  • 특히나, 인접 행렬의 행과 열의 순서는 임의로 결정되는 경우가 많다.



✅ DGL라이브러리와 GraphSAGE를 이용한 정점 분류¶

라이브러리 로드¶

In [1]:
import numpy as np
import time
import torch
import torch.nn as nn
import torch.nn.functional as F
# Deep Graph Library
import dgl
from dgl.data import CoraGraphDataset
from sklearn.metrics import f1_score
Using backend: pytorch

Cora 인용 그래프 불러오기¶

In [2]:
'''
    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개를 사용합니다.
    본 실습에서는 Validation을 진행하지않습니다.

    요약하자면, 앞서 학습시킬 모델은 Cora 데이터셋의 
    [논문 내 등장 단어들, 논문들 사이의 인용관계]를 활용하여 논문의 주제를 예측하는 모델입니다.
'''

# Cora Graph Dataset 불러오기
G = CoraGraphDataset()
numClasses = G.num_classes

G = G[0]
# 노드들의 feauture & feature의 차원
features = G.ndata['feat']
inputFeatureDim = features.shape[1]

# 각 노드들의 실제 라벨
labels = G.ndata['label']

# 학습/테스트에 사용할 노드들에 대한 표시
trainMask = G.ndata['train_mask']        
testMask = G.ndata['test_mask']
  NumNodes: 2708
  NumEdges: 10556
  NumFeats: 1433
  NumClasses: 7
  NumTrainingSamples: 140
  NumValidationSamples: 500
  NumTestSamples: 1000
Done loading data from cached files.

GraphSAGE 정의의 첫 단계로 각 층을 정의¶

In [3]:
# 기존에 구현되어 있는 SAGEConv 모듈을 불러와서 GraphSAGE 모델을 구축한다.
from dgl.nn.pytorch.conv import SAGEConv
class GraphSAGE(nn.Module):
    '''
        graph               : 학습할 그래프
        inFeatDim           : 데이터의 feature의 차원
        numHiddenDim        : 모델의 hidden 차원
        numClasses          : 예측할 라벨의 경우의 수
        numLayers           : 인풋, 아웃풋 레이어를 제외하고 중간 레이어의 갯수
        activationFunction  : 활성화 함수의 종류
        dropoutProb         : 드롭아웃 할 확률
        aggregatorType      : [mean, gcn, pool (for max), lstm]
    '''
    def __init__(self,graph, inFeatDim, numHiddenDim, numClasses, numLayers, activationFunction, dropoutProb, aggregatorType):
        super(GraphSAGE, self).__init__()
        self.layers = nn.ModuleList()
        self.graph = graph

        # 인풋 레이어
        self.layers.append(SAGEConv(inFeatDim, numHiddenDim, aggregatorType, dropoutProb, activationFunction))
       
        # 히든 레이어
        for i in range(numLayers):
            self.layers.append(SAGEConv(numHiddenDim, numHiddenDim, aggregatorType, dropoutProb, activationFunction))
        
        # 출력 레이어 (마지막 층에는 activation함수가 없다.)
        # Output 차원은 class의 개수로 맞춰준다.
        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

역전파을 통해 GraphSAGE를 학습한다.¶

In [4]:
def train(model, lossFunction, features, labels, trainMask, optimizer, numEpochs):
    executionTime=[]
    flag=True
    
    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)
        
        if epoch<5 or epoch>numEpochs-5:
            print("Epoch {:05d} | Time(s) {:.4f} | Loss {:.4f} | Accuracy {:.4f}".format(epoch, np.mean(executionTime), loss.item(), acc))
        elif flag:
            print('...')
            flag=False

평가 및 테스트 함수¶

In [5]:
# 모델 학습 결과를 평가할 함수
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)

def evaluateTest(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)
        macro_f1 = f1_score(labels, indices, average = 'macro')
        correct = torch.sum(indices == labels)
        return correct.item() * 1.0 / len(labels), macro_f1

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))

파라미터 설정¶

In [6]:
# 하이퍼파라미터 초기화
dropoutProb = 0.5
learningRate = 1e-2
numEpochs = 50
numHiddenDim = 128
numLayers = 2
weightDecay = 5e-4
aggregatorType = "gcn"

이제 전체 과정 진행¶

In [7]:
# 모델 생성
model = GraphSAGE(G, inputFeatureDim, numHiddenDim, numClasses, numLayers, F.relu, dropoutProb, aggregatorType)
print(model)

# 손실함수
lossFunction = torch.nn.CrossEntropyLoss()
# 옵티마이저 초기화
optimizer = torch.optim.Adam(model.parameters(), lr=learningRate, weight_decay=weightDecay)
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)
    )
  )
)
In [8]:
train(model, lossFunction, features, labels, trainMask, optimizer, numEpochs)
Epoch 00000 | Time(s) 0.6860 | Loss 1.9373 | Accuracy 0.1786
Epoch 00001 | Time(s) 0.4610 | Loss 1.9464 | Accuracy 0.4786
Epoch 00002 | Time(s) 0.3923 | Loss 1.7560 | Accuracy 0.4071
Epoch 00003 | Time(s) 0.3388 | Loss 1.6410 | Accuracy 0.7857
Epoch 00004 | Time(s) 0.3158 | Loss 1.3236 | Accuracy 0.9000
...
Epoch 00046 | Time(s) 0.2115 | Loss 0.0480 | Accuracy 0.9929
Epoch 00047 | Time(s) 0.2111 | Loss 0.0444 | Accuracy 1.0000
Epoch 00048 | Time(s) 0.2101 | Loss 0.0388 | Accuracy 1.0000
Epoch 00049 | Time(s) 0.2096 | Loss 0.0468 | Accuracy 1.0000
In [9]:
test(model, features, labels, testMask)
Test Accuracy 0.7830
Test macro-f1 0.7825

'AI > 이론' 카테고리의 다른 글

Image classification II  (0) 2021.03.09
Annotation data efficient learning  (2) 2021.03.08
Image classification I  (0) 2021.03.08
그래프 신경망이란 무엇일까? (심화)  (0) 2021.02.26
그래프를 추천시스템에 어떠게 활용할까? (심화)  (0) 2021.02.25
그래프의 정점을 어떻게 벡터로 표현할까?  (0) 2021.02.25
그래프를 추천시스템에 어떻게 활용할까?(기본)  (0) 2021.02.24
그래프의 구조를 어떻게 분석할까?  (0) 2021.02.24
'AI/이론' 카테고리의 다른 글
  • Image classification I
  • 그래프 신경망이란 무엇일까? (심화)
  • 그래프를 추천시스템에 어떠게 활용할까? (심화)
  • 그래프의 정점을 어떻게 벡터로 표현할까?
N-analyst
N-analyst
  • N-analyst
    개발자CuCu
    N-analyst
  • 전체
    오늘
    어제
  • 공지사항

    • 티스토리에서 원하는 글 찾는 방법
    • 분류 전체보기 (140)
      • 티스토리 (4)
      • 알고리즘 (5)
        • 알고리즘 정리 (1)
        • 백준 (4)
      • 마크다운(Typora) (13)
        • 사용법 (13)
      • 에러 (1)
        • 파이썬 (1)
      • 데이터 분석 (5)
        • python_analysis (3)
        • Machine Learning (2)
      • AI (109)
        • 파이토치로 시작하는 딥러닝 기초 (2)
        • 부스트 캠프 AI tech (41)
        • 이론 (66)
      • 파이썬(python) (1)
        • 기타 (1)
      • 웹 프로그래밍 (1)
        • 설정 팁 (1)
  • 블로그 메뉴

    • 홈
    • 태그
  • 인기 글

  • 최근 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.6
N-analyst
그래프 신경망이란 무엇일까? (기본)
상단으로

티스토리툴바