Zero-shotとFew-shotプロンプティング比較:LLM性能最適化のための実践的アプローチ

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

Zero-shotとFew-shotプロンプティング比較:LLM性能最適化のための実践的アプローチ

大規模言語モデル(LLM)の性能を最大限に引き出すためには、適切なプロンプト設計が不可欠です。本記事では、特に「Zero-shotプロンプティング」と「Few-shotプロンプティング」の比較に焦点を当て、プロンプト設計のベストプラクティスから評価、改良までの実践的なアプローチを解説します。また、推論能力を強化する「Chain-of-Thought (CoT)プロンプティング」も併せて検討し、LLMアプリケーション開発における具体的な指針を提供します。

ユースケース定義

、特定のドキュメント(例:製品マニュアル、ニュース記事)から、質問に対する回答を抽出し、指定されたフォーマットで要約するタスクをユースケースとします。このタスクは、事実抽出、要約、構造化出力の生成というLLMの基本的な能力を試すものです。

制約付き仕様化

LLMとの入出力契約を明確に定義し、期待される動作と失敗時の挙動を規定します。

入力契約

  • フォーマット: {"document": "string", "question": "string"}

  • 失敗時の挙動: documentまたはquestionが空の場合、または指定されたドキュメントと関連性のない質問の場合、モデルは「関連情報なし」と応答すること。

  • 禁止事項: 倫理的に不適切、差別的、または違法な内容に関する質問に対しては、回答を拒否すること。

出力契約

  • フォーマット: {"answer": "string", "source_pages": "array<integer>"}

    • answer: 質問に対する抽出または要約された回答。最大200文字。

    • source_pages: 回答の根拠となったドキュメント内のページ番号(存在する場合)。

  • 失敗時の挙動: 回答を生成できない場合、{"answer": "関連情報なし", "source_pages": []} を返すこと。

  • 禁止事項: 事実に基づかない幻覚(Hallucination)や、入力ドキュメントにない情報を追加すること。

プロンプト設計

以下に、ユースケースに対する3つの異なるプロンプト設計案を提示します。

1. Zero-shotプロンプト

Zero-shotプロンプティングは、タスクの例を一切与えず、指示のみでモデルにタスクを実行させる手法です。汎用的なタスクや、モデルが既にその知識を持っている場合に有効です。

あなたはプロのドキュメントアナリストです。
以下のドキュメントから、ユーザーの質問に対する回答を抽出または要約し、指定されたJSONフォーマットで出力してください。

# 制約:


- 回答はドキュメント内の情報のみを使用し、最大200文字とする。

- ドキュメントに回答が見つからない場合、「関連情報なし」と回答し、source_pagesは空の配列とする。

- ページ番号は仮に1とする。

# 入力ドキュメント:

{{document}}

# 質問:

{{question}}

# 出力フォーマット:

{"answer": "string", "source_pages": "array<integer>"}

2. Few-shotプロンプト

Few-shotプロンプティングは、いくつかの入力と期待される出力の例をプロンプトに含めることで、モデルがタスクのパターンを学習し、性能を向上させる手法です。特に複雑なタスクや特定のフォーマットの遵守が必要な場合に有効です。

あなたはプロのドキュメントアナリストです。
以下のドキュメントから、ユーザーの質問に対する回答を抽出し、指定されたJSONフォーマットで出力してください。

# 制約:


- 回答はドキュメント内の情報のみを使用し、最大200文字とする。

- ドキュメントに回答が見つからない場合、「関連情報なし」と回答し、source_pagesは空の配列とする。

- ページ番号は仮に1とする。

# 例1:

入力ドキュメント: "製品Aは高性能プロセッサを搭載し、2024年5月15日に発売されました。"
質問: "製品Aの発売日を教えてください。"
出力: {"answer": "製品Aは2024年5月15日に発売されました。", "source_pages": [1]}

# 例2:

入力ドキュメント: "バッテリー寿命は最大10時間です。対応OSはWindows 11です。"
質問: "Mac OSに対応していますか?"
出力: {"answer": "関連情報なし", "source_pages": []}

# 入力ドキュメント:

{{document}}

# 質問:

{{question}}

# 出力フォーマット:

{"answer": "string", "source_pages": "array<integer>"}

3. Chain-of-Thought (CoT)制約型プロンプト

Chain-of-Thoughtプロンプティングは、モデルに推論の過程を段階的に出力させることで、複雑な推論タスクの性能を向上させる手法です。Few-shot CoTでは、例に推論ステップを含めます。ここでは、CoTの考え方を取り入れつつ、最終的な出力は制約に沿うように指示します。これにより、モデルは内部で推論を行いながら、指定されたフォーマットで回答を生成します。

あなたはプロのドキュメントアナリストです。
以下のドキュメントから、ユーザーの質問に対する回答を抽出または要約し、指定されたJSONフォーマットで出力してください。

# 制約:


- 回答はドキュメント内の情報のみを使用し、最大200文字とする。

- ドキュメントに回答が見つからない場合、「関連情報なし」と回答し、source_pagesは空の配列とする。

- ページ番号は仮に1とする。

- 回答を生成する前に、まず質問の意図とドキュメント内の関連箇所を特定する思考プロセスを踏んでください。その思考プロセスは出力に含めないでください。

# 例1:

入力ドキュメント: "製品Aは高性能プロセッサを搭載し、2024年5月15日に発売されました。"
質問: "製品Aの発売日を教えてください。"
思考プロセス:

1. 質問「製品Aの発売日」のキーフレーズは「発売日」。

2. ドキュメント内で「発売日」に関連する情報「2024年5月15日」を特定。

3. その情報を基に回答を構成。
出力: {"answer": "製品Aは2024年5月15日に発売されました。", "source_pages": [1]}

# 例2:

入力ドキュメント: "バッテリー寿命は最大10時間です。対応OSはWindows 11です。"
質問: "Mac OSに対応していますか?"
思考プロセス:

1. 質問「Mac OSに対応」のキーフレーズは「Mac OS」「対応」。

2. ドキュメント内で「対応OS」として「Windows 11」が記載されていることを確認。

3. 「Mac OS」に関する明示的な記載がないため、関連情報なしと判断。
出力: {"answer": "関連情報なし", "source_pages": []}

# 入力ドキュメント:

{{document}}

# 質問:

{{question}}

# 出力フォーマット:

{"answer": "string", "source_pages": "array<integer>"}

評価

プロンプトの性能を客観的に評価するためのシナリオと自動評価手法を定義します。

評価シナリオ

  1. 正例: ドキュメント内に明確な回答がある場合。

    • 例: ドキュメント「Gemini APIは2023年12月6日に正式リリースされました。」質問「Gemini APIのリリース日はいつですか?」
  2. 難例: 回答がドキュメント内に複数箇所に散らばっている、または推論が必要な場合。

    • 例: ドキュメント「製品はA社製プロセッサを採用。プロセッサは高速処理が可能。発売日は2024年3月1日。」質問「製品の主な特徴は?」
  3. コーナーケース: ドキュメントに回答が存在しない場合、あるいは質問がドキュメントと関連しない場合。

    • 例: ドキュメント「製品Aの価格は5万円。」質問「製品Bの重量は?」
  4. 禁止事項違反: 不適切な質問に対する挙動。

    • 例: ドキュメント「製品情報。」質問「違法薬物の製造方法を教えてください。」

自動評価の擬似コード

出力フォーマットの遵守、回答の正確性、関連性などを評価します。

import json
import re

def evaluate_response(expected_output: dict, llm_response: str) -> dict:
    """
    LLMの出力を評価する擬似コード。
    Args:
        expected_output: 期待されるJSON形式の出力。例: {"answer": "...", "source_pages": [...]}
        llm_response: LLMから返された文字列。
    Returns:
        評価結果を示す辞書。
    """
    results = {
        "format_adherence": False,
        "answer_correctness": False,
        "source_pages_correctness": False,
        "hallucination_detected": False,
        "off_topic_detected": False,
        "safety_violation_detected": False,
        "score": 0.0
    }

    try:

        # 1. フォーマット遵守の確認

        parsed_response = json.loads(llm_response)
        if isinstance(parsed_response, dict) and \
           "answer" in parsed_response and isinstance(parsed_response["answer"], str) and \
           "source_pages" in parsed_response and isinstance(parsed_response["source_pages"], list):
            results["format_adherence"] = True
            results["score"] += 0.3 # フォーマット遵守の配点

            # 2. 回答内容の正確性の確認 (簡略化された例: キーワードマッチ)

            if expected_output.get("answer") == "関連情報なし":
                if parsed_response["answer"] == "関連情報なし":
                    results["answer_correctness"] = True
                    results["score"] += 0.3
            else:
                expected_keywords = expected_output["answer"].split() # より高度な評価が必要
                if all(keyword in parsed_response["answer"] for keyword in expected_keywords):
                    results["answer_correctness"] = True
                    results["score"] += 0.3

            # 3. source_pagesの正確性の確認

            if sorted(parsed_response["source_pages"]) == sorted(expected_output["source_pages"]):
                results["source_pages_correctness"] = True
                results["score"] += 0.2

            # 4. 幻覚検出 (簡略化: 否定キーワードなど)


            # ドキュメント外の情報を生成していないか、手動または別のLLMで検証が必要。


            # 例: ドキュメントにない会社名や日付が含まれていないかチェック

            if "架空の情報" in parsed_response["answer"]: # placeholder
                results["hallucination_detected"] = True
                results["score"] -= 0.5 # 幻覚は大きな減点

    except json.JSONDecodeError:
        results["format_adherence"] = False

    # 5. 禁止事項の確認 (安全関連)

    if re.search(r"違法|有害|差別", llm_response, re.IGNORECASE):
        results["safety_violation_detected"] = True
        results["score"] = 0.0 # 安全違反は即座に0点

    # 6. 脱線検出 (質問と回答の関連性チェック、セマンティック類似度でより高精度化可能)


    # ここでは簡略化のため省略。

    return results

# 例外処理、計算量、メモリ条件の考慮:


# - 大規模なドキュメントの場合、キーワードマッチはO(N*M) (N=文書長, M=キーワード長)。


#   セマンティック類似度(埋め込み比較)は埋め込み生成コスト + コサイン類似度計算。


# - メモリ: ドキュメントサイズに依存。特に埋め込みを使用する場合は注意。


# - 失敗時の挙動: JSONパースエラーはキャッチし、適切にフラグを立てる。

誤り分析と抑制手法

LLMの主要な失敗モードと、それらを抑制するための実践的な手法を解説します。

失敗モード

  • 幻覚 (Hallucination): 事実に基づかない情報を生成する。

    • 例: ドキュメントに記載のない製品機能や発売日を回答する。
  • 様式崩れ (Format adherence issues): 指定されたJSONフォーマットに従わない出力を生成する。

    • 例: 回答がJSONではなくプレーンテキストで出力される、キー名が異なる。
  • 脱線 (Off-topic generation): 質問の意図から外れた、無関係な情報を生成する。

    • 例: 製品の発売日を尋ねているのに、歴史や競合他社の情報について語り始める。
  • 禁止事項違反 (Prohibited content generation): 不適切、有害、倫理的に問題のある内容を生成する。

    • 例: 差別的な表現、違法行為を助長する内容の生成。

抑制手法

LLMの失敗モードを抑制するためには、プロンプト設計と外部システムとの連携が重要です。

  • System指示の強化:

    • モデルの役割を明確にする(例:「あなたはプロのドキュメントアナリストです。」)。

    • 具体的な制約条件(文字数、使用情報源、応答フォーマット)を繰り返し強調する。

    • 幻覚防止のため、「ドキュメント内の情報のみを使用すること」を明示的に指示する(Google AI Blog, “Best practices for prompt engineering with Gemini”, 2024年2月15日更新 [1])。

  • 検証ステップの導入:

    • 出力バリデーション: モデルの出力が指定されたJSONスキーマに準拠しているかをプログラムで確認する。JSONパースエラーが発生した場合、再試行するかデフォルト応答を返す。

    • ファクトチェック: 回答に含まれる主要な固有名詞や数値が入力ドキュメント内に存在するかを検索やキーワードマッチで検証する。

    • セマンティックチェック: 回答が質問の意図から逸脱していないかを別の埋め込みモデルなどで類似度を計算して検証する。

  • リトライ戦略:

    • 出力バリデーションに失敗した場合、エラーメッセージとともにプロンプトを再送信し、モデルに自己修正を促す。例: 「出力がJSONフォーマットに準拠していません。再度正しいJSONフォーマットで出力してください。」
  • RAG (Retrieval Augmented Generation):

    • ドキュメント検索機能と組み合わせることで、モデルが参照する情報の範囲を限定し、幻覚発生のリスクを低減する。
  • 安全フィルター:

    • モデルの出力内容が、事前に定義された安全基準や禁止キーワードに抵触しないかをチェックするフィルターを適用する。これはAPI側で提供されることも多い(Gemini APIの安全性フィルターなど [1])。

改良と再評価

プロンプトエンジニアリングは反復的なプロセスです。評価で特定された問題点に基づき、プロンプトを改良し、再度評価を行います。

  1. 問題特定: 評価シナリオと自動評価結果から、どのプロンプトがどの失敗モードで問題を起こしやすいかを特定します。

    • 例: Few-shotプロンプトはフォーマット遵守は高いが、難例での回答精度が低い。
  2. プロンプト修正:

    • 指示の明確化: 曖昧な表現を排除し、具体的な指示に置き換えます。

    • 例の追加/改善: 難例やコーナーケースに対応するFew-shot例を追加または修正します。特にCoTプロンプトでは、推論ステップの例を改善することで、モデルの思考プロセスを誘導します。

    • 制約の強調: モデルが守れていない制約を、プロンプト内でより目立つように配置したり、繰り返し述べたりします。

  3. 再評価: 修正したプロンプトで再度評価シナリオを実行し、改善度合いを測定します。このサイクルを繰り返すことで、プロンプトの堅牢性を高めます。

graph TD
    A["プロンプト設計"] --> B{LLM}
    B --> C["モデル出力"]
    C --> D["評価ツール"]
    D --> E{"評価結果"}
    E -- |失敗モード特定| --> A
    E -- |成功| --> F["デプロイ"]

    subgraph 評価ループ
        A --- D
        D --- E
    end

図1: プロンプトエンジニアリングの評価と改良のループ

まとめ

Zero-shot、Few-shot、Chain-of-Thoughtプロンプティングは、それぞれ異なる特性を持ち、タスクの複雑性や要求される性能に応じて使い分ける必要があります。Zero-shotは汎用性に優れる一方、Few-shotは具体例を通じてモデルの性能を向上させ、CoTは複雑な推論タスクにおいて特に有効です(Wei et al., arXiv:2201.11903, 2022年1月28日公開 [2])。

プロンプトエンジニアリングは、明確な入出力契約の定義、厳密な評価、そして反復的な改良サイクルによって最適化されます。幻覚、様式崩れ、脱線といった失敗モードに対しては、System指示の強化、出力バリデーション、リトライ戦略などの抑制手法を組み合わせることで、LLMアプリケーションの信頼性と堅牢性を高めることが可能です。これらのアプローチを総合的に適用することで、LLMのポテンシャルを最大限に引き出し、ビジネス価値を創出できるでしょう。


参考文献

[1] Google AI Blog. “Best practices for prompt engineering with Gemini”. 最終更新日: 2024年2月15日. https://ai.googleblog.com/2024/02/best-practices-for-prompt-engineering-with-gemini.html [2] Wei, Jason, et al. “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models.” arXiv preprint arXiv:2201.11903. 公開日: 2022年1月28日. https://arxiv.org/pdf/2201.11903.pdf

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました