콘텐츠로 이동
Data Prep
상세

Graph Analytics (그래프 분석)

메타 정보

항목 내용
분류 Graph Theory / Network Science / GNN
핵심 논문 "Semi-Supervised Classification with Graph Convolutional Networks" (ICLR 2017), "Inductive Representation Learning on Large Graphs" (NeurIPS 2017), "Graph Attention Networks" (ICLR 2018)
주요 저자 Thomas Kipf, Max Welling (GCN); William Hamilton, Rex Ying, Jure Leskovec (GraphSAGE); Petar Velickovic et al. (GAT)
핵심 개념 그래프 구조 데이터에서 노드/엣지/그래프 수준의 패턴을 학습하고 예측
관련 분야 Social Network Analysis, Knowledge Graphs, Drug Discovery, Fraud Detection

정의

Graph Analytics는 노드(node)와 엣지(edge)로 구성된 그래프 구조 데이터에서 패턴과 인사이트를 추출하는 분석 기법이다. 전통적 그래프 이론부터 최신 GNN(Graph Neural Networks)까지를 포괄한다.

그래프 표현:

  G = (V, E)
  V: 노드 집합 (사용자, 분자, 문서 등)
  E: 엣지 집합 (관계, 결합, 인용 등)

  인접 행렬 A:          특성 행렬 X:
  [0 1 1 0]            [x_1^T]   <- 노드 1의 특성 벡터
  [1 0 0 1]            [x_2^T]
  [1 0 0 1]            [x_3^T]
  [0 1 1 0]            [x_4^T]

  노드 특성:  X in R^{N x F}  (N: 노드 수, F: 특성 차원)
  인접 행렬:  A in R^{N x N}
  엣지 특성:  E in R^{|E| x D} (선택적)

그래프 데이터의 유형

유형 특징 예시
Undirected 방향 없는 엣지 친구 관계, 분자 결합
Directed 방향 있는 엣지 팔로우, 인용, 웹 링크
Weighted 가중치 있는 엣지 거리, 유사도, 거래 금액
Bipartite 두 유형의 노드 사용자-상품, 저자-논문
Heterogeneous 다중 노드/엣지 타입 Knowledge Graph
Temporal/Dynamic 시간에 따라 변화 소셜 네트워크 진화
Hypergraph 엣지가 2개 이상 노드 연결 그룹 관계, 공저

전통적 그래프 분석

1. 중심성 지표 (Centrality Measures)

노드의 "중요도"를 정량화하는 지표들.

중심성 지표:

Degree Centrality:
  C_D(v) = deg(v) / (N - 1)
  직관: 직접 연결이 많을수록 중요

Betweenness Centrality:
  C_B(v) = sum_{s != v != t} (sigma_{st}(v) / sigma_{st})
  직관: 다른 노드들 사이의 최단 경로에 많이 포함될수록 중요
  응용: 네트워크 병목점, 브릿지 노드 탐지

Closeness Centrality:
  C_C(v) = (N - 1) / sum_{u != v} d(v, u)
  직관: 다른 모든 노드에 평균적으로 가까울수록 중요

Eigenvector Centrality:
  C_E(v) = (1/lambda) * sum_{u in N(v)} C_E(u)
  직관: 중요한 이웃을 가진 노드가 중요
  확장: PageRank (Google의 웹페이지 순위)

PageRank:
  PR(v) = (1-d)/N + d * sum_{u in B(v)} PR(u) / L(u)
  d: damping factor (보통 0.85)
  B(v): v를 가리키는 노드 집합
  L(u): u의 outgoing link 수
import networkx as nx
import numpy as np

# 그래프 생성
G = nx.karate_club_graph()

# 중심성 계산
degree = nx.degree_centrality(G)
betweenness = nx.betweenness_centrality(G)
closeness = nx.closeness_centrality(G)
pagerank = nx.pagerank(G, alpha=0.85)

# 상위 5개 노드
for name, centrality in [('Degree', degree), ('Betweenness', betweenness),
                          ('PageRank', pagerank)]:
    top5 = sorted(centrality.items(), key=lambda x: x[1], reverse=True)[:5]
    print(f"\n{name} Top 5:")
    for node, score in top5:
        print(f"  Node {node}: {score:.4f}")

2. 커뮤니티 탐지 (Community Detection)

그래프 내에서 밀접하게 연결된 노드 그룹(커뮤니티)을 발견하는 기법.

커뮤니티 구조:

+---+---+---+     +---+---+
| a - b - c |-----|d  - e |
| |   |     |     |  \   |
| f - g     |     |   h  |
+-----------+     +------+
  Community 1     Community 2

내부 연결(intra) >> 외부 연결(inter)

Modularity

Newman & Girvan (2004). 커뮤니티 분할의 품질을 측정하는 지표.

Modularity Q:

  Q = (1/2m) * sum_{ij} [A_{ij} - (k_i * k_j) / (2m)] * delta(c_i, c_j)

  A_{ij}: 인접 행렬
  k_i: 노드 i의 degree
  m: 총 엣지 수
  delta(c_i, c_j): 같은 커뮤니티면 1, 아니면 0

  Q in [-0.5, 1]
  Q > 0.3: 의미 있는 커뮤니티 구조 존재

주요 알고리즘

알고리즘 원리 시간 복잡도 특징
Louvain Modularity 탐욕적 최적화 O(N log N) 빠르고 실용적
Leiden Louvain 개선 (connected 보장) O(N log N) 더 안정적 결과
Label Propagation 이웃의 레이블 전파 O(m) 매우 빠름
Spectral Clustering Laplacian 고유벡터 분해 O(N^3) 이론적 보장
Girvan-Newman 높은 betweenness 엣지 제거 O(m^2 * N) 정확하나 느림
Infomap 정보 흐름 최소화 O(m) 방향 그래프에 강함
import networkx as nx
from networkx.algorithms import community

G = nx.karate_club_graph()

# Louvain (networkx 내장)
communities_louvain = community.louvain_communities(G, resolution=1.0)
print(f"Louvain: {len(communities_louvain)} communities")

# Modularity 계산
modularity = community.modularity(G, communities_louvain)
print(f"Modularity: {modularity:.4f}")

# Label Propagation
communities_lpa = community.label_propagation_communities(G)
print(f"LPA: {len(list(communities_lpa))} communities")

존재하지 않는 엣지의 형성 가능성을 예측한다.

Link Prediction:

현재 그래프:          예측:
a -- b               a -- b
|    |               |  X |    <- a-d 연결 예측?
c -- d               c -- d

점수 함수 (heuristic):

  Common Neighbors:    |N(u) intersection N(v)|
  Jaccard:             |N(u) intersection N(v)| / |N(u) union N(v)|
  Adamic-Adar:         sum_{w in N(u) intersection N(v)} 1/log(|N(w)|)
  Preferential Attach: |N(u)| * |N(v)|

  Adamic-Adar 직관:
  "공통 이웃이 있되, 그 이웃이 선택적이면 더 의미있다"

4. Graph Embedding (Node2Vec, DeepWalk)

그래프의 노드를 저차원 벡터 공간에 매핑한다.

Graph Embedding:

그래프 공간              벡터 공간 (d차원)
  a -- b                 a(0.2, 0.8)  b(0.3, 0.7)
  |    |       embed     
  c -- d      ------>    c(0.1, 0.6)  d(0.4, 0.5)

DeepWalk (Perozzi et al., KDD 2014):
  1. 랜덤 워크로 노드 시퀀스 생성
  2. Word2Vec (Skip-gram)으로 임베딩 학습

Node2Vec (Grover & Leskovec, KDD 2016):
  1. BFS/DFS 편향된 랜덤 워크
  2. p, q 파라미터로 탐색 전략 제어
     p: return parameter (이전 노드로 돌아갈 확률)
     q: in-out parameter (BFS vs DFS 편향)
     q < 1: BFS 성향 (지역 구조)
     q > 1: DFS 성향 (전역 구조)
from node2vec import Node2Vec

# Node2Vec 임베딩
node2vec = Node2Vec(G, dimensions=64, walk_length=30, 
                     num_walks=200, p=1, q=0.5)
model = node2vec.fit(window=10, min_count=1)

# 노드 임베딩 벡터
embedding = model.wv['0']  # 노드 0의 임베딩

# 유사 노드 검색
similar = model.wv.most_similar('0', topn=5)

Graph Neural Networks (GNN)

메시지 패싱 프레임워크

대부분의 GNN은 메시지 패싱(Message Passing) 프레임워크를 따른다.

Message Passing Neural Network (MPNN):

각 레이어에서:
  1. Message: 이웃으로부터 메시지 수집
     m_v^{(l)} = AGG({h_u^{(l-1)} : u in N(v)})

  2. Update: 자신의 표현 갱신
     h_v^{(l)} = UPDATE(h_v^{(l-1)}, m_v^{(l)})

K개 레이어 스택:
  h_v^{(0)} = x_v  (초기 노드 특성)

  Layer 1: 1-hop 이웃 정보 집계
  Layer 2: 2-hop 이웃 정보 집계 (이웃의 이웃)
  ...
  Layer K: K-hop 이웃 정보 집계

  최종 h_v^{(K)}: K-hop 수용 영역(receptive field)의 정보 인코딩

1. GCN (Graph Convolutional Network)

Kipf & Welling (ICLR 2017). 스펙트럴 그래프 이론에 기반한 단순하고 효과적인 GNN.

GCN Layer:

  H^{(l+1)} = sigma(D_hat^{-1/2} * A_hat * D_hat^{-1/2} * H^{(l)} * W^{(l)})

  A_hat = A + I_N          (자기 루프 추가)
  D_hat = diag(sum(A_hat))  (차수 행렬)

  직관:
  - 각 노드의 표현을 이웃의 표현과 자기 자신의 평균으로 갱신
  - 정규화(D_hat^{-1/2})로 degree 차이에 의한 스케일 문제 해결

노드별 연산:
  h_v^{(l+1)} = sigma(sum_{u in N(v) union {v}} (1/sqrt(d_v * d_u)) * h_u^{(l)} * W^{(l)})
import torch
import torch.nn as nn
import torch.nn.functional as F

class GCNLayer(nn.Module):
    def __init__(self, in_features, out_features):
        super().__init__()
        self.weight = nn.Parameter(torch.FloatTensor(in_features, out_features))
        nn.init.xavier_uniform_(self.weight)

    def forward(self, x, adj_hat):
        """
        x: (N, in_features) 노드 특성
        adj_hat: (N, N) 정규화된 인접 행렬 (D^{-1/2} A_hat D^{-1/2})
        """
        support = torch.mm(x, self.weight)       # (N, out_features)
        output = torch.spmm(adj_hat, support)    # 이웃 집계
        return output

class GCN(nn.Module):
    def __init__(self, n_features, n_hidden, n_classes, dropout=0.5):
        super().__init__()
        self.gc1 = GCNLayer(n_features, n_hidden)
        self.gc2 = GCNLayer(n_hidden, n_classes)
        self.dropout = dropout

    def forward(self, x, adj):
        x = F.relu(self.gc1(x, adj))
        x = F.dropout(x, self.dropout, training=self.training)
        x = self.gc2(x, adj)
        return F.log_softmax(x, dim=1)

# 정규화된 인접 행렬 생성
def normalize_adj(adj):
    """D^{-1/2} (A + I) D^{-1/2}"""
    adj = adj + torch.eye(adj.size(0))
    degree = adj.sum(dim=1)
    d_inv_sqrt = degree.pow(-0.5)
    d_inv_sqrt[d_inv_sqrt == float('inf')] = 0
    d_mat = torch.diag(d_inv_sqrt)
    return d_mat @ adj @ d_mat

2. GraphSAGE

Hamilton et al. (NeurIPS 2017). 귀납적(inductive) 학습이 가능한 GNN으로, 학습 시 보지 못한 새 노드에도 적용 가능하다.

GraphSAGE:

GCN의 한계:
  - Transductive: 학습 시 전체 그래프 필요
  - 새 노드 추가 시 재학습 필요

GraphSAGE 해결:
  - 이웃 샘플링 + 집계 함수 학습
  - Inductive: 새 노드에도 즉시 적용 가능

알고리즘:
  for k = 1, ..., K:
    for v in V:
      1. 이웃 샘플링: N_sample(v) = SAMPLE(N(v), S)  (S개 이웃)
      2. 이웃 집계:   h_{N(v)}^k = AGG_k({h_u^{k-1} : u in N_sample(v)})
      3. 자기 결합:   h_v^k = sigma(W^k * CONCAT(h_v^{k-1}, h_{N(v)}^k))
      4. 정규화:      h_v^k = h_v^k / ||h_v^k||_2

집계 함수 옵션:
  Mean:  mean({h_u : u in N(v)})
  LSTM:  LSTM(PERMUTE({h_u : u in N(v)}))
  Pool:  max({sigma(W_pool * h_u + b) : u in N(v)})

3. GAT (Graph Attention Network)

Velickovic et al. (ICLR 2018). 어텐션 메커니즘으로 이웃의 중요도를 학습한다.

GAT Layer:

1. Attention coefficient 계산:
   e_{ij} = LeakyReLU(a^T * [W*h_i || W*h_j])

2. 정규화 (softmax):
   alpha_{ij} = softmax_j(e_{ij}) = exp(e_{ij}) / sum_{k in N(i)} exp(e_{ik})

3. 가중 집계:
   h_i' = sigma(sum_{j in N(i)} alpha_{ij} * W * h_j)

Multi-head Attention (K heads):
   h_i' = CONCAT_{k=1}^{K} sigma(sum_{j in N(i)} alpha_{ij}^k * W^k * h_j)
   또는 (마지막 레이어):
   h_i' = sigma((1/K) * sum_{k=1}^{K} sum_{j in N(i)} alpha_{ij}^k * W^k * h_j)

핵심 차이 (GCN vs GAT):
  GCN: 이웃 가중치 = 1/sqrt(d_i * d_j)  (구조 기반, 고정)
  GAT: 이웃 가중치 = alpha_{ij}          (학습 기반, 적응적)
import torch
import torch.nn as nn
import torch.nn.functional as F

class GATLayer(nn.Module):
    def __init__(self, in_features, out_features, n_heads=8, dropout=0.6):
        super().__init__()
        self.n_heads = n_heads
        self.head_dim = out_features // n_heads

        self.W = nn.Linear(in_features, out_features, bias=False)
        self.a = nn.Parameter(torch.FloatTensor(n_heads, 2 * self.head_dim))
        nn.init.xavier_uniform_(self.a.unsqueeze(0))

        self.leaky_relu = nn.LeakyReLU(0.2)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, edge_index):
        """
        x: (N, in_features)
        edge_index: (2, E) 엣지 인덱스
        """
        N = x.size(0)
        h = self.W(x).view(N, self.n_heads, self.head_dim)  # (N, heads, dim)

        src, dst = edge_index

        # Attention coefficients
        h_src = h[src]  # (E, heads, dim)
        h_dst = h[dst]  # (E, heads, dim)

        e = (torch.cat([h_src, h_dst], dim=-1) * self.a).sum(dim=-1)  # (E, heads)
        e = self.leaky_relu(e)

        # Softmax per destination node
        alpha = torch.zeros(N, self.n_heads, device=x.device)
        # (simplified -- 실제로는 scatter_softmax 사용)
        alpha = F.softmax(e, dim=0)  # 근사
        alpha = self.dropout(alpha)

        # Weighted aggregation
        out = torch.zeros(N, self.n_heads, self.head_dim, device=x.device)
        out.index_add_(0, dst, alpha.unsqueeze(-1) * h_src)

        return out.view(N, -1)  # (N, n_heads * head_dim)

GCN vs GraphSAGE vs GAT 비교

특성 GCN GraphSAGE GAT
집계 방식 정규화된 평균 샘플링 + 학습 가능 Attention 가중
학습 방식 Transductive Inductive 둘 다 가능
이웃 처리 전체 이웃 샘플링 전체 이웃
확장성 중간 높음 (미니배치) 중간
해석성 낮음 중간 높음 (attention weights)
이질적 그래프 기본 지원 X 확장 가능 확장 가능

태스크별 분류

Node-Level Tasks

태스크 설명 예시
Node Classification 노드의 클래스 예측 논문 주제 분류, 사용자 프로파일링
Node Regression 노드의 연속값 예측 분자 내 원자의 전하 예측
Node Clustering 유사 노드 그룹핑 커뮤니티 탐지

Edge-Level Tasks

태스크 설명 예시
Link Prediction 미래 연결 예측 추천, 소셜 네트워크
Edge Classification 엣지 유형 분류 Knowledge Graph Completion

Graph-Level Tasks

태스크 설명 예시
Graph Classification 그래프 전체의 클래스 예측 분자 독성 예측, 약물 발견
Graph Regression 그래프의 연속값 예측 분자 성질 예측
Graph Generation 새 그래프 생성 약물 분자 설계

주요 벤치마크

데이터셋 도메인 노드 수 태스크
Cora 논문 인용 2,708 Node Classification
CiteSeer 논문 인용 3,327 Node Classification
PubMed 의학 논문 19,717 Node Classification
ogbn-arxiv arXiv 논문 169,343 Node Classification
ogbn-products Amazon 상품 2,449,029 Node Classification
ogbl-collab 공저 네트워크 235,868 Link Prediction
ZINC 분자 12,000 graphs Graph Regression
QM9 분자 성질 130,000 graphs Graph Regression

Cora 노드 분류 성능

방법 정확도 (%)
DeepWalk + Logistic 67.2
GCN (2-layer) 81.5
GAT (8-head) 83.0
GraphSAGE-mean 78.9
GIN 82.7
Graph Transformer 84.0+

실전 파이프라인 (PyG)

import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv, GATConv, SAGEConv
from torch_geometric.datasets import Planetoid

# 1. 데이터 로드
dataset = Planetoid(root='/tmp/Cora', name='Cora')
data = dataset[0]

# 2. GNN 모델 정의
class GNNClassifier(torch.nn.Module):
    def __init__(self, in_channels, hidden_channels, out_channels, 
                 model_type='gcn', heads=8, dropout=0.6):
        super().__init__()
        self.dropout = dropout
        self.model_type = model_type

        if model_type == 'gcn':
            self.conv1 = GCNConv(in_channels, hidden_channels)
            self.conv2 = GCNConv(hidden_channels, out_channels)
        elif model_type == 'gat':
            self.conv1 = GATConv(in_channels, hidden_channels // heads, heads=heads)
            self.conv2 = GATConv(hidden_channels, out_channels, heads=1)
        elif model_type == 'sage':
            self.conv1 = SAGEConv(in_channels, hidden_channels)
            self.conv2 = SAGEConv(hidden_channels, out_channels)

    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, p=self.dropout, training=self.training)
        x = self.conv2(x, edge_index)

        return F.log_softmax(x, dim=1)

# 3. 학습
model = GNNClassifier(
    in_channels=dataset.num_features,
    hidden_channels=64,
    out_channels=dataset.num_classes,
    model_type='gat'
)
optimizer = torch.optim.Adam(model.parameters(), lr=0.005, weight_decay=5e-4)

def train():
    model.train()
    optimizer.zero_grad()
    out = model(data)
    loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask])
    loss.backward()
    optimizer.step()
    return loss.item()

def evaluate():
    model.eval()
    out = model(data)
    pred = out.argmax(dim=1)

    accs = {}
    for split in ['train_mask', 'val_mask', 'test_mask']:
        mask = getattr(data, split)
        correct = pred[mask] == data.y[mask]
        accs[split] = correct.sum().item() / mask.sum().item()
    return accs

# 4. 학습 루프
for epoch in range(200):
    loss = train()
    if epoch % 20 == 0:
        accs = evaluate()
        print(f"Epoch {epoch:3d} | Loss: {loss:.4f} | "
              f"Train: {accs['train_mask']:.4f} | "
              f"Val: {accs['val_mask']:.4f} | "
              f"Test: {accs['test_mask']:.4f}")

GNN의 한계와 해결책

한계 설명 해결 접근
Over-Smoothing 레이어 깊어지면 노드 표현이 동질화 DropEdge, PairNorm, JKNet
Over-Squashing 먼 노드의 정보가 병목에서 손실 Graph Transformer, Rewiring
Expressiveness WL-test 한계 (일부 그래프 구분 불가) GIN, Higher-order GNN
Scalability 대규모 그래프에서 메모리/시간 문제 Mini-batch (GraphSAGE), Cluster-GCN
Heterophily 이웃과 클래스가 다른 경우 성능 저하 H2GCN, GPR-GNN

최근 동향 (2024-2025)

트렌드 설명
Graph Transformers Self-attention을 그래프에 적용, over-smoothing 완화
Graph Foundation Models 다양한 도메인에 전이 가능한 범용 그래프 모델
Dynamic GNN 시간에 따라 변화하는 그래프의 학습
Heterogeneous GNN 다중 노드/엣지 타입을 통합 처리
GNN + LLM Knowledge Graph 기반 LLM 추론 강화
Trustworthy GNN 공정성, 프라이버시, 강건성, 설명가능성 연구
3D Graph (Geometric) 분자/단백질의 3D 구조 학습 (Equivariant GNN)
Temporal Knowledge Graphs 시간 축을 포함한 Knowledge Graph 완성

참고 문헌

  1. Kipf, T. N. & Welling, M. (2017). Semi-Supervised Classification with Graph Convolutional Networks. ICLR 2017.
  2. Hamilton, W. L., Ying, R., & Leskovec, J. (2017). Inductive Representation Learning on Large Graphs. NeurIPS 2017.
  3. Velickovic, P., et al. (2018). Graph Attention Networks. ICLR 2018.
  4. Xu, K., et al. (2019). How Powerful are Graph Neural Networks? ICLR 2019. (GIN)
  5. Grover, A. & Leskovec, J. (2016). node2vec: Scalable Feature Learning for Networks. KDD 2016.
  6. Wu, Z., et al. (2021). A Comprehensive Survey on Graph Neural Networks. IEEE TNNLS.
  7. Liu, Y., et al. (2024). A Survey of Graph Neural Networks in Real World. arXiv:2403.04468.