Knowledge Graphs and LLMs in Action · Chapter 12

관계를 판별하는 기계:
GNN 노드 분류와 링크 예측

제12장은 그래프 신경망을 이론의 전시장에서 현실의 작업장으로 옮긴다. 하나의 길에서는 비트코인 거래망의 노드를 합법과 불법으로 가르고, 다른 길에서는 사용자와 영화 사이에 아직 보이지 않는 연결을 예측한다. 서로 다른 풍경이지만, 데이터 준비-그래프 변환-인코더-디코더-평가라는 한 줄기 길로 이어진다.

관계는 눈에 보이는 선만으로 완성되지 않는다. 이미 이어진 선에서 이웃의 성격을 읽고, 아직 그어지지 않은 선의 가능성을 헤아릴 때 그래프는 비로소 판단의 도구가 된다.
범위: 원서 302-334쪽 태스크: Node Classification · Link Prediction 모델: GCN · GAT · GraphSAGE 도구: PyTorch Geometric 사례: AML · Movie Recommendation
Opening · 두 개의 현장, 하나의 설계도

제12장의 지형

이 장은 제11장에서 익힌 메시지 패싱과 인코더-디코더 개념을 실제 시스템으로 조립한다. 첫 사례는 거래 노드의 성격을 맞히는 분류 문제이고, 둘째 사례는 사용자와 영화 사이의 연결 유무를 맞히는 예측 문제다. 장의 진짜 주제는 개별 모델보다도, 서로 다른 그래프 태스크를 같은 종단 프레임워크 안에 놓는 방법이다.

1

입력을 그래프로 바꾼다

CSV와 같은 반구조화 원천에서 연속 ID, 특징 텐서, edge_index, 라벨을 구성한다. 모델보다 앞선 이 작업이 그래프 학습의 토대를 만든다.

2

GNN으로 관계를 읽는다

GCN, GAT, GraphSAGE가 이웃의 특징을 서로 다른 방식으로 집계하여 노드 및 사용자-영화 표현을 만든다.

3

태스크별로 복호화한다

노드 분류는 log-softmax와 교차엔트로피를, 링크 예측은 내적과 이진 교차엔트로피를 사용한다.

장 전체의 관점. GNN의 이름만 바꾸는 일이 핵심이 아니다. 그래프의 종류, 지도 신호의 위치, 메시지 패싱에 쓰는 간선과 평가에 쓰는 간선을 어떻게 나누는지가 시스템의 신뢰성을 결정한다.
12.0 · Figure 12.1

공통 종단 프레임워크

원문은 두 사례를 같은 다섯 단계로 정리한다. 아래 전환기를 누르면 동일한 골격이 노드 분류와 링크 예측에서 어떻게 달라지는지 확인할 수 있다.

태스크 전환기

입력과 디코더는 달라지지만, 그래프 처리와 GNN 인코딩의 중심축은 유지된다.

GCN

차수 정규화를 이용해 이웃 정보를 평균적으로 확산한다. 계산이 단순하고 강한 기준선이 된다.

GAT

이웃 간선마다 학습 가능한 주의 계수를 부여한다. 표현력은 높지만 파라미터와 시간이 늘어난다.

GraphSAGE

이웃을 집계하여 새 노드에도 적용 가능한 표현을 학습한다. 두 사례에서 전반적으로 안정적인 균형을 보인다.

12.1 · Node Classification

거래망에서 수상한 노드를 찾는 일

자금세탁 방지 문제에서 거래는 고립된 행이 아니다. 어느 거래가 어디에서 흘러와 어디로 향했는지가 중요하다. 원문은 비트코인 거래를 노드로, 지불 흐름을 방향 간선으로 두고 각 거래를 합법(licit) 또는 불법(illicit)으로 분류한다.

Elliptic CSVfeaturesedgelistclasses PyG Datax · edge_index · ytrain/val/test mask 동질 GNNGCN · GAT · SAGE2-layer encoder Decoderlog_softmaxcross entropy 합법불법

동질 그래프라는 말은 단순히 그래프가 작다는 뜻이 아니다. 노드 타입은 거래 하나, 간선 타입은 비트코인 지불 흐름 하나로 통일되어 있다는 뜻이다. 이 때문에 PyG의 Data 객체와 동질 GNN을 직접 적용할 수 있다.

12.1.1-12.1.2 · Elliptic

데이터의 밑그림: 20만 거래와 234,355개의 흐름

203,769거래 노드
234,355방향 지불 간선
166익명화 노드 특징
3라벨 상태: 합법·불법·미상
77.15%
라벨 미상

라벨 불균형은 배경이 아니라 문제의 일부다

Unknown · 157,205개 · 77.15%Licit · 42,019개 · 20.62%Illicit · 4,545개 · 2.23%

학습과 평가는 알려진 라벨만 사용한다. unknown 노드는 그래프에는 남아 있으나 지도 신호의 대상으로 선택되지 않는다.

세 파일의 역할

elliptic_txs_features.csv

203,769행, 167열. 첫 열은 거래 ID이고 나머지 166열은 익명화 특징이다.

elliptic_txs_edgelist.csv

234,355행, 2열. 원천 거래 ID와 목표 거래 ID가 방향 지불 흐름을 만든다.

elliptic_txs_classes.csv

203,769행, 2열. 각 거래 ID에 1, 2 또는 unknown 라벨을 연결한다.

전처리의 네 수순

① 연속 ID 매핑

긴 원본 거래 ID를 0부터 시작하는 행 인덱스로 변환한다.

tx_id → mapped_id
② edge_index 구성

원천·목표 ID 쌍을 크기 [2, 234355]의 간선 텐서로 옮긴다.

[source; target]
③ 특징 텐서 생성

원본 ID 열을 제거하고 크기 [203769, 166]의 실수 텐서를 만든다.

x ∈ R^(N×166)
④ 라벨 인코딩

문자형 클래스 값을 정수 라벨로 변환하여 PyTorch long 텐서로 저장한다.

y ∈ {0,1,2}^N
⑤ 알려진 라벨 마스크

분류 학습에 사용할 두 클래스만 True로 두고 unknown을 제외한다.

known_mask
⑥ 재현 가능한 분할

알려진 노드의 인덱스를 섞어 80:10:10 마스크를 구성한다.

train/val/test_mask
12.1.3 · PyG Data

동질 그래프를 학습 가능한 그릇에 담기

PyG의 Data 객체는 노드 특징 x, 간선 edge_index, 라벨 y를 한데 묶는다. 이어서 각 노드가 어느 단계에 속하는지를 불리언 마스크로 표시한다.

Data(x=node_features, edge_index=edge_index, y=node_labels)

분할 탐색기

원문 표 12.4-12.5의 분할 규모와 클래스 분포를 그대로 표시한다.

합법
불법

막대 높이는 선택된 분할 안에서의 상대 비중을 보여준다. 원문에 적힌 백분율은 정합성 점검 절에서 별도로 검토한다.

마스크가 필요한 이유

그래프의 모든 노드는 메시지 패싱에 참여할 수 있지만, 손실 계산은 지정된 학습 노드에서만 수행해야 한다. 이 구분이 반지도 학습의 핵심이다.

직접 분할의 의미

노드 분류 사례는 마스크를 수동으로 만든다. 뒤의 링크 예측 사례에서는 PyG의 RandomLinkSplit가 간선 분할과 음성 샘플 생성을 맡는다.

12.1.4 · Encoder-Decoder

세 인코더, 하나의 분류 디코더

노드 분류 모델은 두 층의 GNN 인코더로 이웃 정보를 모은 뒤, log-softmax로 두 클래스의 로그 확률을 만든다. 학습에서는 교차엔트로피 손실이 실제 라벨과 예측의 간격을 줄인다.

H = GNN(X, E)   →   log P(y|v) = log_softmax(H)

인코더 비교

모델을 선택하면 구현 차이와 노드 분류 실험 비용이 바뀐다.

구현의 뼈대

BaseGraphModel

입력-은닉, 은닉-출력의 두 convolution 층과 중간 ReLU를 공통 골격으로 둔다.

SAGE

기본 클래스에 SAGEConv를 전달하여 두 층을 구성한다. 별도 추가 인수가 없어 구현이 간결하다.

GAT

첫 층은 8개 attention head를 연결하고, 마지막 층은 head 1개와 concat=False를 사용한다.

12.1.5 · Evaluation

합법을 잘 맞히는 것과 불법을 놓치지 않는 것은 다르다

원문은 400 epoch, T4 Colab 환경에서 모델 비용을 비교하고, 검증 세트의 weighted precision·recall·F1 추세와 테스트 세트의 혼동행렬을 함께 읽는다. 불균형 데이터에서는 전체 평균만으로는 불법 거래 탐지 능력을 파악하기 어렵다.

인코더파라미터훈련 시간원문 해석
GCN2,72319.02초가장 효율적이나 불법 노드 식별이 가장 약함
GAT22,02543.45초주의 계수 때문에 가장 무거우며 성능은 SAGE에 근접
SAGE5,42736.71초weighted F1과 전체 균형에서 가장 우수

AML 혼동행렬 탐색기

테스트 세트에서 불법 클래스를 양성으로 두고, Figure 12.6의 값을 재구성한다.

열: 예측 클래스 · 행: 실제 클래스

불법 Precision
불법 Recall
불법 F1

위 세 값은 혼동행렬로부터 불법 클래스를 양성으로 두고 계산한 파생 지표다. 원문의 Figure 12.5는 클래스 분포를 반영한 weighted 평균 추세이므로 서로 같은 값이 아니다.

GCN은 값싼 길이고, GAT는 세밀한 길이며, SAGE는 균형 잡힌 길이다. AML에서는 그 균형이 단지 성능표의 미관이 아니라 놓친 불법 거래의 수로 나타난다.
12.2 · Link Prediction

아직 없는 선을 예측하는 추천 시스템

영화 추천에서 사용자와 영화는 같은 종류의 노드가 아니다. 사용자는 취향의 주체이고 영화는 장르 특징을 가진 대상이다. 원문은 이 둘을 이질 그래프로 묶고, 기존 평점을 링크로 보아 앞으로 존재할 가능성이 높은 사용자-영화 연결을 예측한다.

9,742영화 노드
610구현상의 사용자 노드
100,836평점 간선
20장르 이진 특징

movies.csv

9,742행, 3열. movieId, title, genres를 포함하며, 모델은 movieId와 장르 문자열을 사용한다.

ratings.csv

100,836행, 4열. userId, movieId, rating, timestamp를 포함하며, 링크 구성에는 사용자와 영화 ID가 쓰인다.

장르는 문장에서 벡터로 바뀐다

파이프 문자로 나뉜 장르 문자열을 one-hot 벡터로 변환한다. 예컨대 Adventure 장르라면 해당 차원만 1이 된다. 영화 하나가 여러 장르를 가질 수 있으므로 여러 차원이 동시에 1이 될 수 있다.

영화 ID 1

Adventure · Animation · Children · Comedy · Fantasy

movie_feat

9742 × 20 실수 텐서

[0,1,1,1,1,…]
12.2.2-12.2.3 · HeteroData

두 종류의 노드와 두 방향의 메시지

사용자와 영화는 서로 다른 특징 공간을 갖는다. PyG의 HeteroData는 노드 타입별 저장소와 관계 타입별 edge_index를 제공한다. 원문은 (user, rates, movie)를 기본 관계로 만들고, 메시지가 양쪽으로 흐르도록 역방향 관계도 추가한다.

U1U2U3M1M2M3

HeteroData 구성

user.node_id = [610]
movie.node_id = [9742]
movie.x = [9742, 20]
(user, rates, movie).edge_index = [2, 100836]

ToUndirected()(movie, rev_rates, user) 관계를 만들어 영화에서 사용자로도 메시지가 돌아가게 한다.

RandomLinkSplit의 세 가지 분리

데이터셋 분할

전체 간선을 학습 80%, 검증 10%, 테스트 10%로 나눈다.

메시지와 지도 신호 분리

학습 간선의 70%는 메시지 패싱, 30%는 링크 예측 감독에 사용한다.

음성 간선 생성

검증과 테스트에서 실제 링크 하나당 존재하지 않는 링크 두 개를 생성한다.

객체메시지 패싱 간선평가/감독 간선설명
Training56,46924,201두 집합은 서로 겹치지 않음
Validation80,67030,249양성 10,083 + 음성 20,166
Testing90,75330,249학습+검증 간선을 메시지 패싱에 활용

음성 샘플링 계산기

양성 링크 수와 음성 비율이 배치의 감독 간선 수를 어떻게 바꾸는지 계산한다.

감독 간선 합계
384

원문의 LinkNeighborLoader 설정은 num_neighbors=[20,10], neg_sampling_ratio=2, batch_size=128, shuffle=True다.

Implementation Atlas

Listing 12.1-12.25 역할 지도

코드 목록은 두 사례의 구현 순서를 그대로 드러낸다. 아래 항목은 원문의 코드를 복제하지 않고 각 Listing의 기능을 요약한 것이다.

12.1-12.3 · 거래 ID와 간선

원본 거래 ID를 연속 ID로 매핑하고, 매핑된 원천·목표 쌍으로 edge_index를 만들며 샘플 텐서를 확인한다.

12.4-12.5 · 특징과 라벨

거래 ID 열을 제거해 166차원 특징 텐서를 만들고, LabelEncoder로 클래스 값을 정수로 변환한다.

12.6-12.9 · PyG Data와 마스크

Data 객체를 만들고 known/unknown 마스크, 80:10:10 크기, 세 불리언 마스크를 구성한다.

12.10 · NodeClassifier

GNN 인코더의 출력에 log_softmax를 적용하는 노드 분류용 종단 모듈을 정의한다.

12.11-12.13 · 동질 GNN

공통 2층 기본 모델, SAGEConv 기반 SAGE, 다중 head GATConv 기반 GAT를 구현한다.

12.14 · 장르 특징

파이프 구분 장르를 더미 변수로 변환해 9,742×20 영화 특징 텐서를 만든다.

12.15-12.16 · 사용자-영화 간선

사용자와 영화 ID를 각각 연속 ID로 매핑하고, 100,836개 평점 링크의 edge_index를 만든다.

12.17 · HeteroData

user와 movie 노드 저장소, 장르 특징, rates 관계를 채운 뒤 역방향 관계를 추가한다.

12.18-12.20 · 링크 분할

RandomLinkSplit 설정과 학습·검증·테스트 HeteroData의 edge_index 및 edge_label_index 크기를 확인한다.

12.21 · LinkNeighborLoader

2-hop 이웃 [20,10], 음성 비율 2, edge batch 128로 미니배치 하위 그래프를 샘플링한다.

12.22 · MovieLensLinkPredictor

초기 임베딩, 이질 GNN, 사용자-영화 내적 디코더를 하나의 종단 모델로 묶는다.

12.23 · MovieLensEmbedding

사용자는 ID 임베딩을, 영화는 장르 선형변환과 ID 임베딩의 합을 사용한다.

12.24 · HeteroBaseModel

동질 기본 GNN을 PyG의 to_hetero와 metadata로 이질 GNN으로 자동 변환한다.

12.25 · HeteroSAGE

동질 SAGE 기본 모델을 이질 그래프의 관계 타입에 적용하는 구체 클래스를 정의한다.

Critical Reading

원문 수치와 서술의 정합성 점검

원문을 충실히 읽되, 표와 그림이 완전히 일치한다고 가정하지 않는다. 아래 항목은 외부 지식으로 수정한 것이 아니라, 같은 장 안의 수치를 서로 대조하여 확인한 차이다.

확인된 불일치 또는 주의점

라벨 설명의 역전: 표 12.1은 원본 라벨 1을 illicit, 2를 licit으로 제시한다. 그러나 Listing 12.5 뒤의 문장은 인코딩된 0을 licit(원본 1), 1을 illicit(원본 2)이라고 적어 앞의 정의와 반대로 서술한다.

표 12.5의 백분율: Training의 3,606/37,251은 약 9.68%인데 표에는 9.78%로 적혀 있다. Testing의 476/4,657은 약 10.22%인데 표에는 9.45%로 적혀 있다.

분할 표와 혼동행렬의 클래스 합계: 표 12.5의 테스트 분포는 licit 4,181, illicit 476이다. Figure 12.6의 각 혼동행렬은 실제 행 합계가 licit 4,206, illicit 451로 나타난다. 원문이 언급한 무작위성 또는 서로 다른 실행 결과의 혼용 가능성이 있다.

MovieLens 학습 간선 총계: 본문은 학습 세트를 80,669개라고 설명하지만 Listing 12.19의 message-passing 56,469개와 supervision 24,201개를 합하면 80,670개다.

평가 집계 방식: Figure 12.5와 12.10의 추세는 weighted 평균을 사용한다. 반면 혼동행렬에서 특정 양성 클래스만 계산한 precision·recall·F1은 다른 순위를 만들 수 있다. 두 결과는 모순이라기보다 집계 기준이 다른 평가다.

Summary

제12장이 남기는 설계 원칙

1
그래프 태스크는 데이터 모델에서 시작한다.

원본 ID, 특징, 타입, 방향성을 제대로 옮기지 못하면 좋은 GNN도 문제를 복구하지 못한다.

2
하나의 프레임워크가 여러 태스크를 품는다.

그래프 처리-인코더-디코더 구조를 유지하고 태스크별 입력과 손실을 교체한다.

3
동질성과 이질성은 구현을 바꾼다.

Data와 HeteroData, GCNConv와 GraphConv, 단일 특징 공간과 타입별 임베딩이 갈린다.

4
메시지 패싱 간선과 감독 간선을 분리한다.

링크 예측에서는 같은 간선을 입력과 정답으로 중복 사용하지 않도록 분리해야 한다.

5
불균형에서는 평균만 보지 않는다.

weighted 지표와 클래스별 혼동행렬을 함께 읽어 중요한 소수 클래스를 놓치지 않는다.

6
모델 선택은 비용과 오류의 성격을 함께 본다.

SAGE는 두 사례에서 안정적이고, GCN은 효율·재현율, GAT는 주의 기반 표현력을 제공한다.

그래프 학습의 성패는 가장 화려한 모델 이름에서 갈리지 않는다. 무엇을 노드로 삼고, 어떤 선을 학습에 보이며, 어떤 오류를 더 무겁게 볼 것인지 정하는 데서 갈린다.