[DSPy] 08.Metric :Programming—not prompting—Language Models

[DSPy] 08.Metric :Programming—not prompting—Language Models
Table of Contents

    DSPy는 머신 러닝 프레임워크이므로 평가(진행 상황 추적) 및 최적화(DSPy가 프로그램을 더 효과적으로 만들 수 있도록)를 위한 automatic metrics에 대해 고려해야 합니다.

    metric이란 무엇이며 내 작업에 대한 metric을 어떻게 정의하나요?

    메트릭은 데이터에서 예시를 가져와 시스템의 출력을 가져와 그 출력이 얼마나 좋은지 정량화한 점수를 반환하는 함수일 뿐입니다. 무엇이 시스템의 출력을 좋거나 나쁘게 만들까요?

    간단한 작업의 경우 ‘정확도’ 또는 ‘정확히 일치’ 또는 ‘F1 점수’가 될 수 있습니다. 간단한 분류나 짧은 형식의 QA 작업이 이에 해당할 수 있습니다.

    그러나 대부분의 애플리케이션의 경우, 시스템은 긴 형식의 결과물을 출력합니다. 여기서 메트릭은 아마도 출력의 여러 속성을 검사하는 더 작은 DSPy 프로그램일 것입니다(아마도 LM의 AI 피드백을 사용할 수도 있습니다).

    첫 번째 시도에서 이 작업을 제대로 수행하기는 어렵겠지만 간단한 것부터 시작하여 반복해야 합니다.

    Simple metrics

    DSPy 메트릭은 예제(예: 학습 또는 개발 세트)와 DSPy 프로그램의 출력 예측을 가져와서 플로트(또는 int 또는 bool) 점수를 출력하는 Python의 함수일 뿐입니다.

    메트릭은 trace라는 선택적 세 번째 인수도 허용해야 합니다. 잠시 무시해도 되지만 최적화를 위해 메트릭을 사용하려는 경우 몇 가지 강력한 트릭을 사용할 수 있습니다.

    다음은 example.answer와 pred.answer를 비교하는 간단한 메트릭의 예입니다. 이 특정 메트릭은 bool을 반환합니다.

    def validate_answer(example, pred, trace=None):
               return example.answer.lower() == pred.answer.lower()

    어떤 사람들은 이러한 유틸리티(기본 제공)가 편리하다고 생각합니다:

    dspy.evaluate.metrics.answer_exact_match
    dspy.evaluate.metrics.answer_passage_match

    여러 프로퍼티를 확인하는 등 메트릭이 더 복잡할 수 있습니다. 아래 메트릭은 트레이스가 None인 경우(즉, 평가 또는 최적화에 사용되는 경우) float 반환하고 그렇지 않은 경우(즉, 데모를 부트스트랩하는 데 사용되는 경우) bool 을 반환합니다.

    def validate_context_and_answer(example, pred, trace=None):
        # check the gold label and the predicted answer are the same
        answer_match = example.answer.lower() == pred.answer.lower()
    
        # check the predicted answer comes from one of the retrieved contexts
        context_match = any((pred.answer.lower() in c) for c in pred.context)
    
        if trace is None: # if we're doing evaluation or optimization
            return (answer_match + context_match) / 2.0
        else: # if we're doing bootstrapping, i.e. self-generating good demonstrations of each step
            return answer_match and context_match

    좋은 지표를 정의하는 것은 반복적인 과정이므로 초기 평가를 수행하고 데이터와 결과물을 살펴보는 것이 중요합니다.

    평가

    metric이 있으면 간단한 Python 루프에서 평가를 실행할 수 있습니다.

    scores = []
    for x in devset:
      pred = program(**x.inputs())
      score = metric(x, pred)
      scores.append(score)

    일부 유틸리티가 필요한 경우, 기본 제공 평가 유틸리티를 사용할 수도 있습니다. 병렬 평가(여러 스레드)나 입력/출력 샘플 및 메트릭 점수 표시와 같은 작업에 도움이 될 수 있습니다.

    from dspy.evaluate import Evaluate
    
    # 코드에서 재사용할 수 있는 Evaluate를 설정하세요.
    evaluator = Evaluate(devset=YOUR_DEVSET, num_threads=1, display_progress=True, display_table=5)
    
    # 평가 시작.
    evaluator(YOUR_PROGRAM, metric=YOUR_METRIC)

    중급: metric에 AI 피드백 사용하기

    대부분의 애플리케이션에서 시스템은 긴 형식의 출력을 출력하므로 메트릭은 LM의 AI 피드백을 사용하여 출력의 여러 차원을 확인해야 합니다. 이 간단한 서명이 유용할 수 있습니다.

    # 자동 평가를 위한 서명을 정의합니다.
    class Assess(dspy.Signature):
        """Assess the quality of a tweet along the specified dimension."""
    
        assessed_text = dspy.InputField()
        assessment_question = dspy.InputField()
        assessment_answer = dspy.OutputField(desc="Yes or No")

    예를 들어, 아래는 생성된 트윗이 (1) 주어진 질문에 올바르게 답변하는지, (2) 참여도가 높은지 확인하기 위해 GPT-4-turbo를 사용하는 간단한 측정지표입니다. 또한 (3) len(트윗) <= 280자인지도 확인합니다.

    gpt4T = dspy.OpenAI(model='gpt-4-1106-preview', max_tokens=1000, model_type='chat')
    
    def metric(gold, pred, trace=None):
        question, answer, tweet = gold.question, gold.answer, pred.output
    
        engaging = "Does the assessed text make for a self-contained, engaging tweet?"
        correct = f"The text should answer `{question}` with `{answer}`. Does the assessed text contain this answer?"
        
        with dspy.context(lm=gpt4T):
            correct =  dspy.Predict(Assess)(assessed_text=tweet, assessment_question=correct)
            engaging = dspy.Predict(Assess)(assessed_text=tweet, assessment_question=engaging)
    
        correct, engaging = [m.assessment_answer.lower() == 'yes' for m in [correct, engaging]]
        score = (correct + engaging) if correct and (len(tweet) <= 280) else 0
    
        if trace is not None: return score >= 2
        return score / 2.0

    컴파일할 때 추적은 None이 아니며, 엄격하게 판단하고 싶기 때문에 점수가 >= 2인 경우에만 True를 반환합니다. 그렇지 않으면 1.0 만점의 점수를 반환합니다(즉, 점수 / 2.0).

    고급: DSPy 프로그램을 metric으로 사용하기

    메트릭 자체가 DSPy 프로그램인 경우 반복하는 가장 강력한 방법 중 하나는 메트릭 자체를 컴파일(최적화)하는 것입니다. 메트릭의 출력은 보통 단순한 값(예: 5점 만점)이므로 몇 가지 예제를 수집하여 메트릭의 메트릭을 쉽게 정의하고 최적화할 수 있기 때문에 이 방법은 일반적으로 쉽습니다.

    고급: trace에 액세스하기

    평가 실행 중에 메트릭이 사용되는 경우 DSPy는 프로그램의 단계를 trace하려고 시도하지 않습니다.

    하지만 컴파일(최적화) 중에는 DSPy가 LM 호출을 trace합니다. 이 trace에는 각 DSPy 예측자에 대한 입력/출력이 포함되며 이를 활용하여 최적화를 위한 중간 단계를 검증할 수 있습니다.

    def validate_hops(example, pred, trace=None):
        hops = [example.question] + [outputs.query for *_, outputs in trace if 'query' in outputs]
    
        if max([len(h) for h in hops]) > 100: return False
        if any(dspy.evaluate.answer_exact_match_str(hops[idx], hops[:idx], frac=0.8) for idx in range(2, len(hops))): return False
    
        return True