본문 바로가기
고민의 흔적../AI Agent 행동예측 해커톤

(연구일지 #7 새 아키텍처 축 탐색과 정체)Dacon AI Agent 행동 예측 대회

by sungjinp0805 2026. 7. 11.

새 아키텍처 축 탐색: kf-deberta 이후의 정체

이전 글에서는 v23a 이후 진행한 여러 실패 실험을 정리했다.

탐색 4형제 specialist, seed 교체, 자기증류, au 가중 강화 등 여러 시도를 했지만 대부분 v23a를 넘지 못했다.

v23a LB: 0.7758666688
v24 LB:  0.7697683433
v25 LB:  0.7748409894
v26 LB:  0.7640960177
v27 LB:  0.7751608972
v28 LB:  0.7755004009
 

이 구간까지 오면서 한 가지는 분명했다.

기존 앙상블 위에 후처리를 조금 얹거나, 특정 약점을 겨냥한 별도 모델을 붙이는 방식만으로는 v23a를 크게 넘기 어려웠다.

 

또 하나의 문제도 있었다.

로컬 holdout이 서버 LB의 방향을 항상 맞히지 못했다.
v24 specialist와 v26 자기증류처럼 holdout에서는 좋아 보였지만, 실제 LB에서는 크게 반납한 실험이 이미 있었다.

 

그래서 이 시점부터는 holdout을 절대적인 기준으로 보기 어려웠다.

그럼에도 holdout은 여전히 필요했다.
명백히 나쁜 실험을 빠르게 거르고, 제출 후보를 좁히는 데는 유용했다.

 

하지만 최종 우열은 결국 서버 LB로 확인해야 했다.


v23a 재제출: 전제 확인

v23a를 그대로 다시 제출한 기록이 있다.

결과는 이전과 완전히 같았다.

v23a 기존 제출: 0.7758666688
v23a 재제출:   0.7758666688
 

소수점 10자리까지 동일했다.

다만 이걸 새로운 발견처럼 받아들인 것은 아니었다.
애초에 이번 대회는 같은 제출 파일이면 같은 결과가 나오는 결정적인 채점 구조라고 보고 있었다.

 

재제출은 그 전제를 사후적으로 확인한 정도에 가까웠다.

중요한 문제는 채점 서버가 랜덤하게 흔들리느냐가 아니었다.

 

실제로 어려웠던 것은 로컬 holdout과 서버 테스트셋 사이의 방향 차이였다.

같은 제출이 같은 LB를 낸다는 사실은 운영상 유용했다.
하지만 그것이 곧 holdout에서 좋아 보이는 실험이 서버에서도 반드시 좋아진다는 뜻은 아니었다.

 

그래서 이후의 기준은 이렇게 정리했다.

LB는 같은 제출에 대해 결정적으로 나온다.
하지만 holdout은 서버 방향을 항상 맞히지 않는다.
 

이 기준을 가지고 다음 축을 찾기 시작했다.


클래스 prior 조정은 통하지 않았다

먼저 해볼 수 있는 것은 작은 후처리였다.

모델이 특정 클래스를 너무 적게 예측하거나, 반대로 너무 많이 예측한다면 클래스별 확률에 상수를 곱해 결정 경계를 조금 움직일 수 있다.

 

예를 들어 ask_user를 더 많이 예측하게 하거나, web_search, list_directory를 조금 줄이는 식이다.

실험은 다음과 같았다.

ask_user × 1.3
web_search × 0.85
list_directory × 0.8
 

결과는 모두 좋지 않았다.

ca1 ask_user × 1.3:       0.77489046
ca2 web_search × 0.85:    0.7754896782
ca3 list_directory × 0.8: 0.7751489852
 

모두 v23a보다 낮았다.

이 결과를 보고, v23a의 결정 경계는 단순한 prior 조정으로 더 좋아질 상태는 아니라고 판단했다.

 

겉으로 보기에는 특정 클래스가 부족해 보일 수 있지만, 확률을 손으로 밀어주는 방식은 전체 균형을 깨뜨릴 수 있었다.

새로운 상승을 만들려면, 단순 보정이 아니라 다른 종류의 모델이 필요했다.


RoBERTa seed만으로는 부족했다

v23a의 핵심은 [SRC]를 넣은 RoBERTa base seed 앙상블이었다.

0.6 × 평균(seed45, seed46, seed47, seed48)
+
0.4 × 고전 ML 블렌드
 

이 조합은 단순했지만 강했다.

하지만 같은 계열 안에서 seed를 바꾸거나, large 모델을 다시 넣는 실험은 기대만큼 좋지 않았다.

large 모델을 포함한 v23b는 v23a를 넘지 못했다.

v23a: 0.7758666688
v23b: 0.7732078801
 

같은 RoBERTa 계열 안에서 seed를 바꾸고, 비율을 조정하고, large를 넣어보는 방식은 어느 정도 한계에 도달한 것처럼 보였다.

그래서 다음으로는 다른 백본을 섞어보기로 했다.

 

목표는 단순히 더 강한 단일 모델을 찾는 것이 아니었다.

이미 앙상블이 강한 상황에서는, 단일 성능이 조금 높은 모델보다 기존 모델들과 다르게 틀리는 모델이 더 중요할 수 있었다.


v29: kf-deberta 도입

새로 시도한 백본은 kf-deberta였다.

기존 주력 모델이 RoBERTa 계열이었다면, 이번에는 DeBERTa 계열의 한국어 모델을 섞어보는 방향이었다.

 

이 실험에서 기대한 것은 두 가지였다.

1. RoBERTa와 다른 방식으로 문맥을 읽을 것
2. 기존 seed 앙상블과 다른 오류를 낼 것
 

v29는 다음과 같은 구성으로 만들었다.

고전 ML
+ RoBERTa seed48
+ RoBERTa seed51
+ kf-deberta
 

결과는 다음과 같았다.

v29 LB: 0.7756735615
 

점수 자체만 보면 v23a보다 아주 조금 낮았다.

하지만 중요한 비교 대상은 v23a가 아니라, 같은 3멤버 구조의 RoBERTa 조합이었다.


RoBERTa seed만으로 구성한 3멤버 조합보다 kf-deberta를 섞은 조합이 더 좋게 나왔다.

즉, kf-deberta는 단순히 독립 모델 하나로 강한 것이 아니라, 앙상블 안에서 기존 RoBERTa seed들과 다른 정보를 제공하는 것으로 보였다.

 

이때 처음으로 아키텍처 다양성이라는 축이 열렸다.


v30: kf refit으로 신기록

v29에서 가능성을 확인한 뒤, kf-deberta를 100% refit한 모델로 교체했다.

결과는 바로 좋아졌다.

v30 LB: 0.7774001002
 

v23a를 넘어 새로운 최고 점수가 나왔다.

이전까지는 여러 실험이 v23a 근처에서 막혔는데, kf-deberta를 섞은 뒤 다시 점수가 올라간 것이다.

 

이 결과는 의미가 있었다.

단순히 seed를 더 추가하거나, 기존 확률을 조정하는 방식이 아니라, 다른 아키텍처의 강한 모델을 앙상블에 넣는 것이 실제 서버 점수로 이어졌다.

 

이 시점에서 전략은 다시 조금 바뀌었다.

RoBERTa seed를 계속 늘리는 것보다, kf-deberta 계열을 더 키우거나 다른 백본을 추가해보는 쪽이 더 가능성이 있어 보였다.


v31: kf만 강하면 되는 것은 아니었다

v30 이후에는 kf-deberta seed를 더 늘려봤다.

kf72, kf73 모델을 학습했고, 둘을 함께 사용한 순수 kf 조합을 만들었다.

 

로컬 기준으로는 좋아 보였다.

v31 core holdout: 0.7601
 

하지만 LB 결과는 기대와 달랐다.

v31 LB: 0.7742949894
 

v30보다 크게 떨어졌다.

 

이 결과가 중요했다.

처음에는 kf-deberta가 좋은 축이라면, kf 모델을 더 넣으면 더 좋아질 것이라고 생각할 수 있다.
하지만 실제로는 그렇지 않았다.

 

kf 모델 두 개만 넣은 조합은 개별적으로는 좋아 보여도, 앙상블 전체에서는 RoBERTa와 kf-deberta를 섞은 조합보다 약했다.

여기서 얻은 교훈은 이거였다.

개별 멤버가 강한 것과
앙상블 안에서 좋은 멤버인 것은 다르다.
 

v31은 오히려 아키텍처 혼합의 중요성을 더 강하게 보여줬다.


v31b: 혼합 구조 유지 + 강한 kf

v31이 실패했기 때문에, 다음에는 구조를 바꾸지 않고 kf 멤버만 교체해봤다.

v30의 구조는 유지했다.

고전 ML
+ RoBERTa seed48
+ RoBERTa seed51
+ kf-deberta
 

여기서 kf만 kf71에서 kf72로 바꿨다.

결과는 다시 올랐다.

v31b LB: 0.7790319801
 

0.779를 넘었다.

이 결과는 v31과 함께 봐야 의미가 있다.

 

kf만 두 개 넣은 v31은 실패했다.
하지만 RoBERTa와 kf-deberta를 섞은 구조를 유지한 채, 그 안의 kf 멤버를 더 강한 seed로 바꾸자 점수가 올랐다.

 

즉, 이 구간에서 중요한 것은 단순히 “kf가 좋다”가 아니었다.

정확히는 다음에 가까웠다.

RoBERTa + kf-deberta의 혼합 구조가 좋고,
그 안에서 kf 멤버 품질을 올리면 이득이 난다.
 

이후 kf73도 확인했지만, kf72만큼 좋지는 않았다.

v31c LB: 0.7774043989
 

kf71과 비슷한 수준이었고, kf72가 이 구간에서는 가장 좋은 kf seed로 남았다.


v32: 다양성만으로는 부족했다

kf-deberta가 먹혔기 때문에, 다음으로는 세 번째 아키텍처를 섞어보는 실험을 했다.

deberta-v3 계열 모델을 추가해, 다음과 같은 트라이아키텍처 구성을 만들었다.

고전 ML
+ RoBERTa
+ kf-deberta
+ deberta-v3
 

이 실험의 기대는 단순했다.

kf-deberta가 RoBERTa와 달라서 도움이 됐다면, deberta-v3도 또 다른 방식으로 틀리면서 앙상블에 도움이 될 수 있지 않을까?

하지만 결과는 좋지 않았다.

v32 LB: 0.7747513423
 

v31b보다 크게 낮았다.

이 실험은 다양성에 대한 생각을 조금 수정하게 만들었다.

처음에는 “다르게 틀리는 모델이면 좋다”고 생각했다.


하지만 v32 결과를 보면, 다양성만으로는 부족했다.

앙상블 멤버는 달라야 하지만, 동시에 충분히 강해야 했다.

deberta-v3는 기존 모델들과 다른 오류를 내는 측면은 있었지만, 개별 성능이 약했고 그 약점이 앙상블 전체에 더 크게 반영된 것으로 보였다.

 

그래서 이 구간의 결론은 이렇게 바뀌었다.

다양성은 필요하다.
하지만 약한 다양성은 도움이 되지 않는다.
강하면서도 다르게 틀리는 모델이 필요하다.
 

v33, v34: 작은 probe들

이후에는 v31b 구조를 기준으로 작은 변경을 몇 가지 확인했다.

먼저 RoBERTa 멤버를 바꿔봤다.

v33: s51 → s47
LB: 0.7784334651
 

v31b보다 낮았다.

이 결과로 현재 조합에서는 s51이 s47보다 더 나은 멤버라는 것을 확인했다.

 

다음으로 블렌드 비율을 바꿨다.

기존에는 seed 평균과 고전 ML을 0.6 : 0.4로 섞고 있었다.
혹시 kf-deberta가 들어간 새 구조에서는 비율을 조금 낮추는 것이 좋을 수도 있어서 0.55를 실험했다.

v34: BLEND_W 0.6 → 0.55
LB: 0.778739012
 

역시 v31b보다 낮았다.

작은 probe들은 큰 상승을 만들지는 못했지만, 현재 조합이 꽤 예민한 균형 위에 있다는 점을 확인해줬다.


v35: kf768로 소폭 상승

다음으로는 kf-deberta의 입력 길이를 늘린 모델을 사용했다.

당시에는 더 긴 context를 보면 성능이 오를 수 있다고 생각했다.
그래서 MAX_LEN을 768로 늘린 kf 모델을 실험했다.

결과는 좋았다.

v35 LB: 0.7796121958
 

v31b보다 약 0.0006 올랐다.

이 구간에서는 의미 있는 상승이었다.

 

다만 이 상승을 순수하게 “긴 문맥을 더 봤기 때문”이라고 단정하기는 어렵다.

MAX_LEN 외에도 seed와 batch 구성 차이가 있었고, 이후 확인해보니 실제 tokenizer 기준으로 512를 초과하는 샘플 비율도 낮았다.


따라서 v35의 상승은 장문맥 효과라기보다, kf 멤버 교체와 학습 조건 변화가 함께 만든 결과로 보는 쪽이 더 안전했다.

대회 중에는 어떤 실험이 좋아지면 그 이유를 빠르게 해석해야 한다.
하지만 여러 변수가 동시에 바뀐 경우에는 원인을 하나로 단정하면 위험하다.

 

그래서 이 글에서는 v35를 “장문맥 효과”라고 확정하지 않고, kf 멤버 품질을 높인 실험으로만 기록한다.


v36: FGM은 이 구성에서는 좋지 않았다

다음으로는 FGM을 적용한 모델을 시도했다.

FGM은 적대적 학습 방식의 일종으로, 모델이 작은 입력 변화에도 더 안정적으로 예측하도록 만드는 정규화 기법이다.

 

holdout 기준으로는 나쁘지 않았다.

FGM holdout: 0.7587
 

하지만 LB 결과는 좋지 않았다.

v36 LB: 0.7768663005
 

v35는 물론이고 v31b보다도 낮았다.

이 결과를 보고, 당시에는 FGM을 더 밀어붙이지 않기로 했다.


다시 병목을 보기 시작했다

v35까지 오면서 점수는 0.7796까지 올라왔다.

v23a: 0.7758666688
v30:  0.7774001002
v31b: 0.7790319801
v35:  0.7796121958
 

분명히 상승은 있었다.

하지만 상승폭은 점점 작아졌다.


kf-deberta는 새로운 축이었지만, 그 이후로는 kf seed 교체, 입력 길이 증가, 비율 조정, 추가 백본, FGM 같은 실험이 대부분 작은 변화에 그쳤다.

그래서 다시 질문이 바뀌었다.

지금 모델은 어디서 틀리고 있는가?
 

단순히 새 모델을 하나 더 넣는 것이 아니라, 남은 오류의 구조를 봐야 했다.

분석해보니 여러 클래스에서 top-1은 틀리지만 top-2 안에는 정답이 들어오는 경우가 많았다.

 

즉, 모델이 완전히 모르는 것이 아니었다.
후보를 두 개 정도까지는 잘 좁히는데, 마지막 선택에서 자주 틀리고 있었다.

 

특히 여전히 어려운 것은 탐색 계열 클래스였다.

read_file
grep_search
glob_pattern
list_directory
 

이 네 클래스는 고전 ML 단계부터 계속 병목이었다.

초기에는 단어빈도와 메타피처로 구분하려고 했고, 이후에는 트랜스포머와 앙상블로 성능을 올렸다.


하지만 마지막까지 4형제 사이의 혼동은 남았다.

이때의 문제는 단순히 “모델이 약하다”가 아니었다.

 

모델은 대략적인 후보를 알고 있었다.

하지만 read_file과 grep_search, glob_pattern과 list_directory 사이에서 마지막 결정을 자주 틀렸다.

그래서 다음으로는 이 마지막 선택을 보정할 수 있는지 확인했다.


확률 재조합은 큰 돌파구가 되지 못했다

먼저 생각할 수 있는 방법은 기존 모델들의 예측 확률을 다시 조합하는 것이다.

이미 각 모델은 14개 클래스에 대한 확률을 내고 있다.
그 확률들을 이용해서, top-2 후보 중 어떤 것을 고를지 다시 판단하는 작은 모델을 만들 수 있다.

 

하지만 당시 실험에서는 기존 앙상블을 넘지 못했다.

이 결과만 놓고 보면, 이미 앙상블의 확률 평균 자체가 꽤 잘 정리된 상태였고, 그 위에서 확률만 다시 조합하는 방식은 새로운 정보를 만들기 어려워 보였다.

 

그래서 이 방향은 더 깊게 밀지 않았다.

다만 여기서도 결론은 좁게 잡는 것이 맞다.

당시 구현한 확률 재조합은 큰 돌파구가 되지 못했다.
 

이 정도로 정리하였다


표면 피처와 k-NN도 효과가 작았다

다음으로는 표면적인 피처를 더 보거나, 비슷한 샘플을 찾아보는 방식도 확인했다.

표면 피처는 파일명, 확장자, 경로, 특정 키워드처럼 사람이 직접 보기 쉬운 신호들이다.

 

하지만 이런 피처들은 이미 고전 ML 단계에서 상당 부분 사용하고 있었다.
새로 추가해도 큰 정보가 되지 않았다.

 

k-NN 방식도 시도했다.

k-NN은 현재 샘플과 가장 비슷한 과거 샘플을 찾아 그 라벨을 참고하는 방식이다. 

이번 문제에서는 모델이 top-2 후보까지는 잘 좁히는 경우가 많았기 때문에, 

비슷한 과거 사례를 이용하면 마지막 선택을 보정할 수 있지 않을까 기대했다.

하지만 실제로는 충분히 가까운 이웃을 찾기 어려웠다. 

문장은 비슷해 보여도 세션 상태나 열린 파일, 직전 행동에 따라 정답 행동이 달라졌기 때문에, 

단순 유사도 기반 k-NN은 큰 돌파구가 되지 못했다.


specialist도 당시 구현에서는 앙상블을 넘지 못했다

그 다음은 다시 specialist였다.

이전에도 탐색 4형제 specialist는 한 번 실패했지만, 병목이 명확해진 상황에서 다시 확인할 필요가 있었다.

 

이번에는 kf-deberta 기반으로 4형제만 구분하는 specialist를 학습했다.

목표는 분명했다.

 

"전체 14개 클래스를 보는 모델보다, read_file, grep_search, glob_pattern, list_directory만 집중해서 보는 모델이 마지막 선택을 더 잘할 수 있지 않을까?"라는 기대였다.

하지만 결과는 기대보다 낮았다.

 

4형제 specialist는 기존 앙상블의 4형제 구분 성능을 넘지 못했다.

이어서 추론군 클래스에 대해서도 specialist를 시도했다.

ask_user
plan_task
lint_or_typecheck
run_tests
run_bash
web_search
 

이쪽도 결과는 비슷했다.
부분집합 전용 모델이 전체 앙상블보다 좋지 않았다.

 

처음에는 전체 문제를 14개 클래스로 한 번에 풀기보다, 헷갈리는 부분만 따로 떼어내면 좋아질 수 있다고 생각했다.

하지만 당시 구현에서는 그렇지 않았다.

 

부분집합 specialist는 전체 맥락을 잃었고, 여러 모델을 평균내는 앙상블의 안정성도 잃었다.
결과적으로 더 날카로워지기보다 오히려 불안정해졌다.

그래서 이 구간에서는 specialist를 더 밀지 않았다.

 

다만 이것도 “specialist 계열 전체가 불가능하다”는 뜻은 아니다.
이 글에서는 당시 시도한 독립 specialist 구현이 앙상블을 넘지 못했다 정도로만 정리한다.


같은 방식의 반복으로는 더 이상 크게 오르지 않았다

v29부터 v35까지는 분명 성과가 있었다.

kf-deberta는 실제로 새로운 축이었다.


RoBERTa seed만으로는 만들기 어려웠던 상승을 만들었고, v31b와 v35에서 새로운 최고점을 만들었다.

하지만 그 이후의 실험들은 대부분 비슷한 패턴을 보였다.

강한 멤버를 바꿔본다
다른 백본을 하나 더 넣어본다
비율을 살짝 바꿔본다
후처리로 결정 경계를 밀어본다
specialist로 마지막 선택을 맡겨본다
 

이런 실험들은 대부분 큰 진전으로 이어지지 않았다.

점수는 조금씩 오르거나 내렸지만, 새로운 돌파구라고 부를 만한 변화는 없었다.

이 시점에서 kf-deberta 이후의 방향은 어느 정도 정리됐다.

1. 아키텍처 다양성은 효과가 있다.
2. 하지만 약한 다양성은 도움이 되지 않는다.
3. 같은 계열 모델만 늘리는 것은 빠르게 포화된다.
4. 당시 구현한 후처리와 specialist는 앙상블을 넘지 못했다.
5. holdout은 후보 필터일 뿐, 서버 우열을 보장하지 않는다.
 

즉, kf-deberta는 살아남은 축이었지만, 그 축을 단순히 같은 방식으로 계속 밀어붙이는 것만으로는 충분하지 않았다.


정리

이 구간의 시작점은 v23a였다.

v23a LB: 0.7758666688
 

v23a 재제출 결과는 동일했다.

v23a 재제출: 0.7758666688
 

하지만 중요한 문제는 채점 노이즈가 아니었다.
핵심은 holdout이 서버 방향을 항상 맞히지 못한다는 점이었다.

그 이후 여러 새 축을 실험했다.

v29:  kf-deberta 도입
v30:  kf refit으로 신기록
v31:  pure kf 조합 실패
v31b: 혼합 구조 유지 + 강한 kf로 상승
v32:  deberta-v3 추가 실패
v35:  kf768 교체로 소폭 상승
v36:  FGM 구성 실패
 

가장 큰 발견은 kf-deberta였다.

RoBERTa seed만 늘리는 대신, 다른 아키텍처를 섞자 점수가 올랐다.
특히 RoBERTa와 kf-deberta를 함께 사용하는 혼합 구조가 중요했다.

 

하지만 그 이후에는 비슷한 방식의 반복이 이어졌다.

kf seed를 바꾸고, 다른 백본을 넣고, 비율을 조정하고, FGM을 넣고, 후처리와 specialist를 시도했다.
대부분은 큰 상승으로 이어지지 않았다.

 

결국 이 구간의 결론은 다음과 같았다.

kf-deberta는 새 축이었다.
하지만 kf-deberta 이후의 실험들은 대부분 같은 방식의 반복이었고,
그 방식만으로는 더 큰 돌파구가 나오지 않았다.
 

정체된 느낌을 받았고 불필요한 부분을 계속 깊게 파고드는 느낌이 들었다.

그래서 다음 단계에서는 다시 입력 데이터와 고전 ML 쪽으로 돌아가보려 했다.

 

새 모델을 더 붙이는 것이 아니라,
현재 모델들이 놓치고 있는 신호가 데이터 안에 아직 남아 있는지 다시 봐야 했다.