출력 정제: BestOfN 및 Refine
출력 정제: BestOfN 및 Refine (Output Refinement: BestOfN and Refine)
BestOfN과 Refine는 둘 다 캐싱을 우회하기 위해 서로 다른 rollout ID로 여러 번 LM 호출을 만들어 예측의 신뢰성과 품질을 개선하도록 설계된 DSPy 모듈이에요. 두 모듈 모두 N번의 시도에 도달하거나 reward_fn이 threshold 이상의 보상을 반환하면 멈춥니다.
출처: 문서
본문
BestOfN
BestOfN은 서로 다른 rollout ID로 제공된 모듈을 (최대 N번까지) 여러 번 실행하는 모듈이에요. 지정된 임계값을 통과하는 첫 번째 예측을 반환하거나, 임계값을 충족하는 예측이 없으면 가장 높은 보상을 얻은 예측을 반환합니다.
기본 사용법 (Basic Usage)
모델로부터 단어 하나짜리 답변을 얻을 확률을 최대화하고 싶다고 가정해 볼게요. BestOfN을 사용해 여러 rollout ID를 시도하고 가장 좋은 결과를 반환할 수 있어요.
import dspy
def one_word_answer(args, pred: dspy.Prediction) -> float:
return 1.0 if len(pred.answer.split()) == 1 else 0.0
best_of_3 = dspy.BestOfN(
module=dspy.ChainOfThought("question -> answer"),
N=3,
reward_fn=one_word_answer,
threshold=1.0
)
result = best_of_3(question="What is the capital of Belgium?")
print(result.answer) # Brussels
오류 처리 (Error Handling)
기본적으로 모듈이 시도 중 오류를 만나면 N번의 시도에 도달할 때까지 계속 시도해요. fail_count 매개변수로 이 동작을 조정할 수 있어요:
best_of_3 = dspy.BestOfN(
module=qa,
N=3,
reward_fn=one_word_answer,
threshold=1.0,
fail_count=1
)
best_of_3(question="What is the capital of Belgium?")
# 첫 실패 후 오류를 발생시킵니다
Refine
Refine은 자동 피드백 루프를 추가해 BestOfN의 기능을 확장해요. 실패한 각 시도(마지막 시도 제외) 후에 모듈 성능에 대한 상세한 피드백을 자동으로 생성하고, 이 피드백을 이후 실행의 힌트로 사용합니다.
기본 사용법 (Basic Usage)
import dspy
def one_word_answer(args, pred: dspy.Prediction) -> float:
return 1.0 if len(pred.answer.split()) == 1 else 0.0
refine = dspy.Refine(
module=dspy.ChainOfThought("question -> answer"),
N=3,
reward_fn=one_word_answer,
threshold=1.0
)
result = refine(question="What is the capital of Belgium?")
print(result.answer) # Brussels
오류 처리 (Error Handling)
BestOfN처럼 Refine도 기본적으로 오류가 발생해도 최대 N번까지 시도해요. fail_count 매개변수로 이를 제어할 수 있어요:
# 첫 오류 후 중단합니다
refine = dspy.Refine(
module=qa,
N=3,
reward_fn=one_word_answer,
threshold=1.0,
fail_count=1
)
비교: BestOfN vs. Refine
두 모듈은 비슷한 목적을 제공하지만 접근 방식이 달라요:
BestOfN은 단순히 서로 다른 rollout ID를 시도하고reward_fn이 정의한 대로 가장 좋은 결과 예측을 선택해요.Refine은 피드백 루프를 추가해, 이전 예측과reward_fn안의 코드를 사용해 모듈 자신의 성능에 대한 상세한 피드백을 LM으로 생성하고, 이를 이후 실행의 힌트로 사용해요.
실용 예제 (Practical Examples)
사실적 정확성 보장 (Ensuring Factual Correctness)
import dspy
class FactualityJudge(dspy.Signature):
"""Determine if a statement is factually accurate."""
statement: str = dspy.InputField()
is_factual: bool = dspy.OutputField()
factuality_judge = dspy.ChainOfThought(FactualityJudge)
def factuality_reward(args, pred: dspy.Prediction) -> float:
statement = pred.answer
result = factuality_judge(statement)
return 1.0 if result.is_factual else 0.0
refined_qa = dspy.Refine(
module=dspy.ChainOfThought("question -> answer"),
N=3,
reward_fn=factuality_reward,
threshold=1.0
)
result = refined_qa(question="Tell me about Belgium's capital city.")
print(result.answer)
요약 — 응답 길이 제어 (Summarization - Controlling Response Length)
import dspy
def ideal_length_reward(args, pred: dspy.Prediction) -> float:
"""
Reward the summary for being close to 75 words with a tapering off for longer summaries.
"""
word_count = len(pred.summary.split())
distance = abs(word_count - 75)
return max(0.0, 1.0 - (distance / 125))
optimized_summarizer = dspy.BestOfN(
module=dspy.ChainOfThought("text -> summary"),
N=50,
reward_fn=ideal_length_reward,
threshold=0.9
)
result = optimized_summarizer(
text="[Long text to summarize...]"
)
print(result.summary)
dspy.Suggest 및 dspy.Assert에서의 마이그레이션 (Migration from dspy.Suggest and dspy.Assert)
BestOfN과 Refine은 DSPy 2.6부터 dspy.Suggest와 dspy.Assert를 대체하는 모듈이에요.