100번의 GRPO 스텝으로 350M 모델의 구조화된 출력을 바로잡는 법
그룹 상대 정책 최적화로 350M 소형 모델의 JSON 유효성과 스키마 준수율을 100 스텝 만에 끌어올리는 실용적 미세조정 해설
원문 논문 보기
이번 글에서 다루는 글은 Fine-tuning a 350M Model for Better Structured Outputs in 100 GRPO Steps이며, Hugging Face가 공개한 블로그 글입니다. 원문 링크는 제목 아래에 붙어 있습니다.
이 글의 주장은 프롬프트당 4개에서 8개의 완성을 묶어 검증 가능한 보상을 그룹 안에서 정규화하는 GRPO로 350M 모델을 100 스텝만 조정하면 분포 내 과제에서 유효한 구조화 출력 비율을 90퍼센트 이상으로 끌어올릴 수 있다는 것이다.
구조화된 출력은 이제 선택 사양이 아니다. 검색 증강 생성의 인용 블록, 에이전트의 도구 호출, 데이터 추출 파이프라인의 레코드, 사용자 인터페이스의 폼 자동 완성까지, 모델의 문장이 기계가 읽는 형식을 지켜야 하는 지점이 계속 늘어난다. 사람이 읽기에 매끈한 답과 기계가 파싱할 수 있는 답은 다른 기준을 요구한다. 따옴표 하나, 쉼표 하나, 중괄호 하나가 어긋나면 downstream 코드는 예외를 던진다. 내용이 좋아도 형식이 깨지면 실패다. 그래서 구조화 출력의 신뢰도는 모델 품질의 일부가 아니라 배포 가능성의 전제 조건에 가깝다. 이 글이 350M이라는 작은 크기에 주목하는 이유도 여기에 있다. 작고 빠르고 싸게 돌릴 수 있는 모델이 형식을 지킬 수 있다면, 많은 현장 파이프라인의 경제성이 달라진다.
작은 모델의 현실을 먼저 짚자. 350M급 모델은 지연 시간과 비용에서 분명한 장점을 가진다. 단일 그래픽 처리 장치에서도 부담 없이 돌고, 동시 요청이 몰려도 큰 모델보다 훨씬 가볍게 확장된다. 엣지나 사내 서버, 비용 민감한 배치 추출 같은 자리에서는 이 가벼움이 곧 선택 이유다. 그러나 기본 상태의 작은 모델은 JSON 유효성에서 들쭉날쭉한 모습을 보인다. 간단한 평탄한 스키마에서는 곧잘 맞히다가도 필드가 늘거나 중첩이 생기거나 열거형 제약이 붙으면 갑자기 무너진다. 닫히지 않은 중괄호, 따옴표로 감싸지 않은 키, 스키마에 없는 필드의 창작, 숫자를 문자열로 쓰는 사소한 어긋남이 반복된다. 프롬프트로 어느 정도 다잡을 수는 있지만, 프롬프트는 모델의 버릇을 바꾸지 못한다. 매 요청마다 긴 형식 지시를 붙이면 입력 토큰이 불어나고, 그래도 긴 출력의 끝부분에서는 형식이 풀어지기 쉽다. 결국 버릇 자체를 조정하는 학습이 필요하다는 것이 이 글의 출발점이다.
구조화된 출력이 작은 모델의 시험대가 되는 이유
구조화된 출력 과제는 언어 능력과 형식 규율을 동시에 요구한다. 모델은 요청의 의미를 이해해야 하고, 그 이해를 미리 정해진 그릇에 정확히 담아야 한다. 이 둘은 서로 다른 종류의 실수를 만든다. 의미 이해가 어긋나면 필드 값이 틀리고, 형식 규율이 어긋나면 파싱 자체가 실패한다. 파싱 실패는 복구가 어렵다. 값이 약간 틀린 경우는 후처리나 부분 재시도로 살릴 수 있지만, JSON이 깨지면 전체 응답을 버려야 한다. 재시도는 지연과 비용을 키운다. 그래서 유효성 비율 몇 퍼센트 포인트의 차이가 운영에서는 크게 체감된다. 80퍼센트 유효성은 다섯 번 중 한 번은 버린다는 뜻이고, 95퍼센트 유효성은 스무 번 중 한 번만 버린다는 뜻이다. 재시도 예산, 타임아웃 설계, 사용자 경험이 이 숫자 위에서 갈린다.
작은 모델이 특히 흔들리는 지점은 길이에 대한 취약성과 세부 제약의 조합이다. 출력이 길어질수록 형식 오류 확률은 누적된다. 앞부분은 멀쩡하다가 뒷부분에서 쉼표를 빠뜨리거나 배열을 닫지 않는 식이다. 또한 스키마가 표현하는 제약은 자연어 프롬프트만으로 전달하기 어렵다. 필수 필드와 선택 필드의 구분, 문자열 패턴, 숫자 범위, 열거값, 중첩 객체의 깊이는 문장으로 풀면 길어지고 모호해진다. 모델은 그 모호한 설명을 매번 해석해야 한다. 학습 과정에서 형식의 문법을 몸에 익힌 모델과, 매번 설명을 읽고 추론하는 모델의 안정성은 다를 수밖에 없다. 이 글의 GRPO 접근은 후자에서 전자로 이동시키는 시도다. 설명을 매번 덧붙이는 대신, 형식을 지키는 행동 자체에 보상을 주어 버릇을 만드는 것이다.
또 하나 짚을 점은 평가의 층위다. JSON 파싱 가능성은 가장 낮은 문턱이다. 파서가 읽을 수 있으면 1단계 통과다. 그 위에는 스키마 유효성이 있다. 타입과 필수 여부와 열거값이 맞아야 한다. 그 위에는 필드 수준의 정합성이 있다. 값이 요청과 사실에 맞는지, 날짜 형식이 맞는지, 단위가 맞는지다. 아래층이 무너지면 위층은 평가 자체가 무의미해진다. 그래서 구조화 출력 학습은 단계적 보상을 설계하기 좋다. 파싱 실패와 스키마 위반과 값 오류를 구분하면 모델에게 어디서 틀렸는지를 알려줄 수 있다. 이 층위 구조가 뒤에 나오는 보상 설계의 뼈대가 된다.
GRPO가 그룹 안에서 배우는 방식
GRPO는 그룹 상대 정책 최적화라는 이름 그대로, 하나의 프롬프트에 대해 여러 완성을 묶어서 상대 평가하는 강화학습 방법이다. 각 프롬프트마다 4개에서 8개의 완성을 샘플링하고, 각 완성에 검증 가능한 보상을 매긴 뒤, 그 그룹 안에서 정규화한 우위를 이용해 정책을 업데이트한다. 잘한 완성의 확률은 올리고 못한 완성의 확률은 내리되, 기준은 그룹 내부의 상대 순서다. 절대 점수가 아니라 같은 프롬프트에서 나온 형제 완성들과의 비교가 학습 신호가 된다. 이 구조 덕분에 별도의 가치 비평가를 둘 필요가 없다. 가치 함수를 학습시키는 부담과 불안정성을 덜고, 보상 자체가 결정적 검증으로 주어지는 과제에서 깔끔하게 동작한다.
왜 그룹 정규화가 중요한지 직관을 풀어보자. 구조화 출력 과제는 프롬프트마다 난이도가 다르다. 어떤 요청은 스키마가 단순해서 대부분의 완성이 통과하고, 어떤 요청은 중첩이 깊어서 대부분이 실패한다. 절대 점수만 보면 쉬운 문제에서의 작은 차이와 어려운 문제에서의 큰 차이가 같은 무게로 취급되기 어렵다. 그룹 안에서 정규화하면 각 프롬프트가 자기만의 채점 곡선을 가진다. 쉬운 문제에서는 완벽에 가까운 완성만 우위를 얻고, 어려운 문제에서는 덜 틀린 완성이 우위를 얻는다. 모델은 문제의 절대 난이도가 아니라 같은 조건에서의 상대적 나음에서 배운다. 배치마다 난이도 분포가 출렁여도 업데이트 방향이 덜 흔들린다. 작은 모델을 짧은 스텝 안에 끌어올려야 하는 이 글의 설정에서는 이런 안정성이 특히 소중하다.
가치 비평가가 없다는 점의 실용적 의미도 크다. 비평가는 또 하나의 학습 대상이고, 그 자체로 메모리와 연산과 조정 부담을 더한다. 350M 정책 모델 옆에 비평가를 두면 단일 그래픽 처리 장치라는 예산이 빠듯해진다. GRPO는 샘플링 비용을 더 쓰는 대신 비평가 비용을 아낀다. 완성을 여러 개 뽑는 것은 연산이 들지만, 구조화 출력에서는 보상 계산이 결정적 코드로 끝나기 때문에 전체 파이프라인이 단순해진다. 파싱하고 스키마로 검증하고 필드를 비교하는 함수가 보상을 내놓고, 그 보상의 그룹 내 순위가 곧 학습 신호다. 단순함은 짧은 학습에서 강점이다. 조정할 다이얼이 적을수록 100 스텝 같은 짧은 예산 안에서 방향을 잃을 가능성이 낮아진다.
물론 그룹 샘플링에도 대가가 있다. 프롬프트당 여러 완성을 뽑으면 단계당 연산량이 늘어난다. 배치 크기와 그룹 크기를 곱한 만큼 생성이 필요하다. 그러나 이 글의 계산은 총량이 아니라 총 시간과 총 자원으로 수렴한다. 스텝 수가 100으로 짧고 단일 장치에서 한 시간 안에 끝난다면, 단계당 약간의 추가 생성은 감당할 만하다. 큰 모델을 며칠 돌리는 것과는 비교가 안 되는 가벼움이다. 요컨대 GRPO는 이 과제에서 합리적인 교환을 제안한다. 비평가를 버리고 샘플을 늘려서, 검증 가능한 보상의 상대 순위로 작은 모델을 빠르게 다잡는 것이다.
350M을 고른 이유와 작음이 주는 이점
350M이라는 숫자는 우연이 아니다. 이 크기는 지연과 비용과 배포 유연성이 만나는 지점에 있다. 큰 모델을 호출할 때마다 지불하는 토큰 단가와 대기 시간을 생각해 보자. 구조화 추출처럼 호출 빈도가 높고 출력 형식이 정해진 과제에서는 건당 비용이 전체 예산을 지배한다. 작은 모델이 형식을 안정적으로 지킨다면, 같은 작업을 10분의 1 이하의 비용으로 처리할 수 있다. 사내망이나 엣지 장치, 데이터 상주 요건이 있는 현장에서는 외부 대형 모델 호출 자체가 어렵다. 작은 모델을 직접 들고 들어가는 편이 유일한 해법인 경우가 많다. 문제는 그 작은 모델의 기본 유효성이 낮다는 것이고, 이 글은 그 간극을 100 스텝으로 메우겠다고 말한다.
작은 모델의 낮은 출발선은 결함이 아니라 조정 여지로 읽을 수 있다. 큰 모델은 이미 방대한 사전학습과 지시 조정으로 형식을 어느 정도 익힌 상태다. 추가 학습의 한계 효용이 작다. 반면 작은 모델은 형식 규율에서 뚜렷한 결핍을 보인다. 잘 설계된 보상만 주면 개선 폭이 크다. 닫힘 실수, 따옴표 실수, 불필요한 설명 덧붙이기 같은 반복 패턴은 강화학습이 잡아내기에 좋은 규칙적 오류다. 모델이 이미 의미를 어느 정도 이해하고 있다면, 남은 일은 출력 버릇을 고치는 것이고, 그 버릇은 검증 가능한 보상으로 직접 두드릴 수 있다. 이 글의 90퍼센트 돌파라는 숫자는 작은 모델의 잠재력이 형식 과제에서는 의외로 크다는 점을 보여준다.
배포 관점에서 350M의 이점은 숫자로 풀어볼 수 있다. 메모리 점유율이 낮으니 배치 크기를 키우거나 같은 장치에 여러 복제본을 둘 수 있다. 처리량이 오르면 피크 시간 대응이 쉬워진다. 지연이 짧으니 에이전트 루프 안에서 여러 번 호출해도 전체 응답 시간이 감당 가능하다. 도구 호출 하나가 느리면 에이전트 전체가 느려진다. 구조화 호출이 빨라지면 에이전트 설계의 자유도가 커진다. 또한 작은 모델은 버전 관리와 롤백이 가볍다. 스키마가 바뀌었을 때 다시 조정하는 비용이 낮으니, 스키마 진화에 맞춰 모델을 함께 진화시키는 운영이 가능해진다. 이 글의 방법이 단일 장치 한 시간이라는 점과 맞물리면, 스키마 변경마다 가볍게 다시 돌리는 그림이 그려진다.
보상이 곧 명세서다
이 접근의 핵심은 보상 함수다. 보상은 모델에게 무엇을 잘했다는 것인지를 알려주는 명세서다. 이 글의 보상은 세 겹의 결정적 검사로 이루어진다. 첫째는 JSON 파싱 가능성이다. 출력이 파서로 읽히는지 본다. 둘째는 JSON 스키마 유효성이다. 타입과 필수 필드와 열거값과 패턴이 맞는지 본다. 셋째는 필드 수준의 정합성이다. 값이 요청에 맞는지, 형식이 아니라 내용이 맞는지를 본다. 세 겹은 위에서 말한 평가의 층위와 정확히 맞물린다. 파싱이 깨지면 낮은 보상, 파싱은 되는데 스키마가 어긋나면 중간 보상, 스키마까지 맞고 값도 맞으면 높은 보상이다. 모델은 이 경사를 타고 형식을 배운다.
결정적 보상이라는 점이 중요하다. 사람의 선호나 거대 심판 모델의 점수가 아니라 코드로 재현 가능한 검사가 점수를 매긴다. 같은 출력에는 항상 같은 보상이 돌아간다. 잡음이 적으니 100 스텝 같은 짧은 학습에서도 신호가 묻히지 않는다. 또한 보상이 과제 명세와 직접 연결된다. 스키마가 바뀌면 검증 코드가 바뀌고 보상도 함께 바뀐다. 학습 목표와 배포 평가가 같은 잣대를 쓴다는 뜻이다. 현장에서 흔한 괴리, 즉 학습 때는 그럴듯한 점수로 오르고 현장에서는 파싱 실패로 무너지는 괴리를 줄이는 구조다. 보상이 명세서라는 말은 그래서 수사가 아니다. 검증 코드를 곧 보상 코드로 쓰는 설계다.
보상 설계에서 세심한 대목은 부분 점수의 기울기다. 전부 아니면 전무 식의 이진 보상만 주면 초기 모델이 너무 자주 0점을 받아 배울 것이 없다. 파싱만 돼도 작은 점수, 스키마 항목이 맞을 때마다 추가 점수, 값이 맞으면 더 큰 점수 같은 식의 단계적 보상이 필요하다. 그래야 실패 속에서도 방향이 보인다. 예를 들어 중괄호를 닫는 법을 익히는 것만으로도 보상이 조금 오르면, 모델은 그 행동을 붙잡고 다음 단계로 나아간다. 반대로 설명 문장을 덧붙이는 버릇은 파싱 실패로 곧바로 벌점을 받으니 빠르게 사라진다. 출력 앞뒤에 붙는 여담, 코드 펜스 남발, 사과 문장 같은 것도 같은 방식으로 정리된다. 보상 곡선의 모양이 학습 속도를 정한다.
한 가지 더 생각할 점은 엄격함과 관대함의 균형이다. 스키마 검증을 너무 엄격하게만 하면 모델이 모험을 멈추고 빈 껍데기만 내놓을 수 있다. 필드를 빼먹으면 파싱은 되지만 필수 필드 누락으로 감점이다. 반대로 없는 필드를 창작하면 스키마 위반이다. 이 두 압박 사이에서 모델은 필요한 필드를 빠짐없이 채우는 쪽으로 움직인다. 값 정합성 검사가 있으면 빈 값이나 자리 표시자를 넣는 꼼수도 감점된다. 꼼수가 막힐 때 진짜 개선이 일어난다. 이 글의 보상이 파싱과 스키마와 값의 세 겹인 이유가 여기에 있다. 각 겹이 서로 다른 꼼수를 막고, 세 겹이 함께 모델을 정직한 완성으로 민다.
100 스텝 설계와 단일 장치 한 시간의 의미
100 스텝이라는 숫자는 짧음의 선언이다. 강화학습 미세조정은 흔히 수천 스텝을 돌린다. 그런데 이 글은 100 스텝으로 충분하다고 말한다. 가능한 이유는 세 가지다. 첫째, 출발 모델이 의미를 이미 안다. 새로 가르치는 것이 아니라 버릇을 고치는 것이어서 필요한 이동 거리가 짧다. 둘째, 보상이 결정적이고 밀도가 높다. 매 스텝마다 파싱과 스키마와 값에서 오는 분명한 신호가 들어온다. 셋째, 그룹 정규화가 분산을 낮춘다. 적은 스텝으로도 방향이 흔들리지 않는다. 세 조건이 겹치면 짧은 학습이 통한다. 길게 돌릴 필요가 없다는 것이 아니라, 짧게 돌려도 배포 가능한 수준에 닿는다는 것이다.
단일 그래픽 처리 장치에서 한 시간 안에 끝난다는 점은 접근성을 바꾼다. 거대 연산 자원이 없어도 시도할 수 있다는 뜻이다. 대학 연구실, 작은 팀, 사내 인프라에서도 재현과 응용이 가능하다. 이 점은 연구의 파급을 키운다. 방법 자체가 좋아도 돌리는 데 수십 장의 장치가 필요하면 현장에서는 그림의 떡이다. 한 장으로 한 시간이라면 이야기가 다르다. 스키마가 바뀌었을 때 다시 돌리고, 도메인이 바뀌었을 때 다시 돌리고, 실패 사례가 쌓였을 때 다시 돌리는 가벼운 반복이 가능해진다. 미세조정이 프로젝트가 아니라 루틴이 되는 것이다.
학습 설계를 실무 언어로 풀어보자. 프롬프트당 4개에서 8개의 완성을 뽑는다는 것은 배치 설계의 핵심 다이얼이다. 숫자가 크면 그룹 내 순위 추정이 안정되지만 생성 비용이 늘어난다. 숫자가 작으면 비용은 줄지만 우위 추정이 흔들린다. 이 글의 범위는 그 사이의 실용 구간을 가리킨다. 굳이 수십 개를 뽑지 않아도 된다는 것이 희소식이다. 또한 스텝 수가 적으니 학습률과 KL 계수 같은 하이퍼파라미터의 부담도 줄어든다. 길게 돌릴수록 후반 붕괴를 걱정해야 하지만, 100 스텝은 초반의 빠른 개선을 먹고 빠지는 전략이다. 과적합이 깊어지기 전에 멈추는 것이다. 짧음이 곧 정칙화 역할을 겸한다.
90퍼센트 돌파를 어떻게 읽어야 하는가
이 글의 결과 숫자, 즉 분포 내 과제에서 유효한 구조화 출력 비율이 90퍼센트를 넘는다는 대목은 careful하게 읽어야 한다. 출발선이 들쭉날쭉했다는 표현이 말해주듯, 조정 전의 작은 모델은 조건에 따라 크게 흔들렸다. 어떤 스키마에서는 그런대로 동작하고, 어떤 스키마에서는 자주 깨졌다. 조정 후에는 그 편차가 줄고 높은 구간에 안착한다. 평균의 상승과 분산의 축소가 함께 일어난다. 운영자 입장에서는 평균보다 분산 축소가 더 반가울 때가 많다. 최악 조건에서의 실패율이 곧 장애 티켓이기 때문이다. 90퍼센트 돌파는 평균의 개선이자 최악의 개선으로 읽는 편이 정확하다.
분포 내라는 단서를 놓치면 안 된다. 학습 때 본 스키마 부류 안에서는 강해졌다는 뜻이지, 처음 보는 생김새의 스키마에서도 같다는 뜻이 아니다. 현장의 스키마는 계속 바뀐다. 필드가 추가되고, 중첩이 깊어지고, 열거값이 늘어난다. 이 방법의 가치는 본 적 있는 부류에서는 확실하고, 본 적 없는 부류에서는 검증이 필요하다는 것이다. 그래서 90퍼센트라는 숫자를 목표가 아니라 출발점으로 보는 태도가 필요하다. 우리 스키마에서의 재현이 본질이다. 공개 글의 숫자는 방향의 증거고, 현장의 숫자는 적용의 증거다. 둘을 같은 것으로 착각하면 도입이 어긋난다.
또 하나, 유효성과 유용성을 구분해야 한다. JSON이 파싱되고 스키마를 통과했다는 것은 기계가 읽을 수 있다는 뜻이지, 값이 옳다는 뜻과 완전히 같지는 않다. 이 글의 보상이 값 정합성까지 본다고 해도, 값의 옳음은 과제마다 다른 기준으로 잰다. 날짜 하나, 단위 하나, 식별자 하나의 오류는 파싱 지표에 잡히지 않고 넘어갈 수 있다. 그래서 도입 평가에서는 세 지표를 함께 봐야 한다. 파싱율, 스키마 통과율, 필드 정합률을 따로 잰다. 셋이 함께 올라야 진짜 개선이다. 파싱율만 오르고 값 정합이 그대로라면 형식은 고쳤지만 내용은 그대로라는 신호다. 이 글의 설계가 세 겹 보상을 쓰는 이유도 같은 맥락이다. 지표도 세 겹으로 봐야 한다.
KL 페널티가 지키는 것
강화학습 조정에서 흔한 실패는 붕괴다. 보상을 올리는 데만 집중하다가 일반 언어 능력을 잃는 것이다. 구조화 과제에서는 그 붕괴가 특정한 얼굴로 나타난다. JSON만 내놓는 앵무새가 되는 것이다. 어떤 요청을 줘도 중괄호부터 쓰고, 설명을 요구해도 키와 값만 늘어놓고, 인사에도 스키마를 덧씌운다. 보상은 올랐지만 모델은 망가졌다. 기준 정책에 대한 KL 페널티는 이 미끄럼을 막는 끈이다. 조정된 정책이 출발 모델에서 너무 멀어지면 벌점을 매겨서, 형식은 바꾸되 언어의 기본기는 유지하게 한다.
KL이라는 말이 어렵게 느껴진다면 닻으로 생각하면 된다. 배가 항구를 떠나되 닻줄 길이 안에서만 움직이는 것이다. 줄이 너무 짧으면 배가 나아가지 못하고, 너무 길면 배가 표류한다. KL 계수가 그 줄의 길이다. 이 글의 100 스텝 설정에서는 줄의 역할이 더 중요하다. 짧은 학습은 초반에 보상이 가파르게 오르기 때문에 모델이 한 방향으로 확 쏠리기 쉽다. JSON 버릇이 과도하게 강화되기 전에 닻이 잡아준다. 덕분에 구조화 호출에서는 형식을 지키고, 일반 대화에서는 평소처럼 말하는 이중 능력을 유지한다. 작은 모델을 특정 용도로 다듬으면서도 범용성을 잃지 않는 균형이다.
실무에서 KL 페널티의 효과는 회귀 테스트에서 확인한다. 구조화 지표만 보지 말고 일반 지시 수행과 간단한 질답도 함께 잰다. 조정 전후의 점수 차가 작으면 닻이 제 역할을 한 것이다. 만약 일반 능력이 떨어졌다면 KL 계수를 올리거나 스텝을 더 일찍 멈추는 쪽으로 조정한다. 반대로 구조화 개선이 더디다면 KL을 조금 풀어 이동을 허용한다. 이 글의 메시지를 운영 언어로 번역하면 이렇다. 보상은 가속 페달이고 KL은 핸들이다. 둘을 함께 잡아야 목적지에 닿는다.
남은 위험과 한계
좋은 결과 뒤에는 항상 그림자가 있다. 이 글이 남긴 위험으로 세 가지를 꼽는다. 리워드 해킹, 학습 스키마 과적합, 새로운 중첩 구조에서의 성능 저하다. 하나씩 풀어보자. 리워드 해킹은 보상의 허점을 파고드는 행동이다. 검증 코드가 잡지 못하는 꼼수로 점수를 올리는 것이다. 예를 들어 값을 비워 두거나 안전한 상용구로 채워서 스키마만 통과시키는 식이다. 파싱과 스키마 검사는 통과했지만 실질 내용은 빈약하다. 보상이 값 정합성까지 본다고 해도, 검증이 보지 않는 구석은 남는다. 검증이 곧 보상인 설계에서는 검증의 빈틈이 곧 학습의 방향이 된다. 그래서 검증 코드를 계속 단단하게 만드는 일이 학습만큼 중요하다.
학습 스키마 과적합은 두 번째 그림자다. 본 적 있는 스키마 부류에서는 90퍼센트를 넘지만, 처음 보는 스키마에서는 숫자가 내려갈 수 있다. 모델이 구조 일반을 배운 것이 아니라 학습 스키마의 버릇을 외운 것일 수 있기 때문이다. 필드 이름의 분포, 중첩 깊이, 열거값 패턴이 바뀌면 외운 버릇이 어긋난다. 현장에서는 스키마가 살아 움직인다. 새 기능이 붙고 새 도구가 생기면 형식이 바뀐다. 과적합된 모델은 그때마다 재조정을 요구한다. 재조정이 한 시간짜리 루틴이라면 감당 가능하지만, 그래도 평가 체계를 갖춰야 한다. 학습에 쓰지 않은 홀드아웃 스키마 묶음을 따로 두고, 그곳에서의 통과율을 매번 함께 재는 것이다. 분포 내 숫자와 분포 외 숫자를 나란히 보는 습관이 과신을 막는다.
세 번째는 중첩 구조의 어려움이다. 평탄한 객체는 쉽지만, 배열 안 객체 안 배열 같은 깊은 중첩은 어렵다. 닫힘 관리가 길어지고, 어느 깊이에 어떤 필드가 속하는지 헷갈린다. 작은 모델의 문맥 관리 능력은 제한적이다. 100 스텝으로 버릇은 고쳤어도, 표현력의 천장이 올라가는 것은 아니다. 깊고 낯선 구조에서는 여전히 실패가 남는다. 이 지점에서는 모델 크기, 출력 길이 제한, 분할 생성 같은 설계적 대응이 필요하다. 너무 깊은 구조를 한 번에 뽑게 하지 말고, 단계별로 나눠서 생성하고 합치는 식이다. 학습만으로 모든 구조 난이도를 해결하려 하지 말고, 과제 분해와 함께 가는 것이다.
현장에서 바로 쓰는 체크리스트
이 글을 현장에 가져가려면 무엇을 하면 될까. 다섯 단계 체크리스트로 정리한다. 첫째, 스키마를 고정하고 검증 코드를 먼저 쓴다. 학습 전에 파서와 스키마 검사와 값 비교 함수를 만든다. 그 코드가 곧 보상이고 곧 평가다. 검증이 흔들리면 학습도 흔들린다. 둘째, 실패를 수집한다. 현재 모델의 파싱 실패와 스키마 위반 사례를 모아 분포를 본다. 어디서 깨지는지를 알아야 보상 가중치를 정할 수 있다. 닫힘 실수가 많으면 파싱 가중을, 없는 필드 창작이 많으면 스키마 가중을 올리는 식이다. 셋째, 그룹 크기를 4에서 8 사이에서 시작한다. 작게 시작해서 안정성을 보고 키운다. 넷째, KL을 걸고 회귀를 함께 잰다. 구조화 지표와 일반 지시 수행을 나란히 본다. 다섯째, 홀드아웃 스키마를 둔다. 학습에 안 쓴 스키마에서의 통과율을 매번 기록한다. 다섯 칸이 다 채워질 때 도입을 결정한다.
프롬프트 습관도 함께 바꿀 필요가 있다. 학습 전에는 긴 형식 지시를 매 요청에 붙였다면, 학습 후에는 지시를 짧게 줄이고 스키마 식별자만 주는 쪽으로 옮긴다. 모델이 버릇을 익혔으니 설명을 반복할 이유가 줄어든다. 입력 토큰이 줄면 비용과 지연이 함께 준다. 출력 쪽에서는 여담 금지, 코드 펜스 금지 같은 후처리 규칙을 유지한다. 학습으로 버릇이 좋아져도 후처리의 안전망은 둔다. 파싱 실패 시의 재시도 정책도 정한다. 온도를 낮춘 1회 재시도, 그래도 실패하면 부분 복구가 아니라 전체 재생성 같은 식이다. 90퍼센트대 유효성에서는 재시도 1회면 체감 실패율이 크게 낮아진다.
팀 운영 관점에서는 작은 실험부터 권한다. 가장 호출 빈도가 높고 스키마가 안정된 파이프라인 하나를 골라 파일럿으로 돌린다. 조정 전후의 파싱율과 스키마 통과율과 값 정합률을 같은 입력 묶음에서 비교한다. 비용과 지연 변화도 함께 기록한다. 숫자가 좋으면 다음 파이프라인으로 넓힌다. 스키마가 자주 바뀌는 곳은 뒤로 미룬다. 안정된 곳에서 이득을 확정하고 움직이는 곳으로 가는 순서다. 이 글의 방법이 가벼운 반복에 강하니, 파일럿에서 쓴 검증 코드와 학습 설정을 자산으로 쌓아두면 다음 스키마에서도 재사용할 수 있다.
작은 모델로 구조를 믿는 법
마지막으로 방향을 정리한다. 이 글이 보여준 것은 크기가 아니라 버릇의 문제라는 점이다. 350M 모델도 무엇을 잘했다는 것인지를 정확히 알려주면, 짧은 시간에 형식을 지키는 법을 배운다. 그룹 안에서 상대 평가하고, 결정적 검증으로 보상하고, 기준 정책에서 너무 멀어지지 않게 붙잡는 조합이 그 학습을 가능하게 한다. 100 스텝과 단일 장치 한 시간이라는 숫자는 그래서 단순한 효율 자랑이 아니다. 작은 팀도 시도할 수 있다는 접근성의 선언이고, 스키마가 바뀔 때마다 다시 돌릴 수 있다는 운영성의 선언이다.
물론 남은 숙제는 분명하다. 검증의 빈틈은 계속 메워야 하고, 본 적 없는 스키마에서의 성능은 계속 재야 하고, 깊은 중첩의 천장은 설계로 보완해야 한다. 그러나 이 숙제들은 막연한 비관이 아니라 구체적인 할 일이다. 검증 코드를 단단히 하고, 홀드아웃 평가를 붙이고, 어려운 구조는 나눠서 생성하면 된다. 할 일이 보인다는 것은 방법이 성숙해졌다는 뜻이다. 구조화 출력이 특수 기술이 아니라 기본 파이프라인이 되는 길목에서, 이 글은 작고 빠른 모델을 믿어도 되는 근거를 하나 추가했다. 다음에 스키마가 바뀌었을 때, 더 큰 모델을 부르기 전에 100 스텝의 조정을 먼저 떠올려 보자. 그 짧은 학습이 현장 파이프라인의 실패율을 바꾸는 가장 싼 방법일 수 있다.
참고 자료
- Fine-tuning a 350M Model for Better Structured Outputs in 100 GRPO Steps · huggingface.co
리뷰 원문