DSPy によるプロンプト: 新しいアプローチ
導入
言語モデル (LM) を使用し、組み合わせるより良い方法に常に取り組む時代が到来しました。通常、LLM は試行錯誤によって作成された固定の「プロンプト テンプレート」を使用します。 DSPy は、LM パイプラインを管理しやすいテキスト変換グラフに変えることでこれを簡素化する新しいメソッドです。これらのグラフは、プロンプト、微調整、推論の方法を学習および改善できるモジュールを使用しています。
DSPy には、パフォーマンスを向上させるためにこれらのパイプラインを最適化するツールが含まれています。研究によると、DSPy は効果的な LM パイプラインを迅速に作成でき、従来の方法よりもパフォーマンスが大幅に向上します。また、より小型のオープン モデルが、GPT-3.5 などの高度なモデル用に専門家が設計したプロンプトと競合できるようになります。
前提条件
DSPy について説明する前に、次のものが揃っていることを確認してください。
- 基本的なプログラミング知識: Python とそのライブラリに関する知識。
- LLM の理解: 大規模言語モデルとそのプロンプト メカニズムについての基礎的な理解。
- 環境セットアップ: 必要なライブラリがインストールされた、Jupyter Notebook や VS Code などの Python 開発環境へのアクセス。
DSPyとは何ですか?
DSPy は、特に LM を複数回使用する場合に、言語モデル (LM) のプロンプトと重みの最適化を容易にするフレームワークです。 DSPy を使用しない場合、LM を使用して複雑なシステムを構築するには、問題の分解、プロンプトの微調整、ステップの微調整、合成サンプルの生成、より小さな LM の微調整など、多くの手動ステップが必要になるため、面倒で煩雑になる可能性があります。
DSPy は、プログラムのフローをパラメーター (プロンプトと重み) から分離し、望ましい結果に基づいてこれらのパラメーターを調整する新しいオプティマイザーを導入することで、これを合理化します。これにより、GPT-4 や T5 ベースなどの強力なモデルの信頼性と効率が向上します。手動でプロンプトを調整する代わりに、DSPy はアルゴリズムを使用してパラメータを更新するため、コード、データ、またはメトリクスの変更に合わせてプログラムを再コンパイルできます。
ニューラル ネットワークに PyTorch などのフレームワークを使用するようなものだと考えてください。すべての詳細を手動で調整するのではなく、代わりにレイヤーとオプティマイザーを使用して最適なパラメーターを学習します。同様に、DSPy は、LM の操作を自動化および強化するモジュールとオプティマイザーを提供し、手動による調整ではなく、体系的な改善とパフォーマンスの向上を重視します。
DSPy とは何の略ですか?
頭文字「now 」は、スタンフォード NLP 大学によって作成された「Declarative Self-improving Language Programs」を表します。
DSPy は、特に複数ステップのパイプラインの場合、言語モデル (LM) プロンプトと重みを最適化する複雑なプロセスを合理化します。従来は、問題を分析し、プロンプトを改良し、ステップを微調整し、合成サンプルを生成し、より小さなモデルを微調整する必要がありました。変更にはプロンプトの再作成と微調整が必要になるため、これは面倒で時間がかかります。
DSPy は、プログラム フローを LM パラメーターから分離し、オプティマイザーを導入することにより、GPT-3.5、GPT-4、T5 ベース、または Llama2-13b などのモデルの信頼性を強化します。これにより、結果がより効果的になり、エラーが発生しにくくなり、結果に対する信頼感が高まります。
なぜ DSPy が必要なのでしょうか?
「プロンプト テンプレート」は、特定のタスクに対する応答をガイドするために LM に提供される、事前定義された指示またはデモンストレーションです。 多くの場合、プロンプト テンプレートは試行錯誤を通じて作成されます。これは、特定のタスクやシナリオではうまく機能する可能性があることを意味しますが、別のコンテキストでは失敗するか、無関係な結果が生じる可能性があります。これらのテンプレートはハードコードされているため、適応性に欠けており、入力データ、タスク要件、さらには他の言語モデルの変化を効果的に処理できない可能性があります。 特定のプロンプト テンプレートは、特定の LM パイプラインまたはフレームワークに対して効果的に機能する可能性があります。それでも、他のパイプライン、異なる LM、さまざまなデータ ドメイン、さらには異なるタイプの入力に対してはうまく一般化できない可能性があります。この一般化の欠如により、さまざまなユースケースにわたる LM の柔軟性と適用性が制限されます。 さまざまなタスクや LM 用にプロンプト テンプレートを手動で作成したり微調整したりすると、時間と労力がかかる場合があります。タスクの複雑さと多様性が増すにつれて、これらのテンプレートの維持と更新はますます困難になり、非効率になります。
さらに、応答の生成に他の問題が発生する可能性があります。言語モデル (LM) パイプラインおよびフレームワークでハードコードされたプロンプト テンプレートを使用すると、コンテキストと関連性の欠如、出力の不一致、応答品質の低下、不正確さなどの問題が発生することがよくあります。これらの課題は、プロンプト テンプレートの柔軟性とスケーラビリティが限られていることに起因しており、プロンプト テンプレートは手動で作成され、異なる LM モデル、データ ドメイン、または入力のバリエーションにわたって効果的に汎用化できない可能性があります。
では、なぜ DSPy なのでしょうか?
- DSPy は、非構造化テキスト入力の操作から離れて、プログラミングに向けた新しい言語モデル パイプラインを構築することに重点を置いています。
- DSPy モジュールは、ニューラル ネットワーク層に似たタスク適応コンポーネントであり、質問応答や要約などのテキスト変換を抽象化します。
- DSPy コンパイラーは、トレーニング入力と検証メトリックを利用して、プログラムの品質またはコストを最適化します。
- DSPy コンパイラはプログラムのバージョンをシミュレートし、自己改善と効果的なプロンプト生成のためにサンプル トレースをブートストラップします。
- DSPy の最適化はモジュール式であり、データからのモジュール学習を決定するテレプロンプターによって実行されます。
- DSPy は、宣言型モジュールをプロンプト、微調整、推論、拡張の高品質な構成にマッピングできます。
- DSPy プログラミング モデルは、専門家が作成したプロンプトの役割を減らすことに重点を置いています。
- DSPy モジュールを構成すると、コンパイルの数分から数十分以内で単純なプログラムの品質を大幅に向上させることができます。
DSPy の主要コンポーネント
より深く掘り下げる前に、DSPy のいくつかの重要なコンポーネントを理解しましょう
- 署名
- モジュール
- テレプロンプターまたはオプティマイザー
DSPy シグネチャ は関数の宣言であり、テキスト変換の動作を達成するために特定の言語モデルをどのようにプロンプトするかを詳細に説明するのではなく、テキスト変換で何を処理する必要があるかについての簡潔な仕様を提供します。 DSPy シグネチャは、 オプションの命令を含む入力フィールドと出力フィールドで構成されるタプルです。各フィールドには、フィールド名とオプションのメタデータが含まれます。
署名は、構築しているシステムのタイプに焦点を当てます。たとえば、質問 -> 回答、英語の文書 -> フランス語の翻訳、またはコンテンツ -> 概要です。
qa = dspy.Predict (" question -> answer ")
qa(question =" Where is Guaran ´ı spoken?")Out: Prediction ( answer = ’ Guaran ´ı is spoken mainly in South America . ’)
DSPy モジュールは、言語モデルを利用するプログラムを作成するためのコア コンポーネントです。各モジュールには、思考連鎖や ReAct などの特定のプロンプト手法が含まれており、あらゆる DSPy シグネチャで動作するのに十分な多用途性を備えているように設計されています。
これらのモジュールには、プロンプトや言語モデルの重み要素などの調整可能なパラメーターがあり、入力を処理して出力を生成するために呼び出すことができます。さらに、複数の DSPy モジュールを組み合わせて、より大規模で複雑なプログラムを作成できます。 PyTorch のニューラル ネットワーク モジュールからインスピレーションを得た DSPy モジュールは、言語モデル プログラミングに同様の機能をもたらします。
例えば:-
dspy.Predict は基本モジュールであり、他のすべての DSPy モジュールはこのモジュールを使用して構築されます。
モジュールを使用するには、まず特定の署名を使用してモジュールを宣言します。次に、入力引数を使用してモジュールを呼び出し、出力フィールドを抽出します。
sentence = "it's a charming and often affecting journey." # example from the SST-2 dataset.
1) Declare with a signature.
classify = dspy.Predict('sentence -> sentiment')
2) Call with input argument(s).
response = classify(sentence=sentence)
3) Access the output.
print(response.sentiment)
出力:-
Positive
他にも使用できる DSPy モジュールがいくつかあります。
- dspy.思考の連鎖
- dspy.ReAct
- dspy.MultiChainComparison
- dspy.思考のプログラム
などなど。
DSPy の最適化にはDSPy テレプロンプターが使用されます。非常に柔軟でモジュール式です。最適化はテレプロンプターによって実行されます。これは、モジュールがデータからどのように学習するかを指示する多用途の戦略です。
DSPy オプティマイザーは、精度などの指定されたメトリクスを最大化するために、プロンプトや言語モデルの重みなどの DSPy プログラムのパラメーターを微調整するように設計されたアルゴリズムです。 DSPy はさまざまな組み込みオプティマイザーを提供しており、それぞれが異なる戦略を採用しています。通常、DSPy オプティマイザーには 3 つのものが必要です。DSPy プログラム (単一モジュールまたは複雑な複数モジュールのセットアップ)、プログラムの出力を評価してスコアリングするためのメトリック関数 (スコアが高いほどパフォーマンスが良いことを示します)、およびいくつかのトレーニング入力 (ラベルがない場合でも、5 つまたは 10 個のサンプルしかない場合もあります)。大量のデータがあることは有益ですが、DSPy は最小限の入力でも強力な結果が得られるように設計されています。
オプティマイザーはどのようにパフォーマンスを向上させるのでしょうか?
従来のディープ ニューラル ネットワーク (DNN) は、損失関数とトレーニング データを使用した勾配降下法を使用して最適化されます。対照的に、DSPy プログラムは、DSPy モジュールとして統合された複数の言語呼び出しモデル (LM) で構成されます。各モジュールには、LM 重み、命令、入出力動作のデモンストレーションという 3 つの内部パラメータがあります。
DSPy は、LM 重みの勾配降下法と、指示とデモンストレーションを調整するための LM 駆動の最適化を組み合わせた、多段階最適化アルゴリズムを使用して 3 つすべてを最適化できます。一般的な数ショットの例とは異なり、DSPy デモはより堅牢であり、プログラムに基づいて最初から生成および最適化できます。このコンパイルでは、多くの場合、人間が書くよりも優れたプロンプトが生成されます。これは、DSPy オプティマイザーが本質的により創造的だからではなく、体系的により多くのオプションを探索し、メトリクスを直接微調整できるためです。
いくつかの DSPy オプティマイザーを以下に示します。
- ラベル付き少数ショット
- ブートストラップフューショット
- ブートストラップ少数ショットランダム検索
- ブートストラップFewShotWithOptuna
- KNNFewShot
そしてリストは続きます。
さまざまな種類のオプティマイザーに関する詳細については、DSPy のドキュメントを参照することを強くお勧めします。
Langchain と Llamaindex との比較
LangChain と LlamaIndex の概要:
- langchain と llamaindex はどちらも、プロンプト LM の分野で人気のあるライブラリです。
- どちらのライブラリも、アプリケーション開発者向けに事前にパッケージ化されたコンポーネントとチェーンを提供することに重点を置いています。さらに、再利用可能なパイプライン (エージェント、取得パイプラインなど) およびツール (データベース接続、メモリ実装など) の実装も提供します。
DSPy の概要:
- DSPy は、プロンプト エンジニアリングの基本的な課題に取り組むことを目的としており、手動のプロンプト エンジニアリングを使用せずに新しい LM 計算グラフを構築します。
- さらに、コアのコンポーザブル オペレーター、シグネチャ (抽象プロンプト用)、モジュール (抽象プロンプト技術用)、およびオプティマイザーとしてのテレプロンプターが導入されています。
- DSPy は、自動コンパイルと自己改善を通じて、新しい LM パイプラインの迅速な構築と高品質の結果を容易にします。
LangChain と LlamaIndex の大きな違い:
- LangChain と LlamaIndex は手動のプロンプト エンジニアリングに依存していますが、DSPy はこれを解決することを目指しています。
- DSPy は、プロンプトを自動的にブートストラップする構造化フレームワークを提供し、手書きのプロンプトのデモンストレーションの必要性を排除します。
- 2023 年 9 月の時点で、LangChain のコードベースには 1000 文字を超える 50 個の文字列と、プロンプト エンジニアリング専用の多数のファイル (12 個の Prompts.py ファイルと 42 個の Prompt.py ファイル) が含まれていました。対照的に、DSPy には手書きのプロンプトが含まれていないにもかかわらず、さまざまな LM で高品質を実現します。
- DSPy は、ハードコードされたプロンプトよりもモジュール化されており、強力であることが証明されています。
DSPy を始める
パッケージのインストールから始めましょう。
!pip install dspy-ai
#or
!pip install git+https://github.com/stanfordnlp/dspy.git
デフォルトでは、DSPy は pip から最新の openai
をインストールします。
必要なパッケージをインポートし、
import sys
import os
import dspy
from dspy.datasets import HotPotQA
from dspy.teleprompt import BootstrapFewShot
from dspy.evaluate.evaluate import Evaluate
from dsp.utils import deduplicate
開始とデータのロード
turbo = dspy.OpenAI(model='gpt-3.5-turbo') #model name 'gpt-3.5-turbo'
colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts') #the retriever ColBERTv2
dspy.settings.configure(lm=turbo, rm=colbertv2_wiki17_abstracts)
#load the data
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)
HotpotQA は、英語版 Wikipedia をソースとする質問回答データセットで、クラウドソーシングされた約 113,000 の質問で構成されています。
この情報を使用して、質問応答システムを作成します。この目的のために、トレーニングには 20 個のデータ ポイントを使用し、開発または検証セットには 50 個のデータ ポイントを使用します。
# get the train and validation set.
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]
len(trainset), len(devset)
出力:-
(20, 50)
次に、いくつかの例を見ていきます。
train_example = trainset[0]
print(f"Question: {train_example.question}")
print(f"Answer: {train_example.answer}")
出力:-
Question: At My Window was released by which American singer-songwriter?
Answer: John Townes Van Zandt
dev_example = devset[18]
print(f"Question: {dev_example.question}")
print(f"Answer: {dev_example.answer}")
print(f"Relevant Wikipedia Titles: {dev_example.gold_titles}")
出力:-
Question: What is the nationality of the chef and restaurateur featured in Restaurant: Impossible?
Answer: English
Relevant Wikipedia Titles: {'Robert Irvine', 'Restaurant: Impossible'}
チャットボットの作成
私たちは、短い事実に基づいた回答を必要とする質問に対する署名を備えた Basic QA と呼ばれる機能を作成しています。各質問には 1 つの回答があり、1 ~ 5 単語に制限されます。
このシグネチャは、質問に応答するチャットボットを開発するという私たちの目標を定義します。
class BasicQA(dspy.Signature): #Signature
"""Answer questions with short factoid answers."""
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")
次に、dspy.predict
を使用して応答を生成し、Basic QA クラスを渡し、質問例を使用して generate_answer
関数を呼び出します。最後に、出力を印刷して、質問に応答するチャットボットが正しく応答するかどうかをテストします。
# Define the predictor.
generate_answer = dspy.Predict(BasicQA)
Call the predictor on a particular input.
pred = generate_answer(question=dev_example.question)
Print the input and the prediction.
print(f"Question: {dev_example.question}")
print(f"Predicted Answer: {pred.answer}")
出力:-
Question: What is the nationality of the chef and restaurateur featured in Restaurant: Impossible?
Predicted Answer: American
ここでは、答えが間違っているため、修正する必要があります。この出力がどのように生成されたかを調べてみましょう。
turbo.inspect_history(n=1)
turbo.inspect_history(n=1)
Answer questions with short factoid answers.
---
Follow the following format.
Question: ${question}
Answer: often between 1 and 5 words
---
Question: What is the nationality of the chef and restaurateur featured in Restaurant: Impossible?
Answer: American
このシェフはイギリス人とアメリカ人ですが、モデルが単に「アメリカ人」と推測したかどうかは、標準的な答えなのでわかりません。
「思考の連鎖」を紹介しましょう。
Chain of Thoughtを使用したチャットボットの作成
思考の連鎖には一連の中間推論ステップが含まれており、大規模な言語モデルの複雑な推論を実行する能力が大幅に向上します。
generate_answer_with_chain_of_thought = dspy.ChainOfThought(BasicQA)
Call the predictor on the same input.
pred = generate_answer_with_chain_of_thought(question=dev_example.question)
Print the input, the chain of thought, and the prediction.
print(f"Question: {dev_example.question}")
print(f"Thought: {pred.rationale.split('.', 1)[1].strip()}")
print(f"Predicted Answer: {pred.answer}")
Question: What is the nationality of the chef and restaurateur featured in Restaurant: Impossible?
Thought: We know that the chef and restaurateur featured in Restaurant: Impossible is Robert Irvine.
Predicted Answer: British
ここでの回答は、以前に受け取った回答よりも優れています。
以下のコードを自由に実行して、推論とこの応答がどのように生成されるかを確認してください。
turbo.inspect_history(n=1)
RAG アプリケーションの作成
回答を生成するための検索拡張パイプラインを構築します。まず、シグネチャを作成し、次にモジュールを作成し、それを調整するためにオプティマイザを設定し、最後に GenerateAnswer
というクラスを定義して RAG プロセスを実行します。
RAG署名
署名を定義します: コンテキスト、質問 --> 回答
。
class GenerateAnswer(dspy.Signature):
"""Answer questions with short factoid answers."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")
RAGモジュール
モジュールとして機能する RAG クラスでは、init 関数でモデルを定義します。 「Retrieve」と「GenerateAnswer」に焦点を当てます。「Retrieve」は関連する文章をコンテキストとして収集し、「GenerateAnswer」は「ChainOfThought」を使用してユーザーの質問に基づいて予測を提供します。
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)
RAG オプティマイザー
次に、RAG プログラムをコンパイルします。これには、トレーニング セットの使用、検証メトリックの定義、プログラムを最適化するためのテレプロンプターの選択が含まれます。テレプロンプターは、モジュールに対して効果的なプロンプトを選択する強力なオプティマイザーです。 SGD、Adam、RMSProp などの従来の教師あり学習セットアップでオプティマイザーを選択するのと同様に、BootstrapFewShot を単純なデフォルトのテレプロンプターとして使用します。
# Validation logic: check that the predicted answer is correct.Also check that the retrieved context does actually contain that answer.
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
Set up a basic teleprompter, which will compile our RAG program.
teleprompter = BootstrapFewShot(metric=validate_context_and_answer)
Compile!
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)
それでは、このパイプラインを実行してみましょう。
# Ask any question you like to this simple RAG program.
my_question = "What castle did David Gregory inherit?"
Get the prediction. This contains `pred.context` and `pred.answer`.
pred = compiled_rag(my_question)
Print the contexts and the answer.
print(f"Question: {my_question}")
print(f"Predicted Answer: {pred.answer}")
print(f"Retrieved Contexts (truncated): {[c[:200] + '...' for c in pred.context]}")
Question: What castle did David Gregory inherit?
Predicted Answer: Kinnairdy Castle
Retrieved Contexts (truncated): ['David Gregory (physician) | David Gregory (20 December 1625 – 1720) was a Scottish physician and inventor. His surname is sometimes spelt as Gregorie, the original Scottish spelling. He inherited Kinn...', 'Gregory Tarchaneiotes | Gregory Tarchaneiotes (Greek: Γρηγόριος Ταρχανειώτης , Italian: "Gregorio Tracanioto" or "Tracamoto" ) was a "protospatharius" and the long-reigning catepan of Italy from 998 t...', 'David Gregory (mathematician) | David Gregory (originally spelt Gregorie) FRS (? 1659 – 10 October 1708) was a Scottish mathematician and astronomer. He was professor of mathematics at the University ...']
歴史を検証してみましょう。
turbo.inspect_history(n=1)
Context:
[1] «David Gregory (physician) | David Gregory (20 December 1625 – 1720) was a Scottish physician and inventor. His surname is sometimes spelt as Gregorie, the original Scottish spelling. He inherited Kinnairdy Castle in 1664. Three of his twenty-nine children became mathematics professors. He is credited with inventing a military cannon that Isaac Newton described as "being destructive to the human species". Copies and details of the model no longer exist. Gregory's use of a barometer to predict farming-related weather conditions led him to be accused of witchcraft by Presbyterian ministers from Aberdeen, although he was never convicted.»
[2] «Gregory Tarchaneiotes | Gregory Tarchaneiotes (Greek: Γρηγόριος Ταρχανειώτης , Italian: "Gregorio Tracanioto" or "Tracamoto" ) was a "protospatharius" and the long-reigning catepan of Italy from 998 to 1006. In December 999, and again on February 2, 1002, he reinstituted and confirmed the possessions of the abbey and monks of Monte Cassino in Ascoli. In 1004, he fortified and expanded the castle of Dragonara on the Fortore. He gave it three circular towers and one square one. He also strengthened Lucera.»
[3] «David Gregory (mathematician) | David Gregory (originally spelt Gregorie) FRS (? 1659 – 10 October 1708) was a Scottish mathematician and astronomer. He was professor of mathematics at the University of Edinburgh, Savilian Professor of Astronomy at the University of Oxford, and a commentator on Isaac Newton's "Principia".»
Question: What castle did David Gregory inherit?
Reasoning: Let's think step by step in order to produce the answer. We know that David Gregory inherited a castle. The name of the castle is Kinnairdy Castle.
Answer: Kinnairdy Castle
評価する
最後のステップは評価で、RAG モデルのパフォーマンスを評価します。基本的な RAG、コンパイルされていない RAG (オプティマイザーなし)、およびコンパイルされた RAG (オプティマイザーあり) を評価します。これらの評価から得られたスコアを比較します。
基本的なRAG
def gold_passages_retrieved(example, pred, trace=None):
gold_titles = set(map(dspy.evaluate.normalize_text, example['gold_titles']))
found_titles = set(map(dspy.evaluate.normalize_text, [c.split(' | ')[0] for c in pred.context]))
return gold_titles.issubset(found_titles)
evaluate_on_hotpotqa = Evaluate(devset=devset, num_threads=1, display_progress=True, display_table=5)
compiled_rag_retrieval_score = evaluate_on_hotpotqa(compiled_rag, metric=gold_passages_retrieved)
未コンパイルの Baleen RAG (オプティマイザなし)
トレーニング/開発セットで難しい質問を調査すると、より詳細な情報が必要な場合など、単一の検索クエリを修正する必要があることがわかります。これに対処するために、検索拡張 NLP 文献では、さらなる情報を収集するために追加のクエリを生成する GoldEn や Baleen などのマルチホップ検索システムを提案しています。
DSPy を使用すると、RAG 実装の GenerateAnswer シグネチャと、部分的なコンテキストと質問に基づいて不足している情報を見つけるための検索クエリを生成する「ホップ」動作のシグネチャを使用して、そのようなシステムを簡単にシミュレートできます。
class GenerateSearchQuery(dspy.Signature):
"""Write a simple search query that will help answer a complex question."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
query = dspy.OutputField()
次にモジュールを作成します。
class SimplifiedBaleen(dspy.Module):
def __init__(self, passages_per_hop=3, max_hops=2):
super().__init__()
self.generate_query = [dspy.ChainOfThought(GenerateSearchQuery) for _ in range(max_hops)]
self.retrieve = dspy.Retrieve(k=passages_per_hop)
self.generate_answer = dspy.ChainOfThought(GenerateAnswer)
self.max_hops = max_hops
def forward(self, question):
context = []
for hop in range(self.max_hops):
query = self.generate_query[hop](context=context, question=question).query
passages = self.retrieve(query).passages
context = deduplicate(context + passages)
pred = self.generate_answer(context=context, question=question)
return dspy.Prediction(context=context, answer=pred.answer)
Baleen の主な目的は、質問やクエリをいくつかのチャンクに分割して自動的に変更することです。チャンクからコンテキストを取得して変数に保存するため、より正確な回答を生成するのに役立ちます。
Baleen プログラムのゼロショット バージョンを検査する
ゼロショット (コンパイルされていない) 設定でプログラムを使用する場合は、最小限の命令でサブタスクを理解できる、基礎となる言語モデルの機能に依存します。これは、単純で一般的なタスクでは強力なモデル (GPT-4 など) とうまく機能します。ただし、ゼロショット アプローチは、特殊なタスク、新しいドメイン、より効率的なモデルやオープン モデルにはあまり実用的ではありません。 DSPy は、このような状況でのパフォーマンスを向上させることができます。
# Ask any question you like to this simple RAG program.
my_question = "How many storeys are in the castle that David Gregory inherited?"
Get the prediction. This contains `pred.context` and `pred.answer`.
uncompiled_baleen = SimplifiedBaleen() # uncompiled (i.e., zero-shot) program
pred = uncompiled_baleen(my_question)
Print the contexts and the answer.
print(f"Question: {my_question}")
print(f"Predicted Answer: {pred.answer}")
print(f"Retrieved Contexts (truncated): {[c[:200] + '...' for c in pred.context]}")
Question: How many storeys are in the castle that David Gregory inherited?
Predicted Answer: five
Retrieved Contexts (truncated): ['David Gregory (physician) | David Gregory (20 December 1625 – 1720) was a Scottish physician and inventor. His surname is sometimes spelt as Gregorie, the original Scottish spelling. He inherited Kinn...', 'The Boleyn Inheritance | The Boleyn Inheritance is a novel by British author Philippa Gregory which was first published in 2006. It is a direct sequel to her previous novel "The Other Boleyn Girl," an...', 'Gregory of Gaeta | Gregory was the Duke of Gaeta from 963 until his death. He was the second son of Docibilis II of Gaeta and his wife Orania. He succeeded his brother John II, who had left only daugh...', 'Kinnairdy Castle | Kinnairdy Castle is a tower house, having five storeys and a garret, two miles south of Aberchirder, Aberdeenshire, Scotland. The alternative name is Old Kinnairdy....', 'Kinnaird Head | Kinnaird Head (Scottish Gaelic: "An Ceann Àrd" , "high headland") is a headland projecting into the North Sea, within the town of Fraserburgh, Aberdeenshire on the east coast of Scotla...', 'Kinnaird Castle, Brechin | Kinnaird Castle is a 15th-century castle in Angus, Scotland. The castle has been home to the Carnegie family, the Earl of Southesk, for more than 600 years....']
コンパイル済み Baleen RAG (オプティマイザー付き)
まず、次のことを保証する検証ロジックを定義します。
1.予測された答えは正解と一致します。 2.取得したコンテキストには正解が含まれています。 3.生成されたクエリはどれも長すぎません(つまり、100 文字を超えるクエリはありません)。 4.生成されたクエリはいずれも反復的ではありません(つまり、以前のクエリよりも 0.8 以上の F1 スコアを持つクエリはありません)。
def validate_context_and_answer_and_hops(example, pred, trace=None):
if not dspy.evaluate.answer_exact_match(example, pred): return False
if not dspy.evaluate.answer_passage_match(example, pred): return False
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
次に、DSPy の最も基本的なテレプロンプターの 1 つである BootstrapFewShot
を使用します。
teleprompter = BootstrapFewShot(metric=validate_context_and_answer_and_hops)
最後に、オプティマイザーをコンパイルし、コンパイル済みおよびコンパイルされていないヒゲ パイプラインの取得品質を評価します。
compiled_baleen = teleprompter.compile(SimplifiedBaleen(), teacher=SimplifiedBaleen(passages_per_hop=2), trainset=trainset)
uncompiled_baleen_retrieval_score = evaluate_on_hotpotqa(uncompiled_baleen, metric=gold_passages_retrieved)
compiled_baleen_retrieval_score = evaluate_on_hotpotqa(compiled_baleen, metric=gold_passages_retrieved)
比較のためにスコアを印刷してみましょう。
print(f"## Retrieval Score for RAG: {compiled_rag_retrieval_score}") # note that for RAG, compilation has no effect on the retrieval step
print(f"## Retrieval Score for uncompiled Baleen: {uncompiled_baleen_retrieval_score}")
print(f"## Retrieval Score for compiled Baleen: {compiled_baleen_retrieval_score}")
出力:-
## Retrieval Score for RAG: 26.0
## Retrieval Score for uncompiled Baleen: 48.0
## Retrieval Score for compiled Baleen: 60.0
したがって、コンパイルされた Baleen メソッドは、基本的な RAG アプリケーションよりも正確な答えを提供します。 Compiled Baleen は、質問を複数の小さなチャンクに分割し、コンテキストを取得して、より正確な回答を提供します。
compiled_baleen("How many storeys are in the castle that David Gregory inherited?")
turbo.inspect_history(n=3)
結論
この記事では、事前トレーニング済み言語モデル (LM) のパイプラインやその他のツールを使用して AI システムを設計するための新しいプログラミング モデルである DSPy について紹介します。私たちは、DSPy シグネチャ、モジュール、テレプロンプターという 3 つの主要な概念を提示しました。さらに、単純な Q アンド a チャットボットと RAG アプリケーションを作成して、フレームワークを調査しました。これらの実験を通じて、DSPy により、比較的小型の LM を使用して効果的なシステムを迅速に開発できることが実証されました。
記事をお楽しみいただければ幸いです。
参考文献
- DSPy github リポジトリ
- DSPY: 自己改善パイプラインへの宣言型言語モデル呼び出しのコンパイル