2026년 7월 4일 | 개인 프로젝트 · GNN
Closed-triplet component breakdown을 붙이고 나니, 지난 회차에서 봤던 [0, 7] hard negative가 조금 더 선명해졌다. 직전 글에서는 `closed_triplets` feature를 다시 얹었을 때 probability가 0.5291에서 0.9476으로 튀는 장면만 봤다. 이번에는 그 점프를 한 덩어리로 두지 않고, 실제로 train graph 안에서 어떤 closure component가 생겼는지 다시 쪼개 봤다.
점수 하나를 다시 열어 본 이유
처음에는 이 결과를 단순히 “closed-triplet addback이 score를 크게 올렸다”로 적어도 될 것 같았다. 그런데 그렇게 쓰면 다음 작업이 애매해진다. 점수가 오른 이유가 공통 이웃이 많아서인지, endpoint 쪽 closed-triplet count가 같이 올라가서인지, 아니면 특정 feature weight가 작은 closure 하나를 과하게 읽어서인지 구분이 안 된다.
그래서 이번 반복에서는 새 모델을 더 붙이지 않았다. 이미 만든 `closed_triplet_addback_sensitivity` 리포트와 multi-dataset split 정보를 다시 읽어서, target edge를 train graph에 되살리기 전후의 node-level `closed_triplets` 값을 비교했다. 나는 이런 얇은 진단 레이어가 꽤 중요하다고 본다. 성능을 바로 올리지는 않지만, 다음 실험에서 어디를 열어야 하는지 정해 주기 때문이다.
공통 이웃은 하나뿐이었다
결과부터 말하면 `[0, 7]`의 train graph 기준 공통 이웃은 node `1` 하나였다. target edge 자체는 원본 그래프에도 없고 train graph에도 없는 test negative다. 그런데 train graph 안에서는 양 끝점이 node `1`을 같이 보고 있었고, target edge를 add-back하는 순간 이 구조가 하나의 닫힌 삼각형으로 바뀐다.
숫자로 보면 더 단순하다. add-back 전 endpoint closed-triplets mean은 0.0이고, add-back 후에는 1.0이다. common neighbor 쪽도 before mean 0.0에서 after mean 1.0으로 올라간다. 즉 여러 공통 이웃이 누적된 케이스가 아니라, 단일 공통 이웃 하나가 closure component 전체를 만든 케이스였다.
single_common_neighbor_closure로 라벨을 고정했다
이번 리포트에서 새로 붙인 핵심 라벨은 `single_common_neighbor_closure`다. 공통 이웃 수가 1이고, target edge add-back이 실제 closed-triplet delta를 만들었으며, 직전 sensitivity 리포트에서 confidence amplification으로 잡힌 edge에 붙는다. 이 라벨은 모델 성능을 좋다 나쁘다로 자르는 이름이 아니다. “다음에 feature weight attribution을 열어야 하는 edge”를 표시하는 이름에 가깝다.
여기서 재미있는 점은 probability delta가 여전히 크다는 것이다. baseline인 `hybrid_reduced`는 0.5291, `reduced_plus_closed_triplets`는 0.9476이었다. delta는 +0.4185다. 공통 이웃 하나가 만든 closure가 이 정도로 score를 밀어 올렸다면, 다음에는 threshold보다 closed-triplet feature weight 쪽을 먼저 보는 게 맞다. threshold를 올리면 표면적인 false positive는 줄 수 있지만, 왜 0.95까지 올라갔는지는 여전히 모른다.
구현은 기존 산출물 위에 얇게 붙였다
코드 변경은 일부러 작게 유지했다. `build_closed_triplet_component_breakdown_report`는 sensitivity 리포트에서 `next_probe == closed_triplet_component_breakdown`인 row만 골라 처리한다. 그 다음 원본 multi-dataset report에서 dataset 경로와 seed 정보를 찾아 같은 split을 다시 만든다. train graph를 재구성하고, target edge add-back 전후의 closed-triplet snapshot을 계산한 뒤 JSON, CSV, PNG로 저장했다.
테스트는 두 줄기로 잡았다. builder 테스트에서는 `[0, 7]`이 `single_common_neighbor_closure`로 분류되는지 확인했고, runner 테스트에서는 JSON/CSV/PNG 산출물이 모두 생기는지 봤다. 전체 테스트는 42개가 통과했다. 이번처럼 작은 진단 코드는 결과가 작아 보여도, 파일로 남겨 두지 않으면 다음 회차에서 같은 edge를 또 다시 해석하게 된다.
다음에는 weight attribution을 본다
이번 결과만으로 closed-triplet feature가 과하다고 단정하고 싶지는 않다. 샘플 그래프가 작고, hard negative 하나만 보고 있기 때문이다. 다만 지금까지의 흐름은 꽤 일관적이다. `[0, 7]`은 shared hard negative였고, common neighbor pressure가 있었고, closed-triplet addback 뒤에는 high-confidence false positive가 됐고, 이번에는 그 증폭이 단일 공통 이웃 closure에서 나온다는 점까지 확인했다.
그래서 다음 probe는 `closed_triplet_weight_attribution`으로 정했다. 내가 보고 싶은 것은 단순한 feature importance 순위가 아니라, 이 edge의 logit 또는 probability가 어떤 입력 축에서 얼마나 밀리는지다. 만약 closed-triplet contribution이 실제로 과하게 크다면, reduced hybrid 입력판에서 closed-triplet을 다시 넣는 방식 자체를 더 조심해야 한다. 반대로 weight는 크지 않은데 다른 similarity 축과 같이 묶여 튀는 것이라면, feature 조합을 다시 봐야 한다.
작은 프로젝트를 이렇게 쪼개다 보면 가끔 너무 미세한 이야기를 하고 있나 싶다. 그래도 나는 이 단계가 필요하다고 느낀다. “GNN이 negative edge를 positive처럼 봤다”에서 멈추면 고칠 수 있는 게 별로 없다. 적어도 지금은 “공통 이웃 하나가 만든 closed-triplet component가 확신도 false positive를 키운다”까지 좁혀졌고, 다음 파일에서 확인해야 할 축도 하나로 줄었다.
'[AI 실험실] > [개인 프로젝트] GNN' 카테고리의 다른 글
| GNN | 13차원에서 비교한 Closed-triplet 마스킹 재학습 (1) | 2026.07.15 |
|---|---|
| GNN | Closed-triplet addback 확신도 증폭 (0) | 2026.07.01 |
| GNN | Closed-triplet addback 점수 변화 (1) | 2026.06.27 |
| GNN | 공통 이웃 feature 압력 진단 (0) | 2026.06.24 |
| GNN | Negative target slice 분리 (0) | 2026.05.18 |