2026년 7월 30일 목요일

AI 증류기법: 교사 모델의 지식을 데이터와 코드로 옮기는 방법

중국발 AI 모델을 둘러싼 증류 논란은 “작은 모델이 큰 모델처럼 답한다”는 현상을 어떻게 만들어 내는지 묻는 데서 시작한다. 증류는 원래 합법적인 모델 압축·전이 기법이지만, 교사 모델의 출력물을 대량으로 수집해 학생 모델을 만들면 API 약관과 데이터 권리 문제가 겹친다. 앞서 다룬 Kimi K3 증류 논란이 행위의 경계를 물었다면, 기존 AI 모델 증류 논의는 공개 모델 파생 사례를 보여 준다. 이번 글은 AI 증류기법이 실제 파이프라인에서 어떤 데이터와 코드로 구현되는지 살펴본다.

아래 API 수집 예시는 교사 모델 제공자의 허가, 계약, 개인정보 처리 근거가 있다는 전제의 연구용 예시다. 계정 우회, 비정상 자동화, 속도 제한 회피, 비공개 출력 탈취를 다루지 않는다. 합법적인 오픈 모델·자체 모델·허가된 API에서만 실행해야 한다.

1. 증류의 기초: 무엇을 옮기는가

Hinton, Vinyals, Dean이 2015년에 정리한 지식 증류(knowledge distillation)는 큰 교사(teacher) 네트워크의 지식을 작은 학생(student) 네트워크로 옮기는 방법이다. 교사는 정답 하나만 내놓는 것이 아니라 각 선택지에 대한 확률 분포, 즉 ‘soft target’을 제공한다. 학생은 정답 라벨과 교사의 분포를 함께 맞추며 일반화에 필요한 관계까지 배운다. 원 논문은 이 관점을 모델 압축의 출발점으로 만들었다.

LLM 증류에는 크게 두 경로가 있다. 모델의 가중치와 logits를 볼 수 있는 white-box 증류는 중간 표현·logits·attention을 직접 맞춘다. API만 쓸 수 있는 black-box 증류는 질문을 만들고 교사의 답변·선호·코드·추론 흔적을 저장해 학생을 지도한다. 2024년 LLM 증류 서베이는 이 둘을 구분하면서 API 기반 in-context, chain-of-thought, instruction-following 증류를 별도 범주로 정리한다.

구분 교사에서 보이는 신호 학생 학습 목표 장점 한계
logits 증류 토큰별 logits·확률 온도 조절 KL divergence 정보량이 많고 안정적 교사 가중치·logits 접근 필요
feature 증류 중간층 표현·attention 표현 정렬·feature loss 내부 표현까지 전달 구조가 다른 모델에는 적용 어려움
응답 증류 텍스트 답변·선호·코드 SFT·DPO·선호 최적화 API만으로도 가능 교사의 오류·말투·편향을 함께 복제
추론 경로 증류 단계별 풀이·검증 결과 reasoning SFT 또는 RL 작은 모델의 문제 해결력 향상 경로가 진짜 사고를 그대로 뜻하지 않음

기본 손실은 다음처럼 쓸 수 있다. T는 temperature다. 온도를 높이면 교사의 확률 분포가 부드러워져 “정답 외에 어떤 오답을 덜 나쁘게 보는가”까지 학생이 읽을 수 있다.

PYTHON
import torch.nn.functional as F

def distillation_loss(student_logits, teacher_logits, labels, temperature=2.0, alpha=0.7):
    soft_target = F.softmax(teacher_logits / temperature, dim=-1)
    soft_pred = F.log_softmax(student_logits / temperature, dim=-1)
    kd = F.kl_div(soft_pred, soft_target, reduction="batchmean") * (temperature ** 2)
    ce = F.cross_entropy(student_logits.reshape(-1, student_logits.size(-1)), labels.reshape(-1))
    return alpha * kd + (1 - alpha) * ce

API 증류에서는 logits 대신 답변을 학습한다. 이때 “증류 데이터가 많을수록 좋다”는 말은 틀렸다. 교사의 환각, 잘못된 계산, 거절 정책, 장황한 말투까지 복제할 수 있기 때문이다. 데이터의 출처, 검증 상태, 난이도, 중복, 개인정보를 관리하는 것이 모델 크기보다 먼저다.

2. 증류 데이터 수집: 질문을 만들고 기록을 남기는 단계

2.1 증류 데이터 수집의 최소 스키마

증류 데이터 수집은 교사에게 질문을 던지고 답을 파일에 쌓는 일이 아니다. 나중에 같은 결과를 재현하고 삭제·감사를 수행할 수 있도록 요청과 응답의 계보(provenance)를 함께 저장해야 한다.

JSON
{
  "id": "math-000001",
  "prompt": "A train travels 120 km in 2 hours. What is its average speed?",
  "teacher": "authorized-teacher-v1",
  "teacher_snapshot": "2026-06-01",
  "generation": {"temperature": 0.2, "top_p": 0.9, "max_output_tokens": 512},
  "response": "Average speed is 60 km/h.",
  "source": "licensed-evaluation-set",
  "review": {"status": "verified", "method": "exact_answer"},
  "hash": "sha256:..."
}

교사 모델의 서비스 약관이 학습 재사용을 허용하는지 먼저 확인한다. 사용자 입력을 재사용한다면 개인정보 제거·동의·보존기간도 별도 정책으로 정해야 한다. 같은 질문을 반복하면 비용은 늘고 데이터 다양성은 줄어든다. prompt의 언어·길이·분야·난이도를 균형 있게 샘플링하는 편이 낫다.

2.2 허가된 API를 호출하는 안전한 수집기

다음은 OpenAI 호환 API를 가정한 예시다. API 키는 환경변수로만 읽고, 요청 간 간격·재시도·원문 해시·메타데이터를 기록한다. 실제 엔드포인트와 응답 필드는 제공자 문서에 맞춰 바꿔야 한다.

PYTHON
# collect_teacher.py
import hashlib, json, os, time
from pathlib import Path
import requests

API_URL = os.environ["TEACHER_API_URL"]
API_KEY = os.environ["TEACHER_API_KEY"]
MODEL = os.environ.get("TEACHER_MODEL", "authorized-teacher-v1")

def call_teacher(prompt: str) -> dict:
    payload = {
        "model": MODEL,
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.2,
        "top_p": 0.9,
        "max_tokens": 512,
    }
    response = requests.post(
        API_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json=payload,
        timeout=60,
    )
    response.raise_for_status()
    body = response.json()
    answer = body["choices"][0]["message"]["content"]
    return {"answer": answer, "usage": body.get("usage", {})}

def collect(prompts: list[str], output: Path) -> None:
    output.parent.mkdir(parents=True, exist_ok=True)
    with output.open("a", encoding="utf-8") as fp:
        for index, prompt in enumerate(prompts, start=1):
            result = call_teacher(prompt)
            record = {
                "id": f"sample-{index:06d}",
                "prompt": prompt,
                "teacher": MODEL,
                "response": result["answer"],
                "usage": result["usage"],
                "hash": hashlib.sha256(result["answer"].encode()).hexdigest(),
            }
            fp.write(json.dumps(record, ensure_ascii=False) + "\n")
            fp.flush()
            time.sleep(0.25)  # 계약상 허용된 속도 제한 안에서 조정

if __name__ == "__main__":
    prompts = ["Explain binary search with a short Python example."]
    collect(prompts, Path("data/teacher.jsonl"))

이 코드는 대량 수집을 위한 우회기가 아니다. 실제 운영에서는 제공자의 공식 Batch API, 요청 한도, 데이터 보존 설정, 비용 상한을 사용해야 한다. 응답 원문이 민감할 수 있으므로 접근권한을 제한하고, 모델 학습이 끝난 뒤 원본을 보관할지 삭제할지 결정한다.

3. 데이터 가공: 답변을 학습 가능한 대화로 만들기

원시 JSONL은 곧바로 파인튜닝 데이터가 되지 않는다. 먼저 빈 답변·중복·너무 긴 샘플·개인정보·정답이 없는 샘플을 걸러낸다. 분야별 검증기(수식 실행, 코드 테스트, 분류 라벨 일치)를 통과한 샘플만 verified로 표시하고, 학습·검증·테스트 세트를 질문 ID 기준으로 분리한다.

PYTHON
# prepare_dataset.py
import hashlib, json, re
from pathlib import Path

def normalize(text: str) -> str:
    return re.sub(r"\s+", " ", text).strip()

def prepare(src: Path, dst: Path) -> None:
    seen = set()
    dst.parent.mkdir(parents=True, exist_ok=True)
    with src.open(encoding="utf-8") as inp, dst.open("w", encoding="utf-8") as out:
        for line in inp:
            row = json.loads(line)
            prompt, answer = normalize(row["prompt"]), normalize(row["response"])
            key = hashlib.sha256(f"{prompt}\n{answer}".encode()).hexdigest()
            if not prompt or not answer or key in seen or len(answer) > 8000:
                continue
            if row.get("review", {}).get("status") not in {None, "verified"}:
                continue
            seen.add(key)
            out.write(json.dumps({
                "messages": [
                    {"role": "user", "content": prompt},
                    {"role": "assistant", "content": answer},
                ],
                "provenance": {"teacher": row.get("teacher"), "hash": key},
            }, ensure_ascii=False) + "\n")

prepare(Path("data/teacher.jsonl"), Path("data/distill.cleaned.jsonl"))

여기서 중요한 것은 학생에게 교사의 모든 말투를 복사시키는 것이 아니라 목표 능력을 정의하는 일이다. 코드 모델이면 실행 가능한 테스트를 붙이고, 수학이면 정답 검산기를 붙이며, 고객지원이면 정책 문서와 거절 기준을 함께 넣는다. 교사 답변만 남기고 질문·검증 결과·라이선스 정보를 버리면 나중에 모델의 출처를 설명할 수 없다.

허가된 교사 모델에서 질문과 답변이 provenance 기록과 품질 필터를 지나 학습용 대화 데이터와 학생 모델로 이어지는 데이터 파이프라인

<질문 생성부터 검증·파인튜닝까지 이어지는 증류 데이터 파이프라인 2.1>

4. 기초 모델 선정과 모델 파인튜닝

기초 모델은 “가장 큰 모델”보다 목표와 라이선스에 맞춰 고른다. 학생 모델의 토크나이저가 데이터의 언어와 코드 문법을 잘 표현하는지, 상업 이용·파생 모델·증류 허용 조건이 무엇인지, 컨텍스트 길이와 GPU 메모리가 충분한지 확인한다. 데이터와 모델 라이선스가 충돌하면 훈련이 성공해도 배포할 수 없다.

Hugging Face Transformers는 모델별 chat template을 제공한다. 역할 토큰을 임의로 붙이지 말고 apply_chat_template로 포맷해야 한다. Transformers 문서는 모델마다 [INST], <|user|> 같은 제어 토큰이 다르며 중복 special token이 성능을 해칠 수 있다고 설명한다. TRL SFTTrainer 문서dataset_text_field, max_length 등 학습 데이터 전처리 파라미터를 정의한다.

PYTHON
# train_student.py — 예시: 허가된 데이터로 SFT 수행
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM
from trl import SFTConfig, SFTTrainer

BASE = "Qwen/Qwen2.5-7B-Instruct"  # 라이선스와 하드웨어를 먼저 확인
tokenizer = AutoTokenizer.from_pretrained(BASE)
model = AutoModelForCausalLM.from_pretrained(
    BASE, torch_dtype="auto", device_map="auto"
)

dataset = load_dataset("json", data_files={
    "train": "data/distill.cleaned.jsonl",
    "validation": "data/distill.validation.jsonl",
})

def render(row):
    text = tokenizer.apply_chat_template(
        row["messages"], tokenize=False, add_generation_prompt=False
    )
    return {"text": text}

dataset = dataset.map(render)
config = SFTConfig(
    output_dir="runs/student-distill",
    dataset_text_field="text",
    max_length=2048,
    per_device_train_batch_size=1,
    gradient_accumulation_steps=16,
    learning_rate=2e-5,
    num_train_epochs=2,
    logging_steps=10,
    eval_strategy="steps",
    eval_steps=100,
    save_steps=100,
    bf16=True,
)
trainer = SFTTrainer(
    model=model,
    args=config,
    train_dataset=dataset["train"],
    eval_dataset=dataset["validation"],
    processing_class=tokenizer,
)
trainer.train()
trainer.save_model("runs/student-distill/final")

참고로 수집데이터로 학습을 수행시켜서 실제 가중치(weight)의 미세조정이 일어나는 곳이 trainer.train() 이 부분이다. 실제 학습에서는 전체 가중치를 갱신하는 대신 LoRA/PEFT로 시작하면 GPU 메모리와 실험 비용을 줄일 수 있다. 다만 LoRA 어댑터를 합쳐 배포할 때의 라이선스와 병합 절차를 별도로 확인해야 한다. 훈련 손실이 내려간다고 증류가 성공한 것은 아니다. 보지 못한 질문, 교사와 다른 표현, 안전 거절, 긴 문맥, 코드 실행 평가를 함께 비교해야 한다.

5. 최근 논문 두 편이 보여 주는 다음 단계

DA-KD: 쉬운 샘플을 계속 가르치지 않기

DA-KD: Difficulty-Aware Knowledge Distillation for Efficient Large Language Models는 ICML 2025 논문이다. 기존 방식이 샘플 난이도 차이를 무시해 쉬운 문제에도 같은 증류 비용을 쓰는 점을 지적하고, 난이도에 따라 증류 데이터셋을 동적으로 조정한다. 핵심 교훈은 데이터 수를 무작정 늘리는 것이 아니라 학생이 아직 못 푸는 샘플에 교사 예산을 집중해야 한다는 것이다.

RLKD: 평평한 풀이 문장을 넘어 구조를 옮기기

Distilling the Implicit Multi-Branch Structure in LLMs’ Reasoning via Reinforcement Learning은 2025년 공개된 RLKD 연구다. 논문은 교사의 한 줄짜리 reasoning trace를 SFT로 따라 쓰게 하면 실제 문제 선택과 해결의 다중 분기 구조가 평평한 토큰 모방으로 붕괴할 수 있다고 본다. 그래서 Generative Structure Reward Model로 학생과 교사의 구조 정렬을 평가하고 RL로 최적화한다. 논문 결과는 연구진의 실험 설정 안에서 해석해야 하며, “RL 증류가 언제나 SFT보다 낫다”는 일반 법칙은 아니다.

두 연구는 서로 다른 병목을 짚는다. DA-KD는 어떤 데이터를 얼마만큼 호출할지의 비용 문제를, RLKD는 답변을 복사하는 것과 문제 해결 구조를 배우는 것의 차이를 다룬다. 기업이 증류 파이프라인을 만들 때도 데이터 선택과 학습 목표를 따로 설계해야 한다.

6. API 증류 비용은 어떻게 계산하는가

API 증류 비용은 요청 수만으로 계산하지 않는다. 입력 토큰, 출력 토큰, 캐시·배치 할인, 재시도, 검증용 교사 호출, 실패 요청까지 합산해야 한다.

TEXT
총비용 = (입력토큰 / 1,000,000 × 입력단가)
       + (출력토큰 / 1,000,000 × 출력단가)
       + 재시도·검증·도구호출 비용

스크린샷의 계산은 “샘플당 입력 500토큰·출력 1,000토큰”을 가정한다. 따라서 20만 건이면 입력 1억 토큰과 출력 2억 토큰이다. 이때 표에 사용한 Luna 예시 단가(입력 $1·출력 $6)를 적용하면 정가 API는 100×$1 + 200×$6 = $1,300(약 182만 원), Batch는 $650(약 91만 원)이다. 운영 여유를 3050% 더하면 Batch 기준 약 $845$975(약 118만~137만 원)으로 잡는다. 화면에 적힌 $220은 출력 토큰을 2천만 개로 잘못 넣은 계산이므로, 20만 건·출력 1,000토큰 가정에서는 $1,300이 맞다.

실제 가격은 모델·시점·제공자에 따라 달라진다. 2026년 7월 31일 공식 문서를 기준으로 같은 20만 건을 다시 계산하면 다음과 같다. 환산은 계산 편의를 위해 1달러=1,400원을 적용한 근사치이며, 환율·부가세·계정 할인은 반영하지 않았다.

모델·단가(입력/출력, 100만 토큰) 일반 API Batch(50%)
OpenAI GPT-5.6 Sol $5/$30 $6,500(약 910만 원) $3,250(약 455만 원)
OpenAI GPT-5.6 Terra $2/$12 $2,600(약 364만 원) $1,300(약 182만 원)
Gemini 3.5 Flash $1.50/$9 $1,950(약 273만 원) $975(약 137만 원)

OpenAI는 모델 가격표에서 모델별 입력·출력 단가를, Batch API 문서에서 24시간 처리와 50% 할인을 안내한다. Gemini도 공식 가격표에서 Gemini 3.5 Flash의 표준·Batch 단가를 별도로 제시한다. 즉, 스크린샷의 $650은 특정 저가 교사 모델을 가정한 시나리오로는 유효하지만 모든 최신 모델의 실제 비용을 대표하지는 않는다.

실제 예산에는 실패 재시도 510%, 교사 재생성·다중 후보 1030%, LLM 판정·검증 1030%, 평가 세트 생성 35%가 추가된다. 예를 들어 Terra Batch $1,300에 재시도·검증을 40%로 가정하면 약 $1,820(약 255만 원)이다. 검증을 더 싼 모델로 분리하고, 쉬운 샘플을 사전 필터링하며, 캐시와 Batch를 적용하는 이유다. 이 수치는 공개 가격표와 가정한 토큰 수를 곱한 근사 예산일 뿐이며, 실제 청구액은 요청의 입력·출력·추론 토큰, 캐시 적중, 실패 요청, 환율과 세금에 따라 달라진다.

물론 사용수준의 품질로 파인튜닝을 하려면 어느정도의 토큰을 태워야 하는지는 정답이 없다. 위 계산예시의 2억토큰 정도는 그리 많은 양도 아니고 이정도로 충분히 튜닝된다고도 할 수 없다. 하지만 여러 논문의 말처럼 핵심 선별된 교사모델로 최소한의 토큰으로도 가능할 수 있지 않을까?

교사 모델과 학생 모델 사이에서 비용·평가·학습을 반복 확인하는 연구실 실험 환경

<교사 호출 비용과 학생 평가를 함께 관리하는 증류 실험 루프 6.1>

7. 거대 LLM 회사는 왜 모든 증류 시도를 선별하지 못하는가

대형 회사가 증류 시도를 전부 차단하지 못하는 이유는 기술 하나의 실패가 아니라 관측의 비대칭 때문이다.

  1. 정상 사용과 증류 수집은 같은 API 호출처럼 보인다. 교육·평가·고객지원도 많은 질문을 순차적으로 보낼 수 있다.
  2. 공격자는 여러 계정·지역·애플리케이션에 작업을 나누고, 출력 요청의 형태를 일반 질의처럼 섞을 수 있다. 제공자는 오탐을 줄이려면 정상 사용자를 함께 막지 않아야 한다.
  3. 모델 출력에는 소유자의 비밀 키처럼 확실한 식별자가 없다. 문체·정답·거절 패턴은 강한 단서지만, 공개 데이터와 유사한 프롬프트만으로도 나타날 수 있다.
  4. API 제공자는 고객의 프롬프트와 출력 전체를 장기간 저장하지 않을 수 있다. 개인정보·보안·계약상 이유로 분석 가능한 로그가 제한된다.
  5. 교사 모델이 여러 개이면 학생의 출처를 한 회사의 출력으로 귀속하기 어렵다. 공개 데이터, 합성 데이터, 사람의 편집, 여러 모델의 앙상블이 섞일 수 있다.

그래서 탐지는 단일 문장 워터마크보다 비정상적인 질의 분포, 계정 그래프, 요청 간격, 오류율, 출력 재사용 패턴, 모델 간 행동 유사성을 여러 신호로 묶는 방식에 가깝다. 이 신호들은 약관 집행과 보안 조치에는 도움이 되지만, 법정에서 증류를 자동 확정하는 증명서는 아니다. 제공자는 rate limit·계정 검증·출력 추적·계약상 학습 금지 조항을 함께 사용하고, 연구자와 사용자는 허가와 데이터 계보를 기록해야 한다.

결론: 증류의 본질은 코드보다 데이터 계약이다

AI 증류기법의 핵심은 SFTTrainer 한 줄이 아니다. 어떤 교사에게 어떤 질문을 했고, 그 답을 누가 검증했으며, 학생 모델이 무엇을 배웠는지 재현 가능한 데이터 계보를 만드는 일이다. 공개 모델의 허가된 출력, 자체 생성 데이터, 라이선스가 분명한 평가셋으로 증류하면 작은 모델의 비용·지연·온디바이스 배포를 크게 개선할 수 있다. 반대로 비공개 서비스의 출력을 우회 수집하고 출처를 숨기면, 같은 알고리즘도 산업적 신뢰를 무너뜨리는 복제가 된다.

앞으로의 경쟁은 “누가 더 많이 긁었나”보다 “누가 더 적은 교사 호출로, 더 검증 가능한 데이터로, 더 일반화되는 학생을 만들었나”로 이동할 가능성이 높다. 증류는 지능을 훔치는 마법도, 단순한 압축 버튼도 아니다. 허가·비용·데이터 품질·평가·감사를 함께 설계하는 하나의 시스템 공학 문제다.

댓글 없음:

댓글 쓰기

AI 증류기법: 교사 모델의 지식을 데이터와 코드로 옮기는 방법

중국발 AI 모델을 둘러싼 증류 논란은 “작은 모델이 큰 모델처럼 답한다”는 현상을 어떻게 만들어 내는지 묻는 데서 시작한다. 증류는 원래 합법적인 모델 압축·전이 기법이지만, 교사 모델의 출력물을 대량으로 수집해 학생 모델을 만들면 API 약관과 데...