실행 가능한 제어 코드를 스스로 진화시키는 탐색
LLM이 직접 제어 코드를 제안하고 고치고 섞으면서 엘리트 테이블에 쌓아 올리는 EliteTable의 프로그램 탐색 방식을 해설합니다. 여섯 개 제어 과제에서 본 개선과 일반화의 갈림길을 함께 읽어봅니다.
원문 논문 보기
오늘 다룰 논문은 EliteTable: Self-Improving Program Search for Executable Control Policies로, arXiv에 공개된 실행 가능한 제어 정책 탐색 연구이며 식별자는 arXiv:2610.12536입니다. 이 연구가 내세우는 주장은 LLM이 직접 실행 가능한 제어 코드를 제안하고 고치고 섞는 과정을 반복하면서, 점수가 높은 해법을 고정된 크기의 엘리트 테이블에 쌓아 스스로 개선된다는 것입니다. 원문 논문 링크는 이 글 제목 아래에 걸려 있습니다.
제가 이 논문에서 가장 흥미롭게 본 부분은 탐색의 대상이었습니다. 보통 제어 문제를 풀면 신경망 가중치를 조정하는데, 여기서는 가중치가 아니라 읽고 실행할 수 있는 코드 자체를 진화시킵니다. 그래서 점수가 오르는 과정이 숫자의 이동이 아니라 문장의 다듬어짐처럼 보이죠. 저는 이 숫자보다 그 뒤의 구조가 더 중요하다고 봅니다. 코드가 남으면 사람이 읽을 수 있고, 읽을 수 있으면 어디서 틀렸는지 물을 수 있기 때문입니다.
안녕하세요, 패트릭입니다. 얼마 전 집에서 선풍기를 분해했다가 다시 조립한 적이 있습니다. 나사는 몇 개 남았고 바람은 어째서인지 더 약해졌는데, 그때 깨달은 것이 있습니다. 손으로 만지는 물건은 고치는 과정이 눈에 보이지만, 신경망 가중치는 그런 맛이 없다는 것이죠. 숫자를 조금 밀었다고 해서 무엇이 달라졌는지 말로 설명하기 어렵습니다. 그렇다면 제어 정책을 코드로 쓰게 하고, 그 코드를 조금씩 고쳐가며 더 좋은 바람을 찾으면 어떻게 될까요. 오늘은 그 질문을 정면으로 다룬 연구를 함께 읽어보겠습니다.
처음에는 테이블 하나가 뭐가 다르지 싶었습니다
솔직히 고백하면 저는 처음에 제목을 보고 고개를 갸웃했습니다. 엘리트 테이블이라니, 들어보면 점수 좋은 후보를 모아두는 명단 관리처럼 들리니까요. 그런데 논문을 조금 더 들여다보면 진짜 중요한 부분은 명단 자체가 아니라, 명단이 다음 제안을 낳는 방식에 있습니다. 잘된 코드를 버리지 않고 옆에 붙여둔 뒤, LLM에게 보여주면서 다음 후보를 만들게 한다는 것이죠. 생각해보면 사람도 비슷하게 배웁니다. 잘 풀린 문제를 오답 노트가 아니라 정답 노트에 모아두고, 다음에 비슷한 문제를 만나면 그 노트를 먼저 펼치죠.
이 발상이 마음에 드는 이유는 탐색이 기억을 갖게 되기 때문입니다. 매번 처음부터 헤매는 에이전트는 같은 실수를 반복합니다. 반면 테이블이 있으면 이전 세대의 좋은 조각이 다음 세대로 이어집니다. 물론 테이블이 만능은 아닙니다. 크기가 고정되어 있어서 누구를 남기고 누구를 버릴지 계속 골라야 하고, 고르는 기준이 점수 하나뿐이면 점수에는 잘 맞지만 실제로는 약한 코드가 살아남을 수도 있습니다. 저는 여기서 조금 멈칫했습니다. 점수가 높은 것과 쓸모가 많은 것이 늘 같은 것일까요. 그 질문을 들고 다음 장으로 넘어가 보겠습니다.
매번 처음부터 헤매는 탐색은 왜 비쌀까
로봇 팔을 움직이거나 카트를 세우는 제어 문제를 떠올려 보겠습니다. 아침에 연구실에 출근한 학생이 어제 찾은 파라미터를 다 지우고 오늘 다시 무작위로 찍기 시작한다면 어떤 일이 생길까요. 운이 좋으면 금방 좋은 값을 찾겠지만, 대부분은 어제 이미 밟아본 실패를 또 밟게 됩니다. LLM에게 제어 코드를 매번 빈손으로 짜라고 하면 비슷한 일이 벌어집니다. 문법은 그럴듯한데 물리 감각은 없고, 지난번에 잘됐던 아이디어는 흔적도 없이 사라지죠.
그래서 연구진이 꺼낸 처방은 단순하면서도 오래된 지혜에 가깝습니다. 잘된 것을 모아두는 상자를 하나 두고, 새 후보를 만들 때마다 그 상자를 참고하게 하는 것이죠. 이 상자가 바로 고정된 크기의 엘리트 테이블입니다. 크기를 고정한 선택도 의미가 있습니다. 무한히 쌓으면 좋은 것과 나쁜 것의 경계가 흐려지고, 매번 참고할 분량이 늘어나면서 비용도 커지니까요. 적당한 크기로 자르고 점수 순으로 채우는 일은 탐색에 리듬을 줍니다. 이번 세대에서 배운 것을 다음 세대로 넘기는 리듬 말이죠.
저희가 새로운 AI 아키텍처를 볼 때도 benchmark 숫자만 보지는 않습니다. 실제로 무엇이 바뀌었고, 그 변화가 시스템에서 어떤 비용 구조를 만드는지를 함께 봅니다. 이 논문의 테이블은 비용 구조를 직접 건드립니다. 좋은 후보를 다시 설명하지 않아도 되니 토큰이 줄고, 검증된 조각을 재활용하니 시뮬레이션 횟수를 아낄 수 있습니다. 물론 테이블이 있다고 해서 탐색이 저절로 싸지는 것은 아닙니다. 테이블이 나쁜 방향으로 굳으면 오히려 다양성이 줄 수 있는데, 그 이야기는 연산자 부분에서 다시 다루겠습니다.

가중치가 아니라 코드를 진화시키는 이유가 있을까
여기서 재미있는 질문이 생깁니다. 왜 하필 코드일까요. 가중치를 직접 조정하는 편이 더 정밀하지 않을까요. 표면적으로 보면 단순합니다. 실제로는 그렇지 않습니다. 가중치는 기계에게는 편하지만 사람에게는 불투명하고, 코드 하나는 사람에게 읽히지만 기계에게는 다루기 어렵다는 차이가 있습니다. 연구진은 후자를 택했습니다. LLM이 읽고 쓸 수 있는 형태로 정책을 남기면, 탐색 과정 자체가 설명이 된다는 것이죠.
제가 이 선택을 좋게 본 이유는 디버깅 가능성 때문입니다. 카트가 왼쪽으로 쓰러질 때 어떤 조건문이 발동했는지 코드를 펼치면 바로 보입니다. 가중치 행렬에서는 그런 장면을 짚어내기 어렵죠. 반면 코드로 진화시키는 일에는 분명한 대가도 따릅니다. 코드는 한 글자만 바뀌어도 동작이 크게 달라질 수 있어서, 탐색 지형이 울퉁불퉁해집니다. 가중치 공간처럼 조금씩 밀면서 부드럽게 개선하기가 어려운 것이죠. 그래서 연구진은 LLM의 언어 감각에 기대어 울퉁불퉁한 지형을 건너뛰게 합니다. 문법적으로 말이 되면서 물리적으로도 말이 되는 점프를 노리는 셈입니다.
그렇다면 이 흐름은 어디까지 믿을 수 있을까요. 코드는 읽힌다는 장점이 있지만, 읽힌다고 해서 늘 옳다는 뜻은 아닙니다. 그럴듯하게 읽히는 코드가 시뮬레이터에서는 어이없이 넘어지기도 합니다. 저는 그래서 이 논문을 코드 만능론으로 읽지 않았습니다. 코드라는 표현 형식을 택했기 때문에 생기는 탐색의 질감, 즉 사람이 개입할 틈이 넓어진다는 점에 더 주목했습니다.

이건 그냥 프롬프트를 잘 쓰는 이야기가 아닙니다
헷갈리는 지점이 하나 있어서 여기서 짚고 넘어가겠습니다. LLM이 코드를 짠다고 하면 흔히 좋은 프롬프트 하나로 한 방에 푸는 그림을 떠올립니다. 하지만 EliteTable은 그런 이야기가 아닙니다. 한 번의 생성이 아니라 여러 세대에 걸친 탐색이고, 프롬프트 한 줄의 마법이 아니라 테이블과 연산자와 평가가 맞물린 시스템입니다. 프롬프트가 좋아서 풀렸다는 설명은 이 구조를 반만 본 것이죠.
차이를 분명히 하려면 질문을 바꿔야 합니다. 좋은 프롬프트는 무엇인가에서 좋은 탐색은 어떻게 기억하는가로 말이죠. 좋은 프롬프트를 찾는 일은 답을 찾는 일에 가깝고, 좋은 탐색을 만드는 일은 답을 찾는 과정을 설계하는 일에 가깝습니다. EliteTable은 후자에 속합니다. 실제로 논문이 보고하는 것도 단일 코드의 점수가 아니라 세대를 거치며 분포가 어떻게 움직였는지입니다. 처음 세대의 후보들 점수의 중위값이 마지막 세대에서는 어떻게 달라졌는지, 뽑힌 승자의 중위값은 어떻게 바뀌었는지 같은 것이죠.
개인적으로 인상 깊었던 점은, 연구진이 한 방의 성공을 자랑하지 않았다는 것입니다. 열 개씩 독립적으로 초기화한 탐색을 반복해서 견고함을 봤다는 대목에서 태도가 읽혔습니다. 한 번 잘되면 논문이 되지만, 열 번 해서 여러 번 잘돼야 시스템이 되니까요. 실제 시스템 관점에서 보면 다음 질문이 바로 생깁니다. 평균이 올랐는가보다 분산이 어떻게 생겼는가가 더 궁금해집니다.
엘리트 테이블은 어떻게 다음 후보를 낳을까
이제 동작 방식을 머릿속으로 따라가 보겠습니다. 상상해 보세요. 책상 위에 지금까지 잘 달린 제어 코드 몇 장이 놓여 있습니다. 옆에는 LLM이 앉아 있고, 앞에는 시뮬레이터가 있습니다. LLM은 세 가지 방식으로 새 카드를 만듭니다. 하나는 백지에서 새로 제안하기, 또 하나는 기존 해법을 고치기, 마지막은 여러 해법을 섞기인데, 이렇게 만든 후보는 시뮬레이터에서 점수를 받고 점수가 좋으면 테이블에 들어갑니다. 테이블이 가득 차면 낮은 점수는 밀려나죠.
이 세 가지 연산자의 조합이 생각보다 중요합니다. 새로 제안하기만 있으면 기억이 끊기고, 고치기만 있으면 지역 최적점에 갇히기 쉽고, 섞기만 있으면 서로 안 맞는 조각이 붙어 어색한 코드가 나올 수 있습니다. 세 가지를 함께 쓰면 서로의 빈틈을 메웁니다. 새로 제안하기가 다양성을 불어넣고, 고치기가 좋은 조각을 다듬고, 섞기가 떨어져 있던 좋은 아이디어를 한데 모아줍니다. 마치 요리할 때 새 재료를 사 오기도 하고 간을 보기도 하고 두 가지 반찬을 합쳐 새 메뉴를 만들기도 하는 것과 같습니다.
여기서 놓치기 쉬운 대목은 테이블이 단순 저장소가 아니라 제안의 조건이라는 점입니다. LLM은 빈손으로 상상하지 않고 테이블을 보고 상상합니다. 잘된 예시를 조건으로 주면 생성 분포가 달라지죠. 그래서 테이블의 구성 자체가 다음 세대의 성격을 정합니다. 점수만 보고 뽑은 테이블은 점수에 잘 맞는 아이를 낳고, 다양성까지 고려한 테이블은 조금 다른 아이를 낳습니다. 논문은 고정된 크기의 테이블을 썼다고 밝히는데, 저는 그 고정이라는 말이 오히려 질문을 남긴다고 봅니다. 무엇을 버릴 것인가라는 질문 말이죠.

열 개 세대와 쉰 개 슬롯이 만드는 리듬
숫자를 볼 차례입니다. 논문은 여섯 개 Gymnasium 제어 과제에서 각 과제마다 열 개 세대와 세대당 쉰 개 후보 슬롯으로 탐색했다고 설명합니다. 그리고 각 과제를 열 번씩 독립적으로 초기화한 탐색으로 반복했습니다. 숫자를 그냥 읽으면 밋밋하지만, 의미를 풀면 꽤 짠 설계입니다. 열 개 세대는 길지도 짧지도 않은 호흡이고, 쉰 개 슬롯은 한 세대에서 충분히 둘러볼 만한 넓이이며, 열 번 반복은 운이 좋아서 한 번 잘된 경우를 걸러내는 장치니까요.
결과도 그 호흡에 맞춰 읽어야 합니다. 후보 점수의 중위값은 여섯 개 과제 모두에서 처음 세대보다 마지막 세대에서 올랐고, 뽑힌 승자의 중위값은 다섯 개 과제에서 올랐습니다. 중위값이라는 표현이 중요합니다. 평균은 극단적인 성공 하나에 끌려 올라갈 수 있지만, 중위값은 분포 한가운데가 실제로 이동했는지를 보여주니까요. 여섯 개 모두에서 후보 전체의 한가운데가 앞으로 나아갔다는 말은 탐색이 전반적으로 위를 향했다는 뜻입니다. 승자가 다섯 개에서만 오른 대목도 같은 맥락에서 읽힙니다. 후보 전체가 좋아져도 마지막에 고른 하나가 매번 더 좋아지리라는 보장은 없기 때문입니다.
CartPole-v1의 기록은 특히 눈에 띕니다. 열 번 탐색 모두에서 뽑힌 승자가 첫 세대 만에 탐색 점수의 천장에 닿았다고 합니다. 처음에는 저도 이 대목을 보고 웃었습니다. 너무 쉬운 과제라서 탐색이 빛날 틈이 없었다는 뜻처럼 들렸기 때문입니다. 그런데 다시 생각하면 이 기록에도 쓸모가 있습니다. 쉬운 과제에서 테이블이 빨리 포화된다는 것은 시스템이 돌아가고 있다는 신호이자, 어려운 과제와 쉬운 과제를 같은 자로 재면 안 된다는 경고이기도 합니다. 모든 과제가 같은 무게가 아니라는 것이죠.

새 에피소드 앞에서 점수는 왜 갈라질까
여기까지는 탐색 안에서의 이야기였습니다. 진짜 시험은 탐색이 끝난 뒤에 옵니다. 본 적 없는 에피소드를 내밀었을 때 코드가 여전히 서 있을까요. 논문은 열 쌍의 홀드아웃 끝점에서 짝지은 변화를 봤다고 설명하는데, 말 그대로 탐색 때 쓰지 않은 새 출발선 열 개를 놓고 점수가 어떻게 달라졌는지를 잰 것입니다. 결과는 네 개 과제에서 열 쌍 모두 좋아졌고, 두 과제에서는 그렇지 못했습니다. 저는 이 갈림이야말로 논문의 심장이라고 봅니다.
네 개 과제에서 새 에피소드 열 개가 모두 좋아졌다는 말은 우연히 한두 개가 좋아진 것이 아니라 방향 자체가 옮겨갔다는 뜻입니다. 테이블이 외운 답이 아니라 통하는 감각을 모았다는 것이죠. 반면 Acrobot-v1에서는 열 번 탐색 가운데 여섯 번이 홀드아웃에서 떨어졌고, 짝지은 변화의 중위값도 음수였다고 합니다. 후보 점수는 올랐는데 새 무대에서는 떨어진 셈입니다. 이 차이는 생각보다 중요합니다. 탐색 점수가 오른 것과 쓸 수 있는 정책이 된 것은 다른 문제이기 때문입니다.
BipedalWalker-v3와 CarRacing-v3의 기록도 같은 선에서 읽힙니다. 점수는 좋아지는 쪽으로 움직였지만 보고 기준으로 삼은 목표에는 닿지 못했다고 합니다. 저는 이 서술이 솔직해서 좋았습니다. 좋아졌다는 말만 남기면 독자는 다 된 줄 알지만, 목표에는 닿지 못했다는 말이 있으면 어디까지 왔는지가 보입니다. (참고로 제어를 해보신 분은 아시겠지만, 걷기와 레이싱처럼 시간 지평이 길고 실패가 누적되는 과제는 점수 몇 점 올리기가 카트 세우기와는 차원이 다릅니다.) 그래서 저는 이 논문의 점수표를 성공 자랑이 아니라 지도로 읽었습니다. 어디까지는 길이 났고 어디부터는 아직 진흙길인지 보여주는 지도 말이죠.

비교와 제거 실험이 없으면 무엇을 모를까
여기서는 조금 조심해서 읽을 필요가 있습니다. 논문 스스로도 비교와 제거 실험이 필요하다고 밝히는데, 이 말은 지금 결과만으로는 EliteTable의 상대적인 성능이나 각 부품의 기여를 확정하기 어렵다는 뜻입니다. 저는 이 한 줄을 논문의 약점이 아니라 미덕으로 읽었습니다. 무엇을 보여주지 않았는지를 정확히 말해주는 연구는 드물기 때문입니다.
구체적으로 무엇이 비어 있을까요. 먼저 다른 방법과 나란히 놓고 같은 예산으로 달렸을 때 얼마나 나은지 알 수 없습니다. 열 개 세대와 쉰 개 슬롯이라는 예산은 고정되어 있지만, 그 예산을 다른 탐색이나 다른 최적화에 썼다면 어땠을지 비교가 없으면 효율을 말하기 어렵죠. 다음으로 세 가지 연산자 가운데 무엇이 실제로 일을 했는지 알 수 없습니다. 새로 제안하기를 빼도 될지, 섞기가 없었다면 어땠을지 같은 제거 실험이 없으면 설계의 근거가 희미해집니다. 테이블 크기를 바꾸면 어떻게 되는지, 점수만으로 뽑는 규칙을 바꾸면 어떻게 되는지도 마찬가지입니다.
그렇다면 이 빈칸을 어떻게 대해야 할까요. 저는 이렇게 봅니다. 지금 논문은 EliteTable이 동작한다는 존재 증명에 가깝고, 얼마나 효율적인가라는 비교 증명은 다음 순서라는 것이죠. 존재 증명도 가볍지 않습니다. 코드라는 까다로운 공간에서 여러 과제와 여러 반복에 걸쳐 중위값이 올라가고, 일부 과제에서는 새 에피소드로도 옮겨갔다는 기록은 다음 실험을 해볼 만한 이유가 됩니다. 다만 그 이유를 성능 우위로 착각하면 안 됩니다. 좋아졌다는 것과 가장 좋다는 것은 다른 말이니까요.
그래서 엔지니어는 무엇을 가져가면 될까
실제 시스템 관점에서 보면 다음 질문이 바로 생깁니다. 이걸 내 파이프라인에 올리면 무엇이 달라질까요. 저는 세 가지로 정리했습니다. 첫째는 읽을 수 있는 정책이 쌓인다는 점입니다. 테이블에 남는 것은 가중치가 아니라 코드라서, 장애가 나면 어느 줄이 범인인지 물을 수 있습니다. 밤사이 시뮬레이터 조건이 바뀌거나 아침에 센서 범위가 달라져도, 코드를 펼치고 조건문을 고치는 것부터 시작할 수 있죠. 운영의 아침을 지켜주는 장치로는 이만한 맛이 없습니다.
둘째는 탐색 예산을 짜는 감각입니다. 열 개 세대와 쉰 개 슬롯, 그리고 열 번 반복이라는 숫자는 그대로 베끼라는 처방이 아니라 예산표로 읽어야 합니다. 한 세대에 몇 개를 볼 것인가, 몇 세대까지 기다릴 것인가, 운을 걸러내기 위해 몇 번 반복할 것인가 같은 것이죠. 특히 CartPole처럼 첫 세대에 천장에 닿는 과제가 있다는 기록은 과제마다 예산을 다르게 써야 한다는 힌트입니다. 쉬운 과제에는 짧게, 어려운 과제에는 길게 쓰는 식으로 말이죠.
셋째는 일반화 점검이고, 제가 앞으로 보고 싶은 것도 바로 여기입니다. 탐색 점수와 홀드아웃 점수를 짝지어 보는 습관을 파이프라인에 넣으면 좋겠습니다. Acrobot에서 본 것처럼 탐색 안에서는 오르는데 새 무대에서는 떨어지는 경우가 있기 때문입니다. 저는 그래서 테이블에 점수 하나만 적지 말고 새 에피소드에서의 떨림까지 함께 적어두고 싶습니다. 점수는 높은데 떨림도 큰 코드는 승자로 뽑지 않는 식으로 말이죠. 이 선택이 테이블의 성격을 바꿀지, 그 바뀐 테이블이 다시 어떤 후보를 낳을지가 다음으로 궁금한 장면입니다.

코드는 남는데 질문도 남는다
마지막 장에서는 시야를 조금 넓혀보겠습니다. EliteTable이 던지는 더 큰 질문은 이것입니다. 앞으로 제어 정책을 가중치로만 둘 것인가, 읽을 수 있는 형태로도 둘 것인가요. 가중치는 성능을 내는 데 강하고, 코드는 이해하고 고치는 데 강합니다. 둘을 섞는 시스템이 늘어난다면, 탐색과 학습의 경계도 다시 그어질 것 같습니다. LLM이 코드를 짜고 시뮬레이터가 점수를 주고 테이블이 기억을 잇는 이 삼각형은, 어쩌면 그 경계에 놓인 작은 모형인지도 모릅니다.
물론 아직 물을 것이 많습니다. 테이블이 커지면 다양성은 어떻게 지킬까요. 점수 외에 어떤 신호를 함께 써야 새 에피소드에서의 추락을 미리 알 수 있을까요. 연산자 세 가지의 배합을 과제 난이도에 따라 바꿔야 할까요. 비교와 제거 실험이 채워지면 이 질문들 가운데 일부는 답을 얻을 것입니다. 저는 그 답보다 답을 얻는 순서가 궁금합니다. 쉬운 과제에서 통하는 처방이 어려운 과제에서도 통할지, 아니면 어려운 과제용 처방이 따로 필요할지가 말이죠.
그렇다면 사람의 역할은 무엇일까요. 저는 이 논문을 읽고 나서 오히려 사람의 자리가 또렷해졌다고 느꼈습니다. 어떤 과제에 어떤 예산을 쓸지 정하고, 테이블에 무엇을 남길지 규칙을 정하고, 새 무대에서의 떨림을 보고 승자를 고르는 일은 여전히 사람의 판단이기 때문입니다. 코드는 스스로 진화하지만, 진화의 경기장을 설계하는 손은 사람의 것이니까요. 다음에 이 테이블을 다시 펼칠 때는 오늘보다 조금 더 단단한 질문을 들고 오고 싶습니다.