見出し画像

Microsoft guidanceでPlan-and-Solveプロンプトを実装してみた

ChatGPT PluginのLink Readerは信じられないほど便利で、全ての入力情報はLink Readerが理解できるような書式になっているべきだと思えてしまうほどです。

Link Readerを使えば、arXivで公開されている論文の内容をインプットに、簡単な実装をChatGPTに作成してもらうことも容易です。プログラマとしては長々と文章を書かれるよりも、とりあえずコードの形になっていた方が理解が容易なので、簡易的であってもコードの形に落としてくれるのは助かります。

そこで今回はタイトルにあるPlan-and-Solveプロンプト論文を例に簡易コードを生成し、簡易コードを元にMicrosoft guidanceのプロンプトを作成して実行してみたいと思います。

Plan-and-SolveとはCoT(Chain-of-Thought)のように最初からステップバイステップで考えるのではなく、タスクを小さく分割する計画を立ててからタスクを実行し答えを導き出す手法のことで、複数のタスクにおいてCoTよりも常に高い性能を発揮することが確認されています。

論文から簡易コードを生成する

この論文によると、Plan-and-Solve (PS) Promptingは、Large Language Models (LLMs)がゼロショットで連鎖的な思考を改善するための手法です。この手法は、以下の2つのステップで構成されています。

1. 問題理解と計画作成: まず、全体のタスクをより小さなサブタスクに分割する計画を立てます。このステップでは、LLMsに「問題を理解し、問題を解決するための計画を立てる」という指示を与えます。

2. 計画の実行と問題解決: 次に、計画に従ってサブタスクを実行します。このステップでは、LLMsに「計画を実行し、問題をステップバイステップで解決する」という指示を与えます。

さらに、計算エラーや推論ステップの生成品質を改善するために、PS Promptingを拡張したPS+ Promptingも提案されています。PS+ Promptingでは、「関連する変数とそれらに対応する数値を抽出する」および「中間結果を計算する(計算と常識に注意を払う)」という詳細な指示が追加されます。

以下に、これらの要件を基にしたPythonコードの実装例を示します。ただし、LLMsの具体的な実装はOpenAIのGPT-3などのAPIを使用するため、ここでは空の関数として表現します。

def llm(prompt: str) -> str:
    # This function should use a Large Language Model to generate a response based on the given prompt
    # For the sake of this example, it's represented as an empty function
    pass

def understand_problem_and_devise_plan(problem: str) -> str:
    prompt = f"次の問題を理解し、それを解決するための計画を立ててください: {problem}"
    plan = llm(prompt)
    return plan

def execute_plan_and_solve_problem(plan: str) -> str:
    prompt = f"次の計画を実行し、問題をステップバイステップで解決してください: {plan}"
    solution = llm(prompt)
    return solution

def extract_variables_and_numerals(problem: str) -> str:
    prompt = f"次の問題から関連する変数とそれらに対応する数値を抽出してください: {problem}"
    variables_and_numerals = llm(prompt)
    return variables_and_numerals

def calculate_intermediate_results(problem: str, variables_and_numerals: str) -> str:
    prompt = f"次の問題について中間結果を計算してください。計算と常識に注意を払い、次の変数と数値を使用してください: {variables_and_numerals}"
    intermediate_results = llm(prompt)
    return intermediate_results

def solve_problem_with_ps_prompting(problem: str) -> str:
    plan = understand_problem_and_devise_plan(problem)
    solution = execute_plan_and_solve_problem(plan)
    return solution

def solve_problem_with_ps_plus_prompting(problem: str) -> str:
    variables_and_numerals = extract_variables_and_numerals(problem)
    intermediate_results = calculate_intermediate_results(problem, variables_and_numerals)
    plan = understand_problem_and_devise_plan(problem)
    solution = execute_plan_and_solve_problem(plan)
    return solution

簡易コードを参考にguidanceのプロンプトを起こす

本来であればここもguidanceのドキュメントをLink Readerに食わせることでgiudanceプロンプトも自動生成したいところですが、現在のREADMEを参照してもらっても「ちゃんとしたAPIドキュメントがないと生成できません」と困り顔をされてしまいます。これからのライブラリドキュメントは常にChatGPTが理解できるような内容であることが求められていきそうです。

というわけで人間の手で生成してみた結果が以下の通りです。

{{#system~}}
Assistant is a large language model trained by OpenAI.

Assistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.

Assistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.

Overall, Assistant is a powerful system that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.
{{~/system}}

{{#user~}}
{{! 次の{問題}から関連する変数とそれらに対応する数値を抽出してください: }}
Extract relevant variables and their corresponding numerals from the following {problem}.

{problem}: """
{{problem}}
"""
{{~/user}}

{{#assistant~}}
{{gen 'variables_and_numerals' temperature=0 max_tokens=800}}
{{~/assistant}}

{{#user~}}
{{! {問題}について中間結果を計算してください。計算と常識に注意を払い、次の{変数と数値}を使用してください: }}
Calculate intermediate results for the following problem, paying attention to calculations and common sense, and using the following {variables and numerals}.

{variables_and_numerals}: """
{{variables_and_numerals}}
"""
{{~/user}}

{{#assistant~}}
{{gen 'intermediate_results' temperature=0 max_tokens=800}}
{{~/assistant}}

{{#user~}}
{{! {問題}を理解し、それを解決するための計画を立ててください: }}
Understand the {problem} and devise a plan to solve it.
{{~/user}}

{{#assistant~}}
{{gen 'plan' temperature=0 max_tokens=800}}
{{~/assistant}}

{{#user~}}
{{! 次の計画を実行し、問題をステップバイステップで解決してください: }}
Execute the following {plan} and solve the {problem} step by step.

{plan}: """
{{plan}}
"""
{{~/user}}

{{#assistant~}}
{{gen 'solution' temperature=0 max_tokens=800}}
{{~/assistant}}

{{#user~}}
最終的な答えについて簡潔に日本語で答えてください。
{{~/user}}

{{#assistant~}}
{{gen 'final_answer' temperature=0 max_tokens=800}}
{{~/assistant}}

このプロンプトをplan_and_solve.handlebarsとして保存し、以下のコードで実行します。

import guidance

with open('plan_and_solve.handlebars', 'r') as f:
    prompt = f.read()

gpt3_5 = guidance.llms.OpenAI('gpt-3.5-turbo')
guidance.llm = gpt3_5
plan_and_solve = guidance(prompt)
problem = "ある企業の年間売上は100億円で、そのうち40%が人件費、30%が原材料費、10%が広告費に使われ、残りが利益です。この企業の年間利益はいくらですか?"
out = plan_and_solve(problem=problem)

すると以下の結果が得られます:
(※システムメッセージの表示は省略します)

問題に関連する変数と対応する数値の抽出
中間結果の計算
改めて問題を理解し、解くための計画を策定
計画の実行を依頼
計画を実行し、最終的な答えを導き出す

以上までがguidanceをJupyter Notebookで実行した際に出力される表示ですが、最終的な答えだけを得たい場合は以下のようなコードで取得することができます。

print(out["final_answer"])

この企業の年間利益は20億円です。

ChatGPTでそのまま問題を解いてもらったらどうなる?

以下の結果になりました。

ある企業の年間売上は100億円で、そのうち40%が人件費、30%が原材料費、10%が広告費に使われ、残りが利益です。この企業の年間利益はいくらですか?

プロンプト

企業の年間売上は100億円ですが、人件費が40%、原材料費が30%、広告費が10%とされています。これらの費用を差し引いた残りが利益となります。

人件費の割合は40%なので、100億円の40%を計算します。
人件費 = 100億円 × 0.4 = 40億円

同様に、原材料費と広告費も計算します。
原材料費 = 100億円 × 0.3 = 30億円
広告費 = 100億円 × 0.1 = 10億円

利益は残りの金額ですので、売上から人件費、原材料費、広告費を差し引きます。
利益 = 100億円 - 40億円 - 30億円 - 10億円 = 20億円

したがって、この企業の年間利益は20億円となります。

GPT-3.5

ステップバイステップという指示もないのにステップバイステップで処理しているところを見ると、これまで高評価だった強化学習データにステップバイステップで処理した会話テキストが大量に含まれていたのかなと推測します。インストラクションチューニングが強化されていくと、あんまり凝った指示をしなくても、それなりの回答を返してくれるようになりそうですね。

所感

論文をインプットに簡易コードを生成してもらい、理解を深める手法はとてもオススメです。Show Meなどでチャートを書いてもらった方が良いという声もありますが、個人的には疑似コードでも良いからコードの方が分かりやすいんですよねぇ・・・。

「完全に実行可能なコードを生成してください」とオーダーすると、とりあえず仮でも動く形のコードを生成してくれるので、そこからAPI連携系の関数をモックで定義だけ渡す、みたいなことを繰り返していくと精緻化できます。例えば以下のような形です。

プロンプトを引数として、生成された文字列を返す関数を llm(prompt: str) -> str と定義します。この関数を利用して、上記の実装をできるところまで詳細化しなさい。

型をつけておくとより精度高く理解してくれるので、型を可能な限りつけていくのが良いですね。

現場からは以上です。

この記事が気に入ったらサポートをしてみませんか?