AmbigNLG: チュートリアル

AmbigNLG は、NLG の指示における曖昧な仕様を特定し、それを改善することで出力品質を向上させる手法 です。本チュートリアルでは、AmbigNLG を活用する方法について解説します。このチュートリアルでは、以下のステップを順に説明します。 ・指示の中に含まれる曖昧な要素を特定する ・曖昧な指示を明確化し、より効果的なテキスト生成を実現する ・指示の明確化の有無による出力テキストの比較を行う ・インタラクティブな曖昧性軽減を活用し、下流タスクの改善を図る

AmbigNLGは、NLG の指示における曖昧な仕様を特定し、それを改善することで出力品質を向上させる手法 です。本チュートリアルでは、AmbigNLG を活用する方法について解説します。
このチュートリアルでは、以下のステップを順に説明します。

  • 指示の中に含まれる曖昧な要素を特定する
  • 曖昧な指示を明確化し、より効果的なテキスト生成を実現する
  • 指示の明確化の有無による出力テキストの比較を行う
  • インタラクティブな曖昧性軽減を活用し、下流タスクの改善を図る

本チュートリアルでは、AmbigNLG の論文で実施された実験をそのまま再現するのではなく、大規模言語モデル(LLM)のベストプラクティスを取り入れ、曖昧性の軽減をインタラクティブに実装する方法 を紹介します。AmbigNLG の概念をどのように活用し、自身のアプリケーションに適用できるかを学んでいきましょう。

はじめに

AmbigNLG は、自然言語生成(NLG)タスクにおける指示の曖昧性に対処するために設計された手法です。本チュートリアルでは、AmbigNLG を活用して 指示の明確性を向上させ、複雑な NLG タスクにおける LLM のパフォーマンスを最適化する方法を紹介します。

曖昧な指示は、出力品質に大きな影響を与え、LLM がユーザーの意図に合致しない多様な応答を生成する原因となります。AmbigNLG は、曖昧性を体系的に検出し、それを解決するアプローチを提供することで、出力のばらつきを抑え、より一貫性のある結果を得ることを目的としています。

さあ、始めましょう!

セットアップ

まず、必要な環境をセットアップし、必要な依存関係をインストールします。本チュートリアルでは、OpenAI API を使用します。

				
					# Install dependencies
!pip install openai py-rouge jinja2 > /dev/null

import openai

api_key = "your-openai-api_key"
# or
import os
api_key = os.getenv("OPENAI_API_KEY")
# initiate openai client
client = openai.OpenAI(api_key=api_key)
				
			

曖昧な指示によるテキスト生成

ここでは、一般的な自然言語生成(NLG)タスクである 記事の要約 を例に、指示の曖昧性がどのように出力に影響を与えるのかを探ります。

タスクの目的:記事の重要な情報を捉えた 「寿司に関する記事の要約」 を生成する。

初期の指示:「この記事を要約してください!」

一見すると明確に思えるこの指示も、以下のような曖昧な点を含んでいます。

  • 要約の長さはどの程度にすべきか?
  • 歴史、調理方法、文化的意義のどれに焦点を当てるべきか?
  • 学術的、カジュアル、技術的なスタイルのどれを採用するべきか?

次に、この曖昧な指示が出力にどのような影響を与えるかを見ていきましょう。

				
					import requests
from bs4 import BeautifulSoup

def generate_text(instruction: str, input_text: str, model: str = "gpt-4o-mini-2024-07-18"):
    prompt = f"""
Below is an input text that provides further context, paired with an instruction that describes a task. Provide a direct response that appropriately completes the request without additional explanations or details.
# Input text:
{input_text}

# Instruction:
{instruction}

# Response:
"""

    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        n=10,
        top_p=0.9
    )
    return [choice.message.content for choice in response.choices]


sample_url = "https://en.wikipedia.org/wiki/Sushi"
sample_instruction = "Summarize this article!"
sample_input = BeautifulSoup(requests.get(sample_url).text).get_text(strip=True)
sample_outputs = generate_text(sample_instruction, sample_input)
				
			

サンプル出力 1:

寿司は、日本の伝統的な料理であり、酢飯にさまざまな具材(主に生または加熱した海産物)を組み合わせたものです。その起源は、発酵させた魚料理である「なれずし」などの古代の形態に遡ることができ、江戸時代に誕生した握り寿司をはじめ、さまざまなスタイルへと発展しました。

寿司には、ちらし寿司(散らし寿司)、いなり寿司(酢飯を詰めた豆腐の袋)、巻き寿司(海苔で巻いた寿司)など、多様な形態があります。この料理は世界的な人気を博し、カリフォルニアロールなどの西洋風のアレンジやフュージョンスタイルが生まれました。

寿司は一般的に、醤油、わさび、ガリ(生姜の甘酢漬け)とともに提供されます。栄養価が高いとされる一方で、生魚の摂取に伴う寄生虫や水銀汚染などの健康リスクが懸念される場合もあります。寿司の盛り付けはシンプルな皿に美しく配置されることが多く、日本の食文化の中心的な要素となっています。

サンプル出力 2:

寿司は、日本の伝統的な料理であり、酢飯にさまざまな具材を組み合わせたものです。一般的には海産物や野菜、時には肉が使用されます。その歴史は、発酵した魚と米を用いた古代の保存技術に遡り、「なれずし」「生なれ」「早ずし」などの異なるスタイルへと発展してきました。

現在の形である握り寿司は、江戸時代に料理人・華屋与兵衛によって広められました。寿司は、「ちらし寿司(散らし寿司)」「いなり寿司(豆腐の袋に詰めた寿司)」「巻き寿司(海苔で巻いた寿司)」「握り寿司(手で握る寿司)」など、さまざまな種類に分類されます。

寿司は一般的に、わさびや醤油とともに提供され、世界中で楽しまれており、カリフォルニアロールのような西洋風のアレンジも登場しています。栄養面では、寿司は脂肪が少なくタンパク質が豊富な食品ですが、生魚の摂取による寄生虫や水銀汚染などの健康リスクがあると指摘されています。

また、寿司の盛り付けや食事作法は、食文化としての重要な要素のひとつとなっています。

サンプル出力 1 は、寿司の国際的なアレンジや健康面 に重点を置いています。一方、サンプル出力 2 は 歴史的な発展や文化的な側面 により深く踏み込んでいます。
このような出力の違いは、指示の曖昧さによって生じます。要約の長さ、焦点、スタイルに関する具体的な指示が不足しているため、モデルは異なる観点を優先する結果となります。

曖昧性の分類を理解する

現実世界のテキスト指示を綿密に評価した結果、NLG における曖昧性の分類(アンビギュイティ・タクソノミー) を導入しました。この分類は、主に 6 つのカテゴリに分けられます。

分類 概要
コンテクスト
指示に タスク完了に必要な背景情報や外部知識が欠けている場合 に選択します。 この曖昧性を解消することで、タスクに必要なコンテキストが明確になり、適切な出力が得られます。
キーワード
出力テキストに含めるべき特定のキーワードが指示に明示されていない場合 に選択します。 この曖昧性を解消することで、必要なキーワードが確実に出力に反映され、より意図に沿ったテキストが生成されます。
長さ
出力の長さ(単語数や文の数)に関する具体的な指示がない場合 に選択します。 この曖昧性を解消することで、適切な長さの出力が得られ、指示の意図に沿ったテキスト生成が可能になります。
構成
出力テキストの内容構成に関する指示が不足している場合 に選択します。 この曖昧性を解消することで、求められる構造を持った出力が生成され、意図した情報の整理や提示が可能になります。
文体
出力テキストのスタイル(学術的、カジュアル、技術的など)に関する指示が欠けている場合 に選択します。 この曖昧性を解消することで、出力が意図したスタイルに適合し、一貫性のあるテキストが生成されます。
テーマ
出力テキストで扱うべき具体的なテーマが明確に定義されていない場合 に選択します。 この曖昧性を解消することで、出力の方向性が明確になり、意図に沿ったテキストが生成されます。

この曖昧性の定義に基づき、指示内の曖昧性の種類を特定し、それを軽減することが可能です。

曖昧性の特定

LLM を活用することで、指示内に存在する曖昧性のカテゴリを識別 できます。

				
					from pydantic import BaseModel
from enum import Enum

class AmbiguityCategory(str, Enum):
    context = "Context"
    keywords = "Keywords"
    length = "Length"
    planning = "Planning"
    style = "Style"
    theme = "Theme"
    none = "None"

class IdentifiedAmbiguities(BaseModel):
    ambiguity_categories: list[AmbiguityCategory]

def identify_ambiguity(instruction: str, input_text: str, model: str = "gpt-4o-mini-2024-07-18"):
    prompt = f"""Your task involves identifying the category of ambiguity in the given instruction to generate output text from the given input text. Ambiguity in instruction means that there are several possible output texts from the single input text. On the other hand, when the ambiguity is clarified, the task becomes straightforward, leading to a nearly single output.
Here are the available categories: Context, Keywords, Length, Planning, Style, Theme.
* Context: Choose this category if the instruction lacks the required context information, such as background or external knowledge crucial for task completion. Resolving this ambiguity will provide the crucial context for the task.
* Keywords: Select this category if the instruction does not mention specific keywords to be used in the output text. Resolving this ambiguity will ensure that the necessary keywords are incorporated in the output.
* Length: Opt for this category if the instruction does not provide specifics about the desired length of the output, whether in terms of words or sentences. Clearing this ambiguity will lead to a more precise length output.
* Planning: Select this category if the instructions don't provide guidance on content planning for the output document. Resolving this ambiguity will result in the desired structured output.
* Style: Choose this category if the instruction does not specify the style of the output text. Clearing this ambiguity will ensure that the output aligns with the desired style.
* Theme: Choose this category if the instruction does not clearly define the specific theme to be discussed in the output text. Clearing this ambiguity will provide a clear direction for the output.
* None: Choose this category if none of the above apply.

# Input Text:
{input_text}

# Instruction:
{instruction}

# Response:"""
    response = client.beta.chat.completions.parse(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        temperature=0.,
        response_format=IdentifiedAmbiguities
    )

    return [cat.value for cat in response.choices[0].message.parsed.ambiguity_categories]

ambiguities = identify_ambiguity(sample_instruction, sample_input)
print("Identified Ambiguities:", ambiguities)
Identified Ambiguities: ['Length', 'Context', 'Theme']
				
			

LLM による分析の結果、指示内には 「長さ(Length)」「文体(Style)」「テーマ(Theme)」 に関する曖昧性があることが特定されました。これにより、どの要素が不足しているのかを明確に理解し、より具体的な指示へと改善することが可能になります。

指示の改善

特定された曖昧性に基づき、追加の指示を生成し、指示を明確化しました。

長さの明確化: 「100語で要約してください。」
文体の指定: 「フォーマルな文体で書いてください。」
テーマの絞り込み: 「寿司の国際的なアレンジと健康面に焦点を当ててください。」

このように具体的な要件を追加することで、より正確で意図に沿った指示が作成できます。ユーザーは、生成された選択肢から適切なものを選択したり、自分でカスタマイズすることも可能です。

				
					from jinja2 import Template


class AdditionalInstructions(BaseModel):
    category: AmbiguityCategory
    additional_instructions: list[str]

def generate_additional_instructions(instruction: str, input_text: str, ambiguity_category: str, model: str = "gpt-4o-mini-2024-07-18"):
    template = Template("""To resolve the specified ambiguity in the instruction, provide multiple additional instructions as the infilled templates. Each additional instruction strictly adheres to the template format.
Ensure this added information aligns with the primary objective of the task, supports understanding of complex concepts, or aids in narrowing down the scope to generate more precise responses.

# Input Text:
{{ input_text }}

# Instruction:
{{ instruction }}

# Ambiguity to Resolve:
{{ ambiguity_category }}

# Template to Infill:
{% if ambiguity_category == 'Context' %}Additional context: <paragraph>
{% elif ambiguity_category == 'Keywords' %}Include <keywords> in your response.
{% elif ambiguity_category == 'Length' %}Answer with <number> words.
{% elif ambiguity_category == 'Planning' %}Please generate the output based on the following outline: 1. <topic1> 2. <topic2> ...
{% elif ambiguity_category == 'Style' %}Write in a <style> style.
{% elif ambiguity_category == 'Theme' %}Primarily discuss the following theme: <theme>
{% else %}No template found for this category.
{% endif %}""")

    prompt = template.render(
        input_text=input_text,
        instruction=instruction,
        ambiguity_category=ambiguity_category
    )
    response = client.beta.chat.completions.parse(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        temperature=0,
        response_format=AdditionalInstructions,
    )

    return response.choices[0].message.parsed.additional_instructions


def get_user_selected_instruction(candidates: list[str]):
    print("Generated candidate instructions:")
    for idx, candidate in enumerate(candidates, start=1):
        print(f"{idx}: {candidate}")

    user_input = input(f"\nSelect an instruction (1-{len(candidates)}), or type 'skip' to skip, or provide your own: ")

    if user_input.isdigit():
        selection = int(user_input)
        if 1 <= selection <= len(candidates):
            print(f"Selected instruction: {candidates[selection - 1]}")
            return candidates[selection - 1]
    elif user_input.lower() == 'skip':
        print("Skipping instruction selection.")
        return None
    else:
        print(f"Custom instruction provided: {user_input}")
        return user_input

    return None


def refine_instructions(sample_instruction: str, sample_input: str, ambiguities: list[str]):
    additional_instructions = []

    for category in ambiguities:
        category = category.strip()
        print(f"\nSuggested additional instructions for {category}:")

        candidates = generate_additional_instructions(sample_instruction, sample_input, category)
        selected_instruction = get_user_selected_instruction(candidates)

        if selected_instruction:
            additional_instructions.append(selected_instruction)

    refined_instruction = "\n".join([sample_instruction] + additional_instructions)

    return refined_instruction

refined_instruction = refine_instructions(sample_instruction, sample_input, ambiguities)
print("\nFinal Refined Instruction:\n")
print(f"```{refined_instruction}```")

Suggested additional instructions for Length:
Generated candidate instructions:
1: Answer with 100 words.
2: Answer with 200 words.
3: Answer with 300 words.
Selected instruction: Answer with 100 words.

Suggested additional instructions for Context:
Generated candidate instructions:
1: Provide a brief overview of the history of sushi, highlighting its evolution from narezushi to modern forms like nigirizushi and conveyor belt sushi.
2: Summarize the different types of sushi mentioned in the article, including chirashizushi, inarizushi, makizushi, and their regional variations.
3: Explain the significance of sushi in Japanese culture, including its traditional preparation methods and etiquette associated with eating sushi.
Selected instruction: Explain the significance of sushi in Japanese culture, including its traditional preparation methods and etiquette associated with eating sushi.

Suggested additional instructions for Theme:
Generated candidate instructions:
1: Primarily discuss the following theme: The historical evolution of sushi from its origins to modern variations.
2: Primarily discuss the following theme: The different types of sushi and their unique characteristics.
3: Primarily discuss the following theme: The cultural significance of sushi in Japanese cuisine and its global influence.
4: Primarily discuss the following theme: The ingredients used in sushi and their preparation methods.
Skipping instruction selection.

Final Refined Instruction:

```Summarize this article!
Answer with 100 words.
Explain the significance of sushi in Japanese culture, including its traditional preparation methods and etiquette associated with eating sushi.```
				
			

指示の改善効果の評価

明確化された指示を使用することで、より焦点の合った出力を生成できます。

				
					refined_outputs = generate_text(refined_instruction, sample_input)

print("Refined Output:", refined_outputs[0])
				
			

改善後の出力:

寿司は、酢飯にさまざまな具材を組み合わせた日本の伝統的な料理であり、主に生または加熱した海産物が使用されます。その発展には、地域ごとのバリエーションや歴史的な影響が反映されており、握り寿司や巻き寿司といったスタイルが含まれます。寿司の調理では、米と魚の品質が重要視され、何世代にもわたって技術が洗練されてきました。一般的に寿司は手で食べることが推奨され、味の調和が重視されます。その文化的な意義は、日本の料理技術と四季折々の食材を表現する点にあり、寿司は単なる食事ではなく、伝統と職人技の象徴でもあります。

寿司の調理では、米と魚の品質が重要視され、何世代にもわたって技術が洗練されてきました。一般的に寿司は手で食べることが推奨され、味の調和が重視されます。その文化的な意義は、日本の料理技術と四季折々の食材を表現する点にあり、寿司は単なる食事ではなく、伝統と職人技の象徴でもあります。

改善後の出力は、より焦点が定まり、ユーザーの期待により近いものとなりました。長さ、文体、テーマを明確に指定することで、生成されるテキストの精度が向上し、意図に沿った出力が得られます。
従来の出力では、寿司の国際的なアレンジや健康面といったさまざまな話題が含まれていましたが、改善後の出力は、日本の料理文化における寿司の意義やその世界的な影響に的を絞っています。この結果、フォーマルな文体で書かれ、100語の範囲内に収められた、より一貫性のある適切な応答が得られました。

指示の改善が出力に与える影響を数値化するために、元の指示(sample_instruction)と改善後の指示(refined_instruction)による出力の多様性を比較します。
もし改善後の指示によって出力の多様性が減少していれば、それは指示がより明確かつ具体的になり、出力の幅が狭まり、より焦点の合った結果が得られていることを意味します。

				
					from itertools import combinations
import rouge

evaluator = rouge.Rouge(metrics=["rouge-l"], limit_length=False, apply_avg=True, stemming=True,)
sample_score = evaluator.get_scores(*zip(*combinations(sample_outputs, 2)))["rouge-l"]["f"]
refined_score = evaluator.get_scores(*zip(*combinations(refined_outputs, 2)))["rouge-l"]["f"]
print("Diversity Score (Original):", sample_score)
print("Diversity Score (Refined):", refined_score)

Diversity Score (Original): 0.43312789543767966
Diversity Score (Refined): 0.38057679080136764
				
			

元の出力の多様性スコア(sample_score)は、改善後の出力の多様性スコア(refined_score)よりも有意に低いことが確認されました。改善後の出力において多様性スコアが低下していることは、曖昧性が軽減され、より一貫性のある応答が生成されている ことを示しています。

まとめ

本チュートリアルでは、AmbigNLGを活用して指示の曖昧性を特定し、それを改善することで、LLM の出力の精度と一貫性を向上させる方法 を紹介しました。

これらの手法を適用することで、指示の具体性を高め、下流タスクにおける生成コンテンツの品質と一貫性を向上させることが可能になります。

詳細については、EMNLP 2024に採択されたAmbigNLG の論文GitHub リポジトリをご参照ください。

執筆者:Hayate Iso、Megagon Labs

[原文へ – 2024/11/07]

(翻訳:Megagon Labs 東京オフィス)

 

この記事をシェアする
1 Min Read
February 5, 2025
MCRankベンチマークとEXSIR手法を用いることで、構造化された推論によりLLMの性能がこれらの難解なタスクで大幅に向上することを示しました。
1 Min Read
December 16, 2024
複合AIシステムの最適化フレームワークは、精度・コスト・遅延などの多目的最適化や複数プランの最適化、特に予算などの制約管理を含む幅広い目標を達成すべきであると述べています。これらの最適化目標は決して網羅的ではありませんが、エンタープライズ環境において重要な要素です。
2 Min Read
July 31, 2024
MEGAnnoは、大規模言語モデル(LLM)の力と人間の専門知識を組み合わせたデータアノテーションフレームワークで、データラベリングの効率化と精度向上を実現します。本記事では、MEGAnnoの機能を詳しく紹介し、具体的なコードスニペットとともに解説します。