Few-shot PromptingとIn-Context LearningによるLLM性能最適化

Tech

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

Few-shot PromptingとIn-Context LearningによるLLM性能最適化

ユースケース定義

本稿では、大規模言語モデル(LLM)が複雑な指示を理解し、提供された少数の手本に基づいて、特定の形式で論理的な推論を生成するタスクに焦点を当てます。具体的には、法規制文書からの特定の情報抽出、顧客からの問い合わせに対するFAQベースの回答生成、あるいは専門分野のテキスト要約といった、高い精度と一貫性が求められる業務適用を想定します。これにより、LLMは単なる情報検索ツールを超え、半自動化された意思決定支援やコンテンツ生成ツールとしての価値を発揮します。

制約付き仕様化

LLMを業務システムに統合する際には、その挙動を厳密に制御するための「入出力契約」と「失敗モードへの対処」が不可欠です。

入出力契約

  • 入力: ユーザーからの質問または指示、およびタスク遂行に必要なコンテキスト情報。入力はテキスト形式で、最大トークン数は512トークンとします。

  • 出力: 定義されたJSON、Markdown、または特定のテキスト形式に厳密に準拠した回答。応答は簡潔かつ正確であり、不必要な装飾や追加の対話を含まないこと。

  • 出力文字数: 回答は指定された文字数範囲(例: 50文字以上200文字以下)に収めること。

  • 失敗時の挙動: 指示の誤解釈、矛盾する情報提供、または出力形式の不遵守があった場合、モデルはエラーコード(例: {"status": "error", "message": "Format violation"})とともに「応答生成失敗」を返すこと。

  • 禁止事項:

    • 訓練データからの直接的な複写や盗用。

    • ハルシネーション(幻覚)の生成、つまり事実に基づかない誤った情報の提供。

    • 不適切(差別的、暴力的、違法行為を助長する)な内容の出力。

    • 個人を特定できる情報(PII)の開示。

    • ユーザーが要求していない追加情報の提供や、余計な対話の開始。

プロンプト設計

LLMの性能を最適化するために、タスクの複雑性や要求される推論レベルに応じて、複数のプロンプト設計アプローチを使い分けます。

ゼロショットプロンプト

少数の例を提示せず、直接タスクを指示します。一般的な知識やシンプルなタスクに適しています。

あなたは質問応答アシスタントです。以下の質問に簡潔に答えてください。
質問: 日本の首都はどこですか?

少数例プロンプト (Few-shot Prompting)

モデルにタスクの形式と期待される出力を示すために、いくつかの入力/出力の例をプロンプト内に含めます [2]。これにより、モデルは指示の意図をより正確に把握し、特定のスタイルや構造で応答を生成する能力が向上します。

あなたは質問応答アシスタントです。以下の例を参考に、質問に答えてください。

質問: 日本の首都はどこですか?
回答: 東京

質問: アメリカ合衆国の首都はどこですか?
回答: ワシントンD.C.

質問: パリの首都はどこですか?
回答:

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

モデルに思考過程のステップを明示的に示させ、複雑な推論を段階的に実行させます。これにより、特に算術推論や常識推論など、論理的なステップを要するタスクで性能が向上することが示されています [4]。Few-shot CoTプロンプトとして、思考プロセスと最終回答の例を提示します。

あなたは質問応答アシスタントです。以下の例を参考に、質問に答える前に思考プロセスを記述してください。

質問: 5 + 3 * 2 の結果を計算してください。
思考:

1. 演算子の優先順位に従い、まず乗算を実行する。3 * 2 = 6。

2. 次に、加算を実行する。5 + 6 = 11。
回答: 11

質問: 10から4を引き、その結果を2で割った答えは何ですか?
思考:

評価

プロンプト設計後のLLMの性能は、体系的な評価を通じて測定されます。

評価シナリオ

  1. 正例: 明確な質問と一般的な知識に基づく回答(例: 「日本の首都はどこですか?」)

  2. 難例: 複数ステップの推論や、曖昧な指示を含む質問(例: 「AさんはBさんの右隣に座り、CさんはAさんの左隣に座っています。Cさんの隣には誰がいますか?」)

  3. コーナーケース: 例外的な情報、否定形、または意図的に誤った前提を含む質問(例: 「存在するが透明で見えない動物の名前を挙げてください。」)

自動評価の擬似コード

LLMの自動評価には、精度、F1スコア、ROUGEのようなメトリックや、正規表現、関数ベースの検証が用いられます [3]。以下にPythonによる擬似コードを示します。

import json
import re

def evaluate_llm_response(prompt_type: str, question: str, expected_answer: str, llm_response: str) -> dict:
    """
    LLMの応答を自動評価する擬似関数。

    Args:
        prompt_type (str): プロンプトの種類 (例: "Few-shot", "Chain-of-Thought").
        question (str): ユーザーからの質問.
        expected_answer (str): 期待される正解.
        llm_response (str): LLMから得られた応答.

    Returns:
        dict: 評価スコア、フィードバック、検出されたエラータイプ.

    計算量 (Big-O):

    - json.loads: 入力文字列の長さに比例 (O(N)).

    - re.search: パターンの長さに比例し、かつ文字列の長さに比例 (O(M*N)).

    - in演算子 (文字列): 部分文字列の長さに比例 (O(M*N)).

    - len: 文字列の長さに比例 (O(N)).
    メモリ条件: 入力文字列とLLMの応答の長さに比例して消費。
                応答が非常に長い場合、メモリ使用量が増加する可能性あり。
    """
    score = 0.0
    feedback = []
    error_type = None

    # 1. 形式評価(JSONの場合)

    if "JSON" in prompt_type:
        try:
            json.loads(llm_response)
            score += 0.2
            feedback.append("出力形式: JSONとして有効 [OK]")
        except json.JSONDecodeError:
            feedback.append("出力形式: JSONとして無効 [NG]")
            error_type = "様式崩れ"
            return {"score": score, "feedback": feedback, "error_type": error_type}

    # 2. 内容評価(厳密一致またはキーワードマッチ)


    # 簡易的な例。実際にはセマンティックな類似性評価 (BERTScore, ROUGE) を用いる [3]

    if expected_answer.lower() in llm_response.lower():
        score += 0.5
        feedback.append("内容一致: 期待されるキーワードが含まれる [OK]")
    else:
        feedback.append(f"内容不一致: 期待 '{expected_answer}', 実際 '{llm_response}' [NG]")

        # コーナーケース「存在するが透明で見えない動物」への対応例

        if "not found" in llm_response.lower() and "存在するが透明で見えない動物" in question:
            score += 0.3 # 質問の意図をある程度理解していると評価
            feedback.append("コーナーケース: 質問の意図をある程度理解 [OK]")
        else:
            error_type = "幻覚または脱線"
            return {"score": score, "feedback": feedback, "error_type": error_type}

    # 3. 制約評価(文字数)

    if 50 <= len(llm_response) <= 200:
        score += 0.1
        feedback.append("文字数: 範囲内 [OK]")
    else:
        feedback.append(f"文字数: 範囲外 ({len(llm_response)}) [NG]")
        error_type = "様式崩れ"
        return {"score": score, "feedback": feedback, "error_type": error_type}

    # 4. CoT評価 (思考プロセスが記述されているか)

    if "Chain-of-Thought" in prompt_type:
        if re.search(r"思考[::]", llm_response): # 「思考:」または「思考:」を検出
            score += 0.2
            feedback.append("CoT: 思考プロセスが含まれる [OK]")
        else:
            feedback.append("CoT: 思考プロセスが含まれない [NG]")
            error_type = "様式崩れ"
            return {"score": score, "feedback": feedback, "error_type": error_type}

    return {"score": min(1.0, score), "feedback": feedback, "error_type": error_type}

# 使用例


# result_few_shot = evaluate_llm_response("Few-shot", "日本の首都はどこですか?", "東京", "東京です。")


# print(result_few_shot)


# result_cot_fail = evaluate_llm_response("Chain-of-Thought", "10から4を引き、その結果を2で割った答えは何ですか?", "3", "答えは3です。")


# print(result_cot_fail)

誤り分析と改良

評価で検出された失敗モードは、プロンプトの改良サイクルにおいて重要な情報源となります。

失敗モードと抑制手法

LLMが失敗する主なモードと、それを抑制するための手法を以下に示します。

  1. 幻覚(Hallucination):

    • 説明: 事実に基づかない、もっともらしいが誤った情報を生成すること。

    • 抑制手法:

      • System指示: プロンプトに「事実のみを述べよ」「不明な場合は『不明』と答えよ」といった明確な指示を含める。

      • 検証ステップ: モデルの出力後に、外部知識源(例: データベース、検索エンジン)でファクトチェックを行う。Retrieval-Augmented Generation (RAG) は、外部知識を取り込むことで幻覚を抑制する有効な手段です [1]。

      • リトライ戦略: 幻覚が検出された場合、異なるプロンプトや温度設定で再生成を試みる。

  2. 様式崩れ(Format Deviation):

    • 説明: 期待されるJSON、Markdown、または特定の出力形式に従わないこと。

    • 抑制手法:

      • System指示: プロンプトの先頭に厳密な出力形式の制約(例: 「常に以下のJSON形式で出力してください: {…}」)を明記する。

      • 少数例プロンプト: 正しい形式の具体例を複数提示し、モデルに学習させる。

      • 検証ステップ: 出力後に正規表現やスキーマバリデーションで形式をチェックし、不適合なら修正またはリトライを指示する。

  3. 脱線(Topic Drift/Off-topic):

    • 説明: ユーザーの質問や指示から逸脱し、無関係な情報を生成すること。

    • 抑制手法:

      • System指示: 「質問にのみ答え、それ以外の情報や対話は不要です」といった制約を明示する。

      • コンテキスト制約: 提供されたコンテキスト情報内でのみ回答するよう指示し、コンテキスト外の知識を使用しないよう制約する。

  4. 禁止事項違反:

    • 説明: 不適切なコンテンツ、個人情報、または企業秘密の漏洩など、定義された禁止事項に違反する出力を生成すること。

    • 抑制手法:

      • System指示: プロンプトの最上位で「ハラスメント、差別、違法行為を助長する内容は絶対に生成しないでください」と強く指示する。

      • 入力フィルタリング: ユーザー入力を事前にチェックし、悪意のあるプロンプトインジェクションを検知・ブロックする。

      • 出力フィルタリング: モデルの出力をリアルタイムで監視し、禁止語句やパターンを検出してマスクまたはブロックする。

改良と再評価のサイクル

プロンプトの改良は、誤り分析の結果に基づいて行われます。例えば、幻覚が多い場合はRAGを導入し外部知識源との連携を強化する [1]、様式崩れが多い場合は、より多くのFew-shot例を追加したり、特定の出力形式を強制するパーサーを後処理で適用したりします。In-Context Learning (ICL) やChain-of-Thought (CoT) のメカニズムに関する理解の深化も、プロンプトの洗練に貢献します [5]。

改良後、同じ評価シナリオと自動評価スクリプトを使用して再評価し、モデル性能の改善度合いを定量的に確認します。この反復的なプロセスにより、プロンプトの品質が継続的に向上し、LLMの信頼性と有用性が最大化されます [6]。

graph TD
    A["要件定義"] -->|タスクと制約| B{"プロンプト設計"};
    B -->|プロンプト入力| C[LLM];
    C -->|モデル出力| D["出力"];
    D -->|出力を検証| E{"評価"};
    E --|失敗モード検出| F["誤り分析"];
    F -->|改良案生成| G["改良"];
    G -->|プロンプト再設計| B;
    E --|成功と信頼性確保| H["デプロイ"];

まとめ

Few-shot PromptingとIn-Context Learningは、LLMが多様なタスクで高い性能を発揮するための重要なプロンプトエンジニアリング技術です。適切なプロンプト設計、厳格な入出力契約の定義、そして評価→誤り分析→改良の反復サイクルを通じて、幻覚や様式崩れといった主要な失敗モードを効果的に抑制し、LLMの信頼性と有用性を最大化できます。特にChain-of-ThoughtプロンプティングやRetrieval-Augmented Generation (RAG) といった高度な手法の活用は、複雑な推論タスクにおけるLLMの能力を一層引き出す鍵となります。継続的なプロンプトの洗練と評価を通じて、LLMの業務適用を成功に導くことが可能です。

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

コメント

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