데이터 공부를 하다 보면 CNN, RNN 같은 신경망 모델은 익숙해지는데, 그래프 구조 데이터를 다루는 GNN(Graph Neural Network)은 생소한 경우가 많다.
근데 알고 보면 도로망, 소셜 네트워크, 추천 시스템처럼 우리 주변에 그래프 구조인 데이터가 생각보다 엄청 많다. 특히 GIS를 공부하다 보면 도로망이나 공간 네트워크 데이터가 노드-엣지 구조 그 자체이기 때문에 GNN이랑 궁합이 잘 맞는다는 걸 느끼게 된다.
오늘은 GNN의 기본 개념을 정리하고, 실제 도로망 데이터에 적용하는 예제까지 다뤄보려 한다. 레츠꼬 🌐
1. GNN(Graph Neural Network)이란?
GNN은 그래프 구조 데이터를 처리하는 데 특화된 신경망 모델이다. 기존 CNN이나 RNN과 가장 다른 점은 입력 데이터의 형태다. CNN은 이미지(격자 구조), RNN은 시계열(순서 있는 1차원 데이터)처럼 입력이 정형화된 반면, GNN은 노드와 엣지로 이루어진 비정형(non-Euclidean) 구조를 직접 처리할 수 있다.
GNN의 핵심 아이디어는 메시지 패싱(Message Passing)이다. 각 노드가 이웃 노드들의 정보를 반복적으로 받아들이고(aggregation), 이를 합쳐서 자신의 임베딩 벡터를 업데이트하는 방식이다. 이 과정을 여러 레이어에 걸쳐 반복하면, 노드는 점점 더 멀리 있는 이웃의 정보까지 반영한 표현을 학습하게 된다.

이렇게 학습된 노드 임베딩을 활용하면 아래 같은 다양한 테스크를 풀 수 있다.
| 태스크 | 설명 | 예시 |
| 노드 분류 (Node Classification) | 각 노드가 어떤 클래스에 속하는지 예측 | 논문 인용 네트워크에서 논문 카테고리 분류 |
| 링크 예측 (Link Prediction) | 두 노드 사이에 엣지가 생길지 예측 | 소셜 네트워크에서 친구 추천, 도로 연결 예측 |
| 그래프 분류 (Graph Classification) | 그래프 전체를 하나의 클래스로 분류 | 분자 구조 독성 여부 판별 |
2. 공간 데이터와 그래프 구조
그렇다면 도로망 같은 공간 데이터는 어떻게 그래프로 표현될까? 개념은 단순하다. 교차점이나 주요 지점 → 노드(Node), 그 사이를 잇는 도로 → 엣지(Edge)로 정의하면 된다.
노드와 엣지에는 다양한 속성(feature)을 붙일 수 있다. 예를 들면 이런 식이다.
| 구분 | 속성 예시 |
| 노드 속성 | 위도/경도, 교차로 종류, 차선 수, 신호등 유무 |
| 엣지 속성 | 도로 길이, 제한 속도, 방향성(단방향/양방향), 도로 등급 |
이렇게 구축된 도로 그래프는 최단 경로 검색, 교통 흐름 예측, 도로 연결성 분석 등 다양한 공간 분석에 활용된다. GNN은 이런 그래프 구조를 입력으로 받아 학습할 수 있으니, GIS 데이터와 궁합이 자연스럽게 맞는다.
3. GCN 기본 구조 구현하기 (PyTorch Geometric)
GNN을 파이썬으로 구현할 때 가장 많이 쓰는 라이브러리가 PyTorch Geometric(PyG)다.
👉 PyTorch Geometric 공식 문서
GNN의 가장 기본적인 형태인 GCN(Graph Convolutional Network)을 Cora 데이터셋으로 먼저 구현해 보자. Cora는 논문 인용 네트워크 데이터로, 각 논문(노드)이 다른 논문을 인용(엣지)하는 관계로 구성되어 있다. 태스크는 논문의 카테고리를 분류하는 노드 분류(Node Classification)다.
import torch
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid
from torch_geometric.nn import GCNConv
# Cora 데이터셋 불러오기
dataset = Planetoid(root='/tmp/Cora', name='Cora')
# GCN 모델 정의
class GCN(torch.nn.Module):
def __init__(self, in_channels, hidden_channels, out_channels):
super().__init__()
self.conv1 = GCNConv(in_channels, hidden_channels)
self.conv2 = GCNConv(hidden_channels, out_channels)
def forward(self, data):
x, edge_index = data.x, data.edge_index
x = F.relu(self.conv1(x, edge_index)) # 1번째 레이어 + ReLU
x = self.conv2(x, edge_index) # 2번째 레이어
return x
# 학습 설정
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = GCN(dataset.num_node_features, 16, dataset.num_classes).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.cross_entropy(out[data.train_mask], data.y[data.train_mask])
loss.backward()
optimizer.step()
코드 흐름을 간단히 설명하면, GCNConv 레이어는 각 노드가 이웃 노드들의 피처를 가중 평균하여 자신의 표현을 업데이트하는 연산을 한다. 이 과정을 2번 거치면(레이어 2개) 2-hop 이웃까지의 정보를 반영한 임베딩이 만들어진다. 학습은 레이블이 있는 노드(train_mask)에 대해 cross entropy loss를 최소화하는 방식으로 진행된다.
4. 실제 도로망 데이터에 GNN 적용하기
이제 실제 데이터에 적용해 보자. OSMnx 라이브러리를 이용하면 OpenStreetMap에서 실제 도로망 데이터를 바로 불러올 수 있다. 아래 예시는 서울 강남구 도로망 링크노드 정보를 불러와 그래프 구조로 변환하고, PyTorch Geometric 형식으로 바꾸는 코드이다.
4.1. 데이터 불러오기: 도로망 → 그래프
import osmnx as ox
import networkx as nx
# 서울 강남구 도로망 불러오기 (drive: 차도 기준)
G = ox.graph_from_place('Gangnam-gu, Seoul, South Korea', network_type='drive')
ox.plot_graph(G)
# 노드/엣지 수 확인
print(f"노드 수: {len(G.nodes)}, 엣지 수: {len(G.edges)}")

노드 수: 3152, 엣지 수: 8461
4.2. PyTorch Geometric 형식으로 변환
OSMnx로 불러온 NetworkX 그래프를 PyG의 Data 객체로 바꿔줘야 GNN 모델에 넣을 수 있다.
import networkx as nx
import torch
from torch_geometric.data import Data
# MultiGraph → 단순 Graph
G = nx.Graph(G)
# node_index
G = nx.convert_node_labels_to_integers(G)
# 노드 feature
x = torch.tensor(
[[d.get("x", 0.0), d.get("y", 0.0)] for _, d in G.nodes(data=True)],
dtype=torch.float
)
# edge_index
edge_index = torch.tensor(list(G.edges()), dtype=torch.long).t().contiguous()
pyg_data = Data(x=x, edge_index=edge_index)
print(pyg_data)
Data(x=[3152, 2], edge_index=[2, 4950])
변환이 완료되면 pyg_data.x에 노드 피처(위도/경도), pyg_data.edge_index에 엣지 연결 정보가 담긴 표준 PyG 데이터 형태가 된다. 여기에 앞에서 구현한 GCN 모델을 그대로 연결해서 학습할 수 있다.
이 데이터로 특정 교차로가 어떤 종류인지 분류(노드 분류)하거나, 엣지 회귀 (교통량 예측) 같은 테스크를 풀어볼 수 있다.
5. 주의할 점
GNN을 실제 데이터에 붙여보면 몇 가지 주의할 점이 생긴다.
- 노드/엣지 피처 설계가 성능을 좌우한다.
아무 피처나 넣는 것보다 도메인 지식을 반영해서 의미 있는 속성을 골라 넣어야 모델이 제대로 학습한다. 도로망에서는 위경도만 넣는 것보다 도로 등급, 속도 제한, 연결 차선 수 같은 속성을 추가하는 게 훨씬 낫다. - 그래프가 크면 연산량이 급격히 증가한다.
서울 전체 도로망을 한 번에 넣으면 메모리가 터질 수 있다. 이럴 때는 특정 구 단위로 잘라서 서브그래프를 만들거나, 미니배치 학습 방식을 활용하면 된다.
데이터가 적으면 오버피팅에 주의해야 한다.
드롭아웃(Dropout)이나 L2 정규화를 같이 써주는 게 좋고, 레이어를 너무 깊게 쌓으면 오히려 성능이 떨어지는 over-smoothing 문제가 생길 수 있다. 보통 2~3 레이어 정도가 무난하다.
물론 GNN이 모든 경우에 만능은 아니다. 그래프 구조가 불명확하거나 노드 수가 적은 경우, 오히려 일반 신경망보다 성능이 낮게 나오는 경우도 있다. 그래도 공간 데이터처럼 연결 관계가 명확하고 위상 구조가 의미 있는 데이터에서는 기존 모델 대비 확실한 강점을 보이는 편이다.
오늘은 GNN의 기본 개념부터 실제 도로망 데이터에 적용하는 흐름까지 정리해 봤다. 이번 예제는 기초적인 GCN을 썼는데, 다음엔 도로 방향성이나 엣지 속성을 더 잘 활용하는 GAT(Graph Attention Network)나 GraphSAGE 같은 방향으로 확장해 볼 예정이다. 이상 끝! 🌐