メインコンテンツへスキップ
これはインタラクティブなノートブックです。ローカルで実行するか、以下のリンクをご利用ください。
BIG-bench (Beyond the Imitation Game Benchmark) は、大規模言語モデルを評価し、200 以上のタスクにわたる将来的な能力を推定するための共同ベンチマークです。BIG-Bench Hard (BBH) は、現世代の言語モデルが解くことが困難な BIG-Bench の最難関 23 タスクをまとめたスイートです。 このチュートリアルでは、BIG-bench Hard ベンチマークの因果判断タスクを対象に、LLM ワークフローのパフォーマンスを向上させてプロンプティング戦略を評価する方法を説明します。LLM ワークフローの実装とプロンプティング戦略の最適化には DSPy を使用し、LLM ワークフローのトラッキングとプロンプティング戦略の評価には Weave を使用します。 このチュートリアルを完了すると、因果推論のためのベースライン DSPy プログラムを構築し、Weave で評価し、DSPy オプティマイザーを適用してプロンプティング戦略を改善し、最適化されたプログラムとベースラインを比較できるようになります。このチュートリアルは、自身の LLM ワークフローに プロンプト最適化 を適用し、Weave を使用して結果をトラッキング・比較したい実践者を対象としています。

依存ライブラリのインストール

開始する前に、このチュートリアル全体で使用するライブラリをインストールしてください。このチュートリアルでは、次のライブラリを使用します。
  • DSPy:LLMワークフローの構築と最適化に使用します。
  • Weave:LLMワークフローをトラッキングし、プロンプト戦略を評価するために使用します。
  • datasets:HuggingFace Hub から BIG-Bench Hard データセットにアクセスするために使用します。
!pip install -qU dspy weave "datasets<4"
このチュートリアルでは、LLMベンダーとして OpenAI API を使用するため、OpenAI APIキーも必要です。ご自身のAPIキーを取得するには、OpenAIプラットフォームで サインアップ してください。
import os
from getpass import getpass

api_key = getpass("Enter you OpenAI API key: ")
os.environ["OPENAI_API_KEY"] = api_key

Weave を使ってトラッキングを有効にする

このセクションでは Weave を設定し、このチュートリアルで以降に行う DSPy の Call が自動的にトレースされ、Weave UI で確認できるようにします。 Weave は DSPy と統合されています。コードの先頭で weave.init を呼び出すと、DSPy の関数が自動的にトレースされ、その内容を Weave UI で確認できます。詳しくは、DSPy 向け Weave インテグレーションのドキュメント を参照してください。
import weave

weave.init(project_name="dspy-bigbench-hard")
このチュートリアルでは、メタデータの管理に、weave.Object を継承したメタデータクラスを使用します。
class Metadata(weave.Object):
    dataset_address: str = "maveriq/bigbenchhard"
    big_bench_hard_task: str = "causal_judgement"
    num_train_examples: int = 50
    openai_model: str = "gpt-4o-mini"
    openai_max_tokens: int = 2048
    max_bootstrapped_demos: int = 8
    max_labeled_demos: int = 8

metadata = Metadata()
オブジェクトのバージョン管理: Metadata オブジェクトは、それらを利用する関数がトレースされると、自動的にバージョン管理され、あわせてトレースされます。

BIG-Bench Hardデータセットを読み込む

Weave tracking を有効にしたら、次のステップは、DSPyプログラムのトレーニングと評価に使用するデータを準備することです。 このデータセットをHuggingFace Hubから読み込み、トレーニング用セットと検証セットに分割したうえで、Weaveで公開します。公開すると、データセットをバージョン管理できるようになり、weave.Evaluationを使用してプロンプト戦略を評価することもできます。
import dspy
from datasets import load_dataset

@weave.op()
def get_dataset(metadata: Metadata):
    # HuggingFace HubからタスクにあったBIG-Bench Hardデータセットを読み込む
    dataset = load_dataset(metadata.dataset_address, metadata.big_bench_hard_task)[
        "train"
    ]

    # トレーニング用データセットと検証用データセットを作成する
    rows = [{"question": data["input"], "answer": data["target"]} for data in dataset]
    train_rows = rows[0 : metadata.num_train_examples]
    val_rows = rows[metadata.num_train_examples :]

    # `dspy.Example`オブジェクトで構成されるトレーニング用サンプルと検証用サンプルを作成する
    dspy_train_examples = [
        dspy.Example(row).with_inputs("question") for row in train_rows
    ]
    dspy_val_examples = [dspy.Example(row).with_inputs("question") for row in val_rows]

    # データセットをWeaveに公開する。これによりデータのバージョン管理と評価への活用が可能になる
    weave.publish(
        weave.Dataset(
            name=f"bigbenchhard_{metadata.big_bench_hard_task}_train", rows=train_rows
        )
    )
    weave.publish(
        weave.Dataset(
            name=f"bigbenchhard_{metadata.big_bench_hard_task}_val", rows=val_rows
        )
    )

    return dspy_train_examples, dspy_val_examples

dspy_train_examples, dspy_val_examples = get_dataset(metadata)
データセット準備のstepとデータ構造を示すDSPyのデータセット読み込みインターフェース

DSPy プログラム

データセットを Weave に公開したので、次に、後で評価および最適化するベースラインの DSPy プログラムを定義できます。 DSPy は、新しい LM パイプラインの構築を、自由記述の文字列を操作するやり方から、プログラミング (モジュール化されたオペレーターを組み合わせてテキスト変換グラフを構成すること) へと近づけるフレームワークです。これにより、コンパイラがプログラムから最適化された LM 呼び出し戦略とプロンプトを自動生成します。 言語モデルの設定には dspy.LM を使用し、それをデフォルトに設定するには dspy.configure を使用します。
llm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=llm)

因果推論シグネチャを作成する

signature とは、DSPy module の入出力の動作を宣言的に定義するものです。DSPy modules は、ニューラルネットワークの層に似たタスク適応型のコンポーネントで、特定のテキスト変換を抽象化します。
class CausalReasoning(dspy.Signature):
    """You are an expert in causal reasoning. Analyze the given question carefully
    and answer Yes or No. Provide a detailed explanation justifying your answer."""

    question: str = dspy.InputField(desc="The question to be answered")
    answer: str = dspy.OutputField(desc="Yes or No")
    confidence: float = dspy.OutputField(desc="Confidence score between 0 and 1")
    explanation: str = dspy.OutputField(desc="Detailed explanation for the answer")

class CausalReasoningModule(dspy.Module):
    def __init__(self):
        self.prog = dspy.Predict(CausalReasoning)

    @weave.op()
    def forward(self, question: str) -> dict:
        result = self.prog(question=question)
        return {
            "answer": result.answer,
            "confidence": result.confidence,
            "explanation": result.explanation,
        }
BIG-Bench Hard の因果推論サブセットの例を使って、LLMワークフロー、つまり CausalReasoningModule をテストします。
import rich

baseline_module = CausalReasoningModule()

prediction = baseline_module(dspy_train_examples[0]["question"])
rich.print(prediction)
パフォーマンス メトリクスと出力例を含むベースライン DSPy プログラムの評価結果

DSPy プログラムを評価する

ベースラインのプロンプト戦略ができたので、予測した回答と正解を照合するメトリクスを使って、weave.Evaluation で検証セットを評価します。Weave は各例をアプリケーションに渡し、複数のカスタムスコアリング関数で出力を採点します。これにより、アプリケーションのパフォーマンスを把握できるほか、個々の出力やスコアを詳しく確認できる充実した UI も利用できます。 まず、予測した回答が正解と一致するかどうかを判定するスコアリング関数を作成します。Weave のスコアリング関数は、モデルの戻り値を output として受け取り、さらにデータセット例内の一致するキーを追加の引数として受け取ります。ここでは、answer はデータセットから取得され、outputCausalReasoningModule.forward が返す dict です。
@weave.op()
def weave_evaluation_scorer(answer: str, output: dict) -> dict:
    return {"match": int(answer.lower() == output["answer"].lower())}
次に、weave.Evaluation から呼び出せるように、モジュールをトレース対象の関数でラップします。ラッパーの引数名は、モデルが受け取るデータセットの列名と一致している必要があります。
@weave.op()
def predict(question: str) -> dict:
    return baseline_module(question=question)
ここで、評価を定義して実行します。
validation_dataset = weave.ref(
    f"bigbenchhard_{metadata.big_bench_hard_task}_val:v0"
).get()

evaluation = weave.Evaluation(
    name="baseline_causal_reasoning_module",
    dataset=validation_dataset,
    scorers=[weave_evaluation_scorer],
)

await evaluation.evaluate(predict)
DSPy プログラムのパフォーマンスメトリクス、トレース、比較結果を示す Weave 評価ダッシュボード
Python スクリプトから実行している場合は、次のコードを使用して評価を実行できます。
import asyncio
asyncio.run(evaluation.evaluate(predict))
因果推論データセットで評価を実行するには、OpenAI クレジットが約 $0.24 かかります。

DSPy プログラムを最適化する

ベースラインのパフォーマンスを測定できたので、DSPy オプティマイザーを適用し、その結果をベースラインと比較できます。 これで DSPy プログラムのベースラインができたので、指定したメトリクスを最大化するように DSPy プログラムのパラメーターを調整できる BootstrapFewShot オプティマイザーを使って、因果推論のパフォーマンスをさらに向上させてみましょう。
from dspy.teleprompt import BootstrapFewShot

@weave.op()
def get_optimized_program(model: dspy.Module, metadata: Metadata) -> dspy.Module:
    @weave.op()
    def dspy_evaluation_metric(true, prediction, trace=None):
        return prediction["answer"].lower() == true.answer.lower()

    teleprompter = BootstrapFewShot(
        metric=dspy_evaluation_metric,
        max_bootstrapped_demos=metadata.max_bootstrapped_demos,
        max_labeled_demos=metadata.max_labeled_demos,
    )
    return teleprompter.compile(model, trainset=dspy_train_examples)

optimized_module = get_optimized_program(baseline_module, metadata)
テレプロンプターの設定と最適化の進行状況を示す、DSPyプログラム最適化プロセスのインターフェース
因果推論の評価用データセットを実行すると、OpenAI クレジットが約 $0.04 かかります。
最適化済みのプログラム (最適化されたプロンプト戦略) が用意できたので、これを検証セットでもう一度評価し、ベースラインの DSPy プログラムと比較してください。
@weave.op()
def predict_optimized(question: str) -> dict:
    return optimized_module(question=question)

evaluation = weave.Evaluation(
    name="optimized_causal_reasoning_module",
    dataset=validation_dataset,
    scorers=[weave_evaluation_scorer],
)

await evaluation.evaluate(predict_optimized)
パフォーマンスメトリクスと出力品質が改善された、最適化済みDSPyプログラムの評価結果
ベースラインプログラムの評価結果を最適化済みプログラムと比較すると、最適化済みプログラムのほうが因果推論に関する質問に対してより高い精度で回答できていることがわかります。

結論

このチュートリアルでは、DSPyを使ってプロンプト最適化を行い、Weaveでトラッキングと評価を行って、元のプログラムと最適化後のプログラムを比較する方法を学びました。