개발팀이 로컬 모델로 돌려 보니 봇이 필수 필드를 빠뜨렸고, 문서에 없는 숙박비까지 답했다. 프롬프트를 손봐 잡아 놨던 실패가 모델을 바꾸자 그대로 되살아나서 개발팀도 맥이 빠졌다. 회의에서 학습을 시켜 보자는 말이 나왔다.
두 실패를 학습 부족 하나로 묶으면 다음 실험의 목적이 흐려진다. 필드 누락은 출력 규칙을 따르는 행동의 문제고, 숙박비 오답은 근거의 범위를 지키지 못한 문제다. 게다가 검색된 문서에 필요한 정보가 없었다는 사실도 함께 봐야 한다. 파인튜닝(fine-tuning)은 이미 학습된 모델의 일부 또는 전체 파라미터를 추가 데이터로 조정하는 일이고, 서비스를 고치는 여러 수단 중 하나다. 개발팀은 학습을 시작하는 코드 대신 학습을 고를 근거와 데이터 검사기, 비교 실험 계획부터 만들었다. 가중치 학습은 실행하지 않았다.
지식 부족과 행동 불일치
학습된 파라미터에는 문서 저장소처럼 특정 정책의 개정 행을 바꾸거나 사용자별 읽기 권한을 거는 인터페이스가 없다. 비공개 정책을 학습한 뒤 출력 단계의 지시문으로 접근을 통제하는 설계도 피한다. 반대로 근거와 질문이 충분한데도 모델이 여러 사례에서 같은 필드를 빠뜨리거나, 정해진 분류 체계를 안 따르거나, 근거 부족 상태를 일관되게 표현하지 못하면 행동 적응을 검토할 수 있다. 그때도 원인은 여럿일 수 있다. 잘못 적용한 채팅 템플릿, 부족한 출력 토큰, 서로 충돌하는 지시, 기반 모델의 한계가 같은 증상을 만든다.
| 관찰한 실패 | 먼저 확인할 항목 | 다음 비교 후보 |
|---|---|---|
| 새 정책 대신 과거 금액을 답함 | 개정·시행일 필터와 실제 전송 문맥 | 데이터 갱신, 검색·문맥 수정 |
| JSON의 필수 필드가 빠짐 | 출력 한도, 템플릿, 스키마 전달 | 제약 생성, 예시 개선, 모델 교체, SFT |
| 인용은 있으나 답과 무관함 | 정답이 문서에서 나오는지 | 근거 판단 평가, 모델 교체, SFT |
| 초안 없이 등록을 시도함 | 서버의 행동 허용 목록 | 업무 제어 코드 수정 |
| 특정 문체만 반복해서 어긋남 | 지시의 모호함과 대표 예시 | 프롬프트 비교, 충분한 예제로 학습 |
SFT는 지도 파인튜닝(supervised fine-tuning)의 약자로 입력과 바람직한 응답의 쌍을 쓴다. 서버의 권한 검사나 멱등성 오류는 그 쌍을 늘려서 풀 수 없다. 모델이 어떤 요청을 내더라도 코드가 업무 규칙을 지켜야 한다.
대안 비교
프롬프트와 예시는 바꾸고 되돌리기 쉽지만 길어지면 입력 비용과 문맥 점유가 는다. 검색은 질문마다 바뀌는 사실을 공급한다. 모델이 그 사실을 제대로 쓰는지는 따로 확인해야 한다. 제약 생성은 지원되는 구조 안에서 출력을 제한하지만 유효한 숫자 필드에 틀린 금액을 넣는 의미 오류는 따로 검사해야 한다. 모델 교체는 데이터 준비 없이 능력을 비교할 수 있지만 배포 자원·지연·비용이 바뀐다. 학습은 반복되는 행동을 파라미터에 반영하는 대신 데이터 작성과 검토, 학습 실행, 회귀 평가, 모델 버전 관리 비용이 붙고, 기반 모델과 어댑터의 조합도 배포 대상이 된다. 짧아진 프롬프트만 보고 전체 비용이 줄었다고 볼 수 없다.
개발팀은 v1 결과를 보고 v2 프롬프트를 고쳤다. 그때 돌린 생성 사례 셋은 프롬프트를 고치는 데 이미 썼으니, 나중에 파인튜닝을 비교하더라도 독립 시험 성적으로 보고할 수 없다. 개발 실패를 고치는 데는 계속 쓰되 최종 판단에는 작성과 선택 과정에서 보지 않은 질문이 필요하다. 보류는 아직 판정하지 않았다는 뜻이다. 개발팀은 비용을 들이기 전에 실패 원인부터 짚고 더 간단한 대안이 통하는지 먼저 보기로 했다.
학습·개발·시험 분할
{
"id": "T01",
"split": "train",
"family": "education",
"question": "교육비 신청 전에 무엇을 해야 하나요?",
"evidence": {"s1": "교육비는 수강 전에 팀장의 승인을 받아야 한다."},
"target": {
"status": "answered",
"answer": "수강 전에 팀장의 승인을 받아야 한다.",
"citations": [{"source_id": "s1", "quote": "수강 전에 팀장의 승인을 받아야 한다."}],
"clarification": null
}
}
학습 데이터의 한 행에는 질문과 함께 모델이 볼 근거, 기대하는 완전한 응답, 식별자, 분할 이름, 같은 출처에서 파생된 계열을 적는다. 이 형식은 손으로 정한 내부 형식이라 학습 서비스에 올리려면 그 서비스의 형식으로 바꿔야 한다.
같은 문의에서 파생된 대화 차례와 요약도 함께 묶는다. 아홉 행은 데이터 구조와 실패 검사만 보여 주는 표본이다. 학습에 쓸 만한 양이 아니고, 저장소에 공개돼 있으니 평가 자료로도 쓸 수 없다. 직접 실험해 보려면 독립 자료를 따로 만들어야 한다.
samples: 9
families: 5
splits: dev=2, test=3, train=4
scope: structure_and_exact_input_leakage_only
검사기는 모든 행의 필드와 응답 상태를 보고, 인용 검사로 근거에 없는 인용을 거부하고, 같은 식별자의 중복, 서로 다른 분할에 걸친 계열, 질문과 근거가 같은 입력을 잡는다. 동일 입력 비교는 유니코드 NFC와 공백을 정규화하고 근거의 식별자·배열 순서를 빼므로 s1을 renamed로 바꿔도 누수를 숨길 수 없다. 정규화한 입력의 해시는 중복을 찾는 데 쓴다. 개인정보를 가리는 용도로 쓰면 안 된다.
바꿔 쓴 근접 중복, 잘못 표시한 계열, 문서 밖의 정답 유출은 이 검사로 못 찾는다. 답변의 뜻도 판정하지 않아서 “사전 승인이 필요하다”를 정확히 인용하면서 “승인 없이 신청할 수 있다”고 답한 표본도 구조 검사는 통과한다. 이 한계는 테스트에 그대로 박아 뒀다. 자동 검사는 구조만 보고, 정답인지는 사람이 따로 검토한다. 실제 데이터에는 사용 권한, 개인정보 최소화, 정답 근거, 검토자 판단과 이견 처리가 더 필요하다. 운영 로그를 자동으로 정답으로 올리거나 사용자의 민감한 내용을 학습 파일에 복사하지 않는다.
SFT와 LoRA
지도 학습은 입력을 조건으로 정답 응답의 토큰이 나올 확률을 높인다. 입력 와 정답 토큰열 를 두고 쓰는 대표적인 손실은 다음과 같다.
학습 시 각 위치에는 앞서 생성한 오답 대신 정답의 앞부분을 조건으로 준다. 손실이 내려갔다는 것은 그 조건에서 정답 토큰의 확률이 올랐다는 뜻이다. 추론에서는 모델 자신의 출력이 다음 입력이 되므로, 학습 손실만으로 자유 생성의 성공을 판정할 수 없다.
채팅 데이터에서는 어느 토큰에 손실을 적용할지도 정한다. 질문과 검색 문맥은 입력으로 쓰고 응답만 학습 대상으로 삼으려면 입력 토큰에 손실을 적용하지 않는 마스크가 필요하다. TRL의 SFT 문서는 completion-only와 assistant-only 학습 설정을 구분하는데, 선택한 버전·데이터 형식·채팅 템플릿이 의도한 마스크를 만드는지 토큰 수준에서 확인한다. JSONL을 학습 입력으로 바꾼다면 서비스와 같은 지시문·근거 직렬화·채팅 템플릿을 쓰고 target을 완전한 JSON 응답으로 직렬화한다. 학습에서는 누락 필드를 허용하고 추론에서는 필수로 요구하는 식의 차이를 만들지 않는다. 너무 긴 표본도 조용히 자르지 않는다. TRL SFT Trainer
전체 파인튜닝은 모델의 전체 파라미터를 갱신하고 LoRA는 기반 가중치를 고정한 채 저랭크 행렬로 표현한 변화량을 학습한다. 행렬을 통째로 갱신하면 학습 대상이 개지만, 변화량을 두 행렬의 곱으로 두면 개가 된다. 학습하고 저장하고 배포할 대상이 그만큼 줄고 기반 모델 하나에 용도별 어댑터를 갈아 끼워 배포할 수도 있다. 대신 표현할 수 있는 변화가 랭크 안에 갇힌다. 랭크를 올리면 그 제약이 풀리는 만큼 학습 대상도 다시 는다. 실제 구현에는 배율과 적용 모듈 설정이 더 있다.
QLoRA는 고정된 기반 모델을 4비트로 두고 어댑터를 학습하며 NF4, 이중 양자화, 페이지드 옵티마이저를 쓴다. “전체 학습이 4비트로 된다”로 요약하면 필요한 메모리와 수치 동작을 오해하기 쉽다. 추론에 모델을 올린 만큼의 메모리로는 학습이 안 된다. 활성값, 기울기, 옵티마이저 상태, 임시 버퍼가 더 들고 배치 크기와 시퀀스 길이가 영향을 준다. LoRA도 기반 모델의 순전파와 필요한 역전파를 없애지 못한다.
이 책의 CPU 환경에서는 LoRA·QLoRA 학습을 실행하지 않았으므로 배치 크기, 학습 시간, 향상률은 없다. 심화 실험을 한다면 기반 모델 리비전, 라이선스, 토크나이저·템플릿, 학습 코드 버전, 데이터 해시, 분할, 난수 시드, 어댑터 대상과 랭크, 학습률, 스텝, 최종 체크포인트를 함께 보관한다. LoRA 원 논문, QLoRA 원 논문
개선·회귀·비용
| 평가 축 | 기록할 결과 | 판단 시 주의점 |
|---|---|---|
| 출력 형식 | 완결된 생성 수, 스키마·상태 검사 통과 수 | 거부된 응답을 분모에서 빼지 않는다 |
| 근거 정확성 | 각 주장을 문서가 뒷받침하는지 | 인용 문자열의 존재와 구분한다 |
| 근거 부족 대응 | 답할 수 없는 질문의 적절한 보류 | 모두 보류해서 점수를 얻지 않게 답변 가능 사례도 평가한다 |
| 권한·업무 규칙 | 무권한 접근과 승인 없는 실행 시도·실제 실행 | 모델 행동과 서버 차단 결과를 따로 적는다 |
| 회귀 | 기존 정상 질문, 상충·개정·공격 사례의 변화 | 전체 평균이 좋아져도 중요한 실패를 숨기지 않는다 |
| 비용·지연 | 학습·검토·배포 비용, 요청당 자원, 지연 분포 | 실패·재시도·운영 부담을 넣는다 |
학습을 도입할지 가리는 실험이라면 시작하기 전에 비교 대상과 채택 조건을 적는다. 기존 프롬프트, 개선 프롬프트 또는 제약 생성, 대체 모델, 학습 후보를 같은 입력과 같은 서비스 검증기로 평가한다. 후보마다 다른 필수 필드나 인용 규칙을 적용하면 비교가 무너진다. 표본이 적으면 성공률 하나보다 성공 건수와 전체 건수, 실패 내용을 함께 읽는다. 3건 중 3건을 통과해도 그 세 사례를 확인한 데 그친다. 필수 필드 누락은 줄었는데 근거 없는 금액 답변이 늘었다면 채택을 보류하고, 학습 데이터가 답변 사례에 치우쳤는지, 보류 사례의 정답이 일관되는지, 에포크가 개발 손실만 낮췄는지 본다.
비용은 일회성 데이터 검토·학습 비용과 지속적인 추론 비용으로 나눈다. 초기 추가 비용이 , 요청당 절감액이 이면 단순 회수 요청 수는 인데, 정기 재학습과 평가, 배포 유지 비용은 이 식에 들어 있지 않으니 따로 더한다. 품질 기준을 넘지 못한 후보라면 이 계산으로 도입을 정당화하지 않는다.
개발팀은 아직 학습을 시키지 않기로 했다. 데이터 규칙과 분할은 갖췄지만 학습 전후를 견줄 기록이 없어서, 지금 도입하면 무엇이 좋아졌는지 아무도 말할 수 없다.