こんにちは、サイオステクノロジーの佐藤 陽です。
前回に引き続き、プロンプトエンジニアリングについて解説していきたいと思います。
- 思った回答が得られないんだよな
- もっと生成 AI をうまく使いこなしたいな
といった方は最後までご覧ください。
チートシート
テクニック | (個人的)要約 |
システムメッセージ | AIに対してRoleを与えたり、回答方法のルールを制定する |
Few shot-Learning | いくつかの例題をプロンプトに含めたうえで回答させる |
CoT(Chain-of-Thought) | 回答に至るまでのプロセスをプロンプトに含めたうえで回答させる |
Zero-shot CoT | ”ステップバイステップで回答してください”とプロンプトに含めたうえで回答させる |
Self-Consistency | 類似問題および簡単なプロセスを大量にあたえ、AIに学習させたうえで回答させる |
はじめに
前回はプロンプトエンジニアリングの基礎編を紹介しました。
- プロンプトエンジニアリングって何?
- プロンプトエンジニアリングって大切なの?
といった方はこちらもご覧ください。
今回はこれに引き続き、少し応用的な内容を紹介したいと思います。
弁明
先に1点、弁明させてください。
今回色々検証していたのですが、プロンプトの投げ方によっては
- CoT のテクニックを使っても正解が得られなかった
- Zero-shot CoT では正解にたどり着けなかった
- 複数回同じプロンプトを投げた時に正解したり、不正解だったりした
などという挙動が見られました。
そのため、これから示す例はうまくいったケースのみ載せています。(※回答の捏造はしていません。)
回答に揺れがあるので、このあたりが生成 AI を使いこなすにあたって難しい所だなぁと感じています。
ただテクニックとしては有用なものが多いので、ぜひ使いこなしていきたいところです。
環境
前回に引き続き、Azure OpenAI Service を利用していきたいと思います。
モデルは gpt-35-turbo-16k を利用します。
ChatGPT を利用したり、別のモデルを利用した場合でも、問題なくプロンプトエンジニアリングの効果は得られます。
ただし、得られる回答が異なる場合がありますので、その点ご了承ください。
テクニック
システムメッセージ
質問を投げかける前に、AI に対して Role を与えたり、回答のルールを制定することが可能です。
日本だと「深津式プロンプト」という方法が話題になりましたが、それに似たものかと思います。
なおAzure OpenAI Service のチャットプレイグラウンドには「システムメッセージ」という欄が存在し、今回はここで設定していきたいと思います。
もちろんプロンプトに直接含めてもOKです。
例えば今回は以下のようなシステムメッセージを追加しました。
システムメッセージ
あなたは日本の歴史に精通した歴史学者です。 ・質問に対して中学生にもわかるような形で 100 文字以内で回答してください。 ・日本の歴史に関する質問にのみ答えてください。 ・日本の歴史以外の質問に対しては「専門外なので分かりません」と回答してください。
これを踏まえて以下のようなプロンプトを投げます。 プロンプト
桶狭間の戦いについて教えてください
回答
桶狭間の戦いは、1560 年に織田信長と今川義元の間で行われた戦いです。 信長が義元を破り、今川家の勢力を弱めることに成功しました。 この戦いは信長の台頭と勢力拡大のきっかけとなりました。
プロンプト
イオン化傾向が最も高い元素は何ですか?
回答
専門外なので分かりません。
システムメッセージに指定した通り、歴史に関する質問については 100 文字以内で分かりやすく回答してくれました。
また、化学に関する質問には「専門外なので分かりません」と答えてくれています。
ただ一方で、以下のようなやりとりも確認できました。
プロンプト
半導体に電圧を加えるとどうなりますか?
回答
半導体に電圧を加えると、その電気的特性が変化します。 具体的には、半導体の電気抵抗や導電性が変化し、電流が流れるようになったり、電流の量が変化したりします。 この現象を利用して、半導体素子(例えばダイオードやトランジスタ)が動作し、電子機器や回路を制御することが可能となります。
物理的な質問でしたが、回答してしまっています。
もしかしたら
「歴史以外の質問に対しては「専門外なので分かりません」と回答してください。」
という指示が少し曖昧で、判定が行われなかったのでしょうか?
実験的に、システムメッセージには含めず、プロンプトに直接 Role やルールを記載してみました。
プロンプト
あなたは日本の歴史に精通した歴史学者です。 ・質問に対して中学生にもわかるような形で 100 文字以内で回答してください。 ・日本の歴史に関する質問にのみ答えてください。 ・日本の歴史以外の質問に対しては「専門外なので分かりません」と回答してください。 半導体に電圧を加えるとどうなりますか?
回答
専門外なので分かりません。
今回は期待する回答が得られました。
ということは、Azure OpenAI Service の「システムメッセージ機能」の実装に何かしらの影響を受けていることでしょうか…?
ただ、また別の例としてプロンプトにルールのテキストを含めた場合において、日本の歴史以外の質問に回答してくれるケースがありました。
ここらへんは生成AIのModelの都合による気がしており、なかなか使いこなすのが難しい所な気がします。
X-shot Learning
ここでは X-shot Learning と書きましたが、よく知られているものに「Zero-shot Learning」や「Few-shot Learning」があります。
このshotというのは「例題を提示する」ことを指しており、その数が Zero なのか Few なのかの違いです。
このテクニックの本質には前回のブログで説明した、Examples があります。
Zero-shot Learning
Zero-shot は例を提示しないプロンプトの方法になります。 例えば以下の会話のような形です。
プロンプト
以下の文章から嬉しさを読み取り、100 点満点でスコアリングしてください。 やったね。
回答
90 点です。 この文章からも嬉しさが感じられますが、少し控えめな表現となっているため、100 点には届きません。
うーん、肯定的な感情であることはくみ取ってくれていますが、90 点という点数は少し高い気がします。
Few-shot Learning
次に Few-shot Learning のテクニックを使い、いくつかの例を与えてみます。
Azure OpenAI Service には例(shot)を加えるフォームが用意されています。
今回はこちらを利用して、以下のような例を加えていきます。
ユーザー | アシスタント |
もう無理、しんどい… | 5点 |
はぁ… | 10 点 |
そっかー | 50 点 |
やったー! | 80 点 |
まじでーーーーー!やったーーーーーーーー!!! | 100 点 |
再度同じプロンプトを投げます。
プロンプト
以下の文章から嬉しさを読み取り、100 点満点でスコアリングしてください。 やったね。
回答
70 点
うん、まずまず妥当な回答が得られた気がします。
このようにいくつか例を挙げることで生成 AI がその場で学習し、回答の精度を高めてくれます。
例を挙げればあげるほど回答の精度は高くなりますが
プロンプトへ投げるトークン数が増えてしまうことを考えると、コストとのトレードオフはありそうです。
Fine-tuning との比較
よく Few-shot Learning と比較されるのが Fine-tuning です。
Fine-tuning はモデル自体にデータを追加し、モデル全体に再学習させる方法です。
こうすることで特定の分野に非常に強いモデルを作成できたりします。
ただ、これはなかなか骨の折れる作業で、非常に高い専門性が必要となります。
一方で Few-shot Learning はモデル自体に修正は加えません。
既存のモデルに与えるプロンプトを工夫させることで、回答の品質を向上させるのです。
どちらが優れているかは一概に言えず、ケースバイケースかと思います。
Fine-tuning の方が恐らく特定の分野に特化した、強力なモデルを作成できるように思います。
一方で Few-shot Learning の方が柔軟性があり、状況に合わせて最適な回答が得られるように思います。
Chain-of-Thought (CoT)
次に Chain-of-Thought(CoT)のテクニックを見ていきたいと思います。
こちらも Examples の考えに基づくテクニックなのですが、質問から回答に至るまでのステップをプロンプトに含めることで、精度の高い回答を得ることが可能になります。
まず CoT を利用しないケースについて見てみます。
プロンプト
A さんには 2 人の弟がいます。A さんは 10 個のたこ焼きを作って、弟にそれぞれ 4 個ずつあげました。追加で 20 個作って、弟に 5 個ずつ、自分で 5 個食べました。たこ焼きは残り何個ですか?
回答
A さんが最初に作ったたこ焼きは 10 個です。弟にそれぞれ 4 個ずつあげたので、残りは 10 - (2 人の弟 × 4 個) = 10 - (2 × 4) = 10 - 8 = 2 個です。 その後、A さんは追加で 20 個のたこ焼きを作りました。弟にそれぞれ 5 個ずつあげたので、残りは 20 - (2 人の弟 × 5 個) = 20 - (2 × 5) = 20 - 10 = 10 個です。 A さんは自分で 5 個のたこ焼きを食べたので、残りは 10 - 5 = 5 個です。 したがって、たこ焼きの残りは 5 個です。
間違えてくれました!!!
(裏話をすると、間違えさせるのが結構大変でした。頭が良いのも困りものです。)
ではこれを CoT のテクニックを使って改善していきます。
Azure OpenAI Serviceの「例」のところに以下のようなやり取りを追加します。
ユーザー | アシスタント |
A さんには 3 人の弟がいます。A さんは 20 個のたこ焼きを作って、弟にそれぞれ 5 個ずつあげました。 追加で 40 個作って、弟に 3 個ずつ、自分で 5 個食べました。たこ焼きは残り何個ですか? |
3 人の弟に 5 個ずつあげたので、残りのたこ焼きは 20 – (3×5) = 5 個です。 そのため残りは 31 個です。 |
これを踏まえて、先ほどと同じプロンプトを投げます。
回答
最初に A さんは 10 個作りました。 弟が 2 人おり、それぞれ 4 個ずつあげるので合計 8 個を上げました。 10-8=2 個 そのあと追加で 20 個作りました。 2+20=22 個 そのあと弟に 5 個ずつ、自分で 5 個食べました。22-(5×2)-5=7 そのため残りは 7 個です。
自分が与えた例と同じプロセスを踏んで回答し、正解にたどり着いてくれました。
このように考えるプロセスもプロンプトに含めることが CoT のテクニックになります。
Zero-shot CoT Prompting
CoT の派生として、Zero-shot CoT というテクニックもあります。
例も提示せず(Zero-shot)、CoT とは…?という感じですが
「ステップバイステップで回答してください」といったような言葉をプロンプトに付け加えることで実現できるようです。
早速やってみましょう。
プロンプト
A さんには 2 人の弟がいます。A さんは 10 個のたこ焼きを作って、弟にそれぞれ 4 個ずつあげました。追加で 20 個作って、弟に 5 個ずつ、自分で 5 個食べました。たこ焼きは残り何個ですか? ステップバイステップで回答してください。
回答
ステップ 1: 最初に作った 10 個のたこ焼きを考えましょう。 弟に 4 個ずつあげたので、弟たちは合計で 8 個のたこ焼きを受け取りました。 自分で 5 個食べたので、残りのたこ焼きは 10 - 8 - 5 = 2 個です。 ステップ 2: 追加で作った 20 個のたこ焼きを考えましょう。 弟たちに 5 個ずつあげると、合計で弟たちは 2 * 5 = 10 個のたこ焼きを受け取ります。 自分で 5 個食べたので、残りのたこ焼きは 20 - 10 - 5 = 5 個です。 ステップ 3: 最初に作ったたこ焼きと追加で作ったたこ焼きの残りを合計します。 最初に作ったたこ焼きの残り 2 個と、追加で作ったたこ焼きの残り 5 個を合計すると、2 + 5 = 7 個のたこ焼きが残ります。 したがって、たこ焼きは残り 7 個です。
確かにできました! 例題を与えなくても正解にたどり着いてくれました。
実はこれは OpenAI のセミナーでも LLM を最適化する方法として言及されている方法であり、「Give GPTs time to “think”」という表現がされていました。
GPT に考える時間を与えるってなんだか未来感がありますね。
ちなみに記事冒頭でも書きましたが、この手法を取った場合でも回答が誤っているケースもありました。
なのでどの程度の複雑な質問に有効かはしっかり検討する必要がありそうです。
Self-Consistency
Self-Consistency(自己整合性)は、Wang et al. (2022)さんの論文によると
to replace the naive greedy decoding used in chain-of-thought prompting
と定義されています。
日本語に翻訳すると
CoTで使用されるnative greedy decodingを置き換えることを目的としています
うーん、少し分かりづらいですね。
先に実例を見てみたいと思います。
なお、今回プロンプトに与える例が多いので、参考にしたサイトからそのまま拝借しています。
また、「例」の機能は使わず、プロンプトに直接書き込んでいます。
これを踏まえ、以下のようなプロンプトを投げます。
プロンプトの最後に、先程利用したたこ焼きの問題を追加しています。
プロンプト
Q:駐車場に車が 3 台あり、さらに 2 台の車が到着した場合、駐車場には何台の車がありますか? A:駐車場には既に 3 台の車があります。2 台の車が到着しました。これで、車が 3+2 = 5 台あります。 回答は 5 です。 -- Q:リアは 32 個のチョコレートを持っており、彼女の姉妹は 42 個のチョコレートを持っています。 彼らが 35 個食べた場合、彼らが残したピースの数は何ですか? A:リアは 32 個のチョコレートを持っており、リアの姉妹は 42 個のチョコレートを持っていたことを意味します。 つまり、もともとは 32 + 42 = 74 個のチョコレートがありました。35 個食べられました。したがって、 合計で残るのは 74-35 = 39 個のチョコレートです。回答は 39 です。 -- Q:ショーンは 5 つのおもちゃを持っています。クリスマスに、彼は両親からそれぞれ 2 つのおもちゃをもらいました。 今、彼は何個のおもちゃを持っていますか? A:彼は 5 つのおもちゃを持っています。彼は母親から 2 つのおもちゃをもらいました。したがって、 5 + 2 = 7 個のおもちゃがあります。 その後、父親から 2 つのおもちゃが追加されたので、合計で 7 + 2 = 9 個のおもちゃがあります。 回答は 9 です。 -- Q:マイケルは 58 個のゴルフボールを持っています。火曜日に、彼は 23 個のゴルフボールを 失いました。水曜日に、さらに 2 個を失いました。水曜日の終わりには、彼は何個のゴルフボール を持っていましたか? A:マイケルは最初に 58 個のボールを持っていました。火曜日に 23 個を失いましたので、 その後 35 個のボールが残りました。水曜日に 2 個を失ったので、現在 33 個のボールがあります。 回答は 33 です。 -- Q:オリビアは 23 ドル持っています。彼女は 1 つあたり 3 ドルのベーグルを 5 つ買いました。 彼女が残したお金はいくらですか? A:彼女は 1 つあたり 3 ドルのベーグルを 5 つ購入しました。彼女は15ドルを使った。 残したお金は8ドルです。 -- Q:A さんには 2 人の弟がいます。A さんは 10 個のたこ焼きを作って、弟にそれぞれ 4 個ずつあげました。追加で 20 個作って、弟に 5 個ずつ、自分で 5 個食べました。たこ焼きは残り何個ですか?
回答
A:A さんは最初に 10 個のたこ焼きを作りました。 弟たちにそれぞれ 4 個ずつあげたので、10 - (4 + 4) = 2 個のたこ焼きが残ります。 その後、さらに 20 個のたこ焼きを作りました。 弟たちにそれぞれ 5 個ずつあげたので、20 - (5 + 5) = 5 個のたこ焼きが残ります。 したがって、合計で 2 + 5 = 7 個のたこ焼きが残っています。 回答は 7 です。
正解です!
Few-shot CoT と同じような形に見えますが、
(個人的な解釈として)Self-Consistency は
- 回答させたい質問とは少し形式の異なった例を与える
- 解答に至るプロセスをCoTほど詳しく書かない
- 例題の量を多めに与える
といったテクニックなのかな?と感じました。
人間でいうと「テストに向けて問題集をたくさん解いて、解き方を理解したうえでテストに臨む」といった感じでしょうか?
そうすると、問題集と違う問題がテストに出ても対応できますし、応用が利きそうですよね。
まとめ
今回は、前回の基礎編に続き応用編と題してプロンプトのテクニックを紹介しました。
X-Shot Learningや、CoT、Self-Consistencyなど、実際今日から使えるテクニックだと思います。
是非プロンプトに組み込んで使っていってみたいところです。
まだまだ奥が深いプロンプトエンジニアリングなので、さらなる追求をしてみたいと思います。
またネタができたら記事にしていきたいと思うのでこうご期待ください!
ではまた!