판정기가 실행마다 점수를 남기기 시작하자, 그 점수로 설정을 스스로 고치게 해보고 싶었습니다. 한 번에 한 항목만 바꾸고 승격 문턱을 두고 검증셋을 뒀습니다. 나중에 구현을 다시 읽어보니 다음 후보를 제안하는 쪽이 그 검증 기록을 읽고 있었습니다. 자동 탐색기를 만들며 무엇을 맡길 수 있고 무엇은 맡길 수 없는지 확인한 과정입니다.
점수를 올리는 장치는 평가의 빈틈을 찾는 장치이기도 했습니다
독립 판정기가 생기자 실행마다 기준별 점수와 반려 사유가 남았습니다. 이 신호를 답을 한 번 더 만들지 결정하는 데만 쓸 필요는 없어 보였습니다. 같은 유형의 실패가 반복되면 다음 실행의 설정을 고치는 근거로 쓸 수 있습니다.
반복 상한이나 검색 폭, 판정 문턱을 사람이 하나씩 바꿔보던 작업이 있었습니다. 6월 중순 그 작업을 제한된 탐색 문제로 바꿔보기로 했습니다.
가장 단순한 구현은 여러 설정 조합을 실행하고 점수가 가장 높은 조합을 고르는 것입니다. 실제로 그렇게 짜기 시작했습니다.
곧 두 가지가 걸렸습니다. 출력 길이와 반복 횟수를 함께 바꿔 점수가 올랐다면 어느 변경이 효과를 냈는지 알 수 없습니다. 그리고 평가에 쓴 과제와 판정기의 취향에만 잘 맞는 설정이 뽑힐 수 있습니다.
자동 탐색기는 성능을 올리는 장치인 동시에 평가의 빈틈을 훨씬 빠르게 파고드는 장치입니다. 사람이 설정을 바꿀 때는 하루에 몇 번이지만, 자동화하면 그 속도가 달라집니다.
그래서 무게중심을 후보 생성 알고리즘이 아니라 변경을 승격하거나 되돌리는 절차에 뒀습니다. 좋은 후보를 많이 만드는 것보다 나쁜 후보가 채택되지 않는 쪽이 먼저였습니다.
자동화를 붙일 때 흔히 생성 능력부터 봅니다. 실제로 위험한 쪽은 그 결과를 받아들이는 관문입니다. 관문이 없으면 생성 능력이 좋아질수록 잘못된 채택도 같이 빨라집니다.
설정을 통째로 다시 쓰게 했더니 롤백이 파일 덮어쓰기가 됐습니다
첫 구현은 하네스 설정 전체를 모델에게 넘겨 자유롭게 다시 쓰게 했습니다. 다양한 후보가 나왔습니다.
문제는 되돌릴 때 드러났습니다. 시스템 지시문, 반복 상한, 판정 문턱, 활성 전략이 한꺼번에 달라지면 사실상 새 하네스입니다. 점수가 왜 변했는지 분리할 수 없고, 롤백은 이전 설정 파일을 통째로 덮는 수준에 머뭅니다.
그래서 바꿀 수 있는 것을 등록된 항목으로 좁혔습니다. 숫자, 불리언, 열거형마다 자료형과 허용 범위를 두고, 한 번의 변경은 설정 한 곳만 건드립니다.
후보를 만들 때는 바꾸기 전 값과 바꾼 뒤 값뿐 아니라 정확히 반대로 적용할 역변경까지 함께 계산했습니다.
Move
target : max_iterations
before : 4
after : 3
inverse : 3 → 4
이 제약은 탐색 공간을 줄입니다. 여러 값을 함께 움직이는 방법보다 좋은 조합에 천천히 도달합니다.
대신 "검색 폭을 줄였더니 점수가 이렇게 변했다"는 한 단계의 인과가 남습니다. 오프라인 탐색은 기준 설정의 복사본에 후보를 적용하고, 탈락하면 그 복사본을 채택하지 않습니다.
역변경의 용도도 분명히 해뒀습니다. 변경을 되돌릴 수 있는지 검증하고 감사 기록과 런타임 복구에 쓰는 계약이지, 탈락할 때마다 운영 설정에 역변경을 다시 실행한다는 뜻이 아닙니다.
자동 변경 기능을 설계할 때 롤백을 나중 일로 미루면 대체로 스냅샷 복원으로 끝납니다. 그러면 되돌린 뒤에도 무엇이 왜 잘못됐는지가 남지 않습니다.
점수가 목표가 되는 순간 그것은 더 이상 지표가 아니었습니다
한 항목씩 바꿔도 같은 문제셋으로 후보를 만들고 채택하면 과최적화는 남습니다. 개발 과제에 자주 나오는 표현을 판정기가 선호할 뿐, 새로운 입력에서는 나빠질 수 있습니다.
판정 점수 자체가 목표가 되면 탐색기는 판정기의 허점을 학습합니다. 지표가 목표가 되는 순간 지표이기를 그만두는 문제입니다.
그래서 승격 문턱을 세 갈래로 뒀습니다.
기준선 평가
→ 한 항목 변경
→ 개발 점수 개선?
→ 별도 점수 유지?
→ 등록한 보조 지표 유지?
→ 승격 또는 후보 폐기
세 결과를 평균 하나로 합치지 않았습니다. 개발 점수가 크게 올랐다는 이유로 다른 점수의 하락을 상쇄하게 두면 과최적화 신호가 그 평균 안에서 사라집니다.
후보가 탈락했을 때 어느 문턱을 넘지 못했는지 설명할 수 있어야 다음 탐색이 같은 실수를 반복하지 않습니다.
검증셋을 떼어뒀다고 믿었는데 후보 제안 쪽이 그것을 읽고 있었습니다
여기까지 만들고 저희는 held-out 검증을 갖췄다고 생각했습니다. 개발 점수와 별도로 비교 점수를 두고, 둘 다 통과해야 승격하니까요.
구현을 다시 읽어보니 그렇지 않았습니다.
비교 문제셋의 실행 기록이 반환되고, 다음 후보를 제안하는 반성 단계가 그 기록을 읽고 있었습니다. 후보를 만드는 쪽이 검증 결과를 보고 있으면 그 검증은 떼어놓은 것이 아닙니다.
간편 API로 벤치마크를 붙이는 경로에서는 더 명확했습니다. 개발 문제셋과 비교 문제셋이 아예 같은 것으로 설정됐습니다.
문턱 자체도 느슨했습니다. 개발 점수는 아주 작은 값보다 크기만 하면 상승으로 봤고, 하락 허용치는 비교 점수에만 적용했습니다. 모델 판정에는 잡음이 있는데, 그 잡음보다 충분히 큰 개선인지 검정하는 문턱이 없었습니다. 보조 지표도 도메인이 등록한 경우에만 동작합니다.
그러니까 저희가 만든 것은 held-out 검증이 아니라 회귀 방어선이었습니다. 명백히 나빠지는 후보를 거르는 데는 쓸모가 있고, 탐색기가 보지 못한 문제에서의 성능을 보장하지는 않습니다.
이름을 바로잡고 나서 쓰는 법도 달라졌습니다. 실전에서는 문제셋을 최소 세 층으로 나누는 편이 안전합니다.
개발셋 후보를 만드는 데 쓴다 탐색기가 본다
검증셋 승격을 거른다 탐색기가 보면 안 된다
최종셋 탐색이 끝난 뒤 한 번만 연다 탐색기 밖에서 실행한다
지금 구현은 앞의 두 역할이 일부 섞여 있으므로 최종 테스트는 탐색기 밖에서 따로 돌려야 합니다. 시간과 토큰, 비용도 현재 목표 함수의 기본 축이 아니라서 보조 지표와 실행 기록으로 따로 붙여야 합니다.
자동 평가 장치를 도입한 조직이라면 한 번쯤 같은 것을 확인해볼 만합니다. 검증셋이 물리적으로 분리돼 있는지가 아니라, 후보를 만드는 경로가 그 결과를 어떤 형태로도 보고 있지 않은지입니다. 사람이 매일 검증 점수를 보고 다음 실험을 정하는 팀도 같은 상태에 있습니다.
반성은 제안까지만 하고 적용 권한은 갖지 못하게 했습니다
점수만으로는 다음에 어떤 설정을 바꿀지 정하기 어렵습니다. 그래서 평가 피드백을 읽고 다음 변경의 이유를 제안하는 반성 단계를 뒀습니다. 도구를 찾지 못한 사례가 반복되면 검색 폭을 조정하자는 후보를 냅니다.
다만 이 단계가 설정을 직접 쓰지는 못합니다. 제안은 등록된 항목과 자료형, 허용 범위를 통과해야 하고, 한 번에 한 항목이라는 규칙도 지켜야 합니다.
자연어 제안과 실제 적용 권한을 분리한 것입니다. 후보 생성이 아무리 영리해져도 승격 관문과 롤백 계약은 바뀌지 않습니다.
탐색 자체에 종료 조건이 없으면 개선 기능이 비용 폭주가 됩니다
"더 좋은 설정이 나올 때까지"라는 목표에는 끝이 없습니다. 후보 하나가 여러 번의 모델 호출과 판정을 요구하므로, 종료 조건이 없으면 개선 기능이 그대로 비용 폭주 경로가 됩니다.
최대 단계 수와, 연속으로 승격하지 못했을 때 멈추는 인내 횟수를 뒀습니다. 둘 중 하나에 닿으면 현재 기준선을 반환합니다.
조기 종료는 실패가 아닙니다. 정한 범위 안에서 더 나은 후보를 확인하지 못했다는 결과입니다.
탐색 기록에는 단계와 변경, 역변경, 비교 점수의 전후 값, 승격 여부, 과최적화 신호가 남습니다. 다만 개발 점수와 보조 지표 전체, 시간과 토큰, 비용이 지금 기록에 모두 보존되지는 않습니다. 전체 평가 예산과 이미 방문한 조합을 추적하는 기능도 탐색 규모를 키우기 전에 보강해야 할 자리로 남았습니다.
오프라인에서 좋았다는 이유로 운영 권한까지 열지는 않았습니다
설정 자동 변경에는 시간축이 다른 두 문제가 있습니다. 준비된 과제 묶음으로 다음 버전의 기본 설정을 찾는 일과, 지금 사용자 요청이 실패했을 때 이번 실행 안에서 복구하는 일입니다.
둘을 같은 기능으로 만들면 오프라인에서 검증한 변경과 운영 중 즉흥적인 변경이 섞입니다.
탐색기는 전자입니다. 문제셋을 반복 실행하고 문턱을 통과한 후보만 다음 기준 설정으로 남기는 오프라인 장치입니다. 런타임 변경 장치는 후자이고, 별도 문제셋을 돌릴 수 없으므로 훨씬 좁은 권한과 상한 안에서 움직여야 합니다.
그래서 런타임 변경을 세 모드로 나눴습니다.
off 기본값. 아무 값도 바꾸지 않는다
observe 어떤 변경을 제안했을지만 기록하고 설정은 그대로 둔다
act 허용된 변경을 적용하고 역변경을 저널에 남긴다
observe를 거치는 이유는 운영 입력에서 제안 빈도와 오탐을 먼저 보기 위해서입니다. 오프라인 탐색 결과가 좋았다는 이유로 운영 중 자동 변경 권한을 바로 열지 않았습니다.
스스로 고치는 기능에서 정작 설계한 것은 못 하게 막는 쪽이었습니다
이 편에서 만든 것을 한 줄로 줄이면 설정을 스스로 바꾸는 장치입니다. 실제로 시간을 쓴 곳은 반대편이었습니다.
바꿀 수 있는 범위를 좁히고, 한 번에 한 항목으로 제한하고, 역변경을 미리 계산하고, 세 문턱을 따로 두고, 탐색에 종료 조건을 붙이고, 운영 적용을 기본으로 끄는 일이었습니다.
그리고 그렇게 만들어놓고도 저희가 held-out이라고 부르던 것이 실은 회귀 방어선이었습니다. 자동화의 위험은 자동화가 잘못 동작하는 데 있지 않고, 잘 동작하는 것처럼 보이는 검증을 함께 만들어버리는 데 있었습니다.
여기까지가 설정을 자동으로 움직이는 이야기입니다. 다음 편에서는 사람이 직접 비교할 때의 이야기를 다룹니다. 두 모델을 나란히 놓고 재보니 처음 나온 30배 차이가 모델의 차이가 아니었습니다.
이전 편 → 제출 도구를 부르고도 실행이 멈추지 않았습니다 (5편)
다음 편 → 30배 차이가 모델의 차이가 아니었습니다 (7편)

