Prompt Engineering vs. Blind Prompting

Mitchell Hashimoto

プロンプトエンジニアリング vs. ブラインドプロンプティング

原文は Mitchell Hashimoto により に公開されました。 このブログを購読する

「プロンプトエンジニアリング」は言語モデルの発展とともに生まれた言葉で、プロンプティングを活用して言語モデルから情報を効果的に引き出すプロセス、典型的には実世界のアプリケーションで利用することを目的としたプロセスを指します。

今日「プロンプトエンジニアリングをやっている」と主張する人の多くは、実際にはブラインドプロンプティングをしているに過ぎません。1「ブラインドプロンプティング」は私が使っている言葉で、場当たり的な試行錯誤と、最小限あるいはまったくのテストなし、そしてプロンプティングに関するごく表面的な知識だけでプロンプトを作る手法を指します。ブラインドプロンプティングはプロンプトエンジニアリングではありません。

また、プロンプトエンジニアリングが本当に「エンジニアリング」と呼べるのか、それとも単なる「魔術」をハイプに乗った人たちが吹聴しているだけなのか、という懐疑論も多くあります。私の考えでは、そうした懐疑の多くは、私がツイートやブログ記事で見かけてきた「プロンプトエンジニアリング」を謳う内容のほとんどが、せいぜいブラインドプロンプティングに薄い皮を一枚被せた程度のものに過ぎないという事実に根ざしています。

このブログ記事では、プロンプトエンジニアリングは実験的な手法に基づいて磨くことのできる実在のスキルであると主張します。実践的な例を用いて、アプリケーションに実用的な価値をもたらす課題に対する解決策をプロンプトエンジニアリングで作り上げていくプロセスを順に追っていきます。

なお、この記事全体はテキスト出力を前提としています。私が言語モデルを主にテキスト出力のために使ってきたからです。テスト手法などの一部のテクニックは、画像などの他のタイプの出力には1対1では当てはまりません。ただし、この記事の内容はマルチモーダルな入力については問題なく有効です。


プロンプティングとは?

「プロンプティング」や「プロンプト」という言葉をすでに十分理解している方は、このセクションは読み飛ばしていただいて構いません。

言語モデル(この言葉に馴染みがなければChatGPTを想像してください)にとって、「プロンプト」とはモデルへのユーザー生成の入力のことです。ChatGPTで言えば、テキストを入力するボックスだと考えると分かりやすいでしょう。言語モデルはプロンプトに対して「補完(completion)」を推論します。例えば、ChatGPTに「4 + 3 = 」と入力すれば、おそらく「7」と返ってきます。この場合、「4 + 3 = 」がプロンプトで、「7」が補完です。

「プロンプティング」とは、プロンプトを使ってモデルから望む情報を引き出す行為のことです。大量のオフライン学習用データセットも、モデルへのオフラインアクセスも必要なく、エンジニアでなくても直感的に扱えるため、情報を引き出す魅力的なアプローチとなっています。プロンプティングはモデルをチューニングする手法の一つに過ぎません。

そして「プロンプトエンジニアリング」は、より厳密な方法論を指します(本記事で示すとおり)、プロンプティングを活用して実世界のアプリケーションのために信頼できる機能を構築することを目的としています。ChatGPT的なプロンプティングと異なるのは、プロンプトエンジニアリングによって生成されるプロンプトが、通常、特定の問題を確実に解決するために、大量かつ多様な状況で繰り返し使われることを想定している点です。


問題

まず、解決しようとしている問題がなければ始まりません。その問題は、プロンプティングが最善の解決策なのか、あるいはより適した別のアプローチが存在するのかを評価するために使えます。エンジニアリングは、手法のための手法を使うことから始まるのではなく、それが正しい手法であるという信念に突き動かされて始まるものです。

この例では、私たちがカレンダークライアントを作る会社だと仮定しましょう。ユーザーが自然言語でイベントを入力できるようにしたいと考えています。例えば:

Dinner with Alice next Tuesday at Taco Bell

CorpConf on 11/4

1:1 with Bob tomorrow at 10 AM

自然言語の入力から構造化された出力を抽出し、アプリケーション内で使えるイベント記述に変換するには、言語モデルが良い解決策になるかもしれません。

もちろん他にも取りうる解決策はあります。正規表現や文字列検索を使って、よくあるフレーズ(on <Day of Week>tomorrowtodaynext weekなど)を探すこともできます。

言語モデルには独自の利点もあります。他の言語への対応が優れているかもしれないし、タイプミスや文法的な誤りにもより強いかもしれないし、少なくとも正規表現が失敗したときのバックストップとして役立つかもしれません。いずれにせよ、プロンプティングを潜在的な解決策として追求し続けるだけの価値は十分にあります。


デモンストレーションセット

次に、デモンストレーションセットを用意する必要があります。デモンストレーションセットは、想定される入力と期待される出力のペアで構成されます。このセットは複数の目的を果たします。

  1. プロンプトの精度を測定するために使われます。1つのデモンストレーションの入力を使うことで、期待される出力が得られるかを検証できます。

  2. プロンプトの入出力がどのような形であるべきかを規定し、エンジニアである私たちが、それが課題にとって適切な形かを判断できるようにします。

  3. few-shotアプローチを採用する場合、このデモンストレーションセットの一部をexemplar(例示)として利用できます。「few-shot」という言葉に馴染みがない方のために説明すると、few-shotとはプロンプトに加えて例を与えるスタイルのことです。Few-Shot vs Zero-Shotプロンプティングの良い解説はこちらをご覧ください。

上記の(2)は極めて重要です。入力に何を期待し、出力に何を期待するのかについて、ある程度の理解を持っておく必要があります。なぜなら、その両側には[通常]データが特定の形式であることを保証し、特定の形式での出力を期待するソフトウェアが存在するからです。これは、問題を入出力の期待値を持ついくつかの関数に分解する、通常のソフトウェアエンジニアリングと何ら変わりません。

先ほどの例を活用し、完全なデモンストレーションに拡張してみましょう。

Q: Dinner with Alice next Tuesday at Taco Bell
A: next Tuesday

Q: CorpConf on 11/4
A: 11/4

Q: 1:1 with Bob tomorrow at 10 AM
A: tomorrow

デモンストレーションセットのサイズについての注意:このブログ記事ではデモは3つしかありません。実際には、少なくとも十数件は欲しいところです。デモが多ければ多いほどテストは充実しますが、トークン使用量の分だけコストも高くなります。ある程度のサイズを超えると、言語モデルをファインチューニングする方が経済的になることがよくあります。

上記のデモンストレーションでは、2つの重要な決定がなされています。どのようなプロンプティング課題でも、同様の決定を下す必要があります。

第一に、1つの情報だけを抽出しています。イベント名、参加者、時間、場所などを含むイベント全体を抽出し、すぐに使える美しいJSONなどの形式で出力させたくなるかもしれません。モデルにはそれができるかもしれません。しかし、新しい問題に取り組む際には、まず単一の問題に分解することをお勧めします。そうすることで問題が扱いやすくなるだけでなく、複数出力を扱うアプローチが本当に価値があるかどうかを判断するためのベースライン精度も得られます。

第二に、出力を変換していません。すべてを日付に変換したり、大文字・小文字を正しく整えたりしようとはしていません。文字通りのテキスト抽出を行っているだけです。「next Tuesday」のような文字列を極めて高い精度でタイムスタンプに変換できる優れた決定的ライブラリはすでに存在します。これは言語モデルにやらせる必要のないことです。ですから、まずは大まかな日付形式(「next Tuesday」「11/4」「tomorrow」)を取り出すだけにとどめ、それをタイムスタンプに変換する作業は自明な問題として従来のプログラミング手法で解決しましょう。出力がシンプルであればあるほど、高い精度を得やすくなります。

最後に、出力のデコードに関する簡単な注意です:LLMはプロンプトをさまざまな形で補完します。完全な文になるかもしれないし、ピリオドが付くかもしれないし、大文字になるかもしれません。LLMからの出力をどこまで完璧にしたいのか、そしてデモンストレーションセットを検証する前にどの程度正規化するつもりなのかを決めておくべきです。

例えば、テキスト抽出を行う場合、私は通常、出力全体の前後の空白とピリオドを取り除き、小文字に変換する程度であれば妥当だと考えています。JSON生成のようなより高度なことを行う場合は、比較が決定的になるように、JSONを決定的な順序とスタイルでパースして再エンコードするかもしれません。といった具合です。

私のお勧めは、LLMからの出力をできるだけシンプルで柔軟なものに保ち、アプリケーション側で何らかの正規化処理を行うことです。最初からLLMに完全に完璧なフォーマットでの出力を強制しようとしないでください。初期段階でLLMに出力整形をさせすぎると、LLMが本来のタスク(この場合は情報抽出)を実行する能力と、出力を構造化する能力を切り分けることが難しくなります。


プロンプト候補

次に、プロンプト候補をいくつか考えます。プロンプト候補とは、言語モデルから望む振る舞いを引き出せるかもしれないと感じるプロンプトのことです。最初の選択が必ずしも最良のプロンプトになるとは限らないため、複数の候補を用意します。

入門的な内容であることを考慮し、ここでは手動でプロンプトを考えます。効果的に行うためには、プロンプトエンジニアがプロンプトを構築する際に活用すべき基礎知識がいくつかあります。例えば、防御的であるよりも断定的である方が良い、繰り返しが多く長いよりも明確で簡潔な方が良いことが多い、few-shotプロンプトを構築する際はラベルの均等な分布が重要で、ラベルの全集合を示すことが重要である、などです。exemplarを選ぶ際には、LLMが間違えそうなexemplarが通常最も効果的であること、exemplarは短いものから長いものへと並べると最も効果的であることが多いこと、などが示されています。

要出典!申し訳ありませんが、これらの推奨を裏付ける実験的研究を引用していません。正直なところ、それらについて読んだ論文(指摘ごとに複数あることも多い)を探すのが面倒だったのです。もし私の言うことを信じないのであれば、それはそれで構いません。より重要なのは、プロンプティング手法とその有効性に関する実験的研究が存在するということです。ただ、これらをでっち上げたわけではないとお約束します。もっとも、現代のモデルでは一部が古くなっている可能性はあります。

これらのテクニックの一部を解説するだけで一つの記事になるため、この記事の目的ではありません。この記事の目的は、全体的なエンドツーエンドのプロセスを示し、LLMから価値を引き出すためのエンジニアリング手法が存在することを示すことです。

この段階での目標は、いくつかの良いzero-shotプロンプトを考えることです。zero-shotプロンプトはfew-shotに変換でき、さらに思考連鎖(chain of thought)へと発展させることができます。そしてそれらはさらにバッチ化されたプロンプトなどへと発展させられます。したがって、zero-shotは試すべきベースとして必須なので、そこに焦点を当てます。

以下は、私が考えた3つのプロンプト候補です。

Identify the date or day mentioned in the given text and provide it as the output.

Identify the date or day mentioned in the given event description.

Determine the date or day from each input and provide the output accordingly as a single word or date.

どれも理にかなったプロンプトです。十分な教育を受けた人間であれば、どのプロンプトでも非常に高い精度が得られるでしょう。しかし、言語モデルは人間ではないので、同等のパフォーマンスを自動的に期待することはできません。以前示したように、一見もっともらしいプロンプトでも悲惨なパフォーマンスになることがあります。ですから、次のステップはテストと測定によって判断に役立つ情報を得ることです。


プロンプトのテスト

候補となるプロンプトのセットとデモンストレーションセットが揃ったので、精度を測定できます。現在、私が最も良いと感じている方法は、LangChainのようなライブラリを使ってシンプルなPythonスクリプトを構築することです。私のテストでは、通常各デモンストレーションについて次のようなプロンプトテンプレートで実行します。

{{prompt}}. Q: {{input}} A:

私は常にまずzero-shotをテストします。ベースラインとなる精度の指標を得たいからです。そこからfew-shotをテストし、異なる候補間だけでなく、異なるプロンプティングタイプ間でも比較できます。といった具合です。

これはモデルごとに実施する必要があります。より高性能なモデルであっても、同じプロンプトで同じかそれ以上の精度が保証されるわけではありません。精度が下がることもあります2

プロンプトテストの最も基本的な結果は、以下のような表になるはずです。モデル(例えばGPT-3.5 vs GPT-4)といった追加の次元が加わることもあります。

           | Zero-Shot | Few-Shot | ... |
-----------------------------------------
Prompt 1   | 64        | 68       | ... |
-----------------------------------------
Prompt ... | 44        | 52       | ... |
-----------------------------------------
Prompt N   | 23        | 22       | ... |

この表は、Y軸にプロンプト候補、X軸にそれらのプロンプトを用いたプロンプトタイプを示しています。値は正解率をパーセンテージで表したものです。カレンダーアプリの例を続けると、「prompt 1」を1つのデモンストレーションでzero-shotプロンプトとして使うと、次のようになります。

Identify the date or day mentioned in the given text and provide it as the output. Q: CorpConf on 11/4 A:

そして答えとして「11/4」を期待します。few-shot版は次のようになるかもしれません。

Identify the date or day mentioned in the given text and provide it as the output.

Q: Dinner with Alice next Tuesday at Taco Bell. A: next Tuesday

Q: 1:1 with Bob tomorrow at 10 AM. A: tomorrow

Q: CorpConf on 11/4. A:

経験豊富なプロンプターへの注意:few-shotの例では「以下の例を真似してください」のような指示は含まれていません。実験的研究によれば、これは精度を安定的に向上させないことが示されているため、トークンを節約するためにまずは何も付けずにテストするのが好みです。第二に、few-shotのexemplarでは「MM/DD」形式の抽出例が一度も示されておらず、これは形式的には良くありません。実際のfew-shot設定では、あらゆるスタイルの抽出を示すことが重要になる場合があります(Zhao, et al 2021)。

分類問題のような特定のタイプの問題では、混同行列(Strobelt, et al 2022)を使って他のラベルの確率を可視化し、ラベルセットをより適切にチューニングできるかどうかを判断できます。

精度に加えて、使用トークン数、リクエスト数なども測定したいところです。最終的なプロンプトを選ぶ際には、これらすべてを考慮する必要があります。


プロンプトの選択

最後に、アプリケーションに統合するプロンプト候補の1つを選びます。これは必ずしも最も精度の高いプロンプトとは限りません。使用するモデル、必要なトークン数、示された精度に基づくコスト対精度の分析によって決まります。

例えば、few-shotバリアントが最高のパフォーマンスを示しても、テストセットでの精度がわずか4%高いだけで、200%多くのトークンを必要とする(現在のAPI駆動型モデルでは実質的にコストが2倍になる)ことがあります。ビジネス上の判断として、コストを半分に抑えるために4%低い精度で妥協すると決めることもあるでしょう。

あるいは、精度を上げるための他のアプローチを試すために立ち戻ることに決めるかもしれません。例えば、より低コストのモデルで自己整合性デコード戦略(Wang, et al 2022)を試し、精度が十分に向上するかを見てみるといったことです。低コストのモデルでより多くのトークンを使う方が、高コストのモデルで少ないトークンを使うよりも大幅にコストを節約できる場合があります。例えば、現在GPT-4はGPT-3.5の約15倍高価です。つまり、GPT-3.5のプロンプト精度を上げるために実質的に15倍のトークン予算があることになります(レート制限に関する注意点はありますが)。

このブログ記事の例では、Prompt 1のzero-shot版を採用することにするかもしれません。表では64%の精度があり、おそらくトークン数も大幅に少ないからです。少なくともカレンダーアプリケーションのイベントテンプレートを埋めるには64%の精度で十分だと判断するかもしれません。この特定の問題については64%よりはるかに良い結果が出せると思いますが、これはこの記事で使った例の数値に過ぎません。

最も重要なのは、十分な情報に基づいた意思決定を行うためのデータが手に入るということです。


信頼しつつ検証する、そして継続的改善

生成AIの確率的な性質により、プロンプトには何らかの問題が潜んでいる可能性が高いです。テストセットで精度が100%であっても、誤った出力を生む未知の入力が存在する可能性はあります。したがって、信頼しつつ検証し検証の失敗をデモンストレーションセットに追加することで、新たなプロンプトを開発し精度を向上させるべきです。

検証の方法は課題に大きく依存します。カレンダーアプリケーションの例では、ユーザーに明示的に「このイベントは正しいですか?」と尋ね、「いいえ」と答えた場合には人手によるレビューのために自然言語入力をログに記録したいと考えるかもしれません。あるいは、自動的な情報抽出の後にユーザーが手動で変更したイベントを自動的に追跡する方がより良いかもしれません3

別の例として、プロンプトがコード(正規表現やプログラミング言語のテキストなど)を生成する場合、最低限パースを試みることができます。パースがセキュリティ上の懸念になることは決してありません4し、少なくとも構文が正しいかどうかという最も基本的な検証になります。そして再び、この検証が失敗した場合、入力と出力をログに記録し、デモンストレーションセットを拡充して、より良いプロンプトを開発できます。

検証は敵対的プロンプティングへの対策にも役立ちます。敵対的プロンプティング自体が一つの大きなトピックなので、この記事では扱いません。


そして先へ……

このブログ記事では、プロンプトの開発がいかに — 私の考えでは — エンジニアリングの実践となり得るかを示しました。問題を特定し、解決策を形成し、それらの解決策を検証し、継続的改善によって解決策を洗練させていく体系的なアプローチについて説明しました。

この記事のアプローチを、逸話的な経験と蔓延する試行錯誤に頼って何らかの提案された解決策にたどり着き、しばしば時間の経過とともにプロンプトを確実に反復するための適切な体系的インフラを構築しない「ブラインドプロンプティング」と比較してみてください。

このブログ記事は非常に初歩的な内容であることを付け加えておきます。この記事の中には、すでに広く知られているより高度なテクニックで改善できる箇所が複数あります。さらに、敵対的プロンプティングのような重要なトピックもカバーしていません。具体的な例として、few-shotプロンプトに最適な例を選ぶためのより科学的なアプローチも存在しますが、この記事ではできるだけ基本に絞りたかったのです。より高度なテクニックを学びたい場合は、Prompt Engineering by Lilian Wengが素晴らしい概説を提供しています。

さらに、誰もが急速により高次のLLM統合、すなわちプロンプトチェーンやエージェントなどへと移行しています。こうした未来の革新などによって人間のプロンプティングは時代遅れになるだろうと主張する人もいます。それが真実かどうかはさておき、私は「第一原理」5から物事を学ぶことに価値があると信じている人間であり、このようなプロンプティング技術を学ぶことで高次の言語モデル技術を活用する能力が向上したと感じています。また、このような基本的なプロンプティングが、依然として高次の概念のパフォーマンスを向上させるとも信じています。

脚注

  1. おそらく「騒がしい」人々の多くはそうかもしれません。私はこの分野に優れたエンジニアリング手法を本当に適用している素晴らしいプロンプトエンジニアたちにたくさん出会い、多くを学んできました。残念ながら、Twitterなどのプラットフォームで見かけるノイズの多くは、そうではないことが多いのです。

  2. またしても要出典。繰り返しになりますが、面倒なので省略していますが、これは複数の論文で読んだ実験的研究に基づいています。信じたくなければ、ご自身でテストしてみてください。

  3. ここには明らかにプライバシー上の含意があります。これはあくまで一例であり、実際の状況によってはその手法が適切でない場合もあります。

  4. YAML。 😐

  5. 実際の「第一原理」であればさらに低レベルになることは承知しています。ここでは「第一原理」という言葉を、より多くの知識を積み上げる土台となる任意の低レベルな点を指す一般的な比喩として使っています。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント