Skip to content
This page has been auto-translated and may contain errors.View in English

파인튜닝

docs.scrimba.com

**파인튜닝**이란 기존 모델을 가져와 여러분이 준비한 예시로 계속 학습시켜서, 모델의 기본 동작이 여러분이 원하는 방향으로 바뀌도록 만드는 것을 말합니다. 이 장에서는 파인튜닝이 실제로 무엇을 바꾸는지, 어디에 유용한지, 그리고 프롬프트나 검색 대신 언제 파인튜닝을 선택해야 하는지 실질적으로 짚어봅니다.

학습이 실제로 하는 일

파인튜닝이 무엇을 바꾸는지 이해하려면, 모델이 어디서부터 시작되는지 알아두면 도움이 됩니다. 모델은 크게 두 단계를 거쳐 만들어집니다.

첫 번째는 **사전학습(pretraining)**입니다. 모델은 엄청난 양의 텍스트를 읽으며 다음 토큰을 예측하는 작업을 끊임없이 반복하고, 그 과정에서 문법, 사실, 추론 패턴이 부수적으로 모델 안에 자리 잡습니다. 이렇게 만들어진 결과물이 베이스 모델로, 유창하지만 방향성이 없습니다. 여러분의 글을 이어서 써주기는 하지만, 질문에 답하는 유용한 어시스턴트가 되도록 훈련받은 것은 아닙니다.

두 번째 단계는 **포스트트레이닝(post-training)**입니다. 베이스 모델은 잘 정제된 좋은 응답 예시들과, 어떤 답변이 더 나은지에 대한 인간 피드백을 바탕으로 추가 학습을 거칩니다. 바로 이 단계가 단순한 텍스트 예측기를 지시를 따르는 어시스턴트로 바꿔주는 과정입니다. 여러분이 API로 호출하는 모델은 이미 이 두 단계를 모두 거친 상태입니다.

두 단계 모두 같은 방식으로 작동합니다. 모델에게 예시를 보여주고 내부 수치, 즉 파라미터 또는 가중치를 조금씩 조정해서 예측값이 그 예시에 더 가까워지도록 만드는 것입니다. 학습이란 예측 연습을 반복하면서 모델을 영구적으로 바꾸는 과정입니다.

Juno학습이 실제로 하는 일 모델은 두 단계를 거쳐 만들어집니다. 사전학습에서는 방대한 텍스트에 대해 다음 토큰을 예측하는 훈련을 시켜 유창하지만 방향성 없는 베이스 모델을 만듭니다. 이어지는 포스트트레이닝에서는 잘 정제된 좋은 응답 예시와 인간 피드백으로 학습시켜 유용한 어시스턴트로 만듭니다. 두 단계 모두 같은 방식으로 작동합니다. 모델이 보는 예시에 예측값이 가까워지도록 내부 가중치를 조정하는 것입니다.

모델은 두 번의 학습 단계를 거친 뒤 여러분에게 도달하며, 각 단계의 형태를 알아두면 파인튜닝이 무엇을 바꿀 수 있는지 파악할 수 있습니다.

**사전학습**이 먼저 이루어집니다. 모델은 방대한 텍스트 더미를 읽으며 다음 토큰을 예측하는 훈련 하나를 수십억 번 반복합니다. 문법, 세상에 대한 지식, 추론 패턴은 그 과정의 부수적인 결과로 가중치 안에 자리를 잡습니다. 그 결과물이 베이스 모델로, 텍스트를 이어 쓰는 데는 능숙하지만 아직 어시스턴트는 아닙니다. 질문을 이어 쓸지, 아니면 질문에 답을 할지 구분하지 못합니다.

포스트트레이닝은 두 번째 단계이며, 파인튜닝이 본뜨는 대상이 바로 이 단계입니다. 베이스 모델은 잘 정제된 좋은 응답 예시와, 어떤 답이 더 나은지에 대한 인간의 선호 신호를 바탕으로 추가 학습을 거칩니다. 바로 이 과정에서 API로 호출했을 때 볼 수 있는, 지시를 따르는 동작이 만들어집니다.

두 단계 모두 동일한 루프로 진행됩니다. 모델에 예시를 하나 입력하고, 예측값을 목표값과 비교한 뒤, 그 차이를 줄이도록 가중치를 조정합니다. 파인튜닝도 이와 똑같은 루프이며, 다만 여러분의 예시로 범위를 좁히고 제공사의 두 단계가 끝난 뒤에 실행된다는 점만 다릅니다. 학습은 룩업 테이블을 추가하는 것이 아니라 가중치를 수정하여 동작을 바꾸는 방식이며, 바로 이 차이 때문에 파인튜닝이 어떤 일에는 잘 맞고 다른 일에는 그렇지 않게 됩니다.

Juno학습이 실제로 하는 일 모델은 두 단계를 거쳐 여러분에게 도달합니다. 사전학습은 다음 토큰 예측을 반복 훈련시켜 유창한 베이스 모델을 만들고, 포스트트레이닝은 잘 정제된 예시와 선호 데이터로 이를 어시스턴트로 다듬습니다. 두 단계 모두 같은 루프로 작동합니다. 예시를 입력하고 그 방향으로 가중치를 조정하는 것이며, 파인튜닝은 이 루프를 여러분의 데이터로 범위를 좁힌 것입니다. 룩업 테이블을 추가하는 게 아니라 가중치를 수정하는 방식이기 때문에, 동작을 바꾸는 데는 강하지만 사실을 저장하는 데는 약합니다.

모델은 두 번의 학습 단계를 거친 뒤 도착하며, 파인튜닝은 바로 그 두 단계의 경계에 자리합니다.

**사전학습**은 모델당 한 번만 이루어지는 값비싼 단계입니다. 거대한 텍스트 코퍼스, 다음 토큰 예측이라는 단일 목표, 그리고 가중치(모델이 조정 가능한 수치)가 그 과정의 부산물로 문법, 지식, 추론을 흡수합니다. 그 결과물이 베이스 모델로, 유창하지만 유용한 방향으로 조정되어 있지는 않습니다. 이어서 포스트트레이닝은 잘 정제된 시연 데이터와 인간 선호 데이터를 이용해 이 베이스 모델을 어시스턴트로 정렬시킵니다. 여러분이 API 모델을 호출할 때는 이 두 단계가 이미 모두 끝난 상태입니다.

파인튜닝은 포스트트레이닝의 연장선으로, 여러분의 데이터에 범위를 맞춰 이미 정렬이 끝난 모델 위에서 실행됩니다. 메커니즘은 완전히 동일합니다. 예시를 제시하고, 예측값이 목표값에서 얼마나 떨어져 있는지 측정하고, 그 차이를 줄이도록 가중치를 조정합니다. 실무적으로 중요한 결과는, 파인튜닝이 다른 모든 것을 지탱하는 것과 동일한 전역 파라미터를 움직인다는 점입니다. 여러분은 새 모듈을 덧붙이는 것이 아니라 공유된 표면을 살짝 밀어내는 것이며, 바로 이 때문에 파인튜닝은 목표 작업을 개선하면서 동시에 관련 없는 능력을 조용히 떨어뜨릴 수 있습니다.

이렇게 표면을 공유한다는 특성이 이 장 뒤에서 다룰 모든 실패 양상, 즉 과적합, 망각, 드리프트의 원인이 됩니다. 파인튜닝은 동작을 재구성할 뿐, 사실 저장소를 설치하는 것이 아닙니다. 이 점을 기억해두면 옳은 이유로 파인튜닝을 선택하고, 잘못된 이유로는 건너뛸 수 있게 됩니다.

Juno학습이 실제로 하는 일 사전학습은 모델당 한 번만 이루어지는 무거운 작업으로 다음 토큰 예측을 베이스 모델에 훈련시키고, 포스트트레이닝은 이를 어시스턴트로 정렬시키며, 파인튜닝은 이 두 번째 단계를 여러분의 데이터로 이어서 진행하는 것입니다. 매번 같은 루프입니다. 예시를 제시하고 그 방향으로 가중치를 조정합니다. 문제는 새 모듈을 덧붙이는 것이 아니라 공유된 전역 파라미터를 밀어낸다는 점이며, 그래서 한 번의 파인튜닝 실행으로 목표 작업이 좋아지면서 관련 없는 작업이 나빠질 수 있고, 바로 여기서 과적합과 망각이 생겨납니다.

파인튜닝이란 무엇인가

파인튜닝은 앞서 말한 두 번째 단계를 여러분이 직접, 작고 목표가 분명하게 실행하는 것입니다. 이미 학습이 끝난 모델을 가져와, 여러분만의 예시 집합으로 계속 학습시킵니다. 각 예시는 입력과, 모델이 그 입력에 대해 냈으면 하는 출력의 짝으로 이루어져 있습니다. 충분한 예시를 거치면 모델의 가중치가 바뀌어서, 매번 프롬프트로 요청하지 않아도 이런 종류의 응답이 모델의 기본값이 됩니다.

프롬프팅이나 RAG와의 진짜 차이는 변화가 어디에 저장되는지에 있습니다. 프롬프팅과 RAG는 모델을 그대로 둔 채, 호출하는 순간 함께 제공하는 컨텍스트로 방향을 조정합니다. 반면 파인튜닝은 모델 자체를 바꾸기 때문에, 새로운 동작이 모델에 새겨져서 짧은 프롬프트로도 그대로 나타납니다. 실행 시점에 더 좋은 지시를 주는 것이 아니라, 이미 원하는 방향으로 기울어진 모델을 아예 새로 내놓는 것입니다.

실제로는 예시 대화가 담긴 파일을 제공사에 넘기면, 그쪽에서 학습 작업을 실행한 뒤 새로운 **모델 id**를 돌려줍니다. 이 id는 다른 모델과 똑같이 호출하면 되는데, 다만 그 응답이 이제 여러분이 학습시킨 내용을 반영합니다. 파인튜닝은 동작을 모델 안으로 옮기는 것이고, 프롬프팅과 RAG는 고정된 모델을 조종하는 것입니다.

Juno파인튜닝이란 무엇인가 파인튜닝은 여러분이 직접 실행하는, 하나의 작업에 맞춘 작은 포스트트레이닝입니다. 이미 학습된 모델을 여러분의 입력-출력 예시로 계속 학습시켜서, 원하는 동작이 기본값이 될 때까지 반복합니다. 고정된 모델을 컨텍스트로 조종하는 프롬프팅과 RAG와 달리, 파인튜닝은 모델의 가중치를 바꾸기 때문에 동작이 새겨져서 짧은 프롬프트에서도 나타납니다. 예시 파일을 넘기면 새 모델 id를 받아 다른 모델처럼 호출하면 됩니다.

파인튜닝은 하나의 작업으로 범위를 좁혀 여러분이 직접 실행하는 포스트트레이닝입니다. 이미 학습이 끝난 모델에서 시작해서, 각각 입력과 그에 대해 원하는 출력을 짝지은 예시로 계속 학습시킵니다. 충분한 양을 학습시키면 가중치가 자리를 잡아 해당 응답이 기본값이 되고, 프롬프트에 그 응답을 요청하는 지시를 넣을 필요가 없어집니다.

여기서 중요한 경계는 조종하는 힘이 어디에 있느냐입니다. 프롬프팅과 RAG는 가중치를 그대로 둔 채 호출 시점에 제공하는 컨텍스트로 동작을 만들기 때문에, 매 호출마다 지시를 함께 실어 보내야 합니다. 반면 파인튜닝은 처음에 한 번 가중치를 수정해서 동작이 모델 id 안에 실려 있게 하고, 짧은 프롬프트에서도 그대로 유지됩니다. 매번 조종하는가, 아니면 미리 새겨두는가. 바로 이것이 여러분이 감당해야 할 트레이드오프입니다.

작업 흐름은 구체적입니다. 예시 대화가 담긴 파일을 만들고, 학습 작업을 시작하면, 제공사가 새로운 모델 id를 돌려줍니다. 그 id를 다른 모델과 똑같이 호출하면 됩니다.

python
# 각 예시는 실행 시점에 보내는 것과 정확히 같은 형태의 대화로,
# 모델이 배웠으면 하는 출력으로 끝맺습니다.
training_examples = [
    {"messages": [
        {"role": "system", "content": "티켓의 긴급도를 low, medium, high 중 하나로 분류하세요."},
        {"role": "user", "content": "이번 주에 두 번 결제됐는데 오늘 안에 해결해주세요."},
        {"role": "assistant", "content": "high"},
    ]},
    {"messages": [
        {"role": "system", "content": "티켓의 긴급도를 low, medium, high 중 하나로 분류하세요."},
        {"role": "user", "content": "다크 모드 설정이 어디 있나요?"},
        {"role": "assistant", "content": "low"},
    ]},
    # ... 예상되는 입력 범위를 폭넓게 다루는 수백~수천 개의 예시가 더 필요합니다
]
# 메시지 형태와 학습 작업 API는 제공사마다 다르지만, 아이디어는 동일합니다.
Juno파인튜닝이란 무엇인가 파인튜닝은 여러분의 작업에 범위를 맞춘 포스트트레이닝입니다. 입력-출력 예시로 모델을 계속 학습시켜서 원하는 동작이 기본값이 될 때까지 반복합니다. 프롬프팅과 RAG는 매 호출마다 컨텍스트로 고정된 모델을 조종하지만, 파인튜닝은 가중치를 한 번 수정해서 동작이 모델 id 안에 실리게 합니다. 예시 대화 파일을 만들어 학습 작업을 돌리고, 돌려받은 id를 다른 모델처럼 호출합니다.

파인튜닝은 여러분이 관리하는 데이터셋으로 포스트트레이닝을 이어서 실행해, 새 id 뒤에 파생 모델을 만들어내는 것입니다. 각 학습 예시는 입력과 목표 출력의 짝이며, 이 작업은 가중치를 조금씩 밀어서 여러분이 원하는 응답이 모델의 기본값이 되도록 합니다. 동작이 가중치 안에 실려 있기 때문에 짧은 프롬프트만으로도 그 동작을 끌어낼 수 있습니다.

이것을 대안들과 정확히 대조해봐야 하는데, 그 차이는 철학적인 것이 아니라 실무적인 것이기 때문입니다. 프롬프팅과 RAG는 가중치를 그대로 둔 채 컨텍스트(모델이 호출 시점에 읽는 텍스트)를 통해 동작을 주입하므로, 문자열 하나를 수정하고 몇 초 안에 재배포하는 것만으로 동작을 바꿀 수 있습니다. 반면 파인튜닝은 동작을 가중치 안에 새겨 넣으므로, 이를 바꾸려면 새로운 학습 실행, 평가 과정, 그리고 새 모델 id의 재배포가 필요합니다. 실행 시점 조종은 몇 초 만에 수정할 수 있지만, 파인튜닝은 학습 사이클 전체를 거쳐야만 수정할 수 있습니다.

python
# 파인튜닝된 모델도 또 다른 id일 뿐, 호출 형태는 바뀌지 않습니다.
response = client.chat.completions.create(
    model="your-org/urgency-classifier-v1",  # 이 값을 고정하고, 계약의 일부로 취급하세요
    messages=[
        {"role": "system", "content": "티켓의 긴급도를 low, medium, high 중 하나로 분류하세요."},
        {"role": "user", "content": "내보내기 버튼을 눌러도 아무 반응이 없어요."},
    ],
)
# response.choices[0].message.content -> "low"  (레이블만 반환, 학습으로 새겨넣은 동작)
# 위 호출 형태는 OpenAI SDK 스타일이며, 제공사마다 다를 수 있습니다.

이 비용의 비대칭성이야말로, 파인튜닝을 결정하기 전에 몸에 새겨둬야 할 사실입니다. 파인튜닝된 id는 버전이 관리되는 산출물입니다. 그 생애주기, 평가, 그리고 원하는 동작이 바뀔 때마다 필요한 재학습까지 모두 여러분이 책임져야 합니다. id를 고정하고, 버전을 매기고, 학습된 내용이 바뀔 때마다 발생하는 비용을 새로운 작업으로, 즉 단순한 커밋이 아니라는 사실을 감안해 예산을 세우세요.

Juno파인튜닝이란 무엇인가 파인튜닝은 여러분의 데이터로 포스트트레이닝을 이어서 실행해, 동작이 가중치 안에 살아 있는 파생 모델을 새 id 뒤에 만들어냅니다. 프롬프팅과 RAG와의 대조는 실무적입니다. 실행 시점 조종은 몇 초 안에 재배포하는 편집 가능한 문자열이지만, 파인튜닝은 학습 실행, 평가 과정, 재배포를 거쳐야만 바꿀 수 있는 버전 관리 대상 산출물입니다. id를 고정하고 계약처럼 취급하며, 동작이 바뀔 때마다 새로운 작업이 필요하다는 점을 예산에 반영하세요.

무엇에 강하고, 무엇에 약한가

파인튜닝이 제 역할을 하는 지점은 프롬프트로는 붙잡기 어려운 **일관된 동작**이 필요할 때입니다. 매번 정확한 출력 형식을 지키게 하거나, 특정 어조나 사내 스타일을 유지하거나, 분류 같은 좁은 작업을 수천 번의 호출에서도 안정적으로 처리하게 만드는 경우입니다. 또한 호출을 더 저렴하고 빠르게 만들 수도 있습니다. 길게 프롬프트로 설명해야 했을 동작이 모델 안에 이미 새겨져 있어서, 프롬프트가 짧아지기 때문입니다.

파인튜닝이 약한 지점은 지식입니다. 파인튜닝은 모델에게 새로운 사실을 가르치는 데 서투르며, 시간이 지나 바뀌는 사실에는 더더욱 약합니다. 학습 예시들은 정확히 조회 가능한 항목으로 저장되는 게 아니라 일반적인 패턴으로 뒤섞여버리기 때문에, 파인튜닝된 모델도 여전히 세부 사항을 지어내고, 정보가 바뀌는 순간 곧바로 낡아버립니다. 가격이나 정책이 바뀔 때마다 재학습을 하는 건 느리고 비용도 많이 듭니다. "모델이 X를 모른다"는 문제라면, 답은 거의 항상 파인튜닝이 아니라 RAG입니다.

기억할 원칙은 이것입니다. 파인튜닝은 형태를 바꾸기 위한 것이지, 사실을 추가하기 위한 것이 아닙니다. 모델에게는 어떻게 답할지를 가르치고, 무엇으로 답할지는 검색이 맡도록 하세요.

Juno무엇에 강하고, 무엇에 약한가 파인튜닝은 일관된 동작에 강합니다. 고정된 출력 형식, 특정 어조, 안정적으로 처리하는 좁은 작업이 그 예이며, 프롬프트를 줄여 호출 비용도 낮출 수 있습니다. 반면 지식에는 약합니다. 사실을 안정적으로 저장하지 못하고, 정보가 바뀌면 낡아버리며, 재학습 비용도 큽니다. 원칙은 파인튜닝으로는 형태를 바꾸고, 사실은 추가하지 말며, 사실은 RAG에 맡기는 것입니다.

파인튜닝이 효과를 내는 지점은 **형태**입니다. 매 호출마다 정확한 출력 모양을 유지하거나, 고정된 어조나 사내 스타일을 지키거나, 분류처럼 좁은 작업을 수천 번의 요청에서도 안정적으로 유지하는 경우입니다. 프롬프팅만으로는 계속 흔들릴 때, 즉 모델이 대체로는 형식을 지키지만 이후 단계에서 파싱할 만큼 믿을 수 없을 때, 이 효과는 더욱 커집니다. 동작을 모델에 새겨 넣으면, 그 형식이 매 호출마다 지켜지길 바라는 상태에서 벗어날 수 있습니다.

또한 프롬프트를 짧게 만들어주는데, 규모가 커지면 이건 실질적인 비용 절감 수단이 됩니다. 지금 매 요청마다 형식과 몇 가지 예시를 프롬프트에 풀어 써야 한다면, 그 지시 토큰들은 호출마다 비용으로 청구됩니다. 이를 가중치 안으로 옮기면 호출당 프롬프트가 줄어듭니다. 이 계산은 실제로 따져볼 만합니다. 파인튜닝은 한 번만 지불하는 고정된 학습 비용을 가지고, 긴 프롬프트는 영원히 지불해야 하는 호출당 비용을 가지므로, 절약된 토큰이 학습 비용을 뛰어넘을 만큼 호출량이 충분할 때 프롬프트 축소가 이득이 됩니다.

파인튜닝이 약한 지점은 지식입니다. 학습 예시들은 정확히 조회 가능한 항목이 아니라 일반적인 패턴으로 뒤섞여버리기 때문에, 파인튜닝된 모델도 여전히 세부 사항을 지어내고, 가격이나 정책이 바뀌는 순간 곧바로 낡아버립니다. 사실을 갱신하려면 또 다른 학습 실행이 필요합니다. 형태는 파인튜닝으로, 사실은 RAG로 처리하고, 이 둘을 결합하세요. 강력한 패턴은 둘을 동시에 쓰는 것입니다. 형식과 어조에 맞춰 모델을 파인튜닝한 뒤, 실행 시점에 최신 사실을 검색해서 파인튜닝된 모델이 그것을 표현하게 하는 방식입니다.

파인튜닝으로 넘어가기 전에, 프롬프팅이 정말로 실패했는지부터 확인하세요. "실패했다"는 것은 명확한 지시와 잘 고른 예시 몇 개로, 다양한 입력을 두고 테스트해봐도 목표 정확도에 이르지 못한다는 뜻이며, 처음 대충 써본 한 줄짜리 프롬프트가 별로였다는 뜻이 아닙니다. 이 테스트를 해보지 않았다면, 아직 파인튜닝을 할 준비가 되지 않은 것입니다.

Juno무엇에 강하고, 무엇에 약한가 파인튜닝은 형태에 강합니다. 고정된 출력 모양, 일정한 어조, 안정적으로 유지되는 좁은 작업, 그리고 호출마다 토큰을 아껴주는 짧은 프롬프트가 그 예입니다. 비용 계산은 한 번만 내는 학습 비용과 영원히 내야 하는 프롬프트 토큰 비용의 대결이므로, 호출량이 많을 때 프롬프트 축소가 이득이 됩니다. 사실에는 약합니다. 사실은 패턴으로 뒤섞여 낡아버리므로, 파인튜닝된 모델과 RAG를 함께 쓰세요. 형태는 가중치에서, 사실은 검색에서 나오게 하는 것입니다. 그리고 올라가기 전에, 예시를 포함한 실제 프롬프트를 다양한 입력으로 테스트해서 정말로 프롬프팅이 실패했는지 증명하세요.

파인튜닝은 형태에는 안정적이지만 사실에는 그렇지 못하며, 그 이유는 기계적인 것입니다. 형태, 즉 출력 모양, 어조, 작업 관행은 많은 예시에 걸쳐 가중치가 흡수할 수 있는 행동적인 규칙성입니다. 반면 사실은 개별적이고 변할 수 있는 것이며, 가중치는 이를 조회 가능한 행으로 저장하지 않고 뒤섞인 패턴으로 손실이 있는 채로 저장하므로, 파인튜닝된 모델도 여전히 사실을 지어내고 밑바탕이 되는 사실이 바뀌는 순간 곧바로 낡아버립니다.

이 큰 줄기를 넘어서 보면, 진짜 위험은 파인튜닝이 성능을 떨어뜨리는 방식들에 있습니다. **과적합(overfitting)**은 모델이 일반적인 동작을 배우는 대신 학습 데이터셋을, 그 안의 특이한 점까지 포함해, 그대로 외워버리는 현상입니다. 그래서 여러분의 예시는 완벽하게 맞히지만 조금이라도 다른 입력은 헤매게 됩니다. **파괴적 망각(catastrophic forgetting)**은 그 반대편에 있습니다. 좁은 작업에 강하게 학습시키면 베이스 모델이 가지고 있던 관련 없는 일반 능력이 깎여나갈 수 있는데, 이는 공유된 가중치를 움직이고 있기 때문입니다. 즉 분류기는 더 정교해지지만 다른 모든 일에 대한 추론 능력은 무뎌집니다. **분포 드리프트(distribution drift)**는 천천히 진행되는 문제입니다. 지난 분기의 입력 분포에 맞춰 튜닝된 모델은 실제 트래픽이 학습 당시 보지 못한 방향으로 옮겨가면서 점점 성능이 떨어지는데, 이 저하는 측정하기 전까지는 조용히 진행됩니다.

두 가지 결과가 실제 운영 방식을 좌우합니다. 첫째, 감으로 파인튜닝을 배포할 수는 없습니다. 정답을 아는 입력들로 이루어진 **평가 세트(eval set)**를 별도로 준비해서, 학습 전과 후에 각각 채점해 목표 작업이 실제로 개선되었는지, 그리고 그만큼 중요한, 일반 능력이 퇴보하지 않았는지를 증명해야 합니다. 둘째, 원하는 동작이 바뀔 때마다 데이터를 다시 정제하고, 재학습하고, 재평가하고, 새 id를 재배포하는 완전한 작업 하나가 다시 필요합니다. 이 운영상의 부담이야말로, 파라미터 효율적인 방법들이 존재하는 이유입니다. 전체 모델이 아니라 추가된 소수의 가중치만 학습시켜 더 저렴하게 실행하고 저장하는 방식이지만, 파인튜닝을 할지 말지에 대한 판단 자체는 이런 방법으로도 바뀌지 않습니다. 어느 쪽이든, 배포하기 전에 파인튜닝이 성능을 퇴보시키지 않았음을 증명해야 합니다.

Juno무엇에 강하고, 무엇에 약한가 형태는 가중치에 잘 흡수되지만, 사실은 그렇지 않습니다. 파인튜닝된 모델도 여전히 사실을 지어내고 낡아버리므로, 사실은 검색에 맡기세요. 위험 요소는 과적합(학습 데이터를 외워서 새로운 입력에 헤매는 것), 파괴적 망각(가중치가 공유되어 있어 좁은 작업 학습이 관련 없는 능력을 깎아먹는 것), 그리고 실제 트래픽이 학습 데이터에서 벗어나면서 생기는 분포 드리프트입니다. 그러므로 학습 전후에 별도의 평가 세트로 채점해 성능 퇴보가 없음을 증명하는 것을 모든 파인튜닝의 관문으로 두고, 동작이 바뀔 때마다 전체 재학습-재평가-재배포 사이클이 필요하다는 것을 예산에 반영하세요. 바로 이것이 파라미터 효율적인 방법들이 존재하는 이유입니다.

프롬프트, RAG, 아니면 파인튜닝?

대부분의 경우 여기서부터 시작하지 않습니다. 이 세 가지 기법은 **사다리**를 이루며, 바꾸기 가장 쉽고 빠른 것부터 순서대로 놓입니다.

  1. 프롬프팅. 항상 이것부터 시도하세요. 즉시 반복해볼 수 있고 추가 비용이 들지 않으며, 명확한 프롬프트에 예시 몇 개만 더해도 사람들이 생각하는 것보다 훨씬 많은 문제가 풀립니다. 프롬프트를 제대로 작동시키지 못했다면, 아직 파인튜닝을 할 준비가 되지 않은 것입니다.
  2. RAG. 문제가 지식일 때, 즉 모델이 가지고 있지 않거나 시간에 따라 바뀌는 사실이 필요할 때 이것을 선택하세요. RAG는 모델을 손대지 않고 질문 시점에 그 사실을 제공합니다.
  3. 파인튜닝. 문제가 지식이 아니라 동작이고 프롬프팅으로는 충분히 일관되게 만들 수 없을 때, 또는 프롬프트가 너무 길어져서 규모가 커졌을 때 지시를 모델 안에 새겨 넣는 것이 더 저렴하고 빠를 때 이것을 선택하세요.

이 세 가지는 서로 배타적이지 않으며, 가장 강력한 시스템은 이들을 결합합니다. 형식과 어조에 맞춰 모델을 파인튜닝한 뒤, 실행 시점에 최신 사실을 공급하기 위해 RAG를 사용하는 방식입니다. 동작은 학습에서, 지식은 검색에서 얻는 것입니다.

그러니 파인튜닝을 하기 전에 간단한 테스트를 해보세요. 더 나은 프롬프트로 도달할 수 있나요? 그렇다면 그렇게 하세요. 문제가 지식 부족인가요? 그렇다면 RAG입니다.

사다리를 올라가세요. 파인튜닝은 마지막 단이지, 첫 번째 단이 아닙니다. 프롬프트가 이미 제대로 되어 있고, 사실도 이미 마련되어 있는데도 모델이 여전히 충분히 일관되게 동작하지 않을 때에야, 파인튜닝이 딱 맞는 도구가 됩니다.

Juno프롬프트, RAG, 아니면 파인튜닝? 이 세 가지를 사다리로 다루세요. 즉시 저렴하게 시도할 수 있으니 프롬프트부터 시작하고, 모델이 지식을 모를 때 RAG를 더하고, 프롬프팅으로 일관되게 만들 수 없는 동작 문제일 때, 혹은 규모가 커진 상황에서 긴 프롬프트를 줄이고 싶을 때에만 파인튜닝을 하세요. 이들은 함께 잘 어울립니다. 형태는 파인튜닝으로, 사실은 RAG로 처리하세요. 파인튜닝은 첫 번째로 손을 뻗을 도구가 아니라 마지막에 손을 뻗을 도구입니다.

이 세 가지 기법은 **사다리**를 이루며, 바꾸는 데 얼마나 빠르고 저렴한지에 따라 순서가 정해집니다. 그리고 그 순서는 아래에서부터 올라갑니다.

  1. 프롬프팅. 항상 먼저입니다. 추가 학습 비용 없이 몇 초 안에 반복해볼 수 있고, 명확한 지시에 예시 몇 개를 더하면 사람들이 생각하는 것보다 훨씬 많은 문제를 해결합니다. 실제 프롬프트를 테스트해서 부족함이 드러나기 전까지는, 그 위의 단들은 아직 이를 곳이 아닙니다.
  2. RAG. 문제가 지식일 때, 즉 모델이 갖고 있지 않거나 시간에 따라 바뀌는 사실이 필요할 때, 검색이 질문 시점에 그 사실을 제공하고 가중치는 그대로 둡니다. 사실을 갱신하는 일은 재학습이 아니라 데이터 원본을 갱신하는 일이 됩니다.
  3. 파인튜닝. 문제가 프롬프팅으로 붙잡을 수 없는 동작일 때, 또는 프롬프트가 충분히 길어져서 규모가 커진 상황에서는 지시를 가중치에 새겨 넣는 것이 더 저렴할 때 선택합니다.

올바른 단에 서게 만드는 진단은, 문제의 정체를 정확히 짚어내는 것입니다. 잘못된 형식이나 어조는 동작 문제이므로, 프롬프트를 손보고 그다음 파인튜닝을 하면 됩니다. 빠져 있거나 낡은 사실은 지식 문제이므로, 검색을 이용하면 됩니다. 문제를 잘못 진단하면, 지식 문제를 고치려고 파인튜닝을 했다가 결과가 곧 낡아버리는 것을 지켜보게 되거나, 검색 단계가 가져와야 할 사실을 프롬프트에 억지로 채워 넣게 됩니다.

이들은 서로 배타적이지 않으며, 프로덕션에서 손을 뻗어야 할 방식은 이 둘을 결합하는 것입니다. 형태는 파인튜닝으로, 사실은 RAG로, 함께 사용하세요. 형식과 어조에 맞춰 튜닝된 모델에 실행 시점에 최신 사실을 공급하면, 어느 한쪽 기법만으로는 얻을 수 없는 일관된 형태와 신선한 내용을 동시에 얻을 수 있습니다.

파인튜닝을 할 수 있는지 여부는 애초에 모델에 따라 다릅니다. 클로즈드 제공사는 자체 모델에 대해 관리형 파인튜닝을 제공하고, 오픈 웨이트 모델은 여러분이 원하는 조건대로 직접 파인튜닝할 수 있습니다. 바로 이것이 오픈 웨이트를 선택하는 이유 중 하나입니다(오픈 모델과 클로즈드 모델).

Juno프롬프트, RAG, 아니면 파인튜닝? 가장 저렴한 단부터 사다리를 오르세요. 프롬프트를 먼저 하고, 문제가 지식일 때 RAG를 더하고, 프롬프팅으로 붙잡을 수 없는 동작 문제일 때나 규모가 커져 프롬프트를 줄이고 싶을 때만 파인튜닝을 하세요. 진단은 문제의 정체를 짚는 것입니다. 형식이나 어조는 동작을 뜻하고, 빠지거나 낡은 사실은 검색을 뜻합니다. 프로덕션에서 취할 방식은 둘을 함께 쓰는 것입니다. 형태는 파인튜닝으로, 사실은 RAG로, 그러면 일관된 형태와 신선한 내용을 동시에 얻을 수 있습니다.

프롬프트, RAG, 파인튜닝 순서로 이어지는 이 사다리는 반복 비용(iteration cost), 즉 배포한 뒤 동작을 바꾸는 데 얼마나 빠르고 저렴한지에 따라 정렬되어 있습니다. 프롬프트는 몇 초 안에 편집하고 재배포할 수 있는 문자열입니다. 검색 소스는 모델을 손대지 않고 갱신할 수 있는 데이터입니다. 파인튜닝은 학습 실행, 평가 과정, 새 모델 id를 필요로 합니다. 이 순서대로 올라간다는 것은, 더 저렴한 단들이 확실히 목표에 이르지 못한다는 것이 증명되었을 때에만 더 높은 반복 비용을 지불한다는 뜻입니다.

진짜 실력은 문제를 정확히 진단하는 데 있습니다. 실패 양상은 대개 문제와 맞지 않는 도구를 고르는 데서 나옵니다. 동작 문제(잘못된 형태, 잘못된 어조, 일관되지 않은 작업 준수)는 프롬프팅과 그다음 파인튜닝으로 고칩니다. 지식 문제(모델이 갖고 있지 않은 사실, 또는 바뀌는 사실)는 검색으로 고칩니다. 지식 문제를 파인튜닝으로 고치려는 것은 전형적으로 비용이 큰 실수인데, 사실을 학습으로 새겨 넣어도 다음 갱신 시점에는 이미 낡아버리고, 그 출처를 인용할 수도 없기 때문입니다.

이 진단 옆에 운영상의 비대칭성을 함께 두고 봐야 합니다. RAG의 반복되는 비용은 매 호출마다 발생하는 검색 품질과 지연 시간이며, 이는 영원히 지불해야 하지만 실시간으로 조정 가능합니다. 파인튜닝의 반복되는 비용은 학습된 동작이 바뀔 때마다 다시 실행해야 하는 것이고, 게다가 매 실행마다 회귀 위험까지 함께 짊어져야 합니다.

그러므로 이 둘을 결합한 아키텍처는 타협이 아니라 설계 그 자체입니다. 형태는 가중치에 튜닝해 넣고, 사실은 검색에 남겨두며, 각각을 독립적으로 버전 관리하세요. 형태는 드물게 바뀌고 느린 갱신 주기를 견딜 수 있는 반면, 사실은 끊임없이 바뀌고 빠른 갱신 주기를 요구합니다. 이 둘을 분리하면 각자가 자기 속도대로 움직일 수 있게 되고, 코퍼스를 다시 인덱싱하지 않고도 튜닝된 모델을 재평가할 수 있으며, 학습 작업을 다시 돌리지 않고도 코퍼스를 갱신할 수 있습니다.

Juno프롬프트, RAG, 아니면 파인튜닝? 사다리는 반복 비용에 따라 정렬됩니다. 프롬프트는 몇 초 안에 편집할 수 있고, 소스는 모델을 건드리지 않고 갱신할 수 있으며, 파인튜닝은 완전한 학습-평가-재배포 과정을 거쳐야 합니다. 고르기 전에 문제를 진단하세요. 동작은 프롬프팅 다음에 파인튜닝으로, 지식은 검색으로 향합니다. 지식 문제를 파인튜닝으로 고치는 것은 낡고 인용할 수 없는 사실을 내놓는 비용이 큰 실수입니다. 프로덕션 아키텍처는 이 둘을 의도적으로 분리합니다. 형태는 가중치에 튜닝해 넣고, 사실은 검색에 남겨두며, 각각을 독립적으로 버전 관리해서 형태는 느리게, 사실은 빠르게 움직이도록 하는 것입니다.

실제로 해보기

파인튜닝 데이터는 특별한 것이 아닙니다. 각 예시는 실행 시점에 보내는 것과 정확히 같은 형태의 짧은 대화이며, 모델이 냈으면 하는 답으로 끝맺습니다.

python
# 여기서는 몇 개만 보여드리지만, 실제 데이터셋은 실행 시점에 예상되는
# 입력 범위를 폭넓게 다루는 수백~수천 개의 예시로 이루어집니다.
training_examples = [
    {"messages": [
        {"role": "system", "content": "티켓의 긴급도를 low, medium, high 중 하나로 분류하세요."},
        {"role": "user", "content": "이번 주에 두 번 결제됐는데 오늘 안에 해결해주세요."},
        {"role": "assistant", "content": "high"},
    ]},
    {"messages": [
        {"role": "system", "content": "티켓의 긴급도를 low, medium, high 중 하나로 분류하세요."},
        {"role": "user", "content": "다크 모드 설정이 어디 있나요?"},
        {"role": "assistant", "content": "low"},
    ]},
    # ...
]

이것을 파일로 저장해서 제공사에 학습 작업을 요청하고, 끝날 때까지 기다립니다. 돌아오는 것은 새로운 모델 id이며, 이제 다른 모델과 똑같이 호출하면 됩니다.

python
# 파인튜닝된 모델도 또 다른 id일 뿐, 나머지는 그대로입니다.
response = client.chat.completions.create(
    model="your-org/urgency-classifier-v1",
    messages=[
        {"role": "system", "content": "티켓의 긴급도를 low, medium, high 중 하나로 분류하세요."},
        {"role": "user", "content": "내보내기 버튼을 눌러도 아무 반응이 없어요."},
    ],
)
# response.choices[0].message.content -> "low"  (레이블만 반환, 학습으로 새겨넣은 동작)

얻는 것은 동작이 이제 모델 안에 자리 잡았다는 점입니다. 지시는 더 짧아지고, 형식은 수천 번의 호출에서도 유지되며, 그러기 위해 프롬프트를 늘릴 필요도 없습니다. 반대로 감당해야 할 것은 그 주변의 모든 것입니다. 좋은 품질의 **데이터셋**이 필요하고, 학습 작업을 돌려야 하며, 학습된 내용을 바꾸고 싶을 때마다 새로운 작업이 필요합니다. 바로 이것이 파인튜닝이 사다리의 맨 아래에 놓이는 이유입니다. 예시를 업로드하고, 모델 id를 받고, 다른 모델처럼 호출하세요.

Juno실제로 해보기 파인튜닝 데이터는 원하는 답으로 끝나는 예시 대화들로, 수백에서 수천 개 정도 필요합니다. 파일을 업로드하고 학습 작업을 돌리면 다른 모델처럼 호출할 수 있는 새 모델 id를 받습니다. 이후 동작이 모델에 새겨지므로 프롬프트가 짧아지고 형식이 일관되게 유지되지만, 그 대가로 데이터셋을 만들고 바꿔야 할 때마다 작업을 다시 돌려야 합니다.

작업 흐름은 업로드, 학습, 호출이지만, 실제로 성공 여부를 가르는 것은 **데이터셋**이지 작업 자체가 아닙니다. 단순한 개수보다 품질과 범위가 더 중요합니다. 실제 입력의 범위를 폭넓게 담은 깨끗하고 일관된 예시 수백 개가, 잡음이 많거나 서로 모순되는 예시 수천 개보다 낫습니다. 모델은 데이터 안에 실제로 담긴 규칙성이라면 무엇이든 배우기 때문이며, 여기에는 실수도 포함됩니다.

학습에 앞서 데이터를 나누세요. 모델이 학습 중에 전혀 보지 못하는 일부, 즉 **검증 세트(validation set)**를 따로 떼어두고, 학습에 쓰인 예시가 아니라 이 부분에서 정확도를 확인하세요. 모델은 이미 본 데이터에서는 거의 완벽한 점수를 내면서도 새로운 것에는 잘 대응하지 못할 수 있으므로, 이 별도로 떼어둔 수치가 그 동작이 실제로 일반화되는지 알려주는 숫자입니다.

python
# 학습 작업이 전혀 보지 못하는 부분을 따로 떼어두어, 실제 성능을 측정합니다.
split = int(len(training_examples) * 0.8)
train_set = training_examples[:split]        # 학습 작업이 이 데이터로 학습합니다
validation_set = training_examples[split:]   # 결과는 이 데이터로 채점합니다
# 제공사가 새로운 모델 id를 돌려주면, 다른 모델처럼 호출하면 됩니다.
# 작업 API와 분할 처리 방식은 제공사마다 다르며, 일부는 검증 파일을 직접 받습니다.

이런 절차 위에 두 가지 판단이 더 필요합니다. 첫째, 학습 작업을 돌리기 전에 프롬프팅이 정말로 실패했는지 확인하세요. 처음 대충 써본 초안이 아니라, 예시 몇 개를 갖춘 실제 지시를 다양한 입력으로 테스트한 것이어야 합니다. 둘째, 비용을 신중히 따지세요. 파인튜닝은 한 번만 지불하는 고정된 학습 비용이고, 긴 프롬프트는 영원히 지불하는 호출당 비용이므로, 절약된 토큰이 학습 비용만큼의 가치가 있을 만큼 호출량이 충분할 때에만 프롬프트 축소가 이득이 됩니다. 그리고 결합된 패턴을 기억하세요. 형태는 튜닝된 모델에, 모델이 굳이 외우려 하지 말아야 할 사실은 RAG에 맡기는 것입니다.

Juno실제로 해보기 작업 흐름은 업로드-학습-호출이지만, 결과를 결정하는 것은 데이터셋입니다. 깨끗하고 다양한 예시 몇백 개가 잡음이 많은 수천 개보다 낫습니다. 모델이 배우는 것은 실제로 데이터 안에 있는 내용이기 때문입니다. 학습 예시가 아니라 검증 세트로 채점하세요. 그렇지 않으면 외운 모델이 실제보다 더 좋아 보일 수 있습니다. 먼저 프롬프팅이 정말로 실패했는지 확인하고, 고정된 학습 비용과 영원히 지불하는 프롬프트 토큰 비용을 따진 뒤, 튜닝된 모델은 형태를 다루고 RAG는 사실을 다루도록 짝지으세요.

파이프라인은 업로드, 학습, 평가, 배포이며, 실제 엔지니어링은 학습 호출이 아니라 데이터셋과 **평가 세트**에서 이루어집니다. 여기서는 두 가지 분할 원칙이 핵심입니다. 학습에서 제외한 검증 세트는 동작이 외워진 것이 아니라 일반화되었는지를 알려줍니다. 학습 전후로 채점하는 별도의 held-out 평가 세트는, 파인튜닝이 목표 작업을 개선하면서 일반 능력을 퇴보시키지 않았는지 증명하는 도구입니다. 이는 앞 절에서 다룬 파괴적 망각 점검을 구체적인 절차로 만든 것입니다.

python
# 서로 겹치지 않는 세 조각: 하나로 학습하고, 다른 하나로 의사결정을 조정하고, 마지막으로 판정합니다.
train_set      = examples[:8000]        # 학습 작업이 이 데이터로 학습합니다
validation_set = examples[8000:9000]    # 실행 중 과적합 여부를 지켜봅니다
eval_set       = examples[9000:]        # 성능 퇴보가 없음을 증명하기 위해 학습 전과 후 모두 채점합니다

# 베이스 모델과 파인튜닝된 모델을 동일한 eval_set으로 채점한 뒤 비교합니다.
base_score = run_eval(base_model_id, eval_set)
tuned_score = run_eval(fine_tuned_id, eval_set)
# 튜닝된 점수가 목표 작업에서 베이스를 앞서고 일반 능력 점검도 유지될 때만 배포합니다.
# 평가 채점 방식과 학습 API는 제공사마다 다르지만, 이 원칙은 변하지 않습니다.

과적합과 드리프트의 승패는 데이터셋 품질에서 결정됩니다. 좁거나 특이한 데이터셋은 그 데이터셋만의 특성을 외워버리고 조금이라도 다른 입력에는 헤매는 모델을 만들어냅니다. 그러므로 개수보다 실제 입력 분포에 대한 범위가 더 중요하며, 프로덕션 트래픽이 학습 당시와 달라짐에 따라 계속 다시 측정해야 합니다. 낡은 평가는 조용히 드리프트하는 모델이 심사를 통과하고 사용자 앞에서 실패하는 통로가 됩니다.

설계할 때 반영해야 할 운영상의 현실은, 학습된 동작이 바뀔 때마다 편집이 아니라 완전한 재실행이 필요하다는 점입니다. 매번 다시 정제하고, 재학습하고, 재평가하고, 고정된 새 id를 재배포해야 합니다. 이 반복되는 비용, 그리고 매 실행마다 짊어지는 회귀 위험 때문에, 사실은 실시간으로 갱신되는 RAG에 남겨두고, 파인튜닝은 천천히 바뀌는 동작을 위해 아껴두며, 각 튜닝된 id는 새로운 실행이 성능을 퇴보시켰을 때 되돌아갈 수 있는 버전 관리 대상 산출물로 취급해야 합니다.

Juno실제로 해보기 파이프라인은 업로드-학습-평가-배포이며, 진짜 작업은 호출이 아니라 데이터 분할에 있습니다. 검증 세트는 암기를 잡아내고, 학습 전후로 채점하는 held-out 평가 세트는 성능 퇴보가 없음을 증명합니다. 실제 입력 분포에 대한 데이터셋의 범위가 과적합과 드리프트를 이기는 방법이므로, 트래픽이 학습 데이터에서 벗어남에 따라 계속 다시 측정하세요. 그리고 반복되는 비용을 감안해 설계하세요. 동작이 바뀔 때마다 완전한 재학습-재평가-재배포가 필요하며, 바로 이것이 사실은 RAG에 두고 각 튜닝된 id는 되돌릴 수 있는 버전 관리 대상 산출물로 다루어야 하는 이유입니다.

이것으로 모델이 무엇을 알고 어떻게 답하는지를 형성하는 방법들을 모두 살펴봤습니다. 이 모든 방법은 여러분이 마련해둔 무대 위에서 모델이 텍스트를 예측하도록 만드는 것이었습니다. 다음 장들에서는 모델에게 실제로 행동하는 능력을 부여합니다. 도구 사용에서는 모델이 여러분의 함수를 호출하는 법을 다루고, 이어서 이를 루프 안에서 실행하는 에이전트로 나아갑니다.