度量
DSPy 是一个机器学习框架,因此您必须考虑用于评估(跟踪您的进展)和优化(使 DSPy 使您的程序更有效)的**自动度量**。
什么是度量,以及如何为我的任务定义度量?
度量就是一个函数,它接收您的数据中的示例和您系统的输出,并返回一个分数来量化输出的质量。是什么决定了您系统输出的好坏?
对于简单任务,这可能仅仅是“准确率”或“精确匹配”或“F1 分数”。对于简单的分类或短问答任务,情况可能如此。
然而,对于大多数应用,您的系统会输出长篇内容。在这种情况下,您的度量很可能应该是一个更小的 DSPy 程序,用于检查输出的多个属性(很可能使用来自语言模型的 AI 反馈)。
一次性做到这一点不太可能,但您应该从简单的开始并进行迭代。
简单度量
DSPy 度量只是 Python 中的一个函数,它接收 example
(例如,来自您的训练集或开发集)和您的 DSPy 程序的输出 pred
,并输出一个 float
(或 int
或 bool
)分数。
您的度量还应该接受一个可选的第三个参数 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
您的度量可能更复杂,例如检查多个属性。下面的度量将在 trace is 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
定义一个好的度量是一个迭代的过程,因此进行一些初步评估并查看您的数据和输出是关键。
评估
一旦您有了度量,就可以在一个简单的 Python 循环中运行评估。
scores = []
for x in devset:
pred = program(**x.inputs())
score = metric(x, pred)
scores.append(score)
如果您需要一些实用程序,您也可以使用内置的 Evaluate
实用程序。它可以帮助进行并行评估(多线程)或向您展示输入/输出样本以及度量分数等事情。
from dspy.evaluate import Evaluate
# Set up the evaluator, which can be re-used in your code.
evaluator = Evaluate(devset=YOUR_DEVSET, num_threads=1, display_progress=True, display_table=5)
# Launch evaluation.
evaluator(YOUR_PROGRAM, metric=YOUR_METRIC)
中级:使用 AI 反馈作为您的度量
对于大多数应用,您的系统会输出长篇内容,因此您的度量应该使用来自语言模型的 AI 反馈来检查输出的多个维度。
这个简单的签名可能会派上用场。
# Define the signature for automatic assessments.
class Assess(dspy.Signature):
"""Assess the quality of a tweet along the specified dimension."""
assessed_text = dspy.InputField()
assessment_question = dspy.InputField()
assessment_answer: bool = dspy.OutputField()
例如,下面是一个简单的度量,它检查生成的推文 (1) 正确回答了给定问题,以及 (2) 是否具有吸引力。我们还检查 (3) len(tweet) <= 280
个字符。
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?"
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 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
在编译时,trace is not None
,并且我们希望严格判断,因此只有当 score >= 2
时,我们才会返回 True
。否则,我们返回一个 1.0 分制的分数(即 score / 2.0
)。
高级:使用 DSPy 程序作为您的度量
如果您的度量本身是一个 DSPy 程序,那么迭代的最强大方法之一就是编译(优化)您的度量本身。这通常很容易,因为度量的输出通常是一个简单的值(例如,5 分制的分数),因此度量的度量很容易定义,并且可以通过收集几个示例来优化。
高级:访问 trace
当您的度量在评估运行时使用时,DSPy 不会尝试跟踪您程序的步骤。
但在编译(优化)期间,DSPy 将跟踪您的语言模型调用。跟踪将包含每个 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