고전 ML 재개선: 다시 데이터 안의 신호를 찾기 시작하다
이전 글에서는 kf-deberta를 도입한 이후의 실험을 정리했다.
RoBERTa seed만으로는 큰 상승을 만들기 어려웠고, kf-deberta를 섞으면서 새로운 아키텍처 축이 열렸다.
v30 LB: 0.7774001002
v31b LB: 0.7790319801
v35 LB: 0.7796121958
하지만 이후에도 정체는 계속됐다.
kf seed를 바꾸고, 다른 백본을 추가하고, FGM이나 specialist 같은 방법을 시도했지만 큰 돌파구는 나오지 않았다.
점수는 조금씩 오르거나 내렸지만, 새로운 방향이 명확하게 잡히지는 않았다.
나는 지금까지 Claude와 함께 작업을 이어오고 있었다.
Fable이 다시 풀리고 마침 MAX 계정이 있었기 때문이다.
실험 설계, 코드 작성, 결과 해석까지 대부분 Claude의 제안을 바탕으로 진행했다.
초반에는 이 방식이 잘 맞았다. 실험 속도도 빨랐고, 다양한 방향을 짧은 시간 안에 확인할 수 있었다.
하지만 점수가 0.779 부근에서 오래 정체되면서 조금씩 의문이 생겼다.
Claude는 holdout 점수를 기준으로 실험을 중단하거나, 특정 축에 대해 “더 이상 해볼 수 있는 게 없다”고 판단하는 경우가 많았다.
물론 holdout은 중요한 검증 지표다.
하지만 실험이 진행되며 LB와 holdout의 갭과 오차범위가 큰 것을 계속 관측하게 되었다.
특히 specialist 실험 이후의 결론이 마음에 걸렸다.
Claude는 read_file, grep_search, glob_pattern, list_directory 같은 탐색 계열 클래스가 잘 구분되지 않는 이유를 두고,
입력 안에 구분할 수 있는 신호가 부족하거나 없다고 단정했다.
나는 주최측에서 이러한 데이터를 줬을리가 없다고 생각했다.
또한 리더보드 상위권의 점수와의 격차 역시 작지 않았다.
정말 신호가 없는 건지 아니면 신호는 있는데, 그걸 제대로 꺼내 쓰지 못하는 건지 확인이 필요했다.
특히 메타데이터가 제대로 활용되고 있는지 의심스러웠다.
이번 문제는 현재 문장만 보고 다음 행동을 맞히는 문제가 아니었다. 같은 문장이라도 열린 파일, 직전 action, history, CI 상태에 따라 정답 행동이 달라질 수 있었다.
그래서 이때부터는 나도 실험 방향에 더 직접적으로 개입하기 시작했다.
새로운 모델을 하나 더 붙이기보다,
이미 주어진 데이터 안에서 아직 쓰지 못한 신호가 남아 있는지 다시 확인해보기로 했다.
holdout은 우열 판단자가 아니다
가장 먼저 의심한 것은 holdout을 기준으로 실험을 너무 빨리 버리는 방식이었다.
이전 실험들을 보면 holdout과 LB가 항상 같은 방향으로 움직이지 않았다.
그런데도 클로드는 holdout 점수 하나만 보고 실험을 중단하고 가능성을 하나씩 닫고 있었다.
나는 이 방식이 위험하다고 생각했다.
그래서 기준을 바꿨다.
holdout은 우열 판단자가 아니라 자격 필터다.
최종 우열은 서버 LB로 판단한다.
holdout은 여전히 필요하다.
명백히 나쁜 실험을 거르는 데는 유용하다.
따라서 (지금까지의 LB와 홀드 아웃의 오차중 가장 큰 점수 * 2)를 최고점에서 뺀 값을 버리는 기준으로 널널하게 잡았다.
또한 새로운 정보축이 있는 실험이라면, holdout이 조금 낮더라도 바로 버리지 않고 서버에서 확인할 가능성을 열어두기로 했다.
v37: 고전 ML개선
이후 다시 확인한 부분은 고전 ML 쪽이었다.
돌이켜보면 이 시기의 실험은 Transformer 계열에 지나치게 집중되어 있었다.
RoBERTa seed, kf-deberta, 입력 길이, 백본 교체처럼 대부분의 개선 시도가 Transformer 쪽에서만 이루어졌다.
하지만 최종 앙상블 구조를 보면 고전 ML은 여전히 0.4의 비중을 차지하고 있었다.
그래서 고전 ML에도 `[SRC]` 구분을 적용해보기로 했다.
기존 고전 ML:
current prompt / history / meta 기반 피처
v37 고전 ML:
기존 피처
+ SRC 구분 정보
구성은 다음과 같았다.
v37 구성:
SRC 적용 고전 ML
+ RoBERTa seed48
+ RoBERTa seed51
+ kf768
결과는 다음과 같았다.
v37 LB: 0.7806602638
처음으로 0.780을 넘었다.
이 결과는 의미가 있었다.
7편에서 다룬 실험들은 대부분 Transformer 멤버를 바꾸거나, kf seed를 교체하거나, 새로운 백본을 추가하는 방향이었다.
반면 v37은 새로운 Transformer를 추가한 것이 아니라, 기존 앙상블에 들어가 있던 고전 ML 파트의 입력 방식을 고친 실험이었다.
Transformer 쪽에서는 이미 중요하다고 판단한 `[SRC]` 정보를 고전 ML에도 맞춰 넣었고, 실제로 LB가 올랐다.
이 결과를 보고 Claude에 대한 불신은 더 커졌다.
이미 Transformer에서 효과를 확인한 `[SRC]` 신호를, 앙상블의 40%를 차지하는 고전 ML에 넣을 생각을 하지 않은게 이해가 안됐다.
Claude가 내리는 결론을 갈수록 믿기 어려웠다.
실패한 실험이 정말 그 축 전체를 닫을 만큼 충분한 검증이었는지, 아니면 특정 구현 하나가 실패한 것뿐인지 직접 확인해야 했다.
메타데이터를 “넣었다”와 “활용했다”는 다르다
이후에는 specialist에 대한 결론을 다시 뜯어보기 시작했다.
specialist가 실패했을 때 Claude는 탐색 계열 클래스를 구분할 신호가 없다고 정리하고 가능성을 닫았다.
하지만 v37 결과를 보면, 적어도 고전 ML 쪽에는 아직 반영되지 않은 신호가 남아 있었다.
그래서 다음으로 확인한 것은 메타데이터 활용 방식이었다.
Claude에게 specialist를 만들 때 전체 transcript와 메타 피처를 사용했는지 물었고, 확인 결과 입력 자체는 들어가 있었다.
하지만 입력을 넣었다는 것과, 그 정보를 잘 활용했다는 것은 다르다.
예를 들어 open_files가 입력에 포함되어 있다고 해도, 현재 prompt에 등장한 파일이 실제로 열린 파일 목록에 있는지까지 직접 표현된 것은 아닐 수 있다.
마찬가지로 last_action = grep_search라는 정보가 있어도, 현재 사용자가 그 검색 결과를 다시 확인하라는 요청을 한 것인지는 별도의 관계로 봐야 한다.
즉, 기존 메타 피처가 주로 현재 상태를 요약했다면, 내가 확인하고 싶었던 것은 현재 prompt와 history 사이의 연결 관계였다.
관계형 피처 추가
이후에는 고전 ML에 관계형 피처를 추가했다.
여기서 말하는 관계형 피처는 단순히 “파일이 열려 있는가”, “직전 action이 무엇인가” 같은 상태값이 아니다.
현재 요청과 이전 상태를 직접 비교해서 만든 피처다.
예를 들면 다음과 같다.
현재 prompt에 나온 파일이 open_files 안에 있는가?
현재 prompt에 나온 파일이 이전에 read_file로 읽힌 적이 있는가?
현재 prompt에 나온 파일이 이전에 edit_file의 대상이었는가?
현재 prompt가 직전 assistant action의 결과를 다시 묻는가?
현재 prompt와 직전 user 발화가 같은 대상을 가리키는가?
기존 피처가 이렇게 따로 정보를 줬다면,
open_files_count = 2
prompt_has_filename = 1
last_action = grep_search
관계형 피처는 한 단계 더 직접적인 정보를 준다.
prompt_file_is_open = 1
prompt_file_was_read_before = 1
prompt_refers_to_previous_search_result = 1
이번 문제에서는 같은 문장이라도 이전 상태에 따라 다음 행동이 달라질 수 있었다.
따라서 단순히 메타데이터를 넣는 것만으로는 부족했고, 현재 prompt가 과거 history와 어떻게 이어지는지를 직접 피처로 만들어볼 필요가 있었다.
v39: 관계형 9피처 추가
다음으로 v37의 고전 ML에 관계형 9피처를 추가했다.
구성은 크게 바꾸지 않았다.
v39:
v37
+ 고전 ML에 관계형 9피처 추가
결과는 다음과 같았다.
v39 LB: 0.7809016418
v37보다 소폭 상승했다.
v37 LB: 0.7806602638
v39 LB: 0.7809016418
상승폭은 크지 않았지만, 중요한 점은 관계형 피처가 실제 LB에서도 전달됐다는 것이다.
v37에서는 [SRC]를 고전 ML에 반영하면서 0.780을 넘었고, v39에서는 prompt와 history 사이의 관계를 추가하면서 다시 한 번 소폭 상승했다.
이 결과를 보고, specialist 이후의 “구분 신호가 부족하다”는 결론은 너무 빠른 판단이었다고 느꼈다.
신호가 전혀 없던 것이 아니라,
기존 방식으로는 그 신호를 충분히 꺼내지 못하고 있었던 쪽에 가까웠다.
이 구간의 의미
v37~v39에서 새로 추가한 것은 거대한 모델이 아니었다.
Transformer 백본을 더 키운 것도 아니고, 새로운 대형 모델을 붙인 것도 아니었다.
오히려 이미 앙상블 안에 있던 고전 ML의 입력 방식을 다시 점검했다.
v37: 고전 ML에 [SRC] 적용
v38: kf 멤버 교체 probe
v39: 고전 ML에 관계형 9피처 추가
이 흐름에서 확인한 것은 두 가지였다.
첫째, 고전 ML은 끝난 축이 아니었다.
Transformer가 주력이 된 뒤에도, 고전 ML은 앙상블의 0.4 비중을 차지하고 있었다.
그런데 그동안 개선 작업은 대부분 Transformer 쪽에 집중되어 있었다.
v37은 이 방치된 축을 다시 손본 실험이었다.
둘째, 메타데이터는 단순히 넣는 것만으로 충분하지 않았다.
[SRC]처럼 데이터 집단을 구분하는 신호도, prompt와 history 사이의 관계를 나타내는 신호도 직접 표현해줘야 효과가 있었다.
클로드는 신뢰를 잃었다.
실패한 실험이 정말 그 가능성 전체를 검증한 것인지, 아니면 특정 구현만 실패한 것인지 구분해야 했다.
v37과 v39는 그 차이를 보여준 실험이었다.
정리
이 구간의 핵심은 고전 ML 재점검이었다.
7편까지는 kf-deberta, seed 교체, 백본 추가처럼 Transformer 계열 실험이 중심이었다.
하지만 v37~v39에서는 고전 ML 쪽 입력 신호를 다시 봤고, 실제로 LB가 올랐다.
v37 LB: 0.7806602638
v38 LB: 0.7805327459
v39 LB: 0.7809016418
결론은 단순했다.
고전 ML이 끝난 것이 아니었다.
고전 ML이 구식 입력 방식에 머물러 있었고, 아직 반영되지 않은 신호가 남아 있었다.
다음 글에서는 이 흐름이 민트 데이터와 메타데이터 시점 문제로 이어진다.
'고민의 흔적.. > AI Agent 행동예측 해커톤' 카테고리의 다른 글
| (연구일지 #10 이전 작업 부검하기)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.11 |
|---|---|
| (연구일지 #9 데이터 증강: 민트)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.11 |
| (연구일지 #7 새 아키텍처 축 탐색과 정체)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.11 |
| (연구일지 #6 새로운 시도와 실패의 연속..)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.09 |
| (연구일지 #5 SRC와 seed 앙상블)Dacon AI Agent 행동 예측 대회 (0) | 2026.07.09 |