2026년 4월 13일 | 개발 일기
left_structural_mean, right_structural_mean 같은 이름이 계속 마음에 걸렸다. 지금 GNN 실험은 무방향 edge를 놓고 링크 예측을 하는데, feature 이름은 마치 좌우가 의미 있는 것처럼 남아 있었기 때문이다. 실제로는 edge를 정렬해서 만든 ordering의 부산물에 더 가까운데, importance 표에서는 이 두 축이 마치 별개 구조 신호처럼 보였다. 그 어색함이 오늘 작업의 출발점이었다.
이번에는 새 baseline을 더 붙이지 않았다. 대신 left/right structural mean을 걷어내고, 같은 정보를 structural_mean_avg와 structural_mean_gap 두 축으로 다시 요약했다. 테스트를 먼저 늘려 계산 방식부터 고정해 두고, 샘플 single-seed와 multi-seed 결과 JSON도 전부 다시 만들었다. 숫자가 극적으로 좋아진 날은 아니지만, 적어도 무방향 그래프에서 ordering 흔적을 feature 이름으로 들고 다니는 상태는 여기서 정리해 두는 편이 맞다고 봤다.
1. 왜 이 축이 계속 거슬렸는지
직전 multi-seed 요약을 읽을 때 제일 이상했던 장면은 importance 순서가 아니라, 같은 structural mean 축이 seed마다 좌우를 번갈아 먹는 모습이었다. seed 5에서는 right_structural_mean이 importance 2위였는데 left_structural_mean은 10위였다. 그런데 seed 11에서는 반대로 left_structural_mean이 4위, right_structural_mean이 9위로 뒤집혔다. 둘 다 본질적으로 node structural feature의 평균인데, 정렬 순서 하나 때문에 해석 문장이 계속 흔들리는 셈이었다.
이런 숫자를 보면 성능이 아니라 설명 방식부터 꼬이고 있다는 느낌이 든다. edge가 방향을 갖는 문제였다면 왼쪽과 오른쪽을 나누는 게 자연스러울 수 있다. 하지만 지금 실험은 normalize된 edge ordering을 쓰는 무방향 링크 예측이다. 그러면 내가 읽고 싶은 건 "먼저 온 노드의 평균"이 아니라, 두 끝점의 구조적 수준이 전반적으로 어떤가, 그리고 두 끝점이 얼마나 다른가 쪽이다.
| seed | 기존 left_structural_mean rank | 기존 right_structural_mean rank | 읽을 때 불편했던 점 |
|---|---|---|---|
| 5 | 10위 | 2위 | 같은 structural mean인데 ordering 때문에 거의 다른 feature처럼 보였다. |
| 7 | 7위 | 3위 | 좌우 차이가 계속 남아 있어 해석이 애매했다. |
| 11 | 4위 | 9위 | 이번에는 반대로 뒤집혀서 더더욱 ordering 흔적처럼 보였다. |
결국 오늘의 질문은 꽤 단순했다. 이 둘을 그대로 두고 importance를 읽는 게 정말 맞나? 조금 더 정확히는, 같은 정보를 더 덜 어색한 형태로 요약할 수 있는데 굳이 좌우 이름을 유지할 이유가 있나 싶은 쪽이었다.
2. 오늘 실제로 바꾼 코드
변경 자체는 작다. 예전에는 `_edge_feature_map()`에서 두 노드의 structural feature 평균을 각각 뽑아 left_structural_mean, right_structural_mean으로 넣었다. 지금은 그 두 값을 바로 쓰지 않고, 평균값과 절대 차이로 다시 요약한다. 이름도 그에 맞춰 structural_mean_avg, structural_mean_gap으로 바꿨다. 이쪽이 지금 문제 설정과 훨씬 더 잘 맞는다.
left/right structural mean
→ structural_mean_avg
→ structural_mean_gap
테스트도 먼저 추가했다. 하나는 edge feature map이 새 키를 내놓는지, 그리고 avg/gap 값이 실제 structural mean에서 계산되는지 확인하는 테스트다. 다른 하나는 feature_linear, feature_linear_hybrid의 feature 목록 안에서 예전 left/right 이름이 완전히 빠지고, 새 avg/gap 이름만 남는지 보는 회귀 테스트다. 이런 이름 바꾸기 작업은 문서만 고치고 넘어가면 나중에 JSON이나 importance 요약에서 예전 키가 슬쩍 살아나는 경우가 있어서, 나는 이런 부분을 테스트로 먼저 막아 두는 편을 선호한다.
그다음에는 샘플 결과를 다시 만들었다. single-seed 결과와 multi-seed 결과를 둘 다 재생성해, 실제 importance와 mean/std가 어떻게 바뀌는지까지 같이 확인했다. 이 작업을 하고 나니 단순 리팩터링이 아니라, 비교판의 설명 축을 고친 반복으로 보는 편이 더 맞겠다는 생각이 들었다.
3. 결과를 다시 보니
제일 먼저 본 건 성능 숫자였다. feature_linear_hybrid의 multi-seed mean AUC는 0.7111 → 0.7055로 아주 조금 내려갔고, 대신 std는 0.1390 → 0.1286으로 약간 줄었다. 반대로 feature_linear은 mean AUC가 0.5500 → 0.5722로 조금 올라갔다. 즉 이번 변화는 "무조건 좋아졌다"라기보다, 좌우 ordering을 걷어내도 비교판이 크게 무너지지 않는지를 확인한 쪽에 가깝다.
| 모델 | 변경 전 mean AUC | 변경 후 mean AUC | 변경 전 std | 변경 후 std |
|---|---|---|---|---|
| feature_linear_hybrid | 0.7111 | 0.7055 | 0.1390 | 0.1286 |
| feature_linear | 0.5500 | 0.5722 | 0.1871 | 0.1929 |
이 표만 보면 약간 애매하다. hybrid는 mean이 조금 내려갔고, plain supervised는 조금 올라갔다. 그런데 나는 오히려 이런 애매함이 좋았다. 설명 축을 더 정직하게 바꾼 뒤에도 결과판이 대체로 유지된다는 건, 적어도 이 정리가 완전히 엉뚱한 방향은 아니라는 뜻이기 때문이다. 특히 hybrid 쪽은 mean AUC보다 std가 조금 줄었다는 점이 더 눈에 들어왔다.
importance 표를 다시 보면 해석도 조금 달라졌다. 이제는 left냐 right냐를 읽지 않아도 된다. 대신 structural_mean_gap이 seed 5에서는 2위, seed 7에서는 3위, seed 11에서는 4위로 남았고, structural_mean_avg는 seed 7과 13에서 각각 4위로 보였다. 아직 완전히 고정된 패턴이라고 말할 정도는 아니지만, 적어도 예전처럼 같은 structural mean 축이 좌우 이름만 바꿔 번갈아 튀는 장면은 더 이상 설명할 필요가 없어졌다.
4. 오늘 남은 감각
이런 작업은 겉으로 보면 소소하다. 새 모델을 붙인 것도 아니고, benchmark 점수를 크게 올린 것도 아니다. 그런데 작은 프로젝트를 오래 굴릴수록 이런 정리가 의외로 오래 남는다. 특히 나처럼 결과 JSON과 importance 표를 자주 다시 읽는 쪽에서는, feature 이름 하나가 해석 문장을 어떻게 틀어 버리는지가 생각보다 크다.
다음에는 여기서 한 걸음 더 가서, structural_mean_avg와 structural_mean_gap이 seed별 importance 변동을 실제로 얼마나 줄였는지 별도 요약층으로 보고 싶다. 그다음에 propagation profile 시각화나 GraphSAGE/GCN encoder 비교로 넘어가면, 적어도 지금보다 덜 찝찝한 feature 판 위에서 다음 실험을 읽을 수 있을 것 같다. 오늘은 성능을 올린 날이라기보다, 설명이 덜 비틀린 상태로 다음 반복을 준비한 날에 가까웠다.
'[개발 일기]' 카테고리의 다른 글
| GraphRAG | History Baseline Guard (1) | 2026.04.25 |
|---|---|
| GNN | GraphSAGE hybrid 입력 비교 (0) | 2026.04.16 |
| GNN | Hybrid ablation multi-seed 요약 (0) | 2026.04.11 |
| GNN 프로젝트: Propagation Profile 진단값 추가 (0) | 2026.04.06 |
| GraphRAG Scoring Profile Eval 루프 (0) | 2026.04.06 |