BootstrapFewShot 계열

BootstrapFewShot 계열

이 페이지가 왜 필요한가요?

BootstrapFewShot 계열 — LabeledFewShot, BootstrapFewShot, BootstrapFewShotWithRandomSearch(BootstrapRS), KNNFewShot, InferRules — 은 DSPy의 데모 튜닝(demo-tuning) 옵티마이저예요. 공통 메커니즘은 하나예요. 예시 trace를 모아(또는 샘플링해) 각 predictor의 demos 목록에 넣는 거죠. 차이는 데모를 어떻게 고르느냐에 있어요. 수동 샘플링, 메트릭 필터 부트스트랩, 데모 셋에 대한 무작위 탐색, 추론 시 KNN 검색, 부트스트랩 trace에서의 규칙 추출까지요.

선택 가이드에서 계열의 한 멤버를 골랐다면, 이 페이지에서 변형들이 어떻게 서로 관련되고, 각각이 기본 위에 무엇을 더하며, 주의할 실패 모드가 무엇인지 이해하면 좋아요.

설계 결정

1. 데모는 옵티마이저가 아니라 predictor 위에 살아요

이 계열의 모든 변형은 predictor.demos에 써요. 그건 각 Predict에 붙는 단순한 파이썬 리스트예요. 옵티마이저는 로더일 뿐이고, predictor가 저장소예요. 컴파일 후 프로그램의 동작은 전적으로 predictor들의 데모와 시그니처에 인코딩되므로 옵티마이저 객체는 버려도 돼요. program.save(path)는 프로그램과 함께 데모를 피클해요.

2. LabeledFewShot은 LM 없는 베이스라인이에요

trainset의 무작위 샘플(또는 결정적 슬라이스), teacher 없음, 메트릭 없음, LM 호출 없음. 데모만으로 효과가 있는지 확인하려고 써요. LabeledFewShot(k=16)만으로 이미 목표에 도달한다면 더 무거운 건 낭비예요.

3. BootstrapFewShot은 trace를 메트릭으로 필터해요

teacher가 각 훈련 예시를 실행하고, 메트릭이 각 완성을 판단하며, 통과한 trace가 데모가 돼요. 베이스라인 위의 모든 변형은 이 루프의 어떤 버전을 써요. 메트릭이 지렛대예요. 엄격하게 하면 적지만 고품질의 데모, 느슨하게 하면 많지만 낮은 품질의 데모를 얻어요.

4. teacher는 기본적으로 student의 딥카피예요

teacher=를 넘기지 않으면 BootstrapFewShotstudent.deepcopy()를 호출해요. 원본이 아니라 그 사본이 trainset에 실행돼요. 이유는, teacher에는 LabeledFewShot 데모가 적용되므로(부트스트랩할 예시가 있어야 함) 그게 student의 컴파일된 상태로 새는 걸 원하지 않기 때문이에요. 단일 LM 부트스트래핑이 정체되면 더 강한 모델을 teacher=로 넘기는 게 표준 업그레이드 경로예요.

5. max_rounds는 라운드마다 새 rollout으로 teacher를 재실행해요

각 라운드는 rollout_id를 올리고 temperature=1.0을 강제해서 LM 캐시를 깨고 새 샘플을 유발해요. 여러 라운드는 한 예시가 통과 trace를 만들 여러 번의 기회를 줘요. 메트릭이 정말 엄격하지 않다면 max_rounds를 2~3보다 높게 두지 마세요. 라운드가 늘수록 LM 비용이 선형으로 커지거든요.

6. max_bootstrapped_demos는 부트스트랩 슬롯을, max_labeled_demos는 결합 총량을 제한해요

앞쪽 max_bootstrapped_demos 슬롯은 추적·검증된 데모가 차지하고, max_labeled_demos까지의 나머지 슬롯은 trainset의 원시 라벨 예시로 채워져요. 기본 max_bootstrapped_demos=4, max_labeled_demos=16은 predictor당 부트스트랩 4개 + 원시 최대 12개예요. 원시 데모는 부트스트랩 과적합에 대한 밸러스트 역할을 해요.

7. 부트스트랩 중인 예시는 teacher의 데모에서 먼저 제거돼요

예시 e에 teacher를 실행하기 직전, 옵티마이저는 e를 모든 predictor의 데모 목록에서 제거해요. 그러지 않으면 teacher가 자기 데모에서 e의 답을 검색해 "성공"할 수 있기 때문이에요. 정리는 예시당 한 번 일어나고, 다음 라운드를 위해 조용히 되돌려져요.

8. BootstrapRSBootstrapFewShot을 N회 실행한 뒤 평가해요

무작위성은 trainset 셔플과 후보별 부트스트랩 데모 개수 변경에서 와요. N개 시드 중 3개가 고정 베이스라인(zero-shot, 라벨 전용, 섞지 않은 부트스트랩)이고 나머지는 무작위예요. valset(기본 trainset)이 동점자 판정자라서 가장 높은 점수의 후보가 이겨요. 한 번의 부트스트랩 통과가 데모 품질을 일관되지 않게 낼 때 쓰세요.

9. KNNFewShot은 컴파일 시점이 아니라 추론 시점에 데모를 골라요

trainset은 생성 시 한 번 임베딩돼요. 각 forward 호출은 새 입력을 임베딩하고, 가장 가까운 k개 훈련 예시를 검색하며, 그 마이크로 trainset에서 BootstrapFewShot을 실행한 뒤 프로그램을 호출해요. 호출별 데모 선택은 입력마다 다른 데모를 보게 해요. 단일 데모 셋이 일반화하지 못할 때 옳은 선택이지만, 모든 forward가 미니 컴파일을 하므로 자주 호출되면 비싸요.

10. InferRules는 부트스트랩 데모에서 해석 가능한 규칙을 추출해요

BootstrapFewShot 위에서, teacher LM에 부트스트랩 데모를 읽고 num_rules개의 자연어 규칙을 만들게 해요. 그 규칙은 각 predictor의 시그니처 명령어에 덧붙여져요. 이득은 해석 가능성이에요. 규칙을 읽고 일반화되는지 판단하고 저장 전에 수정할 수 있어요.

11. 불안정한 메트릭은 계열 전체를 망가뜨려요

베이스라인 위의 모든 변형은 메트릭에 의존해 trace를 필터해요. 메트릭이 비결정적(LM 호출, 검색 무작위성 의존)이면 부트스트랩은 그 특정 실행에서 우연히 통과한 trace를 발견해요. 결과 데모는 프로그램 품질만큼 메트릭 노이즈의 함수예요. 가능하면 메트릭을 결정적으로 만들고, 불가능하면 BootstrapRS와 보류된 valset으로 노이즈를 평균화하는 걸 받아들여야 해요.

12. 컴파일된 프로그램은 데모까지 피클돼요

program.save(path)는 predictor를 그 demos 목록과 함께 직렬화해요. 별도의 데모 추출 단계가 필요 없어요. 이동성 이야기는 스파인 모듈과 같아요. 한 번 컴파일, 저장, 재로드.

API 워크스루

계열 내 역할 기준으로 그룹을 나눴어요.

LabeledFewShot — LM 없는 베이스라인

dspy.LabeledFewShot(k=16) .compile(student, *, trainset, sample=True)

teacher도 메트릭도 부트스트랩 루프도 없어요. .compiletrainset에서 min(k, len(trainset))개 예시를 샘플링해(sample=TrueRandom(0) 시드의 무작위, False면 결정적 첫-k) 같은 집합을 각 predictor의 demos에 붙여요. trainset을 한 번만 통과하고 LM 호출은 없어요.

BootstrapFewShot — 핵심 부트스트래퍼

dspy.BootstrapFewShot(metric=None, metric_threshold=None, teacher_settings=None, max_bootstrapped_demos=4, max_labeled_demos=16, max_rounds=1, max_errors=None) .compile(student, *, teacher=None, trainset)

.compile에서 옵티마이저는 다음을 수행해요.

  1. teacher 초기화. teacher가 없으면 student.deepcopy(). teacher가 컴파일 안 됐고 max_labeled_demos > 0이면 LabeledFewShot(k=max_labeled_demos)를 적용해 teacher가 부트스트랩할 데모를 갖게 해요.
  2. trainset 순회. 각 예시마다 최대 max_rounds번 시도해요. 각 라운드는 현재 예시를 모든 predictor의 데모에서 제거하고, rollout_id=round, temperature=1.0으로 LM을 복사해 teacher를 호출해요.
  3. 메트릭으로 채점. metric(example, prediction, trace)를 호출해요. 참 반환(또는 설정된 경우 숫자 >= metric_threshold)이 trace를 통과로 표시해요. teacher나 메트릭의 예외는 오류 카운터를 늘리고, max_errors를 넘기면 예외를 올려요.
  4. 통과 trace에서 데모 추출. trace의 각 predictor 호출에 대해 dspy.Example(augmented=True, **inputs, **outputs)를 만들어 그 predictor 이름 아래 저장해요.
  5. 데모 할당. 각 predictor에 대해 앞 max_bootstrapped_demos 슬롯은 증강 데모, 나머지(max_labeled_demos까지)는 trainset의 부트스트랩되지 않은 부분에서 샘플링한 원시 라벨 예시로 채워요.

새 컴파일된 모듈을 반환하고, student는 수정되지 않아요.

metric_threshold 는 float 반환 메트릭의 숫자 하한이에요. 없으면 메트릭 반환값을 bool로 강제해요. metric_threshold=0.5는 "점수 0.5 이상이면 통과"라는 뜻이에요.

teacher_settings 는 teacher 호출 동안 dspy.settings에 병합되는 dict예요. teacher가 student와 다른 LM이나 어댑터를 써야 할 때 유용해요.

BootstrapFewShotWithRandomSearch(BootstrapRS) — 후보 탐색

dspy.BootstrapFewShotWithRandomSearch(metric, teacher_settings=None, max_bootstrapped_demos=4, max_labeled_demos=16, max_rounds=1, num_candidate_programs=16, num_threads=None, max_errors=None, stop_at_score=None, metric_threshold=None) .compile(student, *, teacher=None, trainset, valset=None, restrict=None, labeled_sample=True)

num_candidate_programs개 후보를 만들고 승자를 골라요. 그중 3개 시드는 고정 베이스라인이에요.

  • seed=-3: zero-shot(데모 없음)
  • seed=-2: LabeledFewShot(k=max_labeled_demos)
  • seed=-1: trainset을 섞지 않은 BootstrapFewShot

나머지 시드는 trainset을 섞고 부트스트랩 데모 개수를 달리해요. 각 후보는 valset(기본 trainset)에서 평가돼요. 가장 높은 점수의 후보를 반환하며, best_program.candidate_programs(seed, program, score, subscores) 튜플로 정렬된 전체 목록이 담겨 검사할 수 있어요.

stop_at_score=N은 후보의 valset 점수가 N에 도달하면 일찍 멈춰요. "충분히 좋음"이 명확히 정의됐고 나머지 예산을 아끼고 싶을 때 쓰세요.

num_threads는 후보 평가를 병렬화하지, 부트스트랩 단계를 병렬화하지 않아요.

KNNFewShot — 추론 시 데모 검색

dspy.KNNFewShot(k, trainset, vectorizer, **bootstrap_kwargs) .compile(student, *, teacher=None)

생성자는 vectorizer(dspy.Embedder)로 전체 trainset을 임베딩하고 dspy.KNN(k, trainset, vectorizer) 인스턴스를 저장해요. .compile은 student의 forward를 오버라이드해서 매 호출마다:

  1. 새 입력을 임베딩해요.
  2. 가장 가까운 k개 훈련 예시를 검색해요.
  3. k개 예시에 대해 새 BootstrapFewShot(**bootstrap_kwargs)를 만들어 단일 호출 프로그램을 컴파일해요.
  4. 컴파일된 프로그램을 입력에 실행해요.

trainset 임베딩은 생성 시 고정돼요. trainset을 바꾸려면 옵티마이저를 다시 만들어야 해요. 모든 forward 호출이 미니 컴파일을 실행하므로 추론 비용이 증폭돼요.

InferRules — 부트스트랩 위의 규칙 추출

dspy.InferRules(num_candidates=10, num_rules=10, num_threads=None, teacher_settings=None, **bootstrap_kwargs) .compile(student, *, teacher=None, trainset, valset=None)

BootstrapFewShot을 확장해요. 기본 부트스트랩을 한 번 실행한 뒤:

  1. 부트스트랩된 student를 num_candidates번 복사해요.
  2. 각 복사본에 대해 teacher LM이 부트스트랩 데모를 읽고 predictor당 num_rules개의 자연어 규칙을 만들게 해요. 규칙은 RulesInductionProgram 시그니처(examples_text -> natural_language_rules)에 대한 ChainOfThought에서 나와요.
  3. 규칙을 각 predictor의 signature.instructions에 덧붙여요. 앞에 "Please adhere to the following rules..." 서문을 붙여요.
  4. 각 후보를 valset에서 평가하고 최고를 반환해요.

규칙은 보고 편집할 수 있어요. 읽고 유지할 규칙을 정하고, 옵티마이저 출력을 완성된 프로그램이 아니라 출발점으로 취급할 수 있어요.

관련 링크

  • 옵티마이저: 어떤 걸 고를까 — 선택 가이드. 각 계열 멤버가 계열 밖 옵티마이저보다 언제 이기는지 설명해요.
  • 메트릭과 평가 — 모든 부트스트랩 변형이 쓰는 메트릭 형태와, 나쁜 예시 하나가 긴 부트스트랩을 망치지 않게 하는 실패 점수 동작이에요.
  • 모듈: 나만의 모듈 합성하기predictor.demos, _compiled 플래그, deepcopy() 동작 — 부트스트랩 계열이 의존하는 전부예요.
  • GEPA 심화 — 명령어 최적화 대응편. 두 손잡이를 모두 돌려야 할 때 dspy.BetterTogether로 결합해요.