Connect with us

プロンプトエンジニアリング

LLMをDSPyで最適化: AIシステムを構築、最適化、評価するためのステップバイステップガイド

mm
DSPy is a framework for algorithmically optimizing LM prompts and weights

大規模言語モデル(LLM)の能力が拡大するにつれて、LLMの潜在能力を活用した強健なAIシステムを開発することは、ますます複雑な作業になっています。従来のアプローチでは、複雑なプロンプティング技術、ファインチューニング用のデータ生成、およびドメイン固有の制約に従うための手動ガイダンスが必要です。しかし、このプロセスは面倒で、エラーが発生しやすく、人間の介入に大きく依存しています。

ここで、DSPyが登場します。DSPyは、LLMを活用したAIシステムの開発を容易にするために設計された革新的なフレームワークです。DSPyは、LMプロンプトと重みを最適化するための体系的なアプローチを導入し、開発者が最小限の手作業で複雑なアプリケーションを構築できるようにします。

この包括的なガイドでは、DSPyの基本原理、モジュラーなアーキテクチャ、以及提供される強力な機能について探ります。また、実践的な例を示し、DSPyがLLMを使用したAIシステムの開発方法をどのように変えるかを示します。

DSPyとは何か、そしてなぜ必要なのか?

DSPyは、プログラムのフロー(モジュール)と各ステップのパラメータ(LMプロンプトと重み)を分離するフレームワークです。この分離により、LMプロンプトと重みを体系的に最適化でき、開発者はより信頼性が高く、予測可能で、ドメイン固有の制約に従った複雑なAIシステムを構築できます。

従来、LLMを使用したAIシステムの開発には、問題をステップに分解し、各ステップに複雑なプロンプトを作成し、ファインチューニング用の合成例を生成し、LMが特定の制約に従うように手動でガイドするという面倒なプロセスが必要でした。このアプローチは時間がかかり、エラーが発生しやすく、パイプライン、LM、またはデータの小さな変更でもプロンプトとファインチューニングのステップの大量な再作業を必要としました。

DSPyは、これらの課題に対処するために新しいパラダイムを導入します:最適化器。これらのLM駆動のアルゴリズムは、指定されたメトリックを最大化するために、LM呼び出しのプロンプトと重みを調整できます。最適化プロセスを自動化することで、DSPyは開発者が最小限の手作業で強健なAIシステムを構築できるようにします。これにより、LMの出力の信頼性と予測可能性が向上します。

DSPyのモジュラーなアーキテクチャ

DSPyの核心には、複雑なAIシステムの構成を容易にするモジュラーなアーキテクチャがあります。フレームワークは、dspy.ChainOfThoughtdspy.ReActなどのさまざまなプロンプティング技術を抽象化するビルトインモジュールを提供します。これらのモジュールは、より大きなプログラムに組み合わせて構成でき、開発者が特定の要件に合わせた複雑なパイプラインを構築できるようにします。

各モジュールには、インストラクション、ファインチューニング例、LM重みを含む学習可能なパラメータが含まれています。モジュールが呼び出されると、DSPyの最適化器はこれらのパラメータを指定されたメトリックを最大化するためにファインチューニングできます。こうして、LMの出力が指定された制約と要件に従うことが保証されます。

DSPyを使用した最適化

DSPyは、AIシステムのパフォーマンスと信頼性を向上させるために設計された強力な最適化器の範囲を導入します。これらの最適化器は、LM駆動のアルゴリズムを使用して、LM呼び出しのプロンプトと重みを調整し、指定されたメトリックを最大化しながらドメイン固有の制約に従います。

DSPyで利用できる主な最適化器には、次のものがあります:

  1. BootstrapFewShot: この最適化器は、プロンプトに最適化された例を自動的に生成して含めることで、シグネチャを拡張します。ファインチューニングに少数のショット学習を実装します。
  2. BootstrapFewShotWithRandomSearch: BootstrapFewShotを複数回、生成されたデモのランダム検索で適用し、最適化で最も優れたプログラムを選択します。
  3. MIPRO: 各ステップでインストラクションと少数のショット例を生成し、インストラクションの生成がデータとデモに依存します。モジュール全体で生成インストラクションとデモの空間を効果的に検索するためにベイズ最適化を使用します。
  4. BootstrapFinetune: プロンプトベースのDSPyプログラムを、小さいLMの重み更新に蒸留します。基礎となるLLMをファインチューニングして効率を向上させることができます。

これらの最適化器を利用することで、開発者はAIシステムを体系的に最適化でき、高品質の出力を保証しながらドメイン固有の制約と要件に従うことができます。

DSPyの開始

DSPyの力を示すために、質問回答のための回復増強生成(RAG)システムを構築する実践的な例を通じて説明しましょう。

ステップ 1: 言語モデルと回復モデルの設定

最初のステップでは、DSPy内で言語モデル(LM)と回復モデル(RM)を構成します。

DSPyをインストールするには:


pip install dspy-ai

DSPyは、複数のLMとRM API、およびローカル モデル ホスティングをサポートしているため、好みのモデルを簡単に統合できます。


import dspy

# LMとRMを構成する
turbo = dspy.OpenAI(model='gpt-3.5-turbo')
colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')

dspy.settings.configure(lm=turbo, rm=colbertv2_wiki17_abstracts)

ステップ 2: データセットの読み込み

次に、HotPotQAデータセットを読み込みます。このデータセットには、通常マルチホップで回答される複雑な質問と回答のペアのコレクションが含まれています。


from dspy.datasets import HotPotQA

# データセットを読み込む
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)

# '質問'フィールドを入力として指定する
trainset = [x.with_inputs('質問') for x in dataset.train]
devset = [x.with_inputs('質問') for x in dataset.dev]

ステップ 3: シグネチャの構築

DSPyは、モジュールの動作を定義するためにシグネチャを使用します。この例では、答えの生成タスク用のシグネチャを定義し、入力フィールド(コンテキストと質問)と出力フィールド(答え)を指定します。


class GenerateAnswer(dspy.Signature):
"""短い事実の答えで質問に答える。」

コンテキスト = dspy.InputField(desc="関連する事実を含む場合があります")
質問 = dspy.InputField()
答え = dspy.OutputField(desc="通常1〜5語の間")

ステップ 4: パイプラインの構築

RAGパイプラインをDSPyモジュールとして構築します。これは、サブモジュール(dspy.Retrieveとdspy.ChainOfThought)を宣言するための初期化メソッド(__init__)と、質問に答えるためのコントロール フローを記述するためのforwardメソッド(forward)で構成されます。


class RAG(dspy.Module):
def __init__(self, num_passages=3):
super().__init__()

self.retrieve = dspy.Retrieve(k=num_passages)
self.generate_answer = dspy.ChainOfThought(GenerateAnswer)

def forward(self, question):
context = self.retrieve(question).passages
prediction = self.generate_answer(context=context, question=question)
return dspy.Prediction(context=context, answer=prediction.answer)

ステップ 5: パイプラインの最適化

パイプラインが定義されると、DSPyの最適化器を使用して最適化できます。この例では、BootstrapFewShot最適化器を使用します。これは、トレーニングセットと検証メトリックに基づいて、モジュールに効果的なプロンプトを生成および選択します。


from dspy.teleprompt import BootstrapFewShot

# 検証メトリック
def validate_context_and_answer(example, pred, trace=None):
answer_EM = dspy.evaluate.answer_exact_match(example, pred)
answer_PM = dspy.evaluate.answer_passage_match(example, pred)
return answer_EM and answer_PM

# 最適化器を設定する
teleprompter = BootstrapFewShot(metric=validate_context_and_answer)

# プログラムをコンパイルする
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)

ステップ 6: パイプラインの評価

プログラムをコンパイルした後、開発セットでパイプラインのパフォーマンスを評価して、所望の精度と信頼性を確保することが重要です。


from dspy.evaluate import Evaluate

# 評価器を設定する
evaluate = Evaluate(devset=devset, metric=validate_context_and_answer, num_threads=4, display_progress=True, display_table=0)

# コンパイルされたRAGプログラムを評価する
evaluation_result = evaluate(compiled_rag)

print(f"評価結果: {evaluation_result}")

ステップ 7: モデルの履歴の検査

モデルの動作をより深く理解するために、モデルの履歴を検査できます。


# モデルの履歴を検査する
turbo.inspect_history(n=1)

ステップ 8: 予測の作成

パイプラインが最適化および評価された後、新しい質問で予測を作成できます。


# 例の質問
question = "Gary Zukavの最初の本はどの賞を受賞しましたか?"

# コンパイルされたRAGプログラムを使用して予測を作成する
prediction = compiled_rag(question)

print(f"質問: {question}")
print(f"答え: {prediction.answer}")
print(f"回復されたコンテキスト: {prediction.context}")

最小動作例 with DSPy

ここで、GSM8KデータセットとOpenAI GPT-3.5-turboモデルを使用して、DSPy内でのプロンプティング タスクをシミュレートする別の最小動作例を示します。

設定

まず、環境が適切に構成されていることを確認します:


import dspy
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric

# LMを設定する
turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250)
dspy.settings.configure(lm=turbo)

# GSM8Kデータセットから数学の質問を読み込む
gsm8k = GSM8K()
gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10]

print(gsm8k_trainset)

gsm8k_trainsetgsm8k_devsetデータセットには、質問と答えのフィールドを持つ例のリストが含まれています。

モジュールの定義

次に、ステップバイステップの推論に使用するChainOfThoughtモジュールを使用するカスタム プログラムを定義します:


class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought("質問 -> 答え")

def forward(self, question):
return self.prog(question=question)

モデルのコンパイルと評価

ここで、BootstrapFewShotテレプロンプトを使用してコンパイルします:


from dspy.teleprompt import BootstrapFewShot

# 最適化器を設定する
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4)

# gsm8k_metricを使用して最適化する
teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config)
optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset)

# 評価器を設定する
from dspy.evaluate import Evaluate

evaluate = Evaluate(devset=gsm8k_devset, metric=gsm8k_metric, num_threads=4, display_progress=True, display_table=0)
evaluate(optimized_cot)

# モデルの履歴を検査する
turbo.inspect_history(n=1)

この例は、環境を設定し、カスタム モジュールを定義し、モデルをコンパイルおよび評価し、提供されたデータセットとテレプロンプト構成を使用して、DSPyを使用する方法を示しています。

データ管理 in DSPy

DSPyは、トレーニング、開発、テストセットを操作します。データの各例には、入力、中間ラベル、最終ラベルの3種類の値があります。中間または最終ラベルはオプションですが、少なくとも1つの入力例が必要です。

例オブジェクトの作成

DSPyの例オブジェクトは、Pythonの辞書書きに似ていますが、便利なユーティリティが付属しています:


qa_pair = dspy.Example(質問="これは質問ですか?", 答え="これは答えです.")

print(qa_pair)
print(qa_pair.質問)
print(qa_pair.答え)

出力:


Example({質問: 'これは質問ですか?', 答え: 'これは答えです.'}) (input_keys=None)
これは質問ですか?
これは答えです.

入力キーの指定

DSPyでは、Exampleオブジェクトにwith_inputs()メソッドがあり、特定のフィールドを入力としてマークできます:


print(qa_pair.with_inputs("質問"))
print(qa_pair.with_inputs("質問", "答え"))

値はドット演算子を使用してアクセスでき、inputs()およびlabels()メソッドは、入力または非入力キーのみを含む新しいExampleオブジェクトを返します。

最適化器 in DSPy

DSPy最適化器は、DSPyプログラムのパラメータ(プロンプトと/またはLM重み)を、指定されたメトリックを最大化するように調整します。DSPyには、さまざまな戦略を使用するさまざまなビルトイン最適化器があります。

利用可能な最適化器

  • BootstrapFewShot: ラベル付き入力と出力データ ポイントを使用して、少数のショット例を生成します。
  • BootstrapFewShotWithRandomSearch: 生成されたデモのランダム検索でBootstrapFewShotを複数回適用します。
  • COPRO: 各ステップで新しいインストラクションを生成および改良し、座標上昇を使用して最適化します。
  • MIPRO: ベイズ最適化を使用して、インストラクションと少数のショット例を最適化します。

最適化器の選択

不確かな場合は、BootstrapFewShotWithRandomSearchを使用します:

データが非常に少ない場合(10個の例)、BootstrapFewShotを使用します。
少し多くのデータ(50個の例)がある場合、BootstrapFewShotWithRandomSearchを使用します。
大きなデータセット(300個以上の例)がある場合、MIPROを使用します。

ここに、BootstrapFewShotWithRandomSearchの使用方法が示されています:


from dspy.teleprompt import BootstrapFewShotWithRandomSearch

config = dict(max_bootstrapped_demos=4, max_labeled_demos=4, num_candidate_programs=10, num_threads=4)
teleprompter = BootstrapFewShotWithRandomSearch(metric=YOUR_METRIC_HERE, **config)
optimized_program = teleprompter.compile(YOUR_PROGRAM_HERE, trainset=YOUR_TRAINSET_HERE)

最適化されたプログラムの保存と読み込み

最適化器を実行した後、将来の使用のために最適化されたプログラムを保存できます。

最適化されたプログラムを保存する:

optimized_program.save(YOUR_SAVE_PATH)

保存されたプログラムを読み込む:


loaded_program = YOUR_PROGRAM_CLASS()
loaded_program.load(path=YOUR_SAVE_PATH)

高度な機能: DSPyアサーション

DSPyアサーションは、LMの計算制約を自動的に適用し、LMの出力の信頼性、予測可能性、正確性を向上させます。

アサーションの使用

検証関数を定義し、モデルの生成後にアサーションを宣言します。たとえば:


dspy.Suggest(
len(クエリ) <= 100,
"クエリは短く、100文字以下である必要があります",
)

dspy.Suggest(
validate_query_distinction_local(prev_queries, query),
"クエリは次のものと異なる必要があります: " + "; ".join(f"{i+1}) {q}" for i, q in enumerate(prev_queries)),
)

アサーションを使用したプログラムの変換


from dspy.primitives.assertions import assert_transform_module, backtrack_handler

baleen_with_assertions = assert_transform_module(SimplifiedBaleenAssertions(), backtrack_handler)

または、プログラムに直接アサーションをアクティブにします:


baleen_with_assertions = SimplifiedBaleenAssertions().activate_assertions()

アサーション駆動の最適化

DSPyアサーションは、特にBootstrapFewShotWithRandomSearchを含むDSPy最適化と連携して機能します。コンパイルと推論の両方でアサーションを含む設定が含まれます。

結論

DSPyは、言語モデルとそのプロンプトを最適化するための強力で体系的なアプローチを提供します。このガイドで説明されているステップに従うことで、開発者は複雑なAIシステムを簡単に構築、最適化、および評価できます。DSPyのモジュラー デザインと高度な最適化器により、さまざまな言語モデルを効率的に統合でき、NLPとAIの分野で働くすべての人の貴重なツールとなります。

単純な質問回答システムを構築する場合でも、より複雑なパイプラインを構築する場合でも、DSPyは柔軟性と堅牢性を提供して、高パフォーマンスと信頼性を実現します。

私は過去5年間、機械学習とディープラーニングの魅力的世界に没頭してきました。私の情熱と専門知識は、AI/MLに特に焦点を当てた50以上の多様なソフトウェアエンジニアリングプロジェクトに貢献することにつながりました。私の継続的な好奇心は、私がさらに探究したい分野である自然言語処理にも私を引き寄せました。