2026년 5월 18일 | 개발 일기
Negative target slice는 지난 GNN 실험에서 남긴 follow-up edge 목록을 한 번 더 접어 보는 작업이었다. 직전에는 hard_edge_case_table과 feature_bias_slice라는 큐를 만들었다. 그런데 막상 다음 작업을 시작하려고 보니, 그 큐만으로는 같은 negative edge가 여러 config에서 같이 새는지, 아니면 특정 config에서만 튀는지가 바로 보이지 않았다.
이번에는 새 모델을 붙이지 않았다. AUC를 다시 끌어올리는 작업도 아니었다. 대신 이미 만든 negative_context_followup 산출물을 읽어서 target edge를 dataset과 edge id 기준으로 다시 묶었다. 성능 개선보다 실패를 여는 순서를 고정하는 쪽에 가까운 작업이다. 이런 얇은 report가 조금 귀찮긴 한데, 작은 그래프 실험에서는 이 장부가 없으면 금방 "어느 설정이 나빴더라" 정도의 감상으로 돌아가 버린다.
큐를 edge 단위로 다시 접기
기존 follow-up report에는 config별 target edge 목록이 있었다. reduced_plus_closed_triplets는 hard outlier edge table로, reduced_plus_degree는 feature bias slice로 보내는 식이다. 이 구조는 config 중심으로는 좋지만, 공통으로 어려운 negative edge를 보기에는 조금 불편했다. 같은 edge가 두 config에 동시에 들어 있으면, 그건 특정 feature 하나의 문제가 아니라 graph family나 negative sampling context 쪽 문제일 수도 있기 때문이다.
그래서 이번에는 build_negative_target_slice_report()를 추가했다. 함수는 follow-up report의 target_edges를 모두 모은 뒤, dataset + edge를 key로 group한다. 그리고 같은 edge를 몇 개 config가 공유하는지, hard leak이 포함되어 있는지에 따라 네 가지 route로 나눴다.
shared_hard_negative: 여러 config가 같이 놓쳤고, 그중 hard leak이 포함된 edgeshared_borderline_negative: 여러 config가 같이 놓쳤지만 score는 threshold 근처에 붙은 edgeconfig_specific_hard_negative: 한 config에서만 hard하게 튄 edgeconfig_specific_borderline_negative: 한 config에서만 threshold 바로 위에 걸린 edge
이름만 보면 별것 아닌 분류지만, 다음 실험 순서를 정할 때 차이가 꽤 크다. shared hard negative는 먼저 공통 context를 열어야 하고, config-specific borderline은 해당 config의 score surface를 따로 잘라 보는 편이 맞다. 같은 false positive라도 바로 같은 처방으로 보내지 않으려는 장치다.
이번에 갈라진 다섯 edge
| dataset | edge | route | configs | max score |
|---|---|---|---|---|
sample_collab_graph |
[0, 7] |
shared_hard_negative |
reduced_plus_closed_triplets, reduced_plus_degree |
0.9476 |
bipartite_bridge_graph |
[0, 11] |
shared_borderline_negative |
reduced_plus_closed_triplets, reduced_plus_degree |
0.5569 |
bipartite_bridge_graph |
[1, 8] |
shared_borderline_negative |
reduced_plus_closed_triplets, reduced_plus_degree |
0.5545 |
bipartite_bridge_graph |
[4, 8] |
shared_borderline_negative |
reduced_plus_closed_triplets, reduced_plus_degree |
0.5540 |
sample_collab_graph |
[5, 8] |
config_specific_borderline_negative |
reduced_plus_degree |
0.5377 |
결과는 다섯 edge로 정리됐다. route count만 보면 shared_hard_negative 1개, shared_borderline_negative 3개, config_specific_borderline_negative 1개다. config_specific_hard_negative는 이번 판에서는 없었다. 이게 조금 다행이면서도, 한편으로는 공통 failure를 먼저 봐야 한다는 뜻으로 읽혔다.
가장 먼저 열 edge는 sample_collab_graph의 [0, 7]이다. 이 edge는 reduced_plus_closed_triplets에서는 0.9476까지 올라간 hard leak이고, reduced_plus_degree에서도 threshold 위에 걸렸다. 특정 config 하나만의 이상치가 아니라 두 add-back variant가 같이 민감하게 반응한 edge다. 다음 분석에서 이 edge는 positive neighborhood와 two-hop context를 따로 펼쳐 볼 가치가 있다.
bipartite_bridge_graph의 세 edge는 모양이 다르다. 모두 두 config가 같이 놓쳤지만 max score가 0.55 근처다. hard outlier라기보다 threshold 근처 표면이 같이 밀린 쪽에 가깝다. 여기서는 "왜 이 edge 하나가 폭발했나"보다, bipartite graph에서 negative edge score가 왜 두 config 모두에서 0.5 위로 살짝 올라오는지를 보는 편이 맞다.
config 이름보다 공통 실패 먼저 보기
이번 작업을 하면서 한 번 더 느낀 건, config 이름이 원인 설명처럼 보이면 위험하다는 점이다. reduced_plus_closed_triplets라고 적혀 있으면 hard leak도 closed triplets 때문처럼 읽고 싶어진다. 하지만 실제 top edge [0, 7]은 triangle-free context였다. 이름은 실험판의 라벨일 뿐이고, 원인은 edge snapshot과 config 간 겹침을 같이 봐야 한다.
그래서 이번 report에서는 config별 summary와 edge별 summary를 둘 다 남겼다. config summary는 각 설정이 shared target을 얼마나 갖는지, hard target과 borderline target이 몇 개인지 보여 준다. edge summary는 반대로 같은 edge가 몇 개 config에 걸렸는지 보여 준다. 둘 중 하나만 보면 해석이 쉽게 기울어진다.
나는 특히 shared_borderline_negative가 마음에 걸린다. 점수만 보면 큰 문제처럼 안 보일 수 있다. 0.55 근처라 threshold를 조금만 움직이면 빠질 수도 있다. 그런데 두 config가 같이 그 근처로 밀린다는 건, threshold 조정 전에 공통 구조를 봐야 한다는 신호일 수 있다. 작은 graph에서는 이런 약한 신호가 다음 실험 방향을 꽤 많이 바꾼다.
테스트와 정리
테스트는 두 가지를 추가했다. 첫 번째는 같은 edge가 hard config와 broad config에 동시에 들어오면 shared_hard_negative로 잡히는지 보는 테스트다. 두 번째는 runner가 JSON, CSV, PNG를 실제로 쓰는지 확인하는 smoke test다. 전체 테스트는 32개가 통과했다.
이번 변경으로 코드가 아주 조금 더 길어졌다. 대신 다음 작업의 시작점은 훨씬 선명해졌다. 이제 다음에 GNN 프로젝트를 열면, 평균 AUC 표를 다시 보는 대신 [0, 7] shared hard negative부터 열면 된다. 그다음에는 bipartite 쪽 shared borderline 세 edge를 graph family slice로 묶고, 마지막으로 [5, 8]을 degree add-back surface 전용으로 보면 된다.
성능 숫자를 올린 날은 아니지만, 실험이 덜 흐려진 날이었다. 나는 이런 날이 꽤 중요하다고 본다. 모델을 하나 더 붙이는 것보다, 이미 틀린 edge를 어떤 순서로 다시 볼지 정해 두는 쪽이 다음 반복의 속도를 더 많이 아껴 줄 때가 있다.
'[AI 실험실] > [개인 프로젝트] GNN' 카테고리의 다른 글
| GNN | Closed-triplet addback 점수 변화 (1) | 2026.06.27 |
|---|---|
| GNN | 공통 이웃 feature 압력 진단 (0) | 2026.06.24 |
| GNN | Negative context follow-up 추가 (0) | 2026.05.16 |
| GNN | Negative edge score histogram 추가 (0) | 2026.05.14 |
| GNN | Threshold action queue 추가 (0) | 2026.05.13 |