감사 이후 첫 반등: v47, v49, 그리고 앙상블 분해
이전 글에서는 Codex 독립 감사를 통해 기존 실험을 다시 검토한 과정을 정리했다.
감사 결과, 일부 holdout 비교에는 누수가 있었고, raw logit과 probability를 혼용한 스크립트도 있었다. 한 번의 실패를 근거로 특정 접근 전체를 닫은 경우도 확인됐다.
이후에는 새로운 아이디어를 빠르게 추가하기보다, 먼저 현재 멤버들을 같은 조건에서 다시 비교하기로 했다.
기준은 단순했다.
같은 holdout index를 사용한다.
모델별 raw logit에는 softmax를 먼저 적용한다.
같은 직렬화와 클래스 순서를 사용한다.
한 번에 한 멤버만 교체한다.
holdout은 제출 자격만 판단하고, 우열은 LB로 확인한다.
이 기준을 적용한 첫 결과가 v47이었다.
기존 앙상블의 멤버를 다시 비교하다
당시 챔피언은 v45였다.
v45 LB: 0.7809379356
v45는 다음과 같은 구조였다.
이중고전
+ RoBERTa seed48
+ RoBERTa seed51
+ kf768 seed74
하지만 감사 이후 공정하게 멤버 출력을 다시 계산해보니, 기존에 사용하지 않던 kf768_seed78이 교체 후보로 남아 있었다.
여기서 중요한 것은 단독 holdout 점수가 아니었다.
각 멤버가 앙상블 안에서 다른 모델과 얼마나 다르게 틀리는지,
그리고 기존 구조를 최소한으로 바꾸면서 서버에 확인할 수 있는지가 중요했다.
여러 조합을 검토한 뒤 다음 구성을 선택했다.
챔피언 고전 ×1
+ RoBERTa seed48
+ kf768 seed74
+ kf768 seed78
기존 구조에서 RoBERTa seed51을 kf768 seed78로 교체한 형태였다.
RoBERTa를 모두 제거하지 않고 하나는 남겨두었다. 이전 v31에서 kf 계열만으로 구성한 앙상블이 holdout에서는 강했지만, 실제 LB에서는 반납한 경험이 있었기 때문이다.
즉, v47의 목적은 kf 모델을 무작정 늘리는 것이 아니었다.
RoBERTa + kf의 아키텍처 혼합은 유지한다.
그 안에서 상대적으로 강한 멤버로 한 슬롯만 교체한다.
v47: 감사 이후 첫 신기록
v47의 결과는 다음과 같았다.
v47 LB: 0.7820173794
기존 v39와 비교하면 약 0.0011 올랐고, v45도 넘어섰다.
v39 LB: 0.7809016418
v45 LB: 0.7809379356
v47 LB: 0.7820173794
v47은 새로운 backbone이나 새로운 feature를 추가한 실험이 아니었다.
기존 앙상블에서 seed51 → kf768_seed78로 멤버 하나를 교체한 결과였다.
동일한 고전 ML과 seed48, kf768_seed74를 유지했기 때문에 비교 범위도 비교적 명확했다.
이 결과는 감사 이후 세운 방향이 틀리지 않았다는 증거였다.
실험을 더 복잡하게 만드는 것보다, 기존 멤버를 동일 조건에서 다시 비교하고 한 슬롯을 정확하게 교체하는 것이 더 효과적이었다.
성능은 올랐지만 추론 시간은 한계에 도달했다
v47은 점수 면에서는 성공했지만, 새로운 문제가 생겼다.
채점 시간: 약 9분 43초
제한 시간: 10분
남은 여유: 약 17초
v47은 고전 ML 하나와 신경망 세 개를 순차적으로 추론했다.
이미 제한 시간에 거의 도달했기 때문에, 네 번째 신경망을 추가하거나 더 무거운 backbone을 넣는 방식은 사실상 불가능했다.
이후 개선 방향은 자연스럽게 좁아졌다.
모델을 추가할 수는 없다.
기존 세 슬롯 중 하나를 더 강한 멤버로 교체해야 한다.
또는 추론 시간이 거의 들지 않는 고전 ML 쪽을 개선해야 한다.
즉, v47부터는 단순한 성능 경쟁뿐 아니라 정해진 시간 예산 안에서 멤버를 배치하는 문제가 됐다.
두 가지 서로 다른 개선 방향
v47 이후에는 같은 kf768 seed74 슬롯을 대체할 두 후보를 만들었다.
두 모델 모두 기존 kf-deberta 구조를 유지했지만, 바꾼 대상은 완전히 달랐다.
첫 번째 후보: 학습 데이터의 메타데이터 처리를 바꾼 kf768ci74
kf768ci74는 모델 구조나 seed를 바꾼 실험이 아니었다.
기존 kf768 seed74와 같은 seed, 같은 backbone, 같은 split과 학습 설정을 사용하면서,
민트 학습 샘플에 붙는 메타데이터 처리만 변경하려고 만든 모델이었다.
결국 성능이 좋은 모델을 만들기 위해서는 주최측에서 제공한 데이터를 가능한 만큼 활용해야 한다고 생각했다.
기존 민트는 history에서 과거 action을 복원했지만, 메타데이터는 해당 action 시점이 아닌 source row의 이후 상태를 포함했다.
open_files
git_dirty
last_ci_status
budget_tokens_remaining
elapsed_session_sec
처음에는 history 안의 run_tests 기록을 재생해 과거 CI 상태를 복원하고,
복원하기 어려운 메타데이터는 제거하는 방식으로 문제를 줄이려 했다.
이 과정에서 만든 데이터가 train_mint_ci였고, 이를 사용해 학습한 모델이 kf768ci74였다.
다만 이후 감사에서 이 변경은 단순한 “메타데이터 제거”가 아니었다는 사실이 확인됐다.
실제로는 다음 변화들이 동시에 들어가 있었다.
1. 민트의 CI 값을 history 기반 replay 결과로 변경
2. open_files, git_dirty, budget, elapsed 값을 제거
3. 제거된 값이 0으로 들어가면서 민트 행을 구분하는 zero-sentinel 생성
4. Transformer 입력의 [META] 문자열도 함께 변경
더 큰 문제는 CI replay가 rolling history 시작 상태를 none으로 가정했다는 점이었다.
history에는 최근 일부 기록만 남아 있기 때문에, window 밖에서 이미 run_tests가 실행됐을 수 있었다.
따라서 history 안에 테스트 기록이 없다고 CI 상태를 none으로 두는 것은 정확한 복원이 아니었다.
즉, kf768ci74가 실제로 검증한 것은 민트 메타데이터를 정확히 복원한 것이 아닌 다음과 같았다.
불완전한 CI 재작성
+ 일부 메타데이터의 zero-sentinel 처리
+ Transformer 입력 변경
따라서 이 모델이 좋아지거나 나빠지더라도, 결과를 특정 한 요소의 효과로 해석할 수는 없었다.
감사 결과 train_mint_ci는 정확한 CI 복원본이 아니었고, kf768ci74의 변화 역시 마스킹 단독 효과가 아닌 묶음 효과로 재분류됐다.
두 번째 후보: 학습 조건은 유지하고 seed만 바꾼 kf768 seed79
kf768 seed79는 반대로 데이터나 메타데이터 처리 방식을 건드리지 않았다.
기존 kf768 seed74와 다음 조건을 동일하게 유지했다.
같은 kf-deberta backbone
같은 MAX_LEN 768
같은 민트 데이터
같은 feature 구성
같은 holdout split
같은 epoch과 학습 설정
바꾼 것은 random seed 하나뿐이었다.
seed74 → seed79
신경망은 같은 데이터와 같은 설정으로 학습하더라도 초기 가중치와 데이터 순서에 따라 다른 local optimum에 도달한다.
따라서 seed79 실험의 목적은 새로운 정보나 구조를 추가하는 것이 아니었다.
같은 학습 레시피 안에서
현재 앙상블과 더 잘 맞는 다른 예측 경계를 찾을 수 있는가?
를 확인하는 실험이었다.
단일 모델의 holdout 점수만 높다고 좋은 멤버는 아니었다.
기존 모델과 같은 샘플에서 같은 방식으로 틀린다면 앙상블에 추가할 가치가 작기 때문이다.
seed79는 기존 kf 모델들과 같은 계열이었지만, 앙상블 안에서 일부 다른 예측을 만들었다.
그래서 kf768 seed74를 대체하는 후보가 됐다.
두 변화는 서버에서 반대로 나타났다
두 후보를 같은 v47 프레임에 넣어 제출했다.
| v47 | 기준 | s48 + kf74 + kf78 | 0.7820173794 |
| v48 | kf74 → kfci74 | 민트 CI·메타 처리 변경 | 0.7811759252 |
| v49 | kf74 → kf79 | seed만 변경 | 0.7831843076 |
holdout에서는 두 후보 모두 제출 자격을 통과했다.
하지만 서버에서는 결과가 반대로 갈렸다.
kf768ci74를 넣은 v48은 v47보다 내려갔다.
반면 seed만 바꾼 kf768 seed79를 넣은 v49는 약 0.00117 상승하며 새로운 챔피언이 됐다.
여기서 얻은 결론은 “메타데이터 개선은 실패하고 seed 변경이 성공했다”가 아니었다.
kf768ci74에는 여러 변화가 동시에 들어갔고, CI 복원 자체도 정확하지 않았다.
따라서 이 한 번의 제출로 메타데이터 처리 전체를 평가할 수 없었다.
더 좁게 정리해야 했다.
v48:
불완전한 CI 재작성과 zero-sentinel 처리가 결합된
해당 구현은 서버에서 반납했다.
v49:
기존 학습 레시피를 유지한 seed79 모델은
seed74보다 현재 앙상블에서 잘 작동했다.
v49는 데이터나 피처를 추가하지 않고도 상승했다.
새로운 정보축을 찾은 결과는 아니었지만, 같은 레시피에서 학습된 모델도 서로 다른 결정 경계를 만들며 앙상블 성능에 영향을 줄 수 있다는 점을 확인했다.
v50으로 CI 처리만 다시 분리하다
v48에는 CI 재작성과 zero-sentinel 처리가 함께 들어 있었다.
그래서 이후에는 마스킹을 제거하고, train_mint_ci의 CI 변경만 남긴 모델을 만들었다.
이 모델이 kf768cin74였고, 이를 v47의 seed74 슬롯에 넣은 제출이 v50이었다.
kf768ci74:
CI 재작성 + zero-sentinel 마스킹
kf768cin74:
CI 재작성 + 마스킹 없음
결과는 다음과 같았다.
v50 LB: 0.7807140946
v50 역시 v47보다 낮았다.
이를 통해 적어도 train_mint_ci를 Transformer 학습 데이터에 직접 주입한 당시 구현은 서버에서 개선으로 이어지지 않았다고 판단했다.
하지만 이것도 민트 전체의 실패는 아니었다.
문제가 된 것은 rolling history를 기반으로 불완전하게 만든 CI 값과 과거 시점 메타데이터를 Transformer에 직접 넣은 구현이었다.
이 결과가 이후 민트를 직접 학습 데이터에 추가하는 대신, 메타데이터를 제거한 text-only 사전학습으로 재설계하는 계기가 됐다.
그런데 앙상블은 정말 필요한가
v49까지 오면서 한 가지 기본적인 의문이 생겼다.
현재처럼 신경망 세 개와 고전 ML을 모두 사용하는 것이 정말 필요한가?
강한 단일 모델 하나만 사용해도 비슷한 점수가 나오지 않을까?
지금까지는 앙상블이 좋다는 전제 아래 멤버를 교체해 왔다.
하지만 현재 세대의 모델을 기준으로 신경망과 고전 ML이 각각 실제 서버 점수에 얼마나 기여하는지는 직접 분리한 적이 없었다.
그래서 v49를 기준으로 구성 요소를 하나씩 제거한 네 개의 제출을 만들었다.
1NN-only:
kf768 seed79 단독
1NN+C:
0.6 × kf768 seed79
+ 0.4 × 고전 ML
2NN-only:
0.5 × (kf768 seed79 + kf768 seed78)
2NN+C:
0.6 × 평균(kf768 seed79, seed78)
+ 0.4 × 고전 ML
그리고 기준이 되는 v49는 다음과 같았다.
3NN+C:
0.6 × 평균(RoBERTa seed48, kf768 seed79, kf768 seed78)
+ 0.4 × 고전 ML
앙상블을 서버에서 분해한 결과
결과는 다음과 같았다.
| 1NN-only | 0.7688469816 |
| 1NN + 고전 ML | 0.7749214099 |
| 2NN-only | 0.7740727119 |
| 2NN + 고전 ML | 0.7802940499 |
| 3NN + 고전 ML | 0.7831843076 |
결과가 예상보다 명확했다.
먼저 고전 ML의 기여를 볼 수 있었다.
1NN-only → 1NN+C
0.768847 → 0.774921
약 +0.0061
2NN-only → 2NN+C
0.774073 → 0.780294
약 +0.0062
고전 ML은 신경망 구성과 관계없이 약 0.006 정도를 추가했다.
이전까지 고전 ML은 Transformer보다 단독 성능이 낮은 보조 모델처럼 보일 수 있었다.
하지만 서버 분해 결과, 고전 ML은 현재 앙상블에서 가장 큰 단일 보조 기여를 하고 있었다.
두 번째 신경망의 기여도 확인할 수 있었다.
1NN-only → 2NN-only
약 +0.0052
1NN+C → 2NN+C
약 +0.0054
세 번째 신경망인 RoBERTa seed48도 여전히 기여했다.
2NN+C → 3NN+C
약 +0.0029
기여 폭은 앞의 두 요소보다 작았지만, 서버 점수에서는 분명한 상승이었다.
즉, 현재 앙상블은 불필요한 모델을 관성적으로 쌓은 구조가 아니었다.
고전 ML도 기여했다.
두 번째 kf 모델도 기여했다.
RoBERTa 모델도 기여했다.
각 구성 요소가 모두 실제 서버 점수에 양의 기여를 하고 있었다.
가장 강한 단일 모델과 전체 앙상블의 차이
가장 강한 단일 모델은 kf768 seed79였다.
1NN-only: 0.7688469816
전체 앙상블인 v49는 다음 점수를 기록했다.
3NN+C: 0.7831843076
차이는 약 0.0143이었다.
전체 앙상블 가치:
약 +0.0143
이 차이는 작지 않았다.
단일 모델의 성능을 조금 더 높이는 것만으로 현재 앙상블을 대체하기는 어려웠다.
결국 현 구조에서는 개별 모델의 강함뿐 아니라, 서로 다른 모델이 다른 샘플을 맞히는 상보성이 중요했다.
시간 비용도 함께 확인됐다
성능 기여와 함께 각 구성의 채점 시간도 확인할 수 있었다.
1NN: 약 4분 20초
2NN: 약 8분 10초
3NN+C: 약 9분 44초
신경망 한 개를 추가할 때마다 시간이 크게 늘었다.
고전 ML은 약 0.006의 큰 점수 기여를 하면서도 추론 비용은 상대적으로 작았다.
반면 네 번째 신경망을 추가할 시간은 남아 있지 않았다.
이 결과로 이후 방향이 더 명확해졌다.
고전 ML은 제거할 수 없다.
기존 신경망 세 개도 모두 기여하고 있다.
네 번째 모델을 추가할 시간은 없다.
따라서 이후 성능을 올리려면 다음 두 방향밖에 남지 않았다.
현재 세 신경망 슬롯 중 하나를 더 강한 모델로 교체한다.
고전 ML을 추론 시간 증가 없이 개선한다.
이 구간에서 확인한 것
감사 이후의 첫 실험 구간에서 확인한 내용은 다음과 같았다.
첫째, 공정한 비교 기준을 다시 세운 뒤에도 실제 상승 후보를 찾을 수 있었다.
v47과 v49는 새로운 복잡한 구조를 추가한 것이 아니라, 기존 앙상블의 한 슬롯을 더 적합한 멤버로 교체해 신기록을 만들었다.
둘째, holdout은 여전히 최종 우열을 설명하지 못했다.
v48과 v49는 모두 holdout 코어가 v47보다 높았지만, 실제 서버에서는 v48이 하락하고 v49만 상승했다.
셋째, seed 교체축은 완전히 끝나지 않았다.
v49는 동일한 레시피에서 seed74를 seed79로 교체해 약 0.00117을 추가했다.
넷째, 앙상블 구성 요소들은 모두 실제로 기여하고 있었다.
고전 ML: 약 +0.0061~0.0062
두 번째 NN: 약 +0.0052~0.0054
세 번째 NN: 약 +0.0029
다섯째, 성능과 시간 모두 포화에 가까워졌다.
3NN+C가 10분 제한에서 약 16초만 남겼기 때문에, 이후에는 모델 추가가 아니라 멤버 교체와 고전 ML 개선이 필요했다.
정리
감사 이후 첫 성과는 새로운 거대한 모델에서 나오지 않았다.
기존 산출물을 같은 기준에서 다시 비교하고, 앙상블 안에서 멤버 하나를 정확하게 교체한 결과였다.
v45: 0.7809379356
v47: 0.7820173794
v49: 0.7831843076
그리고 앙상블 분해 실험을 통해 현재 구조가 왜 유지되어야 하는지도 확인했다.
단일 모델만으로는 부족했다.
고전 ML은 여전히 강한 보조 신호였다.
두 번째와 세 번째 신경망도 서버 점수에 기여했다.
이제 남은 문제는 명확했다.
현재 앙상블은 성능 면에서도, 추론 시간 면에서도 거의 꽉 차 있었다.
새 모델을 더 넣을 수는 없었다.
따라서 이후 실험은 기존 멤버를 더 강한 모델로 교체하거나,
추론 비용이 작은 데이터·메타데이터 처리 방식을 다시 설계하는 방향으로 이어졌다.
다음 글에서는 v48과 v50에서 문제가 됐던 민트 메타데이터를 다시 검증하고,
rolling history의 한계를 확인한 뒤 민트를 직접 주입하는 대신 2단계 학습으로 재설계한 과정을 정리할 예정이다.
'고민의 흔적.. > AI Agent 행동예측 해커톤' 카테고리의 다른 글
| (연구일지 #13 자동화 하네스 구축)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.21 |
|---|---|
| (연구일지 #12 민트 재검증)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.21 |
| (연구일지 #10 이전 작업 부검하기)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.11 |
| (연구일지 #9 데이터 증강: 민트)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.11 |
| (연구일지 #8 고전 ML 개선)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.11 |