見出し画像

CVPR23 Best Paper に選ばれた VisProg による Compositional Reasoning を用いた地理空間情報への応用

  • テーマ

    • 大規模言語モデル × 地理空間情報

  • 内容

    • VisProg をはじめとする compositional reasoning の紹介

    • Compositional reasoning における地理空間情報への応用

  • 読者想定

    • ChatGPT などの LLM 製品開発に興味がある人

    • 自然言語以外の自律駆動エージェントの開発に興味がある人

    • 地理空間情報の開発に従事している人

  • おことわり

    • 本記事で述べる所感はあくまで著者によるものです。

    • 著者は地理空間情報などの分野に詳しくないため、的外れなことを述べている可能性があります。

00. 背景

VisProg が CVPR+'23 Best Paper に選ばれた様子
  • 著者の私的な開発経緯

    • Skyland Ventures の Generative AI 特化インキュベーション「荒波〜ARANAMI〜」2nd に参加していたが、論文執筆期間と重なって脱落してしまったため消化不良となった製品アイデアを成仏させる。

  • なぜ地理空間情報か?

    • (社会的な現状)生成 AI を用いた製品の多くは、自然言語や音声に基づくテキスト情報がトリガーとなっている。

    • (個人的な興味)センサによる視覚情報の変化、GPS による地理空間情報の変化、タスク残項目によるステータスの変化、時間変化など、自然言語に限定されないトリガーを扱う製品を開発したかった。

    • (地理空間情報の理由)技術的背景から。地理空間情報に基づくタスク(GeoCode、旅行計画、経路探索、コロプレスマップの表示など)の分解要素が、タスク間である程度共有されているのではと思ったことから、compositional reasoning と親和性が高いと感じたため。

01. Compositional Reasoning

  • ReAct, Chain of Thought の課題

    • ボトムアップに逐次処理を行うため、hallucination snowballing の発生や応答速度の低下につながります。

    • モデルによる最終的な予測結果が、Chain of Thought による中間推論過程を反映しないという問題も報告されています(unfaithfulness)。

  • LLM 文脈における Compositional Reasoning とは

    • 与えられた質問に対して特定の構成要素ごとに分割処理を行う推論方法で、 Codex を用いたプログラム推論や Decomposed Prompt による中間推論の生成などが該当します。

      • (追記)^ の Compositional Reasoning については説明不足です。LLM を用いた Question Answering や Visual Question Answering のような文脈では「質問文を特定の推論過程に分解する」という意味で Compositional Reasoning という表現が使われているように思います。一方で SemEval2014 Task1 - Sentences Involving Compositional Knowledge (Marelli+'14) のようなタスクでは「文の意味が、単語の意味と文の構造に従って構成的に理解される」点に焦点を置いています。

    • なお以下では compositional reasoning の長所・短所を紹介しますが、ReAct のような手法は柔軟性に長けている等のメリットもあるため、状況によって正しく使い分けることが必要であるというのが著者の結論になります。

Table.1 プロンプト手法における比較 (Sun+'23 - PEARL)
プログラム・ツールベース推論の比較における個人的な所感(適当)
  • 長所

    • 特に 計画を伴うプログラムベースの推論 ReAct に対して優れていると感じる点は「計画と実行の役割分担が明確でありトップダウン推論が可能であること」かなと思います。

    • ReAct のように時刻 t-1 の行動結果によって時刻 t の行動が決定するボトムアップ型の推論とは異なり、プログラムベースの推論では一度プログラムが生成されると、reflection が発生しない限りプログラムを実行すれば解答が得られるため、中間推論過程の追加によって応答速度が蓄積されることはありません(i.e. プログラムを実行したら一時中断して関数を追加することがない)。

    • またプログラムベースの推論ではありませんが Plan-and-Solve Prompting (Wang+'23, ACL) では、"Let's first understand the problem and devise a plan to solve the problem. Then let's carry out the plan and solve the problem step by step." のような計画立案を誘発させるプロンプトを用いることで、Chain of Thought に対して 計算や推論過程のエラーを軽減 させています。

Table.6 - GSM8K における 100 件の予測誤りに対するエラー分析
(Sun+'23 - Plan-and-Solve Prompting)
  • 短所

    • Plan-and-Solve Prompting における上表からも分かるように semantic misunderstanding による予測誤り については解決が難しい場合があります。

    • 中間推論過程としてのプログラムをプログラマが記述することになれば 初期コストとしての負担 が増えてしまいます。これに対する解決策としては、Sun+'23 (PEARL) で提案されている action mining のような人手で作成した関数シードを用いて LLM が関数を生成するような工夫が考えられます。また Paranjape+'23 (ART) のような tool library を用いた設計も有効であると考えられます。

    • プロンプトの設計次第で プログラムの生成誤り も発生します。実際に下で紹介する VisProg では GQA タスクの予測誤りにおいて 15.7% 程度のプログラム誤りが発生しています。同論文では few-shot の事例を工夫するとこの問題が軽減されることが示唆されていますが、タスクにおける stakes によっては泥臭い努力が必要だと思います。

Gupta+'23 - Visual Programming: Compositional Visual Reasoning Without Training (CVPR)

CVPR Open Access (CVF)
GitHub
Visual Reasoning and Compositional Question Answering [ipynb]
Natural Language Visual Reasoning [ipynb]
Outside Knowledge Object Tagging [ipynb]
Natural Language Image Editing [ipynb]

VisProg (Gupta+'23, CVPR) の解説資料

 上記の論文では、視覚処理を伴う推論タスクにおいて ① GPT-3 による疑似プログラム生成 ② 生成内容を解釈して Python によるプログラムを実行する  VisProg という手法が提案されています。
 先行研究によるマルチモーダルな事前学習済みモデルを使用した VQA とは異なり、中間推論をプログラムとして生成するため、第三者にも解釈しやすい形式となるのが特徴です。その一方で、視覚処理の性能が事前に定義されたモジュールに依存するため、(定義されていない)外部知識を伴うような視覚推論や遅い思考などを行うためには別のモジュールを定義する必要があります。

02. VisProg を地理空間情報に応用(GeoProg)

旅行計画などの地理空間情報に基づくタスクの構成要素がタスク間である程度共通していることから、VisProg を地理空間情報に関連したタスクに応用できないかと考え、以下のような WEB アプリケーション(フロントエンド: React+TypeScript, バックエンド: FastAPI)を作成しました(以下、GeoProg)。

以降ではアプリケーションの簡単な概要と考察、課題・展望について述べたいと思います。

作成した MVP のメイン画面

(概要) どんなアプリケーションか?

  • GeoProg は旅行計画のアシスタントや日常的なマップ情報の取得、複数経路の探索など目的タスクとしています。

    • (入力)自然言語、GPS による地理空間情報など

    • (推論)VisProg によるプログラム生成 + 実行

    • (出力)ユーザ質問に対する応答、旅行計画、地図上の位置情報など

  • なお現在のステータスは開発中であり、以下では目的タスクの一つである日常的なマップ情報の取得 を対象に、GeoCoding や質問応答などについて紹介します。

GeoProg アプリケーションの概要図

GeoProg では (1) GPT-3 によるプログラム生成 (2) 生成されたプログラムの実行を行います。

プログラム生成器に与える few-shot の事例は以下のように定義します。
生成時はこれらの事例を動的に検索し GPT-3 に与えています。

# -*- coding: utf-8 -*-
# prompt.py
# 一部省略

EXAMPLES = [
"""Question: 東京タワーはどこにありますか?
Program:
SLOT=PARSE_REQUEST(request="東京タワーはどこにありますか?", query="東京タワー")
SPOT_LIST=GEOCODE(to=TO)
""",
"""Question: 日本で一番大きい山
Program:
SLOT=PARSE_REQUEST(request="日本で二番目に大きい湖", query="一番大きい 山", to="日本")
PAGE_LIST=SEARCH_GOOGLE_WEB(query=QUERY, to=TO)
LOCATION_LIST=CRAWLER(pages=PAGE_LIST, query=QUERY, to=TO)
SPOT_LIST=GEOCODE(query=LOCATION_LIST)
""",
]

またプログラム実行時に実際に呼び出される関数(クラスメソッド)は以下のように事前に定義しています。

# -*- coding: utf-8 -*-
# search_google_web.py
# 一部省略

from serpapi import GoogleSearch
from loguru import logger

from src.geoprog.program import Program
from src.geoprog.utils.parse import parse_step
from src.geoprog.dtypes import WebPage


class SearchGoogleWebInterpreter():
    step_fn = "SEARCH_GOOGLE_WEB"

    def __init__(self, siterestrict=False):
        logger.debug(f'Registering {self.step_fn} step')

    def _request(self, query:str, ll:str=None):
        return {
            "engine": "google",
            "q": query,
            "gl": "jp",
            "hl": "ja",
            "lr": "lang_ja",
            # "tbm": "nws"
            "google_domain": "google.com",
            "num": 3,
            "type": "search",
            "api_key": os.environ["SERP_API_KEY"],
        }

    def search(self, request:dict) -> List[WebPage]:
        """ SerpAPI の実行 """
        try:
            response = GoogleSearch(request)
            response_dict = response.get_dict()
            return [WebPage(
                title=r.get("title"),
                url=r.get("link"),
                snippet=r.get("snippet"),
                source=r.get("source"),
                breadcrumbs=r.get("displayed_link"),
                date=r.get("date"),
            ) for r in response_dict["organic_results"]]
        except Exception as e:
            logger.error(str(e))
            return []

    def parse(self, prog_step:Program):
        """
        >>> prog_step.prog_str
        PAGE_LIST=SEARCH_GOOGLE_WEB(query=QUERY, to=TO)
        >>> p
        {
           "output_var": "PAGE_LIST",
           "step_fn": "SEARCH_GOOGLE_WEB",
           "kwargs": {"query": "QUERY", "to": "TO"}
        }
        >>> prog_step.state
        {
           "QUERY": "スラムダンクの聖地一覧",
           "TO": "江ノ島",
           "GPS": {"lat": 35.18, "lng": 139.28},
        }
        """
        p = parse_step(prog_step.prog_str)
        output_var, step_fn, kwargs = p["output_var"], p["step_fn"], p["kwargs"]
        assert step_fn == self.step_fn
        query_var = kwargs["query"]
        query_val = prog_step.state[query_var]
        to_var = kwargs.get("to", "")
        to_val = prog_step.state.get(to_var, "")
        return query_val, to_val, output_var

    def execute(self, program:Program, inspect=False) -> List[WebPage]:
        """ 実行時の main 箇所 """
        query, to, output_var = self.parse(program)
        request = self._request(to + " " + query)
        response: List[WebPage] = self.search(request)
        program.state[output_var] = response
        return response

その他、コードの詳細については GitHub を参照してください。

(考察) そもそも地理空間情報に基づくタスクは共通の構成要素に分解することができるのか?

  • 開発段階では旅行計画などの地理空間情報に基づくタスクの構成要素がタスク間である程度共通しているので簡単に作成できると思っていましたが、実際に開発していると検索フィルタの条件や SerpApi の呼び出し先などの選択肢の幅がとにかく広く、優先事項を付けるのが特に大変だと感じました(半ば諦め)。

  • 例えば「日常的なマップ情報の取得」においては、以下のようにある程度カテゴライズ可能な質問集が想定されます。GeoProg ではカテゴライズされた 太字 の機能を持つ関数を作成します:

    • e.g. テキストから地名、期間・人数等の条件を特定

      • 今週末、仙台に旅行に行きます

      • 家族連れで 18:00 から予約可能なレストラン

      • 富士山が見えるキャンプ場

    • e.g. 地名 ⇄ 緯度経度 の変換

      • 東京タワーはどこにありますか?

      • 近くでおすすめの穴場カフェはどこ?

    • e.g. 地名等を問う質問に対して検索を伴った応答生成

      • 日本で 2 番目に大きい湖はどこ?

      • 神奈川県内で 8 月に行われる花火大会の場所一覧

      • 栃木の観光・名産品マップを作って

    • e.g. 2 点間の経路・乗り換え情報の提示

      • 一番近いスターバックスはどちらですか?

      • ここから 2 時間以内で行ける動物園

    • e.g. 複数間の最適な経路探索など複雑な問題

      • 10:00~20:00 京都駅発着で、金・銀閣寺を含めた旅行プラン

      • ディズニーで複数のミッキーが対面しないための移動プラン

(具体例) Google Map では対応していない応答 🍒

以下では実際の応答例の一部を表示しています。

🙆‍♀️ 首都圏の八月に行われる花火大会
(検索内容も別画面で表示)
🙆‍♀️ ぼっち・ざ・ろっくの聖地
🙆‍♀️ 金沢八景でアニメ舞台となった場所
(アニメ作品も別画面で表示している)
🙆‍♀️ 次リンクの観光地情報をマッピング: https://aumo.jp/articles/28313
🙅‍♂️ 日本で面積が上位 10 件に入る湖
(検索誤り)

(課題) 応答速度とモジュール依存の精度問題

  • 条件付きの geocoding に関して言えば、結局 Google Map が便利だなと感じました。UX デザインが優れていることに加え、特に応答速度が 1~2 sec と速すぎるので「近くの美味しいレストラン」のような簡単な検索については Google Map で十二分かなと思いました。

    • GeoProg では LLM が介入するため応答速度が著しく低下してしまいます(ユーザが離脱したくなる)。特にプログラム生成では 6~15秒程度、WEB 検索からのスクレイピングにはページあたり 15 秒程度かかってしまうので、Google Map のようなインターフェースと同様に考えるのは厳しい気持ちになります(関連情報の提示などの待ち時間に対する工夫が必要)。

  • 一方で GeoProg だからこそ実現可能な機能もあると思っていて、様々な API との連携や、関数処理、GPT の内部知識の利用などが強みになるかと思います。

(展望) API 連携、UI、自律駆動、対話型 AI アシスタント

  • 今回作成した GeoProg では Google が提供する API のみを利用しましたが、ホットペッパー グルメサーチ API や Rakuten Webservice などを利用することで、機能の幅を増やすことができます。

  • インターフェースの改善はキリがありませんが、Google マイマップ にインポート可能なデータを UI からダウンロードできたりすると、ユーザと GeoProg の役割分担が明確になり、GeoProg が情報提供の役割に限定できるかなと思います。

  • GPS 情報や時間情報に基づいて、適当な時刻にランチを紹介してくれたり、旅行先で歴史や名産品情報などをクロールしておくことで応答速度を改善したりできると嬉しい。

  • また旅行計画等については一度きりの応答では解決しない場合が多いため、対話形式に共同で作成できるような枠組みが必要だと考えます。function calling 等の利用も考えていきたいです。

Lin+'23 - Decision-Oriented Dialogue for Human–AI Collaboration
https://collaborative-dialogue.github.io/

03. 参考

(関連研究) プログラムを用いた視覚タスク

(関連研究) 質問の分解を伴うプロンプト手法

(関連研究) 計画を伴うトップダウンの推論

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